From 0a0e75983042b1fa8631fe2023537be106ced2a3 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 19 Jun 2019 09:40:08 -0400 Subject: [PATCH 01/68] Add line/col ending to parser --- README.md | 6 + gen/astnodes.js | 446 +++++++++-- gen/parse_tables.js | 1535 +++++++++++++++++++------------------- src/pgen/ast/Python.asdl | 8 +- src/pgen/ast/asdl_js.py | 4 +- 5 files changed, 1165 insertions(+), 834 deletions(-) diff --git a/README.md b/README.md index 24507a8e1b..afd6e5a462 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +This is a BlockPy fork of the Skulpt project. We add a few features: + +* AST parser now captures end of line/column information +* MatPlotLib module +* Exec support + # Welcome to Skulpt [![Join the chat at https://gitter.im/skulpt/skulpt](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/skulpt/skulpt?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) diff --git a/gen/astnodes.js b/gen/astnodes.js index 556bd13068..b815858a60 100644 --- a/gen/astnodes.js +++ b/gen/astnodes.js @@ -136,10 +136,14 @@ Sk.astnodes.FunctionDef = function FunctionDef(/* {identifier} */ name, /* {expr_ty} */ returns, /* {string} */ docstring, /* {int} */ lineno, /* {int} - */ col_offset) + */ col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.name = name; this.args = args; this.body = body; @@ -148,6 +152,8 @@ Sk.astnodes.FunctionDef = function FunctionDef(/* {identifier} */ name, /* this.docstring = docstring; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -166,10 +172,16 @@ Sk.astnodes.AsyncFunctionDef = function AsyncFunctionDef(/* {identifier} */ docstring, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* + {int} */ + endlineno, /* + {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.name = name; this.args = args; this.body = body; @@ -178,6 +190,8 @@ Sk.astnodes.AsyncFunctionDef = function AsyncFunctionDef(/* {identifier} */ this.docstring = docstring; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -188,10 +202,14 @@ Sk.astnodes.ClassDef = function ClassDef(/* {identifier} */ name, /* {asdl_seq body, /* {asdl_seq *} */ decorator_list, /* {string} */ docstring, /* {int} */ lineno, /* - {int} */ col_offset) + {int} */ col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.name = name; this.bases = bases; this.keywords = keywords; @@ -200,44 +218,62 @@ Sk.astnodes.ClassDef = function ClassDef(/* {identifier} */ name, /* {asdl_seq this.docstring = docstring; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Return = function Return(/* {expr_ty} */ value, /* {int} */ lineno, - /* {int} */ col_offset) + /* {int} */ col_offset, /* {int} */ + endlineno, /* {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Delete = function Delete(/* {asdl_seq *} */ targets, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, /* + {int} */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.targets = targets; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Assign = function Assign(/* {asdl_seq *} */ targets, /* {expr_ty} */ value, /* {int} */ lineno, /* - {int} */ col_offset) + {int} */ col_offset, /* {int} */ + endlineno, /* {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.targets = targets; this.value = value; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -245,15 +281,21 @@ Sk.astnodes.Assign = function Assign(/* {asdl_seq *} */ targets, /* {expr_ty} Sk.astnodes.AugAssign = function AugAssign(/* {expr_ty} */ target, /* {operator_ty} */ op, /* {expr_ty} */ value, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, + /* {int} */ endlineno, /* {int} + */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.target = target; this.op = op; this.value = value; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -262,16 +304,22 @@ Sk.astnodes.AnnAssign = function AnnAssign(/* {expr_ty} */ target, /* {expr_ty} */ annotation, /* {expr_ty} */ value, /* {int} */ simple, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.target = target; this.annotation = annotation; this.value = value; this.simple = simple; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -279,16 +327,21 @@ Sk.astnodes.AnnAssign = function AnnAssign(/* {expr_ty} */ target, /* {expr_ty} Sk.astnodes.For = function For(/* {expr_ty} */ target, /* {expr_ty} */ iter, /* {asdl_seq *} */ body, /* {asdl_seq *} */ orelse, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ endlineno, /* {int} + */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.target = target; this.iter = iter; this.body = body; this.orelse = orelse; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -297,60 +350,82 @@ Sk.astnodes.AsyncFor = function AsyncFor(/* {expr_ty} */ target, /* {expr_ty} */ iter, /* {asdl_seq *} */ body, /* {asdl_seq *} */ orelse, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.target = target; this.iter = iter; this.body = body; this.orelse = orelse; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.While = function While(/* {expr_ty} */ test, /* {asdl_seq *} */ body, /* {asdl_seq *} */ orelse, /* - {int} */ lineno, /* {int} */ col_offset) + {int} */ lineno, /* {int} */ + col_offset, /* {int} */ endlineno, /* + {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.test = test; this.body = body; this.orelse = orelse; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.If = function If(/* {expr_ty} */ test, /* {asdl_seq *} */ body, /* {asdl_seq *} */ orelse, /* {int} */ lineno, - /* {int} */ col_offset) + /* {int} */ col_offset, /* {int} */ + endlineno, /* {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.test = test; this.body = body; this.orelse = orelse; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.With = function With(/* {asdl_seq *} */ items, /* {asdl_seq *} */ body, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ endlineno, /* + {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.items = items; this.body = body; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -358,14 +433,20 @@ Sk.astnodes.With = function With(/* {asdl_seq *} */ items, /* {asdl_seq *} */ Sk.astnodes.AsyncWith = function AsyncWith(/* {asdl_seq *} */ items, /* {asdl_seq *} */ body, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.items = items; this.body = body; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -373,16 +454,21 @@ Sk.astnodes.AsyncWith = function AsyncWith(/* {asdl_seq *} */ items, /* Sk.astnodes.Raise = function Raise(/* {expr_ty} */ exc, /* {expr_ty} */ cause, /* {expr_ty} */ inst, /* {expr_ty} */ tback, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ endlineno, /* + {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.exc = exc; this.cause = cause; this.inst = inst; this.tback = tback; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -390,42 +476,58 @@ Sk.astnodes.Raise = function Raise(/* {expr_ty} */ exc, /* {expr_ty} */ cause, Sk.astnodes.Try = function Try(/* {asdl_seq *} */ body, /* {asdl_seq *} */ handlers, /* {asdl_seq *} */ orelse, /* {asdl_seq *} */ finalbody, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, /* {int} */ + endlineno, /* {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.body = body; this.handlers = handlers; this.orelse = orelse; this.finalbody = finalbody; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Assert = function Assert(/* {expr_ty} */ test, /* {expr_ty} */ msg, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ endlineno, /* + {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.test = test; this.msg = msg; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Import = function Import(/* {asdl_seq *} */ names, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, /* + {int} */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.names = names; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -434,234 +536,337 @@ Sk.astnodes.ImportFrom = function ImportFrom(/* {identifier} */ module, /* {asdl_seq *} */ names, /* {int} */ level, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.module = module; this.names = names; this.level = level; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Global = function Global(/* {asdl_seq *} */ names, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, /* + {int} */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.names = names; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Nonlocal = function Nonlocal(/* {asdl_seq *} */ names, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, + /* {int} */ endlineno, /* {int} + */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.names = names; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Expr = function Expr(/* {expr_ty} */ value, /* {int} */ lineno, /* - {int} */ col_offset) + {int} */ col_offset, /* {int} */ + endlineno, /* {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ -Sk.astnodes.Pass = function Pass(/* {int} */ lineno, /* {int} */ col_offset) +Sk.astnodes.Pass = function Pass(/* {int} */ lineno, /* {int} */ col_offset, /* + {int} */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ -Sk.astnodes.Break = function Break(/* {int} */ lineno, /* {int} */ col_offset) +Sk.astnodes.Break = function Break(/* {int} */ lineno, /* {int} */ col_offset, + /* {int} */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Continue = function Continue(/* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Print = function Print(/* {expr_ty} */ dest, /* {asdl_seq *} */ values, /* {int} */ nl, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, /* + {int} */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.dest = dest; this.values = values; this.nl = nl; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Debugger = function Debugger(/* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.BoolOp = function BoolOp(/* {boolop_ty} */ op, /* {asdl_seq *} */ values, /* {int} */ lineno, /* {int} - */ col_offset) + */ col_offset, /* {int} */ endlineno, + /* {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.op = op; this.values = values; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.BinOp = function BinOp(/* {expr_ty} */ left, /* {operator_ty} */ op, /* {expr_ty} */ right, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, /* + {int} */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.left = left; this.op = op; this.right = right; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.UnaryOp = function UnaryOp(/* {unaryop_ty} */ op, /* {expr_ty} */ operand, /* {int} */ lineno, /* - {int} */ col_offset) + {int} */ col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.op = op; this.operand = operand; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Lambda = function Lambda(/* {arguments__ty} */ args, /* {expr_ty} */ body, /* {int} */ lineno, /* {int} - */ col_offset) + */ col_offset, /* {int} */ endlineno, + /* {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.args = args; this.body = body; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.IfExp = function IfExp(/* {expr_ty} */ test, /* {expr_ty} */ body, /* {expr_ty} */ orelse, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, /* + {int} */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.test = test; this.body = body; this.orelse = orelse; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Dict = function Dict(/* {asdl_seq *} */ keys, /* {asdl_seq *} */ values, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ endlineno, /* + {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.keys = keys; this.values = values; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Set = function Set(/* {asdl_seq *} */ elts, /* {int} */ lineno, /* - {int} */ col_offset) + {int} */ col_offset, /* {int} */ endlineno, + /* {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.elts = elts; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.ListComp = function ListComp(/* {expr_ty} */ elt, /* {asdl_seq *} */ generators, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, + /* {int} */ endlineno, /* {int} + */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.elt = elt; this.generators = generators; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.SetComp = function SetComp(/* {expr_ty} */ elt, /* {asdl_seq *} */ generators, /* {int} */ lineno, /* - {int} */ col_offset) + {int} */ col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.elt = elt; this.generators = generators; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -669,15 +874,21 @@ Sk.astnodes.SetComp = function SetComp(/* {expr_ty} */ elt, /* {asdl_seq *} */ Sk.astnodes.DictComp = function DictComp(/* {expr_ty} */ key, /* {expr_ty} */ value, /* {asdl_seq *} */ generators, /* {int} */ lineno, - /* {int} */ col_offset) + /* {int} */ col_offset, /* {int} + */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.key = key; this.value = value; this.generators = generators; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -686,50 +897,72 @@ Sk.astnodes.GeneratorExp = function GeneratorExp(/* {expr_ty} */ elt, /* {asdl_seq *} */ generators, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.elt = elt; this.generators = generators; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Await = function Await(/* {expr_ty} */ value, /* {int} */ lineno, - /* {int} */ col_offset) + /* {int} */ col_offset, /* {int} */ + endlineno, /* {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Yield = function Yield(/* {expr_ty} */ value, /* {int} */ lineno, - /* {int} */ col_offset) + /* {int} */ col_offset, /* {int} */ + endlineno, /* {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.YieldFrom = function YieldFrom(/* {expr_ty} */ value, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, + /* {int} */ endlineno, /* {int} + */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -737,54 +970,75 @@ Sk.astnodes.YieldFrom = function YieldFrom(/* {expr_ty} */ value, /* {int} */ Sk.astnodes.Compare = function Compare(/* {expr_ty} */ left, /* {asdl_int_seq *} */ ops, /* {asdl_seq *} */ comparators, /* {int} */ lineno, /* - {int} */ col_offset) + {int} */ col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.left = left; this.ops = ops; this.comparators = comparators; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Call = function Call(/* {expr_ty} */ func, /* {asdl_seq *} */ args, /* {asdl_seq *} */ keywords, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, /* {int} + */ endlineno, /* {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.func = func; this.args = args; this.keywords = keywords; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Num = function Num(/* {object} */ n, /* {int} */ lineno, /* {int} - */ col_offset) + */ col_offset, /* {int} */ endlineno, /* + {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.n = n; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Str = function Str(/* {string} */ s, /* {int} */ lineno, /* {int} - */ col_offset) + */ col_offset, /* {int} */ endlineno, /* + {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.s = s; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -794,76 +1048,112 @@ Sk.astnodes.FormattedValue = function FormattedValue(/* {expr_ty} */ value, /* /* {expr_ty} */ format_spec, /* {int} */ lineno, /* {int} - */ col_offset) + */ col_offset, /* + {int} */ endlineno, + /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.value = value; this.conversion = conversion; this.format_spec = format_spec; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.JoinedStr = function JoinedStr(/* {asdl_seq *} */ values, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.values = values; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Bytes = function Bytes(/* {bytes} */ s, /* {int} */ lineno, /* - {int} */ col_offset) + {int} */ col_offset, /* {int} */ + endlineno, /* {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.s = s; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.NameConstant = function NameConstant(/* {singleton} */ value, /* {int} */ lineno, /* {int} - */ col_offset) + */ col_offset, /* {int} + */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Ellipsis = function Ellipsis(/* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Constant = function Constant(/* {constant} */ value, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, + /* {int} */ endlineno, /* {int} + */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -872,15 +1162,21 @@ Sk.astnodes.Attribute = function Attribute(/* {expr_ty} */ value, /* {identifier} */ attr, /* {expr_context_ty} */ ctx, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.value = value; this.attr = attr; this.ctx = ctx; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -888,71 +1184,99 @@ Sk.astnodes.Attribute = function Attribute(/* {expr_ty} */ value, /* Sk.astnodes.Subscript = function Subscript(/* {expr_ty} */ value, /* {slice_ty} */ slice, /* {expr_context_ty} */ ctx, /* {int} */ lineno, /* - {int} */ col_offset) + {int} */ col_offset, /* {int} + */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.value = value; this.slice = slice; this.ctx = ctx; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Starred = function Starred(/* {expr_ty} */ value, /* {expr_context_ty} */ ctx, /* {int} - */ lineno, /* {int} */ col_offset) + */ lineno, /* {int} */ col_offset, + /* {int} */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.value = value; this.ctx = ctx; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Name = function Name(/* {identifier} */ id, /* {expr_context_ty} */ ctx, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ endlineno, /* + {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.id = id; this.ctx = ctx; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.List = function List(/* {asdl_seq *} */ elts, /* {expr_context_ty} */ ctx, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ endlineno, /* + {int} */ col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.elts = elts; this.ctx = ctx; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } /** @constructor */ Sk.astnodes.Tuple = function Tuple(/* {asdl_seq *} */ elts, /* {expr_context_ty} */ ctx, /* {int} */ - lineno, /* {int} */ col_offset) + lineno, /* {int} */ col_offset, /* + {int} */ endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.elts = elts; this.ctx = ctx; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } @@ -999,15 +1323,21 @@ Sk.astnodes.ExceptHandler = function ExceptHandler(/* {expr_ty} */ type, /* /* {asdl_seq *} */ body, /* {int} */ lineno, /* {int} */ - col_offset) + col_offset, /* {int} */ + endlineno, /* {int} */ + col_endoffset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); + Sk.asserts.assert(endlineno !== null && endlineno !== undefined); + Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); this.type = type; this.name = name; this.body = body; this.lineno = lineno; this.col_offset = col_offset; + this.endlineno = endlineno; + this.col_endoffset = col_endoffset; return this; } diff --git a/gen/parse_tables.js b/gen/parse_tables.js index a4eccdab7a..3e8150c51e 100644 --- a/gen/parse_tables.js +++ b/gen/parse_tables.js @@ -270,129 +270,127 @@ dfas: 42: 1, 43: 1}], 257: [[[[44, 1]], [[45, 0], [0, 1]]], - {6: 1, - 7: 1, - 9: 1, + {10: 1, 11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], + 28: 1, + 32: 1, + 33: 1, + 36: 1}], 258: [[[[46, 1]], [[47, 0], [0, 1]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], - 259: [[[[48, 1]], [[49, 2]], [[50, 3], [0, 2]], [[49, 4]], [[0, 4]]], - {48: 1}], + 28: 1, + 32: 1, + 33: 1, + 36: 1}], + 259: [[[[48, 1]], [[49, 2]], [[50, 3], [0, 2]], [[49, 4]], [[0, 4]]], {48: 1}], 260: [[[[51, 1]], [[52, 2], [0, 1]], [[51, 1], [0, 2]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, - 16: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1, + 28: 1, + 32: 1, + 33: 1, + 35: 1, + 36: 1, + 39: 1, 53: 1}], - 261: [[[[49, 1], [16, 2], [53, 2]], - [[50, 2], [54, 3], [0, 1]], + 261: [[[[35, 1], [49, 2], [53, 1]], [[49, 3]], + [[50, 1], [54, 3], [0, 2]], [[0, 3]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, - 16: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1, + 28: 1, + 32: 1, + 33: 1, + 35: 1, + 36: 1, + 39: 1, 53: 1}], - 262: [[[[55, 1]], [[30, 0], [43, 0], [0, 1]]], - {6: 1, - 7: 1, - 9: 1, + 262: [[[[55, 1]], [[10, 0], [32, 0], [0, 1]]], + {10: 1, 11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], - 263: [[[[25, 1]], [[49, 2]], [[52, 3], [0, 2]], [[49, 4]], [[0, 4]]], - {25: 1}], - 264: [[[[10, 1]], [[56, 2]], [[0, 2]]], {10: 1}], - 265: [[[[10, 1]], [[57, 2], [56, 2], [58, 2]], [[0, 2]]], {10: 1}], - 266: [[[[6, 1], - [7, 2], - [27, 1], - [9, 1], - [11, 1], - [12, 3], - [34, 4], - [37, 5], - [20, 1], - [26, 1]], - [[0, 1]], - [[7, 2], [0, 2]], - [[59, 1], [60, 6]], - [[61, 1], [62, 7], [63, 7]], - [[64, 1], [63, 8]], - [[59, 1]], - [[61, 1]], - [[64, 1]]], - {6: 1, 7: 1, 9: 1, 11: 1, 12: 1, 20: 1, 26: 1, 27: 1, 34: 1, 37: 1}], - 267: [[[[38, 1], [65, 2]], [[65, 2]], [[66, 2], [0, 2]]], - {6: 1, - 7: 1, - 9: 1, - 11: 1, + 28: 1, + 32: 1, + 33: 1, + 36: 1}], + 263: [[[[21, 1]], [[49, 2]], [[52, 3], [0, 2]], [[49, 4]], [[0, 4]]], {21: 1}], + 264: [[[[42, 1]], [[56, 2]], [[0, 2]]], {42: 1}], + 265: [[[[42, 1]], [[57, 2], [56, 2], [58, 2]], [[0, 2]]], {42: 1}], + 266: [[[[13, 1], + [17, 2], + [11, 2], + [18, 2], + [28, 2], + [20, 2], + [23, 4], + [19, 2], + [27, 5], + [33, 3]], + [[59, 2], [60, 6]], + [[0, 2]], + [[61, 2], [62, 7], [60, 7]], + [[63, 8], [64, 2]], + [[27, 5], [0, 5]], + [[59, 2]], + [[61, 2]], + [[64, 2]]], + {11: 1, 13: 1, 17: 1, 18: 1, 19: 1, 20: 1, 23: 1, 27: 1, 28: 1, 33: 1}], + 267: [[[[12, 1], [65, 2]], [[65, 2]], [[66, 2], [0, 2]]], + {11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, - 26: 1, + 23: 1, 27: 1, - 34: 1, - 37: 1, - 38: 1}], + 28: 1, + 33: 1}], 268: [[[[67, 1], [68, 1], [69, 1], @@ -420,132 +418,132 @@ dfas: 77: 1, 78: 1, 79: 1}], - 269: [[[[14, 1]], [[0, 1]]], {14: 1}], - 270: [[[[13, 1]], - [[26, 2]], - [[48, 3], [34, 4]], + 269: [[[[43, 1]], [[0, 1]]], {43: 1}], + 270: [[[[5, 1]], + [[20, 2]], + [[48, 3], [33, 4]], [[80, 5]], - [[61, 6], [81, 7]], + [[81, 6], [61, 7]], [[0, 5]], - [[48, 3]], - [[61, 6]]], - {13: 1}], - 271: [[[[10, 1], [33, 2]], - [[33, 2]], + [[61, 7]], + [[48, 3]]], + {5: 1}], + 271: [[[[15, 1], [42, 2]], [[82, 3]], + [[15, 1]], [[83, 4]], [[84, 5]], [[85, 6], [0, 5]], [[0, 6]]], - {10: 1, 33: 1}], - 272: [[[[36, 1]], [[86, 2]], [[85, 3], [0, 2]], [[0, 3]]], {36: 1}], - 273: [[[[87, 1], [54, 1]], [[0, 1]]], {10: 1, 33: 1, 36: 1}], + {15: 1, 42: 1}], + 272: [[[[24, 1]], [[86, 2]], [[85, 3], [0, 2]], [[0, 3]]], {24: 1}], + 273: [[[[87, 1], [54, 1]], [[0, 1]]], {15: 1, 24: 1, 42: 1}], 274: [[[[88, 1], + [6, 2], [89, 1], - [8, 2], [90, 1], - [88, 1], + [91, 3], + [92, 1], [83, 1], - [91, 1], - [92, 3], [93, 1], + [90, 1], [94, 1]], [[0, 1]], [[83, 1]], - [[8, 1], [0, 3]]], - {8: 1, 83: 1, 88: 1, 89: 1, 90: 1, 91: 1, 92: 1, 93: 1, 94: 1}], + [[6, 1], [0, 3]]], + {6: 1, 83: 1, 88: 1, 89: 1, 90: 1, 91: 1, 92: 1, 93: 1, 94: 1}], 275: [[[[95, 1]], [[96, 0], [0, 1]]], - {6: 1, - 7: 1, - 9: 1, + {10: 1, 11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], - 276: [[[[97, 1], + 28: 1, + 32: 1, + 33: 1, + 36: 1}], + 276: [[[[56, 1], + [57, 1], + [97, 1], [98, 1], - [58, 1], [99, 1], - [57, 1], + [58, 1], [100, 1], - [56, 1], [101, 1], [102, 1]], [[0, 1]]], - {4: 1, 10: 1, 13: 1, 21: 1, 22: 1, 33: 1, 36: 1, 41: 1, 42: 1}], - 277: [[[[40, 1]], [[0, 1]]], {40: 1}], - 278: [[[[18, 1]], [[0, 1]]], {18: 1}], - 279: [[[[103, 1]], [[56, 2], [104, 2], [99, 2]], [[0, 2]]], {41: 1}], - 280: [[[[41, 1]], + {5: 1, 8: 1, 9: 1, 15: 1, 16: 1, 22: 1, 24: 1, 29: 1, 42: 1}], + 277: [[[[38, 1]], [[0, 1]]], {38: 1}], + 278: [[[[34, 1]], [[0, 1]]], {34: 1}], + 279: [[[[103, 1]], [[56, 2], [104, 2], [99, 2]], [[0, 2]]], {29: 1}], + 280: [[[[29, 1]], [[105, 2]], - [[34, 4], [2, 3]], + [[2, 3], [33, 4]], [[0, 3]], - [[61, 5], [81, 6]], - [[2, 3]], - [[61, 5]]], - {41: 1}], - 281: [[[[106, 1]], [[106, 1], [0, 1]]], {41: 1}], - 282: [[[[39, 1]], [[82, 2]], [[0, 2]]], {39: 1}], - 283: [[[[49, 1], [107, 2], [53, 3]], - [[48, 4], [54, 5], [52, 6], [0, 1]], - [[54, 5], [52, 6], [0, 2]], + [[81, 5], [61, 6]], + [[61, 6]], + [[2, 3]]], + {29: 1}], + 281: [[[[106, 1]], [[106, 1], [0, 1]]], {29: 1}], + 282: [[[[14, 1]], [[82, 2]], [[0, 2]]], {14: 1}], + 283: [[[[53, 3], [49, 2], [107, 1]], + [[54, 5], [52, 4], [0, 1]], + [[54, 5], [48, 6], [52, 4], [0, 2]], [[95, 7]], - [[49, 7]], + [[49, 8], [107, 8], [0, 4]], [[0, 5]], - [[49, 8], [107, 8], [0, 6]], - [[54, 5], [52, 9], [0, 7]], - [[52, 6], [0, 8]], - [[49, 10], [53, 11], [0, 9]], - [[48, 12]], - [[95, 13]], - [[49, 13]], - [[52, 9], [0, 13]]], + [[49, 7]], + [[52, 9], [54, 5], [0, 7]], + [[52, 4], [0, 8]], + [[53, 10], [49, 11], [0, 9]], + [[95, 12]], + [[48, 13]], + [[52, 9], [0, 12]], + [[49, 12]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, - 16: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1, + 28: 1, + 32: 1, + 33: 1, + 35: 1, + 36: 1, + 39: 1, 53: 1}], - 284: [[[[105, 1]], [[108, 2], [0, 1]], [[26, 3]], [[0, 3]]], {26: 1}], - 285: [[[[109, 1]], [[52, 0], [0, 1]]], {26: 1}], - 286: [[[[26, 1]], [[110, 0], [0, 1]]], {26: 1}], - 287: [[[[26, 1]], [[0, 1]]], {26: 1}], + 284: [[[[105, 1]], [[108, 2], [0, 1]], [[20, 3]], [[0, 3]]], {20: 1}], + 285: [[[[109, 1]], [[52, 0], [0, 1]]], {20: 1}], + 286: [[[[20, 1]], [[110, 0], [0, 1]]], {20: 1}], + 287: [[[[20, 1]], [[0, 1]]], {20: 1}], 288: [[[[111, 1]], [[2, 1], [112, 2]], [[0, 2]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], + 28: 1, + 32: 1, + 33: 1, + 36: 1, + 39: 1}], 289: [[[[113, 1]], [[49, 2], [0, 1]], [[108, 3], [52, 3], [0, 2]], @@ -553,75 +551,75 @@ dfas: [[0, 4]]], {113: 1}], 290: [[[[114, 1]], [[115, 0], [0, 1]]], - {6: 1, - 7: 1, - 9: 1, + {10: 1, 11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], + 28: 1, + 32: 1, + 33: 1, + 36: 1}], 291: [[[[116, 1]], - [[117, 2], [50, 3], [118, 4], [0, 1]], - [[111, 4], [62, 4]], + [[50, 2], [117, 3], [118, 4], [0, 1]], [[116, 5], [62, 5]], - [[0, 4]], - [[50, 3], [0, 5]]], + [[0, 3]], + [[62, 3], [111, 3]], + [[50, 2], [0, 5]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, - 16: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], + 28: 1, + 32: 1, + 33: 1, + 35: 1, + 36: 1, + 39: 1}], 292: [[[[95, 1], [107, 1]], [[52, 2], [0, 1]], [[95, 1], [107, 1], [0, 2]]], - {6: 1, - 7: 1, - 9: 1, + {10: 1, 11: 1, 12: 1, - 16: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], - 293: [[[[119, 2], [30, 1], [23, 1], [43, 1]], [[120, 2]], [[0, 2]]], - {6: 1, - 7: 1, - 9: 1, + 28: 1, + 32: 1, + 33: 1, + 35: 1, + 36: 1}], + 293: [[[[10, 1], [36, 1], [119, 2], [32, 1]], [[120, 2]], [[0, 2]]], + {10: 1, 11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], - 294: [[[[2, 0], [112, 1], [121, 0]], [[0, 1]]], + 28: 1, + 32: 1, + 33: 1, + 36: 1}], + 294: [[[[121, 0], [2, 0], [112, 1]], [[0, 1]]], {2: 1, 4: 1, 5: 1, @@ -665,8 +663,8 @@ dfas: 43: 1, 112: 1}], 295: [[[[122, 1], [123, 1], [124, 1], [125, 1], [126, 1]], [[0, 1]]], - {5: 1, 14: 1, 24: 1, 31: 1, 40: 1}], - 296: [[[[33, 1]], + {26: 1, 30: 1, 38: 1, 40: 1, 43: 1}], + 296: [[[[15, 1]], [[82, 2]], [[83, 3]], [[111, 4]], @@ -676,91 +674,91 @@ dfas: [[48, 8]], [[80, 9]], [[0, 9]]], - {33: 1}], - 297: [[[[4, 1]], - [[26, 2]], + {15: 1}], + 297: [[[[16, 1]], + [[20, 2]], [[128, 3]], [[129, 4], [48, 5]], [[49, 6]], [[80, 7]], [[48, 5]], [[0, 7]]], - {4: 1}], - 298: [[[[32, 1]], [[26, 2]], [[52, 1], [0, 2]]], {32: 1}], - 299: [[[[36, 1]], + {16: 1}], + 298: [[[[25, 1]], [[20, 2]], [[52, 1], [0, 2]]], {25: 1}], + 299: [[[[24, 1]], [[49, 2]], [[48, 3]], [[80, 4]], - [[127, 5], [130, 1], [0, 4]], + [[130, 1], [127, 5], [0, 4]], [[48, 6]], [[80, 7]], [[0, 7]]], - {36: 1}], - 300: [[[[26, 1]], [[108, 2], [0, 1]], [[26, 3]], [[0, 3]]], {26: 1}], - 301: [[[[131, 1]], [[52, 2], [0, 1]], [[131, 1], [0, 2]]], {26: 1}], - 302: [[[[35, 1]], - [[105, 2], [20, 3], [110, 3]], - [[29, 4]], - [[105, 2], [20, 3], [29, 4], [110, 3]], - [[132, 5], [16, 5], [34, 6]], + {24: 1}], + 300: [[[[20, 1]], [[108, 2], [0, 1]], [[20, 3]], [[0, 3]]], {20: 1}], + 301: [[[[131, 1]], [[52, 2], [0, 1]], [[131, 1], [0, 2]]], {20: 1}], + 302: [[[[31, 1]], + [[105, 2], [110, 3], [19, 3]], + [[37, 4]], + [[105, 2], [110, 3], [37, 4], [19, 3]], + [[35, 5], [33, 6], [132, 5]], [[0, 5]], [[132, 7]], [[61, 5]]], - {35: 1}], - 303: [[[[29, 1]], [[133, 2]], [[0, 2]]], {29: 1}], - 304: [[[[134, 1], [135, 1]], [[0, 1]]], {29: 1, 35: 1}], - 305: [[[[15, 1]], [[48, 2], [136, 3]], [[49, 4]], [[48, 2]], [[0, 4]]], - {15: 1}], - 306: [[[[15, 1]], [[48, 2], [136, 3]], [[86, 4]], [[48, 2]], [[0, 4]]], - {15: 1}], - 307: [[[[19, 1]], [[26, 2]], [[52, 1], [0, 2]]], {19: 1}], - 308: [[[[8, 1], [137, 2]], [[46, 2]], [[0, 2]]], + {31: 1}], + 303: [[[[37, 1]], [[133, 2]], [[0, 2]]], {37: 1}], + 304: [[[[134, 1], [135, 1]], [[0, 1]]], {31: 1, 37: 1}], + 305: [[[[39, 1]], [[48, 2], [136, 3]], [[49, 4]], [[48, 2]], [[0, 4]]], + {39: 1}], + 306: [[[[39, 1]], [[48, 2], [136, 3]], [[86, 4]], [[48, 2]], [[0, 4]]], + {39: 1}], + 307: [[[[41, 1]], [[20, 2]], [[52, 1], [0, 2]]], {41: 1}], + 308: [[[[6, 1], [137, 2]], [[46, 2]], [[0, 2]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], + 28: 1, + 32: 1, + 33: 1, + 36: 1}], 309: [[[[138, 1]], [[139, 0], [0, 1]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], - 310: [[[[34, 1]], [[61, 2], [140, 3]], [[0, 2]], [[61, 2]]], {34: 1}], - 311: [[[[28, 1]], [[0, 1]]], {28: 1}], + 28: 1, + 32: 1, + 33: 1, + 36: 1}], + 310: [[[[33, 1]], [[140, 2], [61, 3]], [[61, 3]], [[0, 3]]], {33: 1}], + 311: [[[[7, 1]], [[0, 1]]], {7: 1}], 312: [[[[141, 1]], [[53, 2], [0, 1]], [[120, 3]], [[0, 3]]], - {6: 1, - 7: 1, - 9: 1, - 11: 1, + {11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, - 26: 1, + 23: 1, 27: 1, - 34: 1, - 37: 1, - 38: 1}], - 313: [[[[17, 1]], + 28: 1, + 33: 1}], + 313: [[[[4, 1]], [[49, 2], [142, 3], [0, 1]], [[52, 4], [0, 2]], [[49, 5]], @@ -769,62 +767,62 @@ dfas: [[49, 7]], [[52, 8], [0, 7]], [[49, 7], [0, 8]]], - {17: 1}], - 314: [[[[5, 1]], + {4: 1}], + 314: [[[[40, 1]], [[49, 2], [0, 1]], - [[35, 3], [52, 3], [0, 2]], + [[52, 3], [31, 3], [0, 2]], [[49, 4]], [[52, 5], [0, 4]], [[49, 6]], [[0, 6]]], - {5: 1}], - 315: [[[[24, 1]], [[111, 2], [0, 1]], [[0, 2]]], {24: 1}], - 316: [[[[143, 1]], [[142, 0], [144, 0], [0, 1]]], - {6: 1, - 7: 1, - 9: 1, + {40: 1}], + 315: [[[[30, 1]], [[111, 2], [0, 1]], [[0, 2]]], {30: 1}], + 316: [[[[143, 1]], [[144, 0], [142, 0], [0, 1]]], + {10: 1, 11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], - 317: [[[[145, 1]], [[2, 2], [146, 3]], [[0, 2]], [[145, 1], [2, 2]]], - {5: 1, + 28: 1, + 32: 1, + 33: 1, + 36: 1}], + 317: [[[[145, 1]], [[146, 2], [2, 3]], [[145, 1], [2, 3]], [[0, 3]]], + {4: 1, 6: 1, 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, + 13: 1, 14: 1, - 15: 1, - 16: 1, 17: 1, 18: 1, 19: 1, 20: 1, + 21: 1, 23: 1, - 24: 1, 25: 1, 26: 1, 27: 1, 28: 1, - 29: 1, 30: 1, 31: 1, 32: 1, + 33: 1, 34: 1, 35: 1, + 36: 1, 37: 1, 38: 1, 39: 1, 40: 1, + 41: 1, 43: 1}], 318: [[[[48, 1]], [[49, 2], [0, 1]], [[0, 2]]], {48: 1}], 319: [[[[147, 1], @@ -838,38 +836,38 @@ dfas: [155, 1], [156, 1]], [[0, 1]]], - {5: 1, + {4: 1, 6: 1, 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, + 13: 1, 14: 1, - 15: 1, - 16: 1, 17: 1, 18: 1, 19: 1, 20: 1, + 21: 1, 23: 1, - 24: 1, 25: 1, 26: 1, 27: 1, 28: 1, - 29: 1, 30: 1, 31: 1, 32: 1, + 33: 1, 34: 1, 35: 1, + 36: 1, 37: 1, 38: 1, 39: 1, 40: 1, + 41: 1, 43: 1}], - 320: [[[[16, 1]], [[95, 2]], [[0, 2]]], {16: 1}], + 320: [[[[35, 1]], [[95, 2]], [[0, 2]]], {35: 1}], 321: [[[[1, 1], [3, 1]], [[0, 1]]], {4: 1, 5: 1, @@ -911,45 +909,45 @@ dfas: 41: 1, 42: 1, 43: 1}], - 322: [[[[49, 1], [48, 2]], - [[48, 2], [0, 1]], - [[49, 3], [157, 4], [0, 2]], - [[157, 4], [0, 3]], - [[0, 4]]], + 322: [[[[48, 1], [49, 2]], + [[157, 3], [49, 4], [0, 1]], + [[48, 1], [0, 2]], + [[0, 3]], + [[157, 3], [0, 4]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1, + 28: 1, + 32: 1, + 33: 1, + 36: 1, + 39: 1, 48: 1}], 323: [[[[158, 1]], [[52, 2], [0, 1]], [[158, 1], [0, 2]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1, + 28: 1, + 32: 1, + 33: 1, + 36: 1, + 39: 1, 48: 1}], 324: [[[[1, 1], [2, 2]], [[0, 1]], @@ -957,158 +955,158 @@ dfas: [[121, 4]], [[160, 1], [121, 4]]], {2: 1, - 5: 1, + 4: 1, 6: 1, 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, + 13: 1, 14: 1, - 15: 1, - 16: 1, 17: 1, 18: 1, 19: 1, 20: 1, + 21: 1, 23: 1, - 24: 1, 25: 1, 26: 1, 27: 1, 28: 1, - 29: 1, 30: 1, 31: 1, 32: 1, + 33: 1, 34: 1, 35: 1, + 36: 1, 37: 1, 38: 1, 39: 1, 40: 1, + 41: 1, 43: 1}], - 325: [[[[120, 1]], [[161, 0], [16, 0], [162, 0], [41, 0], [163, 0], [0, 1]]], - {6: 1, - 7: 1, - 9: 1, + 325: [[[[120, 1]], [[161, 0], [29, 0], [35, 0], [162, 0], [163, 0], [0, 1]]], + {10: 1, 11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], - 326: [[[[84, 1], [164, 2]], - [[36, 3], [0, 1]], - [[0, 2]], + 28: 1, + 32: 1, + 33: 1, + 36: 1}], + 326: [[[[164, 1], [84, 2]], + [[0, 1]], + [[24, 3], [0, 2]], [[84, 4]], [[127, 5]], - [[49, 2]]], + [[49, 1]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], + 28: 1, + 32: 1, + 33: 1, + 36: 1, + 39: 1}], 327: [[[[165, 1], [84, 1]], [[0, 1]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], + 28: 1, + 32: 1, + 33: 1, + 36: 1, + 39: 1}], 328: [[[[49, 1]], [[52, 2], [0, 1]], [[49, 1], [0, 2]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], + 28: 1, + 32: 1, + 33: 1, + 36: 1, + 39: 1}], 329: [[[[49, 1], [107, 1]], - [[54, 2], [52, 3], [0, 1]], - [[0, 2]], - [[49, 4], [107, 4], [0, 3]], - [[52, 3], [0, 4]]], + [[54, 3], [52, 2], [0, 1]], + [[49, 4], [107, 4], [0, 2]], + [[0, 3]], + [[52, 2], [0, 4]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, - 16: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], + 28: 1, + 32: 1, + 33: 1, + 35: 1, + 36: 1, + 39: 1}], 330: [[[[49, 1], [107, 1]], [[52, 2], [0, 1]], [[49, 1], [107, 1], [0, 2]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, - 16: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], - 331: [[[[26, 1]], [[48, 2], [0, 1]], [[49, 3]], [[0, 3]]], {26: 1}], - 332: [[[[34, 1], [110, 2], [37, 3]], - [[61, 4], [81, 5]], - [[26, 4]], - [[166, 6]], - [[0, 4]], - [[61, 4]], - [[64, 4]]], - {34: 1, 37: 1, 110: 1}], - 333: [[[[21, 1]], + 28: 1, + 32: 1, + 33: 1, + 35: 1, + 36: 1, + 39: 1}], + 331: [[[[20, 1]], [[48, 2], [0, 1]], [[49, 3]], [[0, 3]]], {20: 1}], + 332: [[[[13, 1], [110, 2], [33, 3]], + [[166, 4]], + [[20, 5]], + [[81, 6], [61, 5]], + [[59, 5]], + [[0, 5]], + [[61, 5]]], + {13: 1, 33: 1, 110: 1}], + 333: [[[[8, 1]], [[48, 2]], [[80, 3]], [[167, 4], [168, 5]], @@ -1116,52 +1114,52 @@ dfas: [[48, 7]], [[80, 8]], [[80, 9]], - [[167, 4], [127, 10], [168, 5], [0, 8]], + [[167, 4], [168, 5], [127, 10], [0, 8]], [[0, 9]], [[48, 11]], [[80, 12]], [[168, 5], [0, 12]]], - {21: 1}], - 334: [[[[16, 1], [169, 2], [53, 3]], - [[169, 4], [52, 5], [0, 1]], - [[50, 6], [52, 7], [0, 2]], + {8: 1}], + 334: [[[[169, 1], [35, 2], [53, 3]], + [[50, 4], [52, 5], [0, 1]], + [[169, 6], [52, 7], [0, 2]], [[169, 8]], - [[52, 5], [0, 4]], - [[169, 9], [53, 3], [0, 5]], - [[49, 10]], - [[16, 11], [169, 2], [53, 3], [0, 7]], + [[49, 9]], + [[169, 1], [35, 10], [53, 3], [0, 5]], + [[52, 7], [0, 6]], + [[169, 11], [53, 3], [0, 7]], [[52, 12], [0, 8]], - [[50, 13], [52, 5], [0, 9]], - [[52, 7], [0, 10]], - [[169, 14], [52, 15], [0, 11]], + [[52, 5], [0, 9]], + [[169, 13], [52, 14], [0, 10]], + [[50, 15], [52, 7], [0, 11]], [[0, 12]], - [[49, 4]], - [[52, 15], [0, 14]], - [[169, 16], [53, 3], [0, 15]], - [[50, 17], [52, 15], [0, 16]], - [[49, 14]]], - {16: 1, 26: 1, 53: 1}], - 335: [[[[16, 1], [53, 2], [170, 3]], - [[170, 5], [52, 4], [0, 1]], - [[170, 6]], - [[50, 7], [52, 8], [0, 3]], - [[53, 2], [170, 9], [0, 4]], - [[52, 4], [0, 5]], - [[52, 10], [0, 6]], - [[49, 11]], - [[16, 12], [53, 2], [170, 3], [0, 8]], - [[50, 13], [52, 4], [0, 9]], - [[0, 10]], - [[52, 8], [0, 11]], - [[52, 15], [170, 14], [0, 12]], - [[49, 5]], - [[52, 15], [0, 14]], - [[53, 2], [170, 16], [0, 15]], - [[50, 17], [52, 15], [0, 16]], - [[49, 14]]], - {16: 1, 26: 1, 53: 1}], - 336: [[[[26, 1]], [[0, 1]]], {26: 1}], - 337: [[[[22, 1]], + [[52, 14], [0, 13]], + [[169, 16], [53, 3], [0, 14]], + [[49, 6]], + [[50, 17], [52, 14], [0, 16]], + [[49, 13]]], + {20: 1, 35: 1, 53: 1}], + 335: [[[[35, 2], [170, 1], [53, 3]], + [[50, 4], [52, 5], [0, 1]], + [[170, 6], [52, 7], [0, 2]], + [[170, 8]], + [[49, 9]], + [[170, 1], [35, 10], [53, 3], [0, 5]], + [[52, 7], [0, 6]], + [[170, 11], [53, 3], [0, 7]], + [[52, 12], [0, 8]], + [[52, 5], [0, 9]], + [[170, 13], [52, 14], [0, 10]], + [[50, 15], [52, 7], [0, 11]], + [[0, 12]], + [[52, 14], [0, 13]], + [[170, 16], [53, 3], [0, 14]], + [[49, 6]], + [[50, 17], [52, 14], [0, 16]], + [[49, 13]]], + {20: 1, 35: 1, 53: 1}], + 336: [[[[20, 1]], [[0, 1]]], {20: 1}], + 337: [[[[9, 1]], [[49, 2]], [[48, 3]], [[80, 4]], @@ -1169,94 +1167,91 @@ dfas: [[48, 6]], [[80, 7]], [[0, 7]]], - {22: 1}], + {9: 1}], 338: [[[[49, 1]], [[108, 2], [0, 1]], [[95, 3]], [[0, 3]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], - 339: [[[[42, 1]], [[171, 2]], [[48, 3], [52, 1]], [[80, 4]], [[0, 4]]], - {42: 1}], + 28: 1, + 32: 1, + 33: 1, + 36: 1, + 39: 1}], + 339: [[[[22, 1]], [[171, 2]], [[48, 3], [52, 1]], [[80, 4]], [[0, 4]]], + {22: 1}], 340: [[[[172, 1]], [[173, 0], [0, 1]]], - {6: 1, - 7: 1, - 9: 1, + {10: 1, 11: 1, 12: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 37: 1, - 38: 1, - 43: 1}], - 341: [[[[111, 2], [35, 1]], [[49, 2]], [[0, 2]]], + 28: 1, + 32: 1, + 33: 1, + 36: 1}], + 341: [[[[111, 1], [31, 2]], [[0, 1]], [[49, 1]]], {6: 1, - 7: 1, - 8: 1, - 9: 1, + 10: 1, 11: 1, 12: 1, - 15: 1, + 13: 1, + 17: 1, + 18: 1, + 19: 1, 20: 1, 23: 1, - 26: 1, 27: 1, - 30: 1, - 34: 1, - 35: 1, - 37: 1, - 38: 1, - 43: 1}], - 342: [[[[31, 1]], [[174, 2], [0, 1]], [[0, 2]]], {31: 1}], - 343: [[[[62, 1]], [[0, 1]]], {31: 1}]}, + 28: 1, + 31: 1, + 32: 1, + 33: 1, + 36: 1, + 39: 1}], + 342: [[[[26, 1]], [[174, 2], [0, 1]], [[0, 2]]], {26: 1}], + 343: [[[[62, 1]], [[0, 1]]], {26: 1}]}, states: [[[[1, 1], [2, 1], [3, 2]], [[0, 1]], [[2, 1]]], [[[44, 1]], [[45, 0], [0, 1]]], [[[46, 1]], [[47, 0], [0, 1]]], [[[48, 1]], [[49, 2]], [[50, 3], [0, 2]], [[49, 4]], [[0, 4]]], [[[51, 1]], [[52, 2], [0, 1]], [[51, 1], [0, 2]]], - [[[49, 1], [16, 2], [53, 2]], - [[50, 2], [54, 3], [0, 1]], - [[49, 3]], - [[0, 3]]], - [[[55, 1]], [[30, 0], [43, 0], [0, 1]]], - [[[25, 1]], [[49, 2]], [[52, 3], [0, 2]], [[49, 4]], [[0, 4]]], - [[[10, 1]], [[56, 2]], [[0, 2]]], - [[[10, 1]], [[57, 2], [56, 2], [58, 2]], [[0, 2]]], - [[[6, 1], - [7, 2], - [27, 1], - [9, 1], - [11, 1], - [12, 3], - [34, 4], - [37, 5], - [20, 1], - [26, 1]], - [[0, 1]], - [[7, 2], [0, 2]], - [[59, 1], [60, 6]], - [[61, 1], [62, 7], [63, 7]], - [[64, 1], [63, 8]], - [[59, 1]], - [[61, 1]], - [[64, 1]]], - [[[38, 1], [65, 2]], [[65, 2]], [[66, 2], [0, 2]]], + [[[35, 1], [49, 2], [53, 1]], [[49, 3]], [[50, 1], [54, 3], [0, 2]], [[0, 3]]], + [[[55, 1]], [[10, 0], [32, 0], [0, 1]]], + [[[21, 1]], [[49, 2]], [[52, 3], [0, 2]], [[49, 4]], [[0, 4]]], + [[[42, 1]], [[56, 2]], [[0, 2]]], + [[[42, 1]], [[57, 2], [56, 2], [58, 2]], [[0, 2]]], + [[[13, 1], + [17, 2], + [11, 2], + [18, 2], + [28, 2], + [20, 2], + [23, 4], + [19, 2], + [27, 5], + [33, 3]], + [[59, 2], [60, 6]], + [[0, 2]], + [[61, 2], [62, 7], [60, 7]], + [[63, 8], [64, 2]], + [[27, 5], [0, 5]], + [[59, 2]], + [[61, 2]], + [[64, 2]]], + [[[12, 1], [65, 2]], [[65, 2]], [[66, 2], [0, 2]]], [[[67, 1], [68, 1], [69, 1], @@ -1271,78 +1266,78 @@ states: [78, 1], [79, 1]], [[0, 1]]], - [[[14, 1]], [[0, 1]]], - [[[13, 1]], - [[26, 2]], - [[48, 3], [34, 4]], + [[[43, 1]], [[0, 1]]], + [[[5, 1]], + [[20, 2]], + [[48, 3], [33, 4]], [[80, 5]], - [[61, 6], [81, 7]], + [[81, 6], [61, 7]], [[0, 5]], - [[48, 3]], - [[61, 6]]], - [[[10, 1], [33, 2]], - [[33, 2]], + [[61, 7]], + [[48, 3]]], + [[[15, 1], [42, 2]], [[82, 3]], + [[15, 1]], [[83, 4]], [[84, 5]], [[85, 6], [0, 5]], [[0, 6]]], - [[[36, 1]], [[86, 2]], [[85, 3], [0, 2]], [[0, 3]]], + [[[24, 1]], [[86, 2]], [[85, 3], [0, 2]], [[0, 3]]], [[[87, 1], [54, 1]], [[0, 1]]], [[[88, 1], + [6, 2], [89, 1], - [8, 2], [90, 1], - [88, 1], + [91, 3], + [92, 1], [83, 1], - [91, 1], - [92, 3], [93, 1], + [90, 1], [94, 1]], [[0, 1]], [[83, 1]], - [[8, 1], [0, 3]]], + [[6, 1], [0, 3]]], [[[95, 1]], [[96, 0], [0, 1]]], - [[[97, 1], + [[[56, 1], + [57, 1], + [97, 1], [98, 1], - [58, 1], [99, 1], - [57, 1], + [58, 1], [100, 1], - [56, 1], [101, 1], [102, 1]], [[0, 1]]], - [[[40, 1]], [[0, 1]]], - [[[18, 1]], [[0, 1]]], + [[[38, 1]], [[0, 1]]], + [[[34, 1]], [[0, 1]]], [[[103, 1]], [[56, 2], [104, 2], [99, 2]], [[0, 2]]], - [[[41, 1]], + [[[29, 1]], [[105, 2]], - [[34, 4], [2, 3]], + [[2, 3], [33, 4]], [[0, 3]], - [[61, 5], [81, 6]], - [[2, 3]], - [[61, 5]]], + [[81, 5], [61, 6]], + [[61, 6]], + [[2, 3]]], [[[106, 1]], [[106, 1], [0, 1]]], - [[[39, 1]], [[82, 2]], [[0, 2]]], - [[[49, 1], [107, 2], [53, 3]], - [[48, 4], [54, 5], [52, 6], [0, 1]], - [[54, 5], [52, 6], [0, 2]], + [[[14, 1]], [[82, 2]], [[0, 2]]], + [[[53, 3], [49, 2], [107, 1]], + [[54, 5], [52, 4], [0, 1]], + [[54, 5], [48, 6], [52, 4], [0, 2]], [[95, 7]], - [[49, 7]], + [[49, 8], [107, 8], [0, 4]], [[0, 5]], - [[49, 8], [107, 8], [0, 6]], - [[54, 5], [52, 9], [0, 7]], - [[52, 6], [0, 8]], - [[49, 10], [53, 11], [0, 9]], - [[48, 12]], - [[95, 13]], - [[49, 13]], - [[52, 9], [0, 13]]], - [[[105, 1]], [[108, 2], [0, 1]], [[26, 3]], [[0, 3]]], + [[49, 7]], + [[52, 9], [54, 5], [0, 7]], + [[52, 4], [0, 8]], + [[53, 10], [49, 11], [0, 9]], + [[95, 12]], + [[48, 13]], + [[52, 9], [0, 12]], + [[49, 12]]], + [[[105, 1]], [[108, 2], [0, 1]], [[20, 3]], [[0, 3]]], [[[109, 1]], [[52, 0], [0, 1]]], - [[[26, 1]], [[110, 0], [0, 1]]], - [[[26, 1]], [[0, 1]]], + [[[20, 1]], [[110, 0], [0, 1]]], + [[[20, 1]], [[0, 1]]], [[[111, 1]], [[2, 1], [112, 2]], [[0, 2]]], [[[113, 1]], [[49, 2], [0, 1]], @@ -1351,16 +1346,16 @@ states: [[0, 4]]], [[[114, 1]], [[115, 0], [0, 1]]], [[[116, 1]], - [[117, 2], [50, 3], [118, 4], [0, 1]], - [[111, 4], [62, 4]], + [[50, 2], [117, 3], [118, 4], [0, 1]], [[116, 5], [62, 5]], - [[0, 4]], - [[50, 3], [0, 5]]], + [[0, 3]], + [[62, 3], [111, 3]], + [[50, 2], [0, 5]]], [[[95, 1], [107, 1]], [[52, 2], [0, 1]], [[95, 1], [107, 1], [0, 2]]], - [[[119, 2], [30, 1], [23, 1], [43, 1]], [[120, 2]], [[0, 2]]], - [[[2, 0], [112, 1], [121, 0]], [[0, 1]]], + [[[10, 1], [36, 1], [119, 2], [32, 1]], [[120, 2]], [[0, 2]]], + [[[121, 0], [2, 0], [112, 1]], [[0, 1]]], [[[122, 1], [123, 1], [124, 1], [125, 1], [126, 1]], [[0, 1]]], - [[[33, 1]], + [[[15, 1]], [[82, 2]], [[83, 3]], [[111, 4]], @@ -1370,44 +1365,44 @@ states: [[48, 8]], [[80, 9]], [[0, 9]]], - [[[4, 1]], - [[26, 2]], + [[[16, 1]], + [[20, 2]], [[128, 3]], [[129, 4], [48, 5]], [[49, 6]], [[80, 7]], [[48, 5]], [[0, 7]]], - [[[32, 1]], [[26, 2]], [[52, 1], [0, 2]]], - [[[36, 1]], + [[[25, 1]], [[20, 2]], [[52, 1], [0, 2]]], + [[[24, 1]], [[49, 2]], [[48, 3]], [[80, 4]], - [[127, 5], [130, 1], [0, 4]], + [[130, 1], [127, 5], [0, 4]], [[48, 6]], [[80, 7]], [[0, 7]]], - [[[26, 1]], [[108, 2], [0, 1]], [[26, 3]], [[0, 3]]], + [[[20, 1]], [[108, 2], [0, 1]], [[20, 3]], [[0, 3]]], [[[131, 1]], [[52, 2], [0, 1]], [[131, 1], [0, 2]]], - [[[35, 1]], - [[105, 2], [20, 3], [110, 3]], - [[29, 4]], - [[105, 2], [20, 3], [29, 4], [110, 3]], - [[132, 5], [16, 5], [34, 6]], + [[[31, 1]], + [[105, 2], [110, 3], [19, 3]], + [[37, 4]], + [[105, 2], [110, 3], [37, 4], [19, 3]], + [[35, 5], [33, 6], [132, 5]], [[0, 5]], [[132, 7]], [[61, 5]]], - [[[29, 1]], [[133, 2]], [[0, 2]]], + [[[37, 1]], [[133, 2]], [[0, 2]]], [[[134, 1], [135, 1]], [[0, 1]]], - [[[15, 1]], [[48, 2], [136, 3]], [[49, 4]], [[48, 2]], [[0, 4]]], - [[[15, 1]], [[48, 2], [136, 3]], [[86, 4]], [[48, 2]], [[0, 4]]], - [[[19, 1]], [[26, 2]], [[52, 1], [0, 2]]], - [[[8, 1], [137, 2]], [[46, 2]], [[0, 2]]], + [[[39, 1]], [[48, 2], [136, 3]], [[49, 4]], [[48, 2]], [[0, 4]]], + [[[39, 1]], [[48, 2], [136, 3]], [[86, 4]], [[48, 2]], [[0, 4]]], + [[[41, 1]], [[20, 2]], [[52, 1], [0, 2]]], + [[[6, 1], [137, 2]], [[46, 2]], [[0, 2]]], [[[138, 1]], [[139, 0], [0, 1]]], - [[[34, 1]], [[61, 2], [140, 3]], [[0, 2]], [[61, 2]]], - [[[28, 1]], [[0, 1]]], + [[[33, 1]], [[140, 2], [61, 3]], [[61, 3]], [[0, 3]]], + [[[7, 1]], [[0, 1]]], [[[141, 1]], [[53, 2], [0, 1]], [[120, 3]], [[0, 3]]], - [[[17, 1]], + [[[4, 1]], [[49, 2], [142, 3], [0, 1]], [[52, 4], [0, 2]], [[49, 5]], @@ -1416,16 +1411,16 @@ states: [[49, 7]], [[52, 8], [0, 7]], [[49, 7], [0, 8]]], - [[[5, 1]], + [[[40, 1]], [[49, 2], [0, 1]], - [[35, 3], [52, 3], [0, 2]], + [[52, 3], [31, 3], [0, 2]], [[49, 4]], [[52, 5], [0, 4]], [[49, 6]], [[0, 6]]], - [[[24, 1]], [[111, 2], [0, 1]], [[0, 2]]], - [[[143, 1]], [[142, 0], [144, 0], [0, 1]]], - [[[145, 1]], [[2, 2], [146, 3]], [[0, 2]], [[145, 1], [2, 2]]], + [[[30, 1]], [[111, 2], [0, 1]], [[0, 2]]], + [[[143, 1]], [[144, 0], [142, 0], [0, 1]]], + [[[145, 1]], [[146, 2], [2, 3]], [[145, 1], [2, 3]], [[0, 3]]], [[[48, 1]], [[49, 2], [0, 1]], [[0, 2]]], [[[147, 1], [148, 1], @@ -1438,39 +1433,39 @@ states: [155, 1], [156, 1]], [[0, 1]]], - [[[16, 1]], [[95, 2]], [[0, 2]]], + [[[35, 1]], [[95, 2]], [[0, 2]]], [[[1, 1], [3, 1]], [[0, 1]]], - [[[49, 1], [48, 2]], - [[48, 2], [0, 1]], - [[49, 3], [157, 4], [0, 2]], - [[157, 4], [0, 3]], - [[0, 4]]], + [[[48, 1], [49, 2]], + [[157, 3], [49, 4], [0, 1]], + [[48, 1], [0, 2]], + [[0, 3]], + [[157, 3], [0, 4]]], [[[158, 1]], [[52, 2], [0, 1]], [[158, 1], [0, 2]]], [[[1, 1], [2, 2]], [[0, 1]], [[159, 3]], [[121, 4]], [[160, 1], [121, 4]]], - [[[120, 1]], [[161, 0], [16, 0], [162, 0], [41, 0], [163, 0], [0, 1]]], - [[[84, 1], [164, 2]], - [[36, 3], [0, 1]], - [[0, 2]], + [[[120, 1]], [[161, 0], [29, 0], [35, 0], [162, 0], [163, 0], [0, 1]]], + [[[164, 1], [84, 2]], + [[0, 1]], + [[24, 3], [0, 2]], [[84, 4]], [[127, 5]], - [[49, 2]]], + [[49, 1]]], [[[165, 1], [84, 1]], [[0, 1]]], [[[49, 1]], [[52, 2], [0, 1]], [[49, 1], [0, 2]]], [[[49, 1], [107, 1]], - [[54, 2], [52, 3], [0, 1]], - [[0, 2]], - [[49, 4], [107, 4], [0, 3]], - [[52, 3], [0, 4]]], + [[54, 3], [52, 2], [0, 1]], + [[49, 4], [107, 4], [0, 2]], + [[0, 3]], + [[52, 2], [0, 4]]], [[[49, 1], [107, 1]], [[52, 2], [0, 1]], [[49, 1], [107, 1], [0, 2]]], - [[[26, 1]], [[48, 2], [0, 1]], [[49, 3]], [[0, 3]]], - [[[34, 1], [110, 2], [37, 3]], - [[61, 4], [81, 5]], - [[26, 4]], - [[166, 6]], - [[0, 4]], - [[61, 4]], - [[64, 4]]], - [[[21, 1]], + [[[20, 1]], [[48, 2], [0, 1]], [[49, 3]], [[0, 3]]], + [[[13, 1], [110, 2], [33, 3]], + [[166, 4]], + [[20, 5]], + [[81, 6], [61, 5]], + [[59, 5]], + [[0, 5]], + [[61, 5]]], + [[[8, 1]], [[48, 2]], [[80, 3]], [[167, 4], [168, 5]], @@ -1478,49 +1473,49 @@ states: [[48, 7]], [[80, 8]], [[80, 9]], - [[167, 4], [127, 10], [168, 5], [0, 8]], + [[167, 4], [168, 5], [127, 10], [0, 8]], [[0, 9]], [[48, 11]], [[80, 12]], [[168, 5], [0, 12]]], - [[[16, 1], [169, 2], [53, 3]], - [[169, 4], [52, 5], [0, 1]], - [[50, 6], [52, 7], [0, 2]], + [[[169, 1], [35, 2], [53, 3]], + [[50, 4], [52, 5], [0, 1]], + [[169, 6], [52, 7], [0, 2]], [[169, 8]], - [[52, 5], [0, 4]], - [[169, 9], [53, 3], [0, 5]], - [[49, 10]], - [[16, 11], [169, 2], [53, 3], [0, 7]], + [[49, 9]], + [[169, 1], [35, 10], [53, 3], [0, 5]], + [[52, 7], [0, 6]], + [[169, 11], [53, 3], [0, 7]], [[52, 12], [0, 8]], - [[50, 13], [52, 5], [0, 9]], - [[52, 7], [0, 10]], - [[169, 14], [52, 15], [0, 11]], + [[52, 5], [0, 9]], + [[169, 13], [52, 14], [0, 10]], + [[50, 15], [52, 7], [0, 11]], [[0, 12]], - [[49, 4]], - [[52, 15], [0, 14]], - [[169, 16], [53, 3], [0, 15]], - [[50, 17], [52, 15], [0, 16]], - [[49, 14]]], - [[[16, 1], [53, 2], [170, 3]], - [[170, 5], [52, 4], [0, 1]], - [[170, 6]], - [[50, 7], [52, 8], [0, 3]], - [[53, 2], [170, 9], [0, 4]], - [[52, 4], [0, 5]], - [[52, 10], [0, 6]], - [[49, 11]], - [[16, 12], [53, 2], [170, 3], [0, 8]], - [[50, 13], [52, 4], [0, 9]], - [[0, 10]], - [[52, 8], [0, 11]], - [[52, 15], [170, 14], [0, 12]], - [[49, 5]], - [[52, 15], [0, 14]], - [[53, 2], [170, 16], [0, 15]], - [[50, 17], [52, 15], [0, 16]], - [[49, 14]]], - [[[26, 1]], [[0, 1]]], - [[[22, 1]], + [[52, 14], [0, 13]], + [[169, 16], [53, 3], [0, 14]], + [[49, 6]], + [[50, 17], [52, 14], [0, 16]], + [[49, 13]]], + [[[35, 2], [170, 1], [53, 3]], + [[50, 4], [52, 5], [0, 1]], + [[170, 6], [52, 7], [0, 2]], + [[170, 8]], + [[49, 9]], + [[170, 1], [35, 10], [53, 3], [0, 5]], + [[52, 7], [0, 6]], + [[170, 11], [53, 3], [0, 7]], + [[52, 12], [0, 8]], + [[52, 5], [0, 9]], + [[170, 13], [52, 14], [0, 10]], + [[50, 15], [52, 7], [0, 11]], + [[0, 12]], + [[52, 14], [0, 13]], + [[170, 16], [53, 3], [0, 14]], + [[49, 6]], + [[50, 17], [52, 14], [0, 16]], + [[49, 13]]], + [[[20, 1]], [[0, 1]]], + [[[9, 1]], [[49, 2]], [[48, 3]], [[80, 4]], @@ -1529,56 +1524,56 @@ states: [[80, 7]], [[0, 7]]], [[[49, 1]], [[108, 2], [0, 1]], [[95, 3]], [[0, 3]]], - [[[42, 1]], [[171, 2]], [[48, 3], [52, 1]], [[80, 4]], [[0, 4]]], + [[[22, 1]], [[171, 2]], [[48, 3], [52, 1]], [[80, 4]], [[0, 4]]], [[[172, 1]], [[173, 0], [0, 1]]], - [[[111, 2], [35, 1]], [[49, 2]], [[0, 2]]], - [[[31, 1]], [[174, 2], [0, 1]], [[0, 2]]], + [[[111, 1], [31, 2]], [[0, 1]], [[49, 1]]], + [[[26, 1]], [[174, 2], [0, 1]], [[0, 2]]], [[[62, 1]], [[0, 1]]]], labels: [[0, 'EMPTY'], [317, null], [4, null], [276, null], - [1, 'def'], - [1, 'raise'], - [1, 'True'], - [3, null], - [1, 'not'], - [1, 'null'], - [55, null], - [2, null], - [25, null], - [1, 'class'], - [1, 'break'], - [1, 'lambda'], - [16, null], [1, 'print'], - [1, 'debugger'], - [1, 'nonlocal'], - [52, null], + [1, 'class'], + [1, 'not'], + [1, 'pass'], [1, 'try'], [1, 'while'], - [31, null], - [1, 'return'], - [1, 'assert'], - [1, null], - [1, 'False'], - [1, 'pass'], - [1, 'import'], [15, null], - [1, 'yield'], - [1, 'global'], - [1, 'for'], - [7, null], - [1, 'from'], - [1, 'if'], - [9, null], + [2, null], [54, null], + [9, null], [1, 'del'], - [1, 'continue'], - [49, null], + [1, 'for'], + [1, 'def'], + [1, 'False'], + [1, 'null'], + [52, null], + [1, null], + [1, 'assert'], [1, 'with'], + [25, null], + [1, 'if'], + [1, 'global'], + [1, 'yield'], + [3, null], + [1, 'True'], + [49, null], + [1, 'return'], + [1, 'from'], [14, null], + [7, null], + [1, 'debugger'], + [16, null], + [31, null], + [1, 'import'], + [1, 'continue'], + [1, 'lambda'], + [1, 'raise'], + [1, 'nonlocal'], + [55, null], + [1, 'break'], [316, null], [19, null], [308, null], @@ -1594,27 +1589,27 @@ labels: [297, null], [339, null], [296, null], - [26, null], - [283, null], + [10, null], + [329, null], [8, null], [342, null], - [329, null], - [10, null], + [283, null], + [26, null], [266, null], [332, null], - [45, null], [38, null], - [40, null], - [50, null], [46, null], + [44, null], [41, null], + [37, null], [42, null], + [50, null], + [40, null], + [45, null], [36, null], + [39, null], [43, null], [48, null], - [44, null], - [37, null], - [39, null], [324, null], [260, null], [292, null], @@ -1623,20 +1618,20 @@ labels: [273, null], [327, null], [272, null], + [30, null], + [27, null], [28, null], + [1, 'is'], [21, null], - [27, null], [29, null], - [1, 'is'], - [30, null], [20, null], [290, null], [274, null], + [279, null], + [337, null], + [270, null], [333, null], [299, null], - [270, null], - [337, null], - [279, null], [265, null], [281, null], [264, null], @@ -1652,15 +1647,15 @@ labels: [340, null], [18, null], [330, null], - [268, null], [259, null], + [268, null], [312, null], [293, null], [321, null], + [315, null], [269, null], - [277, null], [314, null], - [315, null], + [277, null], [343, null], [1, 'else'], [310, null], @@ -1669,8 +1664,8 @@ labels: [300, null], [301, null], [285, null], - [303, null], [302, null], + [303, null], [335, null], [275, null], [258, null], @@ -1682,22 +1677,22 @@ labels: [33, null], [319, null], [13, null], - [295, null], - [263, null], + [278, null], + [298, null], + [282, null], [304, null], [311, null], - [307, null], - [313, null], - [282, null], - [298, null], [291, null], - [278, null], + [295, null], + [263, null], + [313, null], + [307, null], [318, null], [322, null], [5, null], [6, null], - [47, null], [17, null], + [47, null], [24, null], [305, null], [306, null], @@ -1711,96 +1706,96 @@ labels: [32, null], [341, null]], keywords: -{'False': 27, - 'null': 9, - 'True': 6, +{'False': 17, + 'null': 18, + 'True': 28, 'and': 47, 'as': 108, - 'assert': 25, - 'break': 14, - 'class': 13, - 'continue': 40, - 'debugger': 18, - 'def': 4, - 'del': 39, + 'assert': 21, + 'break': 43, + 'class': 5, + 'continue': 38, + 'debugger': 34, + 'def': 16, + 'del': 14, 'elif': 130, 'else': 127, 'except': 113, 'finally': 168, - 'for': 33, - 'from': 35, - 'global': 32, - 'if': 36, - 'import': 29, + 'for': 15, + 'from': 31, + 'global': 25, + 'if': 24, + 'import': 37, 'in': 83, - 'is': 92, - 'lambda': 15, - 'nonlocal': 19, - 'not': 8, + 'is': 91, + 'lambda': 39, + 'nonlocal': 41, + 'not': 6, 'or': 139, - 'pass': 28, - 'print': 17, - 'raise': 5, - 'return': 24, - 'try': 21, - 'while': 22, - 'with': 42, - 'yield': 31}, + 'pass': 7, + 'print': 4, + 'raise': 40, + 'return': 30, + 'try': 8, + 'while': 9, + 'with': 22, + 'yield': 26}, tokens: {0: 112, - 1: 26, + 1: 20, 2: 11, - 3: 7, + 3: 27, 4: 2, 5: 159, 6: 160, - 7: 34, + 7: 33, 8: 61, - 9: 37, - 10: 64, + 9: 13, + 10: 59, 11: 48, 12: 52, 13: 146, - 14: 43, - 15: 30, - 16: 16, - 17: 162, + 14: 32, + 15: 10, + 16: 35, + 17: 161, 18: 115, 19: 45, 20: 94, - 21: 89, + 21: 92, 22: 50, 23: 110, 24: 163, - 25: 12, - 26: 59, - 27: 90, - 28: 88, - 29: 91, - 30: 93, - 31: 23, + 25: 23, + 26: 64, + 27: 89, + 28: 90, + 29: 93, + 30: 88, + 31: 36, 32: 173, 33: 144, 34: 142, 35: 53, - 36: 74, - 37: 78, - 38: 68, - 39: 79, - 40: 69, - 41: 72, - 42: 73, - 43: 75, - 44: 77, - 45: 67, - 46: 71, - 47: 161, - 48: 76, - 49: 41, - 50: 70, + 36: 76, + 37: 71, + 38: 67, + 39: 77, + 40: 74, + 41: 70, + 42: 72, + 43: 78, + 44: 69, + 45: 75, + 46: 68, + 47: 162, + 48: 79, + 49: 29, + 50: 73, 51: 129, - 52: 20, - 54: 38, - 55: 10}, + 52: 19, + 54: 12, + 55: 42}, start: 256 }; diff --git a/src/pgen/ast/Python.asdl b/src/pgen/ast/Python.asdl index 9b8a98728d..ae1cd5ea5e 100644 --- a/src/pgen/ast/Python.asdl +++ b/src/pgen/ast/Python.asdl @@ -62,7 +62,7 @@ module Python -- XXX Jython will be different -- col_offset is the byte offset in the utf8 string the parser uses - attributes (int lineno, int col_offset) + attributes (int lineno, int col_offset, int endlineno, int col_endoffset) -- BoolOp() can use left & right? expr = BoolOp(boolop op, expr* values) @@ -102,7 +102,7 @@ module Python | Tuple(expr* elts, expr_context ctx) -- col_offset is the byte offset in the utf8 string the parser uses - attributes (int lineno, int col_offset) + attributes (int lineno, int col_offset, int endlineno, int col_endoffset) expr_context = Load | Store | Del | AugLoad | AugStore | Param @@ -122,7 +122,7 @@ module Python comprehension = (expr target, expr iter, expr* ifs, int is_async) excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body) - attributes (int lineno, int col_offset) + attributes (int lineno, int col_offset, int endlineno, int col_endoffset) -- Skulpt: added the underscore because aguments is a reserved word in -- javascript @@ -130,7 +130,7 @@ module Python arg? kwarg, expr* defaults) arg = (identifier arg, expr? annotation) - attributes (int lineno, int col_offset) + attributes (int lineno, int col_offset, int endlineno, int col_endoffset) -- keyword arguments supplied to call (NULL identifier for **kwargs) keyword = (identifier? arg, expr value) diff --git a/src/pgen/ast/asdl_js.py b/src/pgen/ast/asdl_js.py index 0f86f0d718..ed9bf289bf 100755 --- a/src/pgen/ast/asdl_js.py +++ b/src/pgen/ast/asdl_js.py @@ -362,7 +362,7 @@ def main(asdlfile, outputfile): if not asdl.check(mod): sys.exit(1) - f = open(outputfile, "wb") + f = open(outputfile, "w") f.write(auto_gen_msg) f.write("/* Object that holds all nodes */\n"); @@ -390,6 +390,6 @@ def main(asdlfile, outputfile): if __name__ == "__main__": import sys if len(sys.argv) != 3: - print "usage: asdl_js.py input.asdl output.js" + print("usage: asdl_js.py input.asdl output.js") raise SystemExit() main(sys.argv[1], sys.argv[2]) From 039313b5b850828d8436d9c7cca521ee4dd35e6d Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 19 Jun 2019 10:12:15 -0400 Subject: [PATCH 02/68] Remove unneeded refs to nmber module --- src/env.js | 3 +-- src/lib/processing.js | 2 +- src/lib/time.js | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/env.js b/src/env.js index 06d6c2b70a..9fa5284d7b 100644 --- a/src/env.js +++ b/src/env.js @@ -327,8 +327,7 @@ Sk.setup_method_mappings = function () { return { "round$": { "classes": [Sk.builtin.float_, - Sk.builtin.int_, - Sk.builtin.nmber], + Sk.builtin.int_], 2: null, 3: "__round__" }, diff --git a/src/lib/processing.js b/src/lib/processing.js index c5070a5bae..13e43d96e2 100644 --- a/src/lib/processing.js +++ b/src/lib/processing.js @@ -249,7 +249,7 @@ var $builtinmodule = function (name) { mod.MOVE = new Sk.builtin.str("move"); mod.TEXT = new Sk.builtin.str("text"); mod.WAIT = new Sk.builtin.str("wait"); - mod.NOCURSOR = Sk.builtin.assk$("url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto", Sk.builtin.nmber.str); + mod.NOCURSOR = Sk.builtin.assk$("url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto", Sk.builtin.int_.str); // Hints mod.DISABLE_OPENGL_2X_SMOOTH = new Sk.builtin.int_(1); diff --git a/src/lib/time.js b/src/lib/time.js index d88893a291..2ede7cc575 100644 --- a/src/lib/time.js +++ b/src/lib/time.js @@ -262,7 +262,7 @@ var $builtinmodule = function (name) { */ mod.tzname = Sk.builtin.tuple(timeZoneNames()); - mod.accept2dyear = Sk.builtin.assk$(1, Sk.builtin.nmber.int$); + mod.accept2dyear = Sk.builtin.assk$(1, Sk.builtin.int_); mod.clock = new Sk.builtin.func(function() { var res = 0.0; From 13020777c31bf639cbfcb7af7214376c5086f5e8 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 19 Jun 2019 11:45:44 -0400 Subject: [PATCH 03/68] Add in line/col end to parser --- src/ast.js | 232 ++++++++++++++++++++++++++----------------------- src/compile.js | 4 +- src/parser.js | 8 +- 3 files changed, 131 insertions(+), 113 deletions(-) diff --git a/src/ast.js b/src/ast.js index 6bbf36aaac..4c1c8caaab 100644 --- a/src/ast.js +++ b/src/ast.js @@ -307,8 +307,8 @@ function copy_location(e, n) if (e) { e.lineno = LINENO(n); e.col_offset = n.col_offset; - e.end_lineno = n.end_lineno; - e.end_col_offset = n.end_col_offset; + e.endlineno = n.endlineno; + e.col_endoffset = n.col_endoffset; } return e; } @@ -389,16 +389,16 @@ function astForExceptClause (c, exc, body) { REQ(exc, SYM.except_clause); REQ(body, SYM.suite); if (NCH(exc) === 1) { - return new Sk.astnodes.ExceptHandler(null, null, astForSuite(c, body), exc.lineno, exc.col_offset); + return new Sk.astnodes.ExceptHandler(null, null, astForSuite(c, body), exc.lineno, exc.col_offset, exc.endlineno, exc.col_endoffset); } else if (NCH(exc) === 2) { - return new Sk.astnodes.ExceptHandler(ast_for_expr(c, CHILD(exc, 1)), null, astForSuite(c, body), exc.lineno, exc.col_offset); + return new Sk.astnodes.ExceptHandler(ast_for_expr(c, CHILD(exc, 1)), null, astForSuite(c, body), exc.lineno, exc.col_offset, exc.endlineno, exc.col_endoffset); } else if (NCH(exc) === 4) { var expression = ast_for_expr(c, CHILD(exc, 1)); e = ast_for_expr(c, CHILD(exc, 3)); setContext(c, e, Sk.astnodes.Store, CHILD(exc, 3)); - return new Sk.astnodes.ExceptHandler(ast_for_expr(c, CHILD(exc, 1)), e, astForSuite(c, body), exc.lineno, exc.col_offset); + return new Sk.astnodes.ExceptHandler(ast_for_expr(c, CHILD(exc, 1)), e, astForSuite(c, body), exc.lineno, exc.col_offset, exc.endlineno, exc.col_endoffset); } Sk.asserts.fail("wrong number of children for except clause"); } @@ -446,7 +446,7 @@ function astForTryStmt (c, n) { } Sk.asserts.assert(!!finally_ || handlers.length != 0); - return new Sk.astnodes.Try(body, handlers, orelse, finally_, n.lineno, n.col_offset); + return new Sk.astnodes.Try(body, handlers, orelse, finally_, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } function astForDottedName (c, n) { @@ -459,10 +459,10 @@ function astForDottedName (c, n) { lineno = n.lineno; col_offset = n.col_offset; id = strobj(CHILD(n, 0).value); - e = new Sk.astnodes.Name(id, Sk.astnodes.Load, lineno, col_offset); + e = new Sk.astnodes.Name(id, Sk.astnodes.Load, lineno, col_offset, n.endlineno, n.col_endoffset); for (i = 2; i < NCH(n); i += 2) { id = strobj(CHILD(n, i).value); - e = new Sk.astnodes.Attribute(e, id, Sk.astnodes.Load, lineno, col_offset); + e = new Sk.astnodes.Attribute(e, id, Sk.astnodes.Load, lineno, col_offset, n.endlineno, n.col_endoffset); } return e; } @@ -480,7 +480,7 @@ function astForDecorator (c, n) { } else if (NCH(n) === 5) // call with no args { - return new Sk.astnodes.Call(nameExpr, [], [], null, null, n.lineno, n.col_offset); + return new Sk.astnodes.Call(nameExpr, [], [], null, null, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } else { return ast_for_call(c, CHILD(n, 3), nameExpr); @@ -555,9 +555,9 @@ function ast_for_with_stmt(c, n0, is_async) { body = astForSuite(c, CHILD(n, NCH(n) - 1)); if (is_async) { - return new Sk.astnodes.AsyncWith(items, body, LINENO(n0), n0.col_offset); + return new Sk.astnodes.AsyncWith(items, body, LINENO(n0), n0.col_offset, n0.endlineno, n0.col_endoffset); } else { - return new Sk.astnodes.With(items, body, LINENO(n), n.col_offset); + return new Sk.astnodes.With(items, body, LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); } } @@ -575,7 +575,7 @@ function astForExecStmt (c, n) { if (nchildren === 6) { locals = ast_for_expr(c, CHILD(n, 5)); } - return new Sk.astnodes.Exec(expr1, globals, locals, n.lineno, n.col_offset); + return new Sk.astnodes.Exec(expr1, globals, locals, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } function astForIfStmt (c, n) { @@ -594,7 +594,7 @@ function astForIfStmt (c, n) { return new Sk.astnodes.If( ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), - [], n.lineno, n.col_offset); + [], n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } s = CHILD(n, 4).value; @@ -604,7 +604,7 @@ function astForIfStmt (c, n) { ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), astForSuite(c, CHILD(n, 6)), - n.lineno, n.col_offset); + n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } else if (decider === "i") { nElif = NCH(n) - 4; @@ -627,7 +627,9 @@ function astForIfStmt (c, n) { astForSuite(c, CHILD(n, NCH(n) - 4)), astForSuite(c, CHILD(n, NCH(n) - 1)), CHILD(n, NCH(n) - 6).lineno, - CHILD(n, NCH(n) - 6).col_offset)]; + CHILD(n, NCH(n) - 6).col_offset), + CHILD(n, NCH(n) - 6).endlineno, + CHILD(n, NCH(n) - 6).col_endoffset]; nElif--; } @@ -639,12 +641,14 @@ function astForIfStmt (c, n) { astForSuite(c, CHILD(n, off + 2)), orelse, CHILD(n, off).lineno, - CHILD(n, off).col_offset)]; + CHILD(n, off).col_offset, + CHILD(n, off).endlineno, + CHILD(n, off).col_endoffset)]; } return new Sk.astnodes.If( ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), - orelse, n.lineno, n.col_offset); + orelse, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } Sk.asserts.fail("unexpected token in 'if' statement"); @@ -669,7 +673,7 @@ function ast_for_exprlist (c, n, context) { function astForDelStmt (c, n) { /* del_stmt: 'del' exprlist */ REQ(n, SYM.del_stmt); - return new Sk.astnodes.Delete(ast_for_exprlist(c, CHILD(n, 1), Sk.astnodes.Del), n.lineno, n.col_offset); + return new Sk.astnodes.Delete(ast_for_exprlist(c, CHILD(n, 1), Sk.astnodes.Del), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } function astForGlobalStmt (c, n) { @@ -680,17 +684,17 @@ function astForGlobalStmt (c, n) { for (i = 1; i < NCH(n); i += 2) { s[(i - 1) / 2] = strobj(CHILD(n, i).value); } - return new Sk.astnodes.Global(s, n.lineno, n.col_offset); + return new Sk.astnodes.Global(s, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } function astForAssertStmt (c, n) { /* assert_stmt: 'assert' test [',' test] */ REQ(n, SYM.assert_stmt); if (NCH(n) === 2) { - return new Sk.astnodes.Assert(ast_for_expr(c, CHILD(n, 1)), null, n.lineno, n.col_offset); + return new Sk.astnodes.Assert(ast_for_expr(c, CHILD(n, 1)), null, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } else if (NCH(n) === 4) { - return new Sk.astnodes.Assert(ast_for_expr(c, CHILD(n, 1)), ast_for_expr(c, CHILD(n, 3)), n.lineno, n.col_offset); + return new Sk.astnodes.Assert(ast_for_expr(c, CHILD(n, 1)), ast_for_expr(c, CHILD(n, 3)), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } Sk.asserts.fail("improper number of parts to assert stmt"); } @@ -765,9 +769,13 @@ function astForImportStmt (c, n) { var aliases; var col_offset; var lineno; + var endlineno; + var col_endoffset; REQ(n, SYM.import_stmt); lineno = n.lineno; col_offset = n.col_offset; + endlineno = n.endlineno; + col_endoffset = n.col_endoffset n = CHILD(n, 0); if (n.type === SYM.import_name) { n = CHILD(n, 1); @@ -776,7 +784,7 @@ function astForImportStmt (c, n) { for (i = 0; i < NCH(n); i += 2) { aliases[i / 2] = aliasForImportName(c, CHILD(n, i)); } - return new Sk.astnodes.Import(aliases, lineno, col_offset); + return new Sk.astnodes.Import(aliases, lineno, col_offset, endlineno, col_endoffset); } else if (n.type === SYM.import_from) { mod = null; @@ -826,7 +834,7 @@ function astForImportStmt (c, n) { } } modname = mod ? mod.name.v : ""; - return new Sk.astnodes.ImportFrom(strobj(modname), aliases, ndots, lineno, col_offset); + return new Sk.astnodes.ImportFrom(strobj(modname), aliases, ndots, lineno, col_offset, endlineno, col_endoffset); } throw new Sk.builtin.SyntaxError("unknown import statement", c.c_filename, n.lineno); } @@ -878,11 +886,11 @@ function astForFactor (c, n) { expression = ast_for_expr(c, CHILD(n, 1)); switch (CHILD(n, 0).type) { case TOK.T_PLUS: - return new Sk.astnodes.UnaryOp(Sk.astnodes.UAdd, expression, n.lineno, n.col_offset); + return new Sk.astnodes.UnaryOp(Sk.astnodes.UAdd, expression, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); case TOK.T_MINUS: - return new Sk.astnodes.UnaryOp(Sk.astnodes.USub, expression, n.lineno, n.col_offset); + return new Sk.astnodes.UnaryOp(Sk.astnodes.USub, expression, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); case TOK.T_TILDE: - return new Sk.astnodes.UnaryOp(Sk.astnodes.Invert, expression, n.lineno, n.col_offset); + return new Sk.astnodes.UnaryOp(Sk.astnodes.Invert, expression, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } Sk.asserts.fail("unhandled factor"); @@ -904,13 +912,13 @@ function astForForStmt (c, n) { target = _target[0]; } else { - target = new Sk.astnodes.Tuple(_target, Sk.astnodes.Store, n.lineno, n.col_offset); + target = new Sk.astnodes.Tuple(_target, Sk.astnodes.Store, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } return new Sk.astnodes.For(target, ast_for_testlist(c, CHILD(n, 3)), astForSuite(c, CHILD(n, 5)), - seq, n.lineno, n.col_offset); + seq, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } function ast_for_call(c, n, func, allowgen) @@ -994,7 +1002,7 @@ function ast_for_call(c, n, func, allowgen) return NULL; } starred = new Sk.astnodes.Starred(e, Sk.astnodes.Load, LINENO(chch), - chch.col_offset); + chch.col_offset, chch.endlineno, chch.col_endoffset); args[nargs++] = starred; } else if (TYPE(chch) == TOK.T_DOUBLESTAR) { /* a keyword argument unpacking */ @@ -1061,7 +1069,7 @@ function ast_for_call(c, n, func, allowgen) } } - return new Sk.astnodes.Call(func, args, keywords, func.lineno, func.col_offset); + return new Sk.astnodes.Call(func, args, keywords, func.lineno, func.col_offset, func.endlineno, func.col_endoffset); } function ast_for_trailer(c, n, left_expr) { @@ -1073,7 +1081,7 @@ function ast_for_trailer(c, n, left_expr) { if (TYPE(CHILD(n, 0)) == TOK.T_LPAR) { if (NCH(n) == 2) return new Sk.astnodes.Call(left_expr, NULL, NULL, LINENO(n), - n.col_offset); + n.col_offset, n.endlineno, n.col_endoffset); else return ast_for_call(c, CHILD(n, 1), left_expr, true); } @@ -1082,7 +1090,7 @@ function ast_for_trailer(c, n, left_expr) { if (!attr_id) return NULL; return new Sk.astnodes.Attribute(left_expr, attr_id, Sk.astnodes.Load, - LINENO(n), n.col_offset); + LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); } else { REQ(CHILD(n, 0), TOK.T_LSQB); @@ -1093,7 +1101,8 @@ function ast_for_trailer(c, n, left_expr) { if (!slc) { return NULL; } - return new Sk.astnodes.Subscript(left_expr, slc, Sk.astnodes.Load, LINENO(n), n.col_offset); + return new Sk.astnodes.Subscript(left_expr, slc, Sk.astnodes.Load, + LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); } else { /* The grammar is ambiguous here. The ambiguity is resolved @@ -1118,7 +1127,8 @@ function ast_for_trailer(c, n, left_expr) { } if (!simple) { return new Sk.astnodes.Subscript(left_expr, new Sk.astnodes.ExtSlice(slices), - Sk.astnodes.Load, LINENO(n), n.col_offset); + Sk.astnodes.Load, LINENO(n), n.col_offset, + n.endlineno, n.col_endoffset); } /* extract Index values and put them in a Tuple */ elts = []; @@ -1128,10 +1138,10 @@ function ast_for_trailer(c, n, left_expr) { Sk.asserts.assert(slc.kind == _slice_kind.Index_kind && slc.v.Index.value); elts[j] = slc.v.Index.value; } - e = new Sk.astnodes.Tuple(elts, Sk.astnodes.Load, LINENO(n), n.col_offset); + e = new Sk.astnodes.Tuple(elts, Sk.astnodes.Load, LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); return new Sk.astnodes.Subscript(left_expr, new Sk.astnodes.Index(e), - Sk.astnodes.Load, LINENO(n), n.col_offset); + Sk.astnodes.Load, LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); } } } @@ -1155,36 +1165,36 @@ function ast_for_flow_stmt(c, n) switch (TYPE(ch)) { case SYM.break_stmt: return new Sk.astnodes.Break(LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); case SYM.continue_stmt: return new Sk.astnodes.Continue(LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); case SYM.yield_stmt: { /* will reduce to yield_expr */ var exp = ast_for_expr(c, CHILD(ch, 0)); if (!exp) { return null; } return new Sk.astnodes.Expr(exp, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); } case SYM.return_stmt: if (NCH(ch) == 1) return new Sk.astnodes.Return(null, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); else { var expression = ast_for_testlist(c, CHILD(ch, 1)); if (!expression) { return null; } return new Sk.astnodes.Return(expression, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); } case SYM.raise_stmt: // This is tricky and Skulpt-specific, because we need to handle // both Python 3-style and Python 2-style 'raise' statements if (NCH(ch) == 1) return new Sk.astnodes.Raise(null, null, null, null, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); else if (NCH(ch) >= 2) { var cause = null; var expression = ast_for_expr(c, CHILD(ch, 1)); @@ -1209,7 +1219,7 @@ function ast_for_flow_stmt(c, n) } } return new Sk.astnodes.Raise(expression, cause, inst, tback, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); } /* fall through */ default: @@ -1233,7 +1243,7 @@ function astForArg(c, n) annotation = ast_for_expr(c, CHILD(n, 2)); } - return new Sk.astnodes.arg(name, annotation, n.lineno, n.col_offset); + return new Sk.astnodes.arg(name, annotation, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } /* returns -1 if failed to handle keyword only arguments @@ -1278,7 +1288,7 @@ function handleKeywordonlyArgs(c, n, start, kwonlyargs, kwdefaults) ch = CHILD(ch, 0); forbiddenCheck(c, ch, ch.value, ch.lineno); argname = strobj(ch.value); - kwonlyargs[j++] = new Sk.astnodes.arg(argname, annotation, ch.lineno, ch.col_offset); + kwonlyargs[j++] = new Sk.astnodes.arg(argname, annotation, ch.lineno, ch.col_offset, ch.endlineno, ch.col_endoffset); i += 2; /* the name and the comma */ break; case TOK.T_DOUBLESTAR: @@ -1482,10 +1492,10 @@ function ast_for_funcdef_impl(c, n0, decorator_seq, is_async) { if (is_async) return new Sk.astnodes.AsyncFunctionDef(name, args, body, decorator_seq, returns, type_comment, - LINENO(n0), n0.col_offset, end_lineno, end_col_offset); + LINENO(n0), n0.col_offset, n0.endlineno, n0.col_endoffset); else return new Sk.astnodes.FunctionDef(name, args, body, decorator_seq, returns, type_comment, - LINENO(n), n.col_offset, end_lineno, end_col_offset); + LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); } function astForClassBases (c, n) { @@ -1512,7 +1522,8 @@ function astForClassdef (c, n, decoratorSeq) { forbiddenCheck(c, CHILD(n,3), classname, n.lineno); return new Sk.astnodes.ClassDef(classname, [], [], s, decoratorSeq, - /*TODO docstring*/null, LINENO(n), n.col_offset); + /*TODO docstring*/null, LINENO(n), n.col_offset, + n.endlineno, n.col_endoffset); } if (TYPE(CHILD(n, 3)) === TOK.T_RPAR) { /* class NAME '(' ')' ':' suite */ @@ -1520,7 +1531,8 @@ function astForClassdef (c, n, decoratorSeq) { classname = new_identifier(CHILD(n, 1).value); forbiddenCheck(c, CHILD(n, 3), classname, CHILD(n, 3).lineno); return new Sk.astnodes.ClassDef(classname, [], [], s, decoratorSeq, - /*TODO docstring*/null, LINENO(n), n.col_offset); + /*TODO docstring*/null, LINENO(n), n.col_offset, + n.endlineno, n.col_endoffset); } /* class NAME '(' arglist ')' ':' suite */ @@ -1529,7 +1541,8 @@ function astForClassdef (c, n, decoratorSeq) { var dummy_name; var dummy; dummy_name = new_identifier(CHILD(n, 1)); - dummy = new Sk.astnodes.Name(dummy_name, Sk.astnodes.Load, LINENO(n), n.col_offset); + dummy = new Sk.astnodes.Name(dummy_name, Sk.astnodes.Load, LINENO(n), n.col_offset, + n.endlineno, n.col_endoffset); call = ast_for_call(c, CHILD(n, 3), dummy, false); } s = astForSuite(c, CHILD(n, 6)); @@ -1537,7 +1550,8 @@ function astForClassdef (c, n, decoratorSeq) { forbiddenCheck(c, CHILD(n,1), classname, CHILD(n,1).lineno); return new Sk.astnodes.ClassDef(classname, call.args, call.keywords, s, - decoratorSeq, /*TODO docstring*/null, LINENO(n), n.col_offset); + decoratorSeq, /*TODO docstring*/null, LINENO(n), n.col_offset, + n.endlineno, n.col_endoffset); } function astForLambdef (c, n) { @@ -1552,7 +1566,7 @@ function astForLambdef (c, n) { args = astForArguments(c, CHILD(n, 1)); expression = ast_for_expr(c, CHILD(n, 3)); } - return new Sk.astnodes.Lambda(args, expression, n.lineno, n.col_offset); + return new Sk.astnodes.Lambda(args, expression, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } function astForComprehension(c, n) { @@ -1631,7 +1645,7 @@ function astForComprehension(c, n) { if (NCH(forch) === 1) { comp = new Sk.astnodes.comprehension(t[0], expression, []); } else { - comp = new Sk.astnodes.comprehension(new Sk.astnodes.Tuple(t, Sk.astnodes.Store, n.lineno, n.col_offset), expression, []); + comp = new Sk.astnodes.comprehension(new Sk.astnodes.Tuple(t, Sk.astnodes.Store, n.lineno, n.col_offset, n.endlineno, n.col_endoffset), expression, []); } if (NCH(n) === 5) { n = CHILD(n, 4); @@ -1663,9 +1677,9 @@ function astForIterComp(c, n, type) { elt = ast_for_expr(c, CHILD(n, 0)); comps = astForComprehension(c, CHILD(n, 1)); if (type === COMP_GENEXP) { - return new Sk.astnodes.GeneratorExp(elt, comps, n.lineno, n.col_offset); + return new Sk.astnodes.GeneratorExp(elt, comps, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } else if (type === COMP_SETCOMP) { - return new Sk.astnodes.SetComp(elt, comps, n.lineno, n.col_offset); + return new Sk.astnodes.SetComp(elt, comps, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } } @@ -1802,7 +1816,7 @@ function ast_for_comprehension(c, n) { comp = new Sk.astnodes.comprehension(first, expression, null, is_async); else comp = new Sk.astnodes.comprehension(new Sk.astnodes.Tuple(t, Sk.astnodes.Store, first.lineno, first.col_offset, - for_ch.end_lineno, for_ch.end_col_offset), + for_ch.endlineno, for_ch.col_endoffset), expression, null, is_async); if (NCH(n) == (5 + is_async)) { @@ -1890,13 +1904,13 @@ function ast_for_itercomp(c, n, type) { if (type == COMP_GENEXP) { return new Sk.astnodes.GeneratorExp(elt, comps, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); } else if (type == COMP_LISTCOMP) { return new Sk.astnodes.ListComp(elt, comps, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); } else if (type == COMP_SETCOMP) { return new Sk.astnodes.SetComp(elt, comps, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); } else { /* Should never happen */ return null; @@ -1945,7 +1959,7 @@ function ast_for_dictcomp(c, n) { key = ast_for_expr(c, CHILD(n, 0)); value = ast_for_expr(c, CHILD(n, 2)); comps = astForComprehension(c, CHILD(n, 3)); - return new Sk.astnodes.DictComp(key, value, comps, n.lineno, n.col_offset); + return new Sk.astnodes.DictComp(key, value, comps, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } function ast_for_dictdisplay(c, n) @@ -1964,7 +1978,7 @@ function ast_for_dictdisplay(c, n) } return new Sk.astnodes.Dict(keys, values, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); } function ast_for_gen_expr(c, n) { @@ -1981,10 +1995,10 @@ function astForWhileStmt (c, n) { /* while_stmt: 'while' test ':' suite ['else' ':' suite] */ REQ(n, SYM.while_stmt); if (NCH(n) === 4) { - return new Sk.astnodes.While(ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), [], n.lineno, n.col_offset); + return new Sk.astnodes.While(ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), [], n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } else if (NCH(n) === 7) { - return new Sk.astnodes.While(ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), astForSuite(c, CHILD(n, 6)), n.lineno, n.col_offset); + return new Sk.astnodes.While(ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), astForSuite(c, CHILD(n, 6)), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } Sk.asserts.fail("wrong number of tokens for 'while' stmt"); } @@ -2037,13 +2051,13 @@ function astForBinop (c, n) { ast_for_expr(c, CHILD(n, 0)), getOperator(CHILD(n, 1)), ast_for_expr(c, CHILD(n, 2)), - n.lineno, n.col_offset); + n.lineno, n.col_offset, n.endlineno, n.col_endoffset); var nops = (NCH(n) - 1) / 2; for (i = 1; i < nops; ++i) { nextOper = CHILD(n, i * 2 + 1); newoperator = getOperator(nextOper); tmp = ast_for_expr(c, CHILD(n, i * 2 + 2)); - result = new Sk.astnodes.BinOp(result, newoperator, tmp, nextOper.lineno, nextOper.col_offset); + result = new Sk.astnodes.BinOp(result, newoperator, tmp, nextOper.lineno, nextOper.col_offset, nextOper.endlineno, nextOper.col_endoffset); } return result; } @@ -2065,7 +2079,7 @@ function ast_for_testlist (c, n) { return ast_for_expr(c, CHILD(n, 0)); } else { - return new Sk.astnodes.Tuple(seq_for_testlist(c, n), Sk.astnodes.Load, n.lineno, n.col_offset/*, c.c_arena */); + return new Sk.astnodes.Tuple(seq_for_testlist(c, n), Sk.astnodes.Load, n.lineno, n.col_offset, n.endlineno, n.col_endoffset/*, c.c_arena */); } } @@ -2089,7 +2103,7 @@ function ast_for_exprStmt (c, n) { test: ... here starts the operator precedence dance */ if (NCH(n) === 1) { - return new Sk.astnodes.Expr(ast_for_testlist(c, CHILD(n, 0)), n.lineno, n.col_offset); + return new Sk.astnodes.Expr(ast_for_testlist(c, CHILD(n, 0)), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } else if (CHILD(n, 1).type === SYM.augassign) { ch = CHILD(n, 0); @@ -2119,7 +2133,7 @@ function ast_for_exprStmt (c, n) { expr2 = ast_for_expr(c, ch); } - return new Sk.astnodes.AugAssign(expr1, astForAugassign(c, CHILD(n, 1)), expr2, n.lineno, n.col_offset); + return new Sk.astnodes.AugAssign(expr1, astForAugassign(c, CHILD(n, 1)), expr2, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } else if (CHILD(n, 1).type === SYM.annassign) { // TODO translate the relevant section from ast.c @@ -2145,7 +2159,7 @@ function ast_for_exprStmt (c, n) { else { expression = ast_for_expr(c, value); } - return new Sk.astnodes.Assign(targets, expression, n.lineno, n.col_offset); + return new Sk.astnodes.Assign(targets, expression, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } } @@ -2156,7 +2170,7 @@ function astForIfexpr (c, n) { ast_for_expr(c, CHILD(n, 2)), ast_for_expr(c, CHILD(n, 0)), ast_for_expr(c, CHILD(n, 4)), - n.lineno, n.col_offset); + n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } /** @@ -2415,7 +2429,7 @@ function astForSlice (c, n) { if (ch.type === SYM.sliceop) { if (NCH(ch) === 1) { ch = CHILD(ch, 0); - step = new Sk.astnodes.NameConstant(Sk.builtin.none.none$, Sk.astnodes.Load, ch.lineno, ch.col_offset); + step = new Sk.astnodes.NameConstant(Sk.builtin.none.none$, Sk.astnodes.Load, ch.lineno, ch.col_offset, ch.endlineno, ch.col_endoffset); } else { ch = CHILD(ch, 1); @@ -2441,21 +2455,21 @@ function ast_for_atom(c, n) var s = STR(ch); if (s.length >= 4 && s.length <= 5) { if (s === "None") { - return new Sk.astnodes.NameConstant(Sk.builtin.none.none$, n.lineno, n.col_offset); + return new Sk.astnodes.NameConstant(Sk.builtin.none.none$, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } if (s === "True") { - return new Sk.astnodes.NameConstant(Sk.builtin.bool.true$, n.lineno, n.col_offset); + return new Sk.astnodes.NameConstant(Sk.builtin.bool.true$, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } if (s === "False") { - return new Sk.astnodes.NameConstant(Sk.builtin.bool.false$, n.lineno, n.col_offset); + return new Sk.astnodes.NameConstant(Sk.builtin.bool.false$, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } } name = new_identifier(s, c); /* All names start in Load context, but may later be changed. */ return new Sk.astnodes.Name(name, Sk.astnodes.Load, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); } case TOK.T_STRING: { var str = parsestrplus(c, n); @@ -2483,19 +2497,19 @@ function ast_for_atom(c, n) // } // return NULL; // } - return new Sk.astnodes.Str(str, LINENO(n), n.col_offset, c.end_lineno, n.end_col_offset); + return new Sk.astnodes.Str(str, LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); } case TOK.T_NUMBER: - return new Sk.astnodes.Num(parsenumber(c, ch.value, n.lineno), n.lineno, n.col_offset); + return new Sk.astnodes.Num(parsenumber(c, ch.value, n.lineno), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); case TOK.T_ELLIPSIS: /* Ellipsis */ return new Sk.astnodes.Ellipsis(LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); case TOK.T_LPAR: /* some parenthesized expressions */ ch = CHILD(n, 1); if (TYPE(ch) == TOK.T_RPAR) return new Sk.astnodes.Tuple([], Sk.astnodes.Load, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); if (TYPE(ch) == SYM.yield_expr) { return ast_for_expr(c, ch); @@ -2517,7 +2531,7 @@ function ast_for_atom(c, n) if (TYPE(ch) == TOK.T_RSQB) return new Sk.astnodes.List([], Sk.astnodes.Load, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); REQ(ch, SYM.testlist_comp); if (NCH(ch) == 1 || TYPE(CHILD(ch, 1)) == TOK.T_COMMA) { @@ -2526,7 +2540,7 @@ function ast_for_atom(c, n) return null; } return new Sk.astnodes.List(elts, Sk.astnodes.Load, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); } else { return copy_location(ast_for_listcomp(c, ch), n); @@ -2541,7 +2555,7 @@ function ast_for_atom(c, n) if (TYPE(ch) == TOK.T_RBRACE) { /* It's an empty dict. */ return new Sk.astnodes.Dict(null, null, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.endlineno, n.col_endoffset); } else { var is_dict = (TYPE(CHILD(ch, 0)) == TOK.T_DOUBLESTAR); @@ -2591,7 +2605,7 @@ function ast_for_setdisplay(c, n) { elts[i / 2] = expression; } - return new Sk.astnodes.Set(elts, LINENO(n), n.col_offset); + return new Sk.astnodes.Set(elts, LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); } @@ -2612,30 +2626,30 @@ function astForAtom(c, n) { // All names start in Load context, but may be changed later if (s.length >= 4 && s.length <= 5) { if (s === "None") { - return new Sk.astnodes.NameConstant(Sk.builtin.none.none$, n.lineno, n.col_offset /* c.c_arena*/); + return new Sk.astnodes.NameConstant(Sk.builtin.none.none$, n.lineno, n.col_offset, n.endlineno, n.col_endoffset /* c.c_arena*/); } if (s === "True") { - return new Sk.astnodes.NameConstant(Sk.builtin.bool.true$, n.lineno, n.col_offset /* c.c_arena*/); + return new Sk.astnodes.NameConstant(Sk.builtin.bool.true$, n.lineno, n.col_offset, n.endlineno, n.col_endoffset /* c.c_arena*/); } if (s === "False") { - return new Sk.astnodes.NameConstant(Sk.builtin.bool.false$, n.lineno, n.col_offset /* c.c_arena*/); + return new Sk.astnodes.NameConstant(Sk.builtin.bool.false$, n.lineno, n.col_offset, n.endlineno, n.col_endoffset /* c.c_arena*/); } } var name = new_identifier(s, c) /* All names start in Load context, but may later be changed. */ - return new Sk.astnodes.Name(name, Sk.astnodes.Load, n.lineno, n.col_offset); + return new Sk.astnodes.Name(name, Sk.astnodes.Load, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); case TOK.T_STRING: - return new Sk.astnodes.Str(parsestrplus(c, n), n.lineno, n.col_offset); + return new Sk.astnodes.Str(parsestrplus(c, n), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); case TOK.T_NUMBER: - return new Sk.astnodes.Num(parsenumber(c, ch.value, n.lineno), n.lineno, n.col_offset); + return new Sk.astnodes.Num(parsenumber(c, ch.value, n.lineno), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); case TOK.T_LPAR: // various uses for parens ch = CHILD(n, 1); if (ch.type === TOK.T_RPAR) { - return new Sk.astnodes.Tuple([], Sk.astnodes.Load, n.lineno, n.col_offset); + return new Sk.astnodes.Tuple([], Sk.astnodes.Load, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } if (ch.type === SYM.yield_expr) { return ast_for_expr(c, ch); @@ -2647,11 +2661,11 @@ function astForAtom(c, n) { case TOK.T_LSQB: // list or listcomp ch = CHILD(n, 1); if (ch.type === TOK.T_RSQB) { - return new Sk.astnodes.List([], Sk.astnodes.Load, n.lineno, n.col_offset); + return new Sk.astnodes.List([], Sk.astnodes.Load, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } REQ(ch, SYM.listmaker); if (NCH(ch) === 1 || CHILD(ch, 1).type === TOK.T_COMMA) { - return new Sk.astnodes.List(seq_for_testlist(c, ch), Sk.astnodes.Load, n.lineno, n.col_offset); + return new Sk.astnodes.List(seq_for_testlist(c, ch), Sk.astnodes.Load, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } return ast_for_listcomp(c, ch); @@ -2665,7 +2679,7 @@ function astForAtom(c, n) { ch = CHILD(n, 1); if (n.type === TOK.T_RBRACE) { //it's an empty dict - return new Sk.astnodes.Dict([], null, n.lineno, n.col_offset); + return new Sk.astnodes.Dict([], null, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } else if (NCH(ch) === 1 || (NCH(ch) !== 0 && CHILD(ch, 1).type === TOK.T_COMMA)) { //it's a simple set @@ -2675,7 +2689,7 @@ function astForAtom(c, n) { var expression = ast_for_expr(c, CHILD(ch, i)); elts[i / 2] = expression; } - return new Sk.astnodes.Set(elts, n.lineno, n.col_offset); + return new Sk.astnodes.Set(elts, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } else if (NCH(ch) !== 0 && CHILD(ch, 1).type == SYM.comp_for) { //it's a set comprehension @@ -2691,11 +2705,11 @@ function astForAtom(c, n) { keys[i / 4] = ast_for_expr(c, CHILD(ch, i)); values[i / 4] = ast_for_expr(c, CHILD(ch, i + 2)); } - return new Sk.astnodes.Dict(keys, values, n.lineno, n.col_offset); + return new Sk.astnodes.Dict(keys, values, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } case TOK.T_BACKQUOTE: //throw new Sk.builtin.SyntaxError("backquote not supported, use repr()", c.c_filename, n.lineno); - return new Sk.astnodes.Repr(ast_for_testlist(c, CHILD(n, 1)), n.lineno, n.col_offset); + return new Sk.astnodes.Repr(ast_for_testlist(c, CHILD(n, 1)), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); default: Sk.asserts.fail("unhandled atom", ch.type); @@ -2724,7 +2738,7 @@ function astForAtomExpr(c, n) { } if (start && nch === 2) { - return new Sk.astnodes.Await(e, n.lineno, n.col_offset /*, c->c_arena*/); + return new Sk.astnodes.Await(e, n.lineno, n.col_offset, n.endlineno, n.col_endoffset /*, c->c_arena*/); } for (i = start + 1; i < nch; i++) { @@ -2744,7 +2758,7 @@ function astForAtomExpr(c, n) { if (start) { /* there was an AWAIT */ - return new Sk.astnodes.Await(e, n.line, n.col_offset /*, c->c_arena*/); + return new Sk.astnodes.Await(e, n.line, n.col_offset, n.endlineno, n.col_endoffset /*, c->c_arena*/); } else { return e; @@ -2766,7 +2780,7 @@ function astForPower (c, n) { } if (CHILD(n, NCH(n) - 1).type === SYM.factor) { f = ast_for_expr(c, CHILD(n, NCH(n) - 1)); - e = new Sk.astnodes.BinOp(e, Sk.astnodes.Pow, f, n.lineno, n.col_offset); + e = new Sk.astnodes.BinOp(e, Sk.astnodes.Pow, f, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } return e; } @@ -2775,7 +2789,7 @@ function astForStarred(c, n) { REQ(n, SYM.star_expr); /* The Load context is changed later */ - return new Sk.astnodes.Starred(ast_for_expr(c, CHILD(n ,1)), Sk.astnodes.Load, n.lineno, n.col_offset /*, c.c_arena */) + return new Sk.astnodes.Starred(ast_for_expr(c, CHILD(n ,1)), Sk.astnodes.Load, n.lineno, n.col_offset, n.endlineno, n.col_endoffset /*, c.c_arena */) } function ast_for_expr (c, n) { @@ -2826,17 +2840,17 @@ function ast_for_expr (c, n) { seq[i / 2] = ast_for_expr(c, CHILD(n, i)); } if (CHILD(n, 1).value === "and") { - return new Sk.astnodes.BoolOp(Sk.astnodes.And, seq, n.lineno, n.col_offset /*, c.c_arena*/); + return new Sk.astnodes.BoolOp(Sk.astnodes.And, seq, n.lineno, n.col_offset, n.endlineno, n.col_endoffset /*, c.c_arena*/); } Sk.asserts.assert(CHILD(n, 1).value === "or"); - return new Sk.astnodes.BoolOp(Sk.astnodes.Or, seq, n.lineno, n.col_offset); + return new Sk.astnodes.BoolOp(Sk.astnodes.Or, seq, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); case SYM.not_test: if (NCH(n) === 1) { n = CHILD(n, 0); continue LOOP; } else { - return new Sk.astnodes.UnaryOp(Sk.astnodes.Not, ast_for_expr(c, CHILD(n, 1)), n.lineno, n.col_offset); + return new Sk.astnodes.UnaryOp(Sk.astnodes.Not, ast_for_expr(c, CHILD(n, 1)), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } break; case SYM.comparison: @@ -2851,7 +2865,7 @@ function ast_for_expr (c, n) { ops[(i - 1) / 2] = astForCompOp(c, CHILD(n, i)); cmps[(i - 1) / 2] = ast_for_expr(c, CHILD(n, i + 1)); } - return new Sk.astnodes.Compare(ast_for_expr(c, CHILD(n, 0)), ops, cmps, n.lineno, n.col_offset); + return new Sk.astnodes.Compare(ast_for_expr(c, CHILD(n, 0)), ops, cmps, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } break; case SYM.star_expr: @@ -2891,10 +2905,10 @@ function ast_for_expr (c, n) { } if (is_from) { - return new Sk.astnodes.YieldFrom(exp, n.lineno, n.col_offset); + return new Sk.astnodes.YieldFrom(exp, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } - return new Sk.astnodes.Yield(exp, n.lineno, n.col_offset); + return new Sk.astnodes.Yield(exp, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); case SYM.factor: if (NCH(n) === 1) { n = CHILD(n, 0); @@ -2943,7 +2957,7 @@ function astForPrintStmt (c, n) { seq[j] = ast_for_expr(c, CHILD(n, i)); } nl = (CHILD(n, NCH(n) - 1)).type === TOK.T_COMMA ? false : true; - return new Sk.astnodes.Print(dest, seq, nl, n.lineno, n.col_offset); + return new Sk.astnodes.Print(dest, seq, nl, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); } function astForStmt (c, n) { @@ -2968,7 +2982,7 @@ function astForStmt (c, n) { case SYM.del_stmt: return astForDelStmt(c, n); case SYM.pass_stmt: - return new Sk.astnodes.Pass(n.lineno, n.col_offset); + return new Sk.astnodes.Pass(n.lineno, n.col_offset, n.endlineno, n.col_endoffset); case SYM.flow_stmt: return ast_for_flow_stmt(c, n); case SYM.import_stmt: @@ -2982,7 +2996,7 @@ function astForStmt (c, n) { case SYM.print_stmt: return astForPrintStmt(c, n); case SYM.debugger_stmt: - return new Sk.astnodes.Debugger(n.lineno, n.col_offset); + return new Sk.astnodes.Debugger(n.lineno, n.col_offset, n.endlineno, n.col_endoffset); default: Sk.asserts.fail("unhandled small_stmt"); } diff --git a/src/compile.js b/src/compile.js index a30cfa89ff..3c5f30f13e 100644 --- a/src/compile.js +++ b/src/compile.js @@ -982,7 +982,7 @@ Compiler.prototype.caugassign = function (s) { switch (e.constructor) { case Sk.astnodes.Attribute: to = this.vexpr(e.value); - auge = new Sk.astnodes.Attribute(e.value, e.attr, Sk.astnodes.AugLoad, e.lineno, e.col_offset); + auge = new Sk.astnodes.Attribute(e.value, e.attr, Sk.astnodes.AugLoad, e.lineno, e.col_offset, e.endlineno, e.col_endoffset); aug = this.vexpr(auge, undefined, to); val = this.vexpr(s.value); res = this._gr("inplbinopattr", "Sk.abstr.numberInplaceBinOp(", aug, ",", val, ",'", s.op.prototype._astname, "')"); @@ -992,7 +992,7 @@ Compiler.prototype.caugassign = function (s) { // Only compile the subscript value once to = this.vexpr(e.value); augsub = this.vslicesub(e.slice); - auge = new Sk.astnodes.Subscript(e.value, augsub, Sk.astnodes.AugLoad, e.lineno, e.col_offset); + auge = new Sk.astnodes.Subscript(e.value, augsub, Sk.astnodes.AugLoad, e.lineno, e.col_offset, e.endlineno, e.col_endoffset); aug = this.vexpr(auge, undefined, to, augsub); val = this.vexpr(s.value); res = this._gr("inplbinopsubscr", "Sk.abstr.numberInplaceBinOp(", aug, ",", val, ",'", s.op.prototype._astname, "')"); diff --git a/src/parser.js b/src/parser.js index a00a6846b7..ef6d5bc53b 100644 --- a/src/parser.js +++ b/src/parser.js @@ -193,8 +193,10 @@ Parser.prototype.shift = function (type, value, newstate, context) { var newnode = { type : type, value : value, - lineno : context[0][0], // throwing away end here to match cpython + lineno : context[0][0], col_offset: context[0][1], + endlineno : context[1][0], + col_endoffset: context[1][1], children : null }; if (newnode) { @@ -214,8 +216,10 @@ Parser.prototype.push = function (type, newdfa, newstate, context) { var newnode = { type : type, value : null, - lineno : context[0][0], // throwing away end here to match cpython + lineno : context[0][0], col_offset: context[0][1], + endlineno : context[1][0], + col_endoffset: context[1][1], children : [] }; this.stack[this.stack.length - 1] = { From c141a970bf97a851db75117c12a1476b99a88bec Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 19 Jun 2019 11:47:54 -0400 Subject: [PATCH 04/68] Adopt more skulpty names for line/col end --- gen/astnodes.js | 682 ++++++++--------- gen/parse_tables.js | 1514 +++++++++++++++++++------------------- src/ast.js | 230 +++--- src/compile.js | 4 +- src/parser.js | 8 +- src/pgen/ast/Python.asdl | 8 +- 6 files changed, 1225 insertions(+), 1221 deletions(-) diff --git a/gen/astnodes.js b/gen/astnodes.js index b815858a60..a2f2e22661 100644 --- a/gen/astnodes.js +++ b/gen/astnodes.js @@ -137,13 +137,13 @@ Sk.astnodes.FunctionDef = function FunctionDef(/* {identifier} */ name, /* {string} */ docstring, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.name = name; this.args = args; this.body = body; @@ -152,8 +152,8 @@ Sk.astnodes.FunctionDef = function FunctionDef(/* {identifier} */ name, /* this.docstring = docstring; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -174,14 +174,14 @@ Sk.astnodes.AsyncFunctionDef = function AsyncFunctionDef(/* {identifier} */ /* {int} */ col_offset, /* {int} */ - endlineno, /* + end_lineno, /* {int} */ - col_endoffset) + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.name = name; this.args = args; this.body = body; @@ -190,8 +190,8 @@ Sk.astnodes.AsyncFunctionDef = function AsyncFunctionDef(/* {identifier} */ this.docstring = docstring; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -203,13 +203,13 @@ Sk.astnodes.ClassDef = function ClassDef(/* {identifier} */ name, /* {asdl_seq decorator_list, /* {string} */ docstring, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.name = name; this.bases = bases; this.keywords = keywords; @@ -218,43 +218,44 @@ Sk.astnodes.ClassDef = function ClassDef(/* {identifier} */ name, /* {asdl_seq this.docstring = docstring; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Return = function Return(/* {expr_ty} */ value, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Delete = function Delete(/* {asdl_seq *} */ targets, /* {int} */ lineno, /* {int} */ col_offset, /* - {int} */ endlineno, /* {int} */ - col_endoffset) + {int} */ end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.targets = targets; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -262,18 +263,19 @@ Sk.astnodes.Delete = function Delete(/* {asdl_seq *} */ targets, /* {int} */ Sk.astnodes.Assign = function Assign(/* {asdl_seq *} */ targets, /* {expr_ty} */ value, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.targets = targets; this.value = value; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -282,20 +284,20 @@ Sk.astnodes.AugAssign = function AugAssign(/* {expr_ty} */ target, /* {operator_ty} */ op, /* {expr_ty} */ value, /* {int} */ lineno, /* {int} */ col_offset, - /* {int} */ endlineno, /* {int} - */ col_endoffset) + /* {int} */ end_lineno, /* + {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.target = target; this.op = op; this.value = value; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -305,21 +307,21 @@ Sk.astnodes.AnnAssign = function AnnAssign(/* {expr_ty} */ target, /* {expr_ty} value, /* {int} */ simple, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.target = target; this.annotation = annotation; this.value = value; this.simple = simple; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -327,21 +329,21 @@ Sk.astnodes.AnnAssign = function AnnAssign(/* {expr_ty} */ target, /* {expr_ty} Sk.astnodes.For = function For(/* {expr_ty} */ target, /* {expr_ty} */ iter, /* {asdl_seq *} */ body, /* {asdl_seq *} */ orelse, /* {int} */ lineno, /* {int} */ - col_offset, /* {int} */ endlineno, /* {int} - */ col_endoffset) + col_offset, /* {int} */ end_lineno, /* + {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.target = target; this.iter = iter; this.body = body; this.orelse = orelse; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -351,21 +353,21 @@ Sk.astnodes.AsyncFor = function AsyncFor(/* {expr_ty} */ target, /* {expr_ty} /* {asdl_seq *} */ orelse, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.target = target; this.iter = iter; this.body = body; this.orelse = orelse; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -373,20 +375,20 @@ Sk.astnodes.AsyncFor = function AsyncFor(/* {expr_ty} */ target, /* {expr_ty} Sk.astnodes.While = function While(/* {expr_ty} */ test, /* {asdl_seq *} */ body, /* {asdl_seq *} */ orelse, /* {int} */ lineno, /* {int} */ - col_offset, /* {int} */ endlineno, /* - {int} */ col_endoffset) + col_offset, /* {int} */ end_lineno, /* + {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.test = test; this.body = body; this.orelse = orelse; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -394,38 +396,38 @@ Sk.astnodes.While = function While(/* {expr_ty} */ test, /* {asdl_seq *} */ Sk.astnodes.If = function If(/* {expr_ty} */ test, /* {asdl_seq *} */ body, /* {asdl_seq *} */ orelse, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ col_endoffset) + end_lineno, /* {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.test = test; this.body = body; this.orelse = orelse; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.With = function With(/* {asdl_seq *} */ items, /* {asdl_seq *} */ body, /* {int} */ lineno, /* {int} */ - col_offset, /* {int} */ endlineno, /* - {int} */ col_endoffset) + col_offset, /* {int} */ end_lineno, /* + {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.items = items; this.body = body; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -434,19 +436,19 @@ Sk.astnodes.AsyncWith = function AsyncWith(/* {asdl_seq *} */ items, /* {asdl_seq *} */ body, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.items = items; this.body = body; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -454,21 +456,21 @@ Sk.astnodes.AsyncWith = function AsyncWith(/* {asdl_seq *} */ items, /* Sk.astnodes.Raise = function Raise(/* {expr_ty} */ exc, /* {expr_ty} */ cause, /* {expr_ty} */ inst, /* {expr_ty} */ tback, /* {int} */ lineno, /* {int} */ - col_offset, /* {int} */ endlineno, /* - {int} */ col_endoffset) + col_offset, /* {int} */ end_lineno, /* + {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.exc = exc; this.cause = cause; this.inst = inst; this.tback = tback; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -477,57 +479,57 @@ Sk.astnodes.Try = function Try(/* {asdl_seq *} */ body, /* {asdl_seq *} */ handlers, /* {asdl_seq *} */ orelse, /* {asdl_seq *} */ finalbody, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ col_endoffset) + end_lineno, /* {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.body = body; this.handlers = handlers; this.orelse = orelse; this.finalbody = finalbody; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Assert = function Assert(/* {expr_ty} */ test, /* {expr_ty} */ msg, /* {int} */ lineno, /* {int} */ - col_offset, /* {int} */ endlineno, /* - {int} */ col_endoffset) + col_offset, /* {int} */ end_lineno, + /* {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.test = test; this.msg = msg; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Import = function Import(/* {asdl_seq *} */ names, /* {int} */ lineno, /* {int} */ col_offset, /* - {int} */ endlineno, /* {int} */ - col_endoffset) + {int} */ end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.names = names; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -537,122 +539,122 @@ Sk.astnodes.ImportFrom = function ImportFrom(/* {identifier} */ module, /* {int} */ level, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.module = module; this.names = names; this.level = level; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Global = function Global(/* {asdl_seq *} */ names, /* {int} */ lineno, /* {int} */ col_offset, /* - {int} */ endlineno, /* {int} */ - col_endoffset) + {int} */ end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.names = names; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Nonlocal = function Nonlocal(/* {asdl_seq *} */ names, /* {int} */ lineno, /* {int} */ col_offset, - /* {int} */ endlineno, /* {int} - */ col_endoffset) + /* {int} */ end_lineno, /* {int} + */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.names = names; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Expr = function Expr(/* {expr_ty} */ value, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ col_endoffset) + end_lineno, /* {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Pass = function Pass(/* {int} */ lineno, /* {int} */ col_offset, /* - {int} */ endlineno, /* {int} */ - col_endoffset) + {int} */ end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Break = function Break(/* {int} */ lineno, /* {int} */ col_offset, - /* {int} */ endlineno, /* {int} */ - col_endoffset) + /* {int} */ end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Continue = function Continue(/* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -660,56 +662,57 @@ Sk.astnodes.Continue = function Continue(/* {int} */ lineno, /* {int} */ Sk.astnodes.Print = function Print(/* {expr_ty} */ dest, /* {asdl_seq *} */ values, /* {int} */ nl, /* {int} */ lineno, /* {int} */ col_offset, /* - {int} */ endlineno, /* {int} */ - col_endoffset) + {int} */ end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.dest = dest; this.values = values; this.nl = nl; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Debugger = function Debugger(/* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.BoolOp = function BoolOp(/* {boolop_ty} */ op, /* {asdl_seq *} */ values, /* {int} */ lineno, /* {int} - */ col_offset, /* {int} */ endlineno, - /* {int} */ col_endoffset) + */ col_offset, /* {int} */ + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.op = op; this.values = values; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -717,20 +720,20 @@ Sk.astnodes.BoolOp = function BoolOp(/* {boolop_ty} */ op, /* {asdl_seq *} */ Sk.astnodes.BinOp = function BinOp(/* {expr_ty} */ left, /* {operator_ty} */ op, /* {expr_ty} */ right, /* {int} */ lineno, /* {int} */ col_offset, /* - {int} */ endlineno, /* {int} */ - col_endoffset) + {int} */ end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.left = left; this.op = op; this.right = right; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -738,38 +741,39 @@ Sk.astnodes.BinOp = function BinOp(/* {expr_ty} */ left, /* {operator_ty} */ Sk.astnodes.UnaryOp = function UnaryOp(/* {unaryop_ty} */ op, /* {expr_ty} */ operand, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.op = op; this.operand = operand; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Lambda = function Lambda(/* {arguments__ty} */ args, /* {expr_ty} */ body, /* {int} */ lineno, /* {int} - */ col_offset, /* {int} */ endlineno, - /* {int} */ col_endoffset) + */ col_offset, /* {int} */ + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.args = args; this.body = body; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -777,56 +781,56 @@ Sk.astnodes.Lambda = function Lambda(/* {arguments__ty} */ args, /* {expr_ty} Sk.astnodes.IfExp = function IfExp(/* {expr_ty} */ test, /* {expr_ty} */ body, /* {expr_ty} */ orelse, /* {int} */ lineno, /* {int} */ col_offset, /* - {int} */ endlineno, /* {int} */ - col_endoffset) + {int} */ end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.test = test; this.body = body; this.orelse = orelse; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Dict = function Dict(/* {asdl_seq *} */ keys, /* {asdl_seq *} */ values, /* {int} */ lineno, /* {int} */ - col_offset, /* {int} */ endlineno, /* - {int} */ col_endoffset) + col_offset, /* {int} */ end_lineno, /* + {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.keys = keys; this.values = values; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Set = function Set(/* {asdl_seq *} */ elts, /* {int} */ lineno, /* - {int} */ col_offset, /* {int} */ endlineno, - /* {int} */ col_endoffset) + {int} */ col_offset, /* {int} */ + end_lineno, /* {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.elts = elts; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -834,19 +838,19 @@ Sk.astnodes.Set = function Set(/* {asdl_seq *} */ elts, /* {int} */ lineno, /* Sk.astnodes.ListComp = function ListComp(/* {expr_ty} */ elt, /* {asdl_seq *} */ generators, /* {int} */ lineno, /* {int} */ col_offset, - /* {int} */ endlineno, /* {int} - */ col_endoffset) + /* {int} */ end_lineno, /* {int} + */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.elt = elt; this.generators = generators; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -854,19 +858,19 @@ Sk.astnodes.ListComp = function ListComp(/* {expr_ty} */ elt, /* {asdl_seq *} Sk.astnodes.SetComp = function SetComp(/* {expr_ty} */ elt, /* {asdl_seq *} */ generators, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.elt = elt; this.generators = generators; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -875,20 +879,20 @@ Sk.astnodes.DictComp = function DictComp(/* {expr_ty} */ key, /* {expr_ty} */ value, /* {asdl_seq *} */ generators, /* {int} */ lineno, /* {int} */ col_offset, /* {int} - */ endlineno, /* {int} */ - col_endoffset) + */ end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.key = key; this.value = value; this.generators = generators; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -898,71 +902,71 @@ Sk.astnodes.GeneratorExp = function GeneratorExp(/* {expr_ty} */ elt, /* generators, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.elt = elt; this.generators = generators; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Await = function Await(/* {expr_ty} */ value, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ col_endoffset) + end_lineno, /* {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Yield = function Yield(/* {expr_ty} */ value, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ col_endoffset) + end_lineno, /* {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.YieldFrom = function YieldFrom(/* {expr_ty} */ value, /* {int} */ lineno, /* {int} */ col_offset, - /* {int} */ endlineno, /* {int} - */ col_endoffset) + /* {int} */ end_lineno, /* + {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -971,20 +975,20 @@ Sk.astnodes.Compare = function Compare(/* {expr_ty} */ left, /* {asdl_int_seq *} */ ops, /* {asdl_seq *} */ comparators, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.left = left; this.ops = ops; this.comparators = comparators; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -992,53 +996,53 @@ Sk.astnodes.Compare = function Compare(/* {expr_ty} */ left, /* {asdl_int_seq Sk.astnodes.Call = function Call(/* {expr_ty} */ func, /* {asdl_seq *} */ args, /* {asdl_seq *} */ keywords, /* {int} */ lineno, /* {int} */ col_offset, /* {int} - */ endlineno, /* {int} */ col_endoffset) + */ end_lineno, /* {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.func = func; this.args = args; this.keywords = keywords; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Num = function Num(/* {object} */ n, /* {int} */ lineno, /* {int} - */ col_offset, /* {int} */ endlineno, /* - {int} */ col_endoffset) + */ col_offset, /* {int} */ end_lineno, /* + {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.n = n; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Str = function Str(/* {string} */ s, /* {int} */ lineno, /* {int} - */ col_offset, /* {int} */ endlineno, /* - {int} */ col_endoffset) + */ col_offset, /* {int} */ end_lineno, /* + {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.s = s; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -1049,21 +1053,21 @@ Sk.astnodes.FormattedValue = function FormattedValue(/* {expr_ty} */ value, /* format_spec, /* {int} */ lineno, /* {int} */ col_offset, /* - {int} */ endlineno, + {int} */ end_lineno, /* {int} */ - col_endoffset) + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.value = value; this.conversion = conversion; this.format_spec = format_spec; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -1071,35 +1075,35 @@ Sk.astnodes.FormattedValue = function FormattedValue(/* {expr_ty} */ value, /* Sk.astnodes.JoinedStr = function JoinedStr(/* {asdl_seq *} */ values, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.values = values; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Bytes = function Bytes(/* {bytes} */ s, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ col_endoffset) + end_lineno, /* {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.s = s; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -1107,53 +1111,53 @@ Sk.astnodes.Bytes = function Bytes(/* {bytes} */ s, /* {int} */ lineno, /* Sk.astnodes.NameConstant = function NameConstant(/* {singleton} */ value, /* {int} */ lineno, /* {int} */ col_offset, /* {int} - */ endlineno, /* {int} */ - col_endoffset) + */ end_lineno, /* {int} + */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Ellipsis = function Ellipsis(/* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Constant = function Constant(/* {constant} */ value, /* {int} */ lineno, /* {int} */ col_offset, - /* {int} */ endlineno, /* {int} - */ col_endoffset) + /* {int} */ end_lineno, /* {int} + */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.value = value; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -1163,20 +1167,20 @@ Sk.astnodes.Attribute = function Attribute(/* {expr_ty} */ value, /* {expr_context_ty} */ ctx, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.value = value; this.attr = attr; this.ctx = ctx; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -1185,20 +1189,20 @@ Sk.astnodes.Subscript = function Subscript(/* {expr_ty} */ value, /* {slice_ty} */ slice, /* {expr_context_ty} */ ctx, /* {int} */ lineno, /* {int} */ col_offset, /* {int} - */ endlineno, /* {int} */ - col_endoffset) + */ end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.value = value; this.slice = slice; this.ctx = ctx; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -1206,57 +1210,57 @@ Sk.astnodes.Subscript = function Subscript(/* {expr_ty} */ value, /* {slice_ty} Sk.astnodes.Starred = function Starred(/* {expr_ty} */ value, /* {expr_context_ty} */ ctx, /* {int} */ lineno, /* {int} */ col_offset, - /* {int} */ endlineno, /* {int} */ - col_endoffset) + /* {int} */ end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.value = value; this.ctx = ctx; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.Name = function Name(/* {identifier} */ id, /* {expr_context_ty} */ ctx, /* {int} */ lineno, /* {int} */ - col_offset, /* {int} */ endlineno, /* - {int} */ col_endoffset) + col_offset, /* {int} */ end_lineno, /* + {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.id = id; this.ctx = ctx; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } /** @constructor */ Sk.astnodes.List = function List(/* {asdl_seq *} */ elts, /* {expr_context_ty} */ ctx, /* {int} */ lineno, /* {int} */ - col_offset, /* {int} */ endlineno, /* - {int} */ col_endoffset) + col_offset, /* {int} */ end_lineno, /* + {int} */ end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.elts = elts; this.ctx = ctx; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -1264,19 +1268,19 @@ Sk.astnodes.List = function List(/* {asdl_seq *} */ elts, /* {expr_context_ty} Sk.astnodes.Tuple = function Tuple(/* {asdl_seq *} */ elts, /* {expr_context_ty} */ ctx, /* {int} */ lineno, /* {int} */ col_offset, /* - {int} */ endlineno, /* {int} */ - col_endoffset) + {int} */ end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.elts = elts; this.ctx = ctx; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } @@ -1324,20 +1328,20 @@ Sk.astnodes.ExceptHandler = function ExceptHandler(/* {expr_ty} */ type, /* body, /* {int} */ lineno, /* {int} */ col_offset, /* {int} */ - endlineno, /* {int} */ - col_endoffset) + end_lineno, /* {int} */ + end_col_offset) { Sk.asserts.assert(lineno !== null && lineno !== undefined); Sk.asserts.assert(col_offset !== null && col_offset !== undefined); - Sk.asserts.assert(endlineno !== null && endlineno !== undefined); - Sk.asserts.assert(col_endoffset !== null && col_endoffset !== undefined); + Sk.asserts.assert(end_lineno !== null && end_lineno !== undefined); + Sk.asserts.assert(end_col_offset !== null && end_col_offset !== undefined); this.type = type; this.name = name; this.body = body; this.lineno = lineno; this.col_offset = col_offset; - this.endlineno = endlineno; - this.col_endoffset = col_endoffset; + this.end_lineno = end_lineno; + this.end_col_offset = end_col_offset; return this; } diff --git a/gen/parse_tables.js b/gen/parse_tables.js index 3e8150c51e..cb5c067c43 100644 --- a/gen/parse_tables.js +++ b/gen/parse_tables.js @@ -227,8 +227,8 @@ number2symbol: 342: 'yield_expr', 343: 'yield_stmt'}, dfas: -{256: [[[[1, 1], [2, 1], [3, 2]], [[0, 1]], [[2, 1]]], - {2: 1, +{256: [[[[1, 1], [2, 2], [3, 1]], [[0, 1]], [[3, 1]]], + {3: 1, 4: 1, 5: 1, 6: 1, @@ -270,127 +270,127 @@ dfas: 42: 1, 43: 1}], 257: [[[[44, 1]], [[45, 0], [0, 1]]], - {10: 1, - 11: 1, - 12: 1, + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, 32: 1, - 33: 1, - 36: 1}], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], 258: [[[[46, 1]], [[47, 0], [0, 1]]], - {6: 1, - 10: 1, - 11: 1, - 12: 1, + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, - 36: 1}], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], 259: [[[[48, 1]], [[49, 2]], [[50, 3], [0, 2]], [[49, 4]], [[0, 4]]], {48: 1}], 260: [[[[51, 1]], [[52, 2], [0, 1]], [[51, 1], [0, 2]]], - {6: 1, - 10: 1, + {10: 1, 11: 1, - 12: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, + 34: 1, 35: 1, - 36: 1, 39: 1, + 42: 1, 53: 1}], - 261: [[[[35, 1], [49, 2], [53, 1]], + 261: [[[[10, 1], [53, 1], [49, 2]], [[49, 3]], [[50, 1], [54, 3], [0, 2]], [[0, 3]]], - {6: 1, - 10: 1, + {10: 1, 11: 1, - 12: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, + 34: 1, 35: 1, - 36: 1, 39: 1, + 42: 1, 53: 1}], - 262: [[[[55, 1]], [[10, 0], [32, 0], [0, 1]]], - {10: 1, - 11: 1, - 12: 1, + 262: [[[[55, 1]], [[26, 0], [19, 0], [0, 1]]], + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, 32: 1, - 33: 1, - 36: 1}], - 263: [[[[21, 1]], [[49, 2]], [[52, 3], [0, 2]], [[49, 4]], [[0, 4]]], {21: 1}], - 264: [[[[42, 1]], [[56, 2]], [[0, 2]]], {42: 1}], - 265: [[[[42, 1]], [[57, 2], [56, 2], [58, 2]], [[0, 2]]], {42: 1}], - 266: [[[[13, 1], - [17, 2], - [11, 2], - [18, 2], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], + 263: [[[[37, 1]], [[49, 2]], [[52, 3], [0, 2]], [[49, 4]], [[0, 4]]], {37: 1}], + 264: [[[[18, 1]], [[56, 2]], [[0, 2]]], {18: 1}], + 265: [[[[18, 1]], [[57, 2], [56, 2], [58, 2]], [[0, 2]]], {18: 1}], + 266: [[[[35, 1], + [23, 2], + [13, 2], + [25, 3], + [42, 5], [28, 2], - [20, 2], - [23, 4], - [19, 2], - [27, 5], - [33, 3]], - [[59, 2], [60, 6]], + [29, 4], + [32, 2], + [39, 2], + [34, 2]], + [[35, 1], [0, 1]], [[0, 2]], - [[61, 2], [62, 7], [60, 7]], - [[63, 8], [64, 2]], - [[27, 5], [0, 5]], - [[59, 2]], - [[61, 2]], + [[59, 6], [60, 2], [61, 6]], + [[62, 7], [63, 2]], + [[59, 8], [64, 2]], + [[60, 2]], + [[63, 2]], [[64, 2]]], - {11: 1, 13: 1, 17: 1, 18: 1, 19: 1, 20: 1, 23: 1, 27: 1, 28: 1, 33: 1}], - 267: [[[[12, 1], [65, 2]], [[65, 2]], [[66, 2], [0, 2]]], - {11: 1, - 12: 1, - 13: 1, - 17: 1, - 18: 1, - 19: 1, - 20: 1, + {13: 1, 23: 1, 25: 1, 28: 1, 29: 1, 32: 1, 34: 1, 35: 1, 39: 1, 42: 1}], + 267: [[[[15, 1], [65, 2]], [[65, 2]], [[66, 2], [0, 2]]], + {13: 1, + 15: 1, 23: 1, - 27: 1, + 25: 1, 28: 1, - 33: 1}], + 29: 1, + 32: 1, + 34: 1, + 35: 1, + 39: 1, + 42: 1}], 268: [[[[67, 1], [68, 1], [69, 1], @@ -418,209 +418,209 @@ dfas: 77: 1, 78: 1, 79: 1}], - 269: [[[[43, 1]], [[0, 1]]], {43: 1}], - 270: [[[[5, 1]], - [[20, 2]], - [[48, 3], [33, 4]], - [[80, 5]], - [[81, 6], [61, 7]], - [[0, 5]], - [[61, 7]], - [[48, 3]]], - {5: 1}], - 271: [[[[15, 1], [42, 2]], + 269: [[[[4, 1]], [[0, 1]]], {4: 1}], + 270: [[[[43, 1]], + [[28, 2]], + [[48, 4], [25, 3]], + [[80, 5], [60, 6]], + [[81, 7]], + [[60, 6]], + [[48, 4]], + [[0, 7]]], + {43: 1}], + 271: [[[[33, 2], [18, 1]], + [[33, 2]], [[82, 3]], - [[15, 1]], [[83, 4]], [[84, 5]], [[85, 6], [0, 5]], [[0, 6]]], - {15: 1, 42: 1}], - 272: [[[[24, 1]], [[86, 2]], [[85, 3], [0, 2]], [[0, 3]]], {24: 1}], - 273: [[[[87, 1], [54, 1]], [[0, 1]]], {15: 1, 24: 1, 42: 1}], + {18: 1, 33: 1}], + 272: [[[[7, 1]], [[86, 2]], [[85, 3], [0, 2]], [[0, 3]]], {7: 1}], + 273: [[[[87, 1], [54, 1]], [[0, 1]]], {7: 1, 18: 1, 33: 1}], 274: [[[[88, 1], - [6, 2], [89, 1], [90, 1], - [91, 3], - [92, 1], + [91, 1], + [30, 2], [83, 1], - [93, 1], - [90, 1], + [92, 1], + [92, 1], + [93, 3], [94, 1]], [[0, 1]], [[83, 1]], - [[6, 1], [0, 3]]], - {6: 1, 83: 1, 88: 1, 89: 1, 90: 1, 91: 1, 92: 1, 93: 1, 94: 1}], + [[30, 1], [0, 3]]], + {30: 1, 83: 1, 88: 1, 89: 1, 90: 1, 91: 1, 92: 1, 93: 1, 94: 1}], 275: [[[[95, 1]], [[96, 0], [0, 1]]], - {10: 1, - 11: 1, - 12: 1, + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, 32: 1, - 33: 1, - 36: 1}], - 276: [[[[56, 1], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], + 276: [[[[97, 1], [57, 1], - [97, 1], [98, 1], [99, 1], - [58, 1], + [56, 1], [100, 1], [101, 1], + [58, 1], [102, 1]], [[0, 1]]], - {5: 1, 8: 1, 9: 1, 15: 1, 16: 1, 22: 1, 24: 1, 29: 1, 42: 1}], - 277: [[[[38, 1]], [[0, 1]]], {38: 1}], - 278: [[[[34, 1]], [[0, 1]]], {34: 1}], - 279: [[[[103, 1]], [[56, 2], [104, 2], [99, 2]], [[0, 2]]], {29: 1}], - 280: [[[[29, 1]], + {5: 1, 7: 1, 9: 1, 12: 1, 18: 1, 21: 1, 31: 1, 33: 1, 43: 1}], + 277: [[[[14, 1]], [[0, 1]]], {14: 1}], + 278: [[[[24, 1]], [[0, 1]]], {24: 1}], + 279: [[[[103, 1]], [[100, 2], [104, 2], [56, 2]], [[0, 2]]], {5: 1}], + 280: [[[[5, 1]], [[105, 2]], - [[2, 3], [33, 4]], + [[3, 3], [25, 4]], [[0, 3]], - [[81, 5], [61, 6]], - [[61, 6]], - [[2, 3]]], - {29: 1}], - 281: [[[[106, 1]], [[106, 1], [0, 1]]], {29: 1}], - 282: [[[[14, 1]], [[82, 2]], [[0, 2]]], {14: 1}], - 283: [[[[53, 3], [49, 2], [107, 1]], - [[54, 5], [52, 4], [0, 1]], - [[54, 5], [48, 6], [52, 4], [0, 2]], - [[95, 7]], - [[49, 8], [107, 8], [0, 4]], - [[0, 5]], - [[49, 7]], - [[52, 9], [54, 5], [0, 7]], - [[52, 4], [0, 8]], - [[53, 10], [49, 11], [0, 9]], + [[80, 5], [60, 6]], + [[60, 6]], + [[3, 3]]], + {5: 1}], + 281: [[[[106, 1]], [[106, 1], [0, 1]]], {5: 1}], + 282: [[[[16, 1]], [[82, 2]], [[0, 2]]], {16: 1}], + 283: [[[[53, 1], [107, 2], [49, 3]], + [[95, 4]], + [[52, 5], [54, 6], [0, 2]], + [[52, 5], [48, 7], [54, 6], [0, 3]], + [[52, 8], [54, 6], [0, 4]], + [[107, 9], [49, 9], [0, 5]], + [[0, 6]], + [[49, 4]], + [[53, 10], [49, 11], [0, 8]], + [[52, 5], [0, 9]], [[95, 12]], [[48, 13]], - [[52, 9], [0, 12]], + [[52, 8], [0, 12]], [[49, 12]]], - {6: 1, - 10: 1, + {10: 1, 11: 1, - 12: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, + 34: 1, 35: 1, - 36: 1, 39: 1, + 42: 1, 53: 1}], - 284: [[[[105, 1]], [[108, 2], [0, 1]], [[20, 3]], [[0, 3]]], {20: 1}], - 285: [[[[109, 1]], [[52, 0], [0, 1]]], {20: 1}], - 286: [[[[20, 1]], [[110, 0], [0, 1]]], {20: 1}], - 287: [[[[20, 1]], [[0, 1]]], {20: 1}], - 288: [[[[111, 1]], [[2, 1], [112, 2]], [[0, 2]]], - {6: 1, - 10: 1, - 11: 1, - 12: 1, + 284: [[[[105, 1]], [[108, 2], [0, 1]], [[28, 3]], [[0, 3]]], {28: 1}], + 285: [[[[109, 1]], [[52, 0], [0, 1]]], {28: 1}], + 286: [[[[28, 1]], [[110, 0], [0, 1]]], {28: 1}], + 287: [[[[28, 1]], [[0, 1]]], {28: 1}], + 288: [[[[111, 1]], [[3, 1], [112, 2]], [[0, 2]]], + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, - 36: 1, - 39: 1}], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], 289: [[[[113, 1]], [[49, 2], [0, 1]], - [[108, 3], [52, 3], [0, 2]], + [[52, 3], [108, 3], [0, 2]], [[49, 4]], [[0, 4]]], {113: 1}], 290: [[[[114, 1]], [[115, 0], [0, 1]]], - {10: 1, - 11: 1, - 12: 1, + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, 32: 1, - 33: 1, - 36: 1}], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], 291: [[[[116, 1]], [[50, 2], [117, 3], [118, 4], [0, 1]], - [[116, 5], [62, 5]], - [[0, 3]], - [[62, 3], [111, 3]], + [[61, 5], [116, 5]], + [[61, 4], [111, 4]], + [[0, 4]], [[50, 2], [0, 5]]], - {6: 1, - 10: 1, + {10: 1, 11: 1, - 12: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, + 34: 1, 35: 1, - 36: 1, - 39: 1}], + 39: 1, + 42: 1}], 292: [[[[95, 1], [107, 1]], [[52, 2], [0, 1]], [[95, 1], [107, 1], [0, 2]]], {10: 1, 11: 1, - 12: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, 32: 1, - 33: 1, + 34: 1, 35: 1, - 36: 1}], - 293: [[[[10, 1], [36, 1], [119, 2], [32, 1]], [[120, 2]], [[0, 2]]], - {10: 1, - 11: 1, - 12: 1, + 39: 1, + 42: 1}], + 293: [[[[26, 1], [11, 1], [19, 1], [119, 2]], [[120, 2]], [[0, 2]]], + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, 32: 1, - 33: 1, - 36: 1}], - 294: [[[[121, 0], [2, 0], [112, 1]], [[0, 1]]], - {2: 1, + 34: 1, + 35: 1, + 39: 1, + 42: 1}], + 294: [[[[3, 0], [121, 0], [112, 1]], [[0, 1]]], + {3: 1, 4: 1, 5: 1, 6: 1, @@ -663,158 +663,158 @@ dfas: 43: 1, 112: 1}], 295: [[[[122, 1], [123, 1], [124, 1], [125, 1], [126, 1]], [[0, 1]]], - {26: 1, 30: 1, 38: 1, 40: 1, 43: 1}], - 296: [[[[15, 1]], + {4: 1, 8: 1, 14: 1, 20: 1, 36: 1}], + 296: [[[[33, 1]], [[82, 2]], [[83, 3]], [[111, 4]], [[48, 5]], - [[80, 6]], + [[81, 6]], [[127, 7], [0, 6]], [[48, 8]], - [[80, 9]], + [[81, 9]], [[0, 9]]], - {15: 1}], - 297: [[[[16, 1]], - [[20, 2]], + {33: 1}], + 297: [[[[12, 1]], + [[28, 2]], [[128, 3]], [[129, 4], [48, 5]], [[49, 6]], - [[80, 7]], + [[81, 7]], [[48, 5]], [[0, 7]]], - {16: 1}], - 298: [[[[25, 1]], [[20, 2]], [[52, 1], [0, 2]]], {25: 1}], - 299: [[[[24, 1]], + {12: 1}], + 298: [[[[38, 1]], [[28, 2]], [[52, 1], [0, 2]]], {38: 1}], + 299: [[[[7, 1]], [[49, 2]], [[48, 3]], - [[80, 4]], - [[130, 1], [127, 5], [0, 4]], + [[81, 4]], + [[127, 5], [130, 1], [0, 4]], [[48, 6]], - [[80, 7]], + [[81, 7]], [[0, 7]]], - {24: 1}], - 300: [[[[20, 1]], [[108, 2], [0, 1]], [[20, 3]], [[0, 3]]], {20: 1}], - 301: [[[[131, 1]], [[52, 2], [0, 1]], [[131, 1], [0, 2]]], {20: 1}], - 302: [[[[31, 1]], - [[105, 2], [110, 3], [19, 3]], - [[37, 4]], - [[105, 2], [110, 3], [37, 4], [19, 3]], - [[35, 5], [33, 6], [132, 5]], + {7: 1}], + 300: [[[[28, 1]], [[108, 2], [0, 1]], [[28, 3]], [[0, 3]]], {28: 1}], + 301: [[[[131, 1]], [[52, 2], [0, 1]], [[131, 1], [0, 2]]], {28: 1}], + 302: [[[[17, 1]], + [[105, 2], [110, 3], [39, 3]], + [[27, 4]], + [[105, 2], [27, 4], [39, 3], [110, 3]], + [[10, 5], [25, 6], [132, 5]], [[0, 5]], [[132, 7]], - [[61, 5]]], - {31: 1}], - 303: [[[[37, 1]], [[133, 2]], [[0, 2]]], {37: 1}], - 304: [[[[134, 1], [135, 1]], [[0, 1]]], {31: 1, 37: 1}], - 305: [[[[39, 1]], [[48, 2], [136, 3]], [[49, 4]], [[48, 2]], [[0, 4]]], - {39: 1}], - 306: [[[[39, 1]], [[48, 2], [136, 3]], [[86, 4]], [[48, 2]], [[0, 4]]], - {39: 1}], - 307: [[[[41, 1]], [[20, 2]], [[52, 1], [0, 2]]], {41: 1}], - 308: [[[[6, 1], [137, 2]], [[46, 2]], [[0, 2]]], - {6: 1, - 10: 1, - 11: 1, - 12: 1, + [[60, 5]]], + {17: 1}], + 303: [[[[27, 1]], [[133, 2]], [[0, 2]]], {27: 1}], + 304: [[[[134, 1], [135, 1]], [[0, 1]]], {17: 1, 27: 1}], + 305: [[[[22, 1]], [[48, 2], [136, 3]], [[49, 4]], [[48, 2]], [[0, 4]]], + {22: 1}], + 306: [[[[22, 1]], [[48, 2], [136, 3]], [[86, 4]], [[48, 2]], [[0, 4]]], + {22: 1}], + 307: [[[[40, 1]], [[28, 2]], [[52, 1], [0, 2]]], {40: 1}], + 308: [[[[137, 1], [30, 2]], [[0, 1]], [[46, 1]]], + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, - 36: 1}], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], 309: [[[[138, 1]], [[139, 0], [0, 1]]], - {6: 1, - 10: 1, - 11: 1, - 12: 1, + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, - 36: 1}], - 310: [[[[33, 1]], [[140, 2], [61, 3]], [[61, 3]], [[0, 3]]], {33: 1}], - 311: [[[[7, 1]], [[0, 1]]], {7: 1}], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], + 310: [[[[25, 1]], [[60, 2], [140, 3]], [[0, 2]], [[60, 2]]], {25: 1}], + 311: [[[[6, 1]], [[0, 1]]], {6: 1}], 312: [[[[141, 1]], [[53, 2], [0, 1]], [[120, 3]], [[0, 3]]], - {11: 1, - 12: 1, - 13: 1, - 17: 1, - 18: 1, - 19: 1, - 20: 1, + {13: 1, + 15: 1, 23: 1, - 27: 1, + 25: 1, 28: 1, - 33: 1}], - 313: [[[[4, 1]], - [[49, 2], [142, 3], [0, 1]], - [[52, 4], [0, 2]], - [[49, 5]], - [[49, 2], [0, 4]], - [[52, 6], [0, 5]], + 29: 1, + 32: 1, + 34: 1, + 35: 1, + 39: 1, + 42: 1}], + 313: [[[[41, 1]], + [[142, 2], [49, 3], [0, 1]], + [[49, 4]], + [[52, 5], [0, 3]], + [[52, 6], [0, 4]], + [[49, 3], [0, 5]], [[49, 7]], [[52, 8], [0, 7]], [[49, 7], [0, 8]]], - {4: 1}], - 314: [[[[40, 1]], + {41: 1}], + 314: [[[[20, 1]], [[49, 2], [0, 1]], - [[52, 3], [31, 3], [0, 2]], + [[52, 3], [17, 3], [0, 2]], [[49, 4]], [[52, 5], [0, 4]], [[49, 6]], [[0, 6]]], - {40: 1}], - 315: [[[[30, 1]], [[111, 2], [0, 1]], [[0, 2]]], {30: 1}], + {20: 1}], + 315: [[[[36, 1]], [[111, 2], [0, 1]], [[0, 2]]], {36: 1}], 316: [[[[143, 1]], [[144, 0], [142, 0], [0, 1]]], - {10: 1, - 11: 1, - 12: 1, + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, 32: 1, - 33: 1, - 36: 1}], - 317: [[[[145, 1]], [[146, 2], [2, 3]], [[145, 1], [2, 3]], [[0, 3]]], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], + 317: [[[[145, 1]], [[3, 2], [146, 3]], [[0, 2]], [[3, 2], [145, 1]]], {4: 1, 6: 1, - 7: 1, + 8: 1, 10: 1, 11: 1, - 12: 1, 13: 1, 14: 1, + 15: 1, + 16: 1, 17: 1, - 18: 1, 19: 1, 20: 1, - 21: 1, + 22: 1, 23: 1, + 24: 1, 25: 1, 26: 1, 27: 1, 28: 1, + 29: 1, 30: 1, - 31: 1, 32: 1, - 33: 1, 34: 1, 35: 1, 36: 1, @@ -823,7 +823,7 @@ dfas: 39: 1, 40: 1, 41: 1, - 43: 1}], + 42: 1}], 318: [[[[48, 1]], [[49, 2], [0, 1]], [[0, 2]]], {48: 1}], 319: [[[[147, 1], [148, 1], @@ -838,26 +838,26 @@ dfas: [[0, 1]]], {4: 1, 6: 1, - 7: 1, + 8: 1, 10: 1, 11: 1, - 12: 1, 13: 1, 14: 1, + 15: 1, + 16: 1, 17: 1, - 18: 1, 19: 1, 20: 1, - 21: 1, + 22: 1, 23: 1, + 24: 1, 25: 1, 26: 1, 27: 1, 28: 1, + 29: 1, 30: 1, - 31: 1, 32: 1, - 33: 1, 34: 1, 35: 1, 36: 1, @@ -866,9 +866,9 @@ dfas: 39: 1, 40: 1, 41: 1, - 43: 1}], - 320: [[[[35, 1]], [[95, 2]], [[0, 2]]], {35: 1}], - 321: [[[[1, 1], [3, 1]], [[0, 1]]], + 42: 1}], + 320: [[[[10, 1]], [[95, 2]], [[0, 2]]], {10: 1}], + 321: [[[[1, 1], [2, 1]], [[0, 1]]], {4: 1, 5: 1, 6: 1, @@ -914,69 +914,69 @@ dfas: [[48, 1], [0, 2]], [[0, 3]], [[157, 3], [0, 4]]], - {6: 1, - 10: 1, - 11: 1, - 12: 1, + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, - 36: 1, + 34: 1, + 35: 1, 39: 1, + 42: 1, 48: 1}], 323: [[[[158, 1]], [[52, 2], [0, 1]], [[158, 1], [0, 2]]], - {6: 1, - 10: 1, - 11: 1, - 12: 1, + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, - 36: 1, + 34: 1, + 35: 1, 39: 1, + 42: 1, 48: 1}], - 324: [[[[1, 1], [2, 2]], - [[0, 1]], + 324: [[[[3, 1], [1, 2]], [[159, 3]], + [[0, 2]], [[121, 4]], - [[160, 1], [121, 4]]], - {2: 1, + [[160, 2], [121, 4]]], + {3: 1, 4: 1, 6: 1, - 7: 1, + 8: 1, 10: 1, 11: 1, - 12: 1, 13: 1, 14: 1, + 15: 1, + 16: 1, 17: 1, - 18: 1, 19: 1, 20: 1, - 21: 1, + 22: 1, 23: 1, + 24: 1, 25: 1, 26: 1, 27: 1, 28: 1, + 29: 1, 30: 1, - 31: 1, 32: 1, - 33: 1, 34: 1, 35: 1, 36: 1, @@ -985,147 +985,147 @@ dfas: 39: 1, 40: 1, 41: 1, - 43: 1}], - 325: [[[[120, 1]], [[161, 0], [29, 0], [35, 0], [162, 0], [163, 0], [0, 1]]], - {10: 1, - 11: 1, - 12: 1, + 42: 1}], + 325: [[[[120, 1]], [[5, 0], [10, 0], [161, 0], [162, 0], [163, 0], [0, 1]]], + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, 32: 1, - 33: 1, - 36: 1}], - 326: [[[[164, 1], [84, 2]], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], + 326: [[[[84, 2], [164, 1]], [[0, 1]], - [[24, 3], [0, 2]], + [[7, 3], [0, 2]], [[84, 4]], [[127, 5]], [[49, 1]]], - {6: 1, - 10: 1, - 11: 1, - 12: 1, + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, - 36: 1, - 39: 1}], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], 327: [[[[165, 1], [84, 1]], [[0, 1]]], - {6: 1, - 10: 1, - 11: 1, - 12: 1, + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, - 36: 1, - 39: 1}], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], 328: [[[[49, 1]], [[52, 2], [0, 1]], [[49, 1], [0, 2]]], - {6: 1, - 10: 1, - 11: 1, - 12: 1, - 13: 1, - 17: 1, - 18: 1, + {11: 1, + 13: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, - 36: 1, - 39: 1}], - 329: [[[[49, 1], [107, 1]], - [[54, 3], [52, 2], [0, 1]], - [[49, 4], [107, 4], [0, 2]], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], + 329: [[[[107, 1], [49, 1]], + [[52, 2], [54, 3], [0, 1]], + [[107, 4], [49, 4], [0, 2]], [[0, 3]], [[52, 2], [0, 4]]], - {6: 1, - 10: 1, + {10: 1, 11: 1, - 12: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, + 34: 1, 35: 1, - 36: 1, - 39: 1}], - 330: [[[[49, 1], [107, 1]], [[52, 2], [0, 1]], [[49, 1], [107, 1], [0, 2]]], - {6: 1, - 10: 1, + 39: 1, + 42: 1}], + 330: [[[[107, 1], [49, 1]], [[52, 2], [0, 1]], [[107, 1], [49, 1], [0, 2]]], + {10: 1, 11: 1, - 12: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, + 34: 1, 35: 1, - 36: 1, - 39: 1}], - 331: [[[[20, 1]], [[48, 2], [0, 1]], [[49, 3]], [[0, 3]]], {20: 1}], - 332: [[[[13, 1], [110, 2], [33, 3]], - [[166, 4]], - [[20, 5]], - [[81, 6], [61, 5]], - [[59, 5]], + 39: 1, + 42: 1}], + 331: [[[[28, 1]], [[48, 2], [0, 1]], [[49, 3]], [[0, 3]]], {28: 1}], + 332: [[[[25, 1], [42, 3], [110, 2]], + [[80, 4], [60, 5]], + [[28, 5]], + [[166, 6]], + [[60, 5]], [[0, 5]], - [[61, 5]]], - {13: 1, 33: 1, 110: 1}], - 333: [[[[8, 1]], + [[64, 5]]], + {25: 1, 42: 1, 110: 1}], + 333: [[[[21, 1]], [[48, 2]], - [[80, 3]], + [[81, 3]], [[167, 4], [168, 5]], [[48, 6]], [[48, 7]], - [[80, 8]], - [[80, 9]], - [[167, 4], [168, 5], [127, 10], [0, 8]], + [[81, 8]], + [[81, 9]], + [[127, 10], [167, 4], [168, 5], [0, 8]], [[0, 9]], [[48, 11]], - [[80, 12]], + [[81, 12]], [[168, 5], [0, 12]]], - {8: 1}], - 334: [[[[169, 1], [35, 2], [53, 3]], + {21: 1}], + 334: [[[[169, 1], [10, 2], [53, 3]], [[50, 4], [52, 5], [0, 1]], [[169, 6], [52, 7], [0, 2]], [[169, 8]], [[49, 9]], - [[169, 1], [35, 10], [53, 3], [0, 5]], + [[169, 1], [53, 3], [10, 10], [0, 5]], [[52, 7], [0, 6]], [[169, 11], [53, 3], [0, 7]], [[52, 12], [0, 8]], @@ -1138,120 +1138,120 @@ dfas: [[49, 6]], [[50, 17], [52, 14], [0, 16]], [[49, 13]]], - {20: 1, 35: 1, 53: 1}], - 335: [[[[35, 2], [170, 1], [53, 3]], - [[50, 4], [52, 5], [0, 1]], - [[170, 6], [52, 7], [0, 2]], - [[170, 8]], - [[49, 9]], - [[170, 1], [35, 10], [53, 3], [0, 5]], - [[52, 7], [0, 6]], - [[170, 11], [53, 3], [0, 7]], - [[52, 12], [0, 8]], - [[52, 5], [0, 9]], - [[170, 13], [52, 14], [0, 10]], - [[50, 15], [52, 7], [0, 11]], - [[0, 12]], - [[52, 14], [0, 13]], - [[170, 16], [53, 3], [0, 14]], - [[49, 6]], - [[50, 17], [52, 14], [0, 16]], - [[49, 13]]], - {20: 1, 35: 1, 53: 1}], - 336: [[[[20, 1]], [[0, 1]]], {20: 1}], + {10: 1, 28: 1, 53: 1}], + 335: [[[[53, 1], [170, 2], [10, 3]], + [[170, 4]], + [[52, 5], [50, 6], [0, 2]], + [[52, 7], [170, 8], [0, 3]], + [[52, 9], [0, 4]], + [[10, 10], [170, 2], [53, 1], [0, 5]], + [[49, 11]], + [[53, 1], [170, 12], [0, 7]], + [[52, 7], [0, 8]], + [[0, 9]], + [[52, 13], [170, 14], [0, 10]], + [[52, 5], [0, 11]], + [[52, 7], [50, 15], [0, 12]], + [[53, 1], [170, 16], [0, 13]], + [[52, 13], [0, 14]], + [[49, 8]], + [[50, 17], [52, 13], [0, 16]], + [[49, 14]]], + {10: 1, 28: 1, 53: 1}], + 336: [[[[28, 1]], [[0, 1]]], {28: 1}], 337: [[[[9, 1]], [[49, 2]], [[48, 3]], - [[80, 4]], + [[81, 4]], [[127, 5], [0, 4]], [[48, 6]], - [[80, 7]], + [[81, 7]], [[0, 7]]], {9: 1}], 338: [[[[49, 1]], [[108, 2], [0, 1]], [[95, 3]], [[0, 3]]], - {6: 1, - 10: 1, - 11: 1, - 12: 1, + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, - 36: 1, - 39: 1}], - 339: [[[[22, 1]], [[171, 2]], [[48, 3], [52, 1]], [[80, 4]], [[0, 4]]], - {22: 1}], + 34: 1, + 35: 1, + 39: 1, + 42: 1}], + 339: [[[[31, 1]], [[171, 2]], [[52, 1], [48, 3]], [[81, 4]], [[0, 4]]], + {31: 1}], 340: [[[[172, 1]], [[173, 0], [0, 1]]], - {10: 1, - 11: 1, - 12: 1, + {11: 1, 13: 1, - 17: 1, - 18: 1, + 15: 1, 19: 1, - 20: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, + 29: 1, 32: 1, - 33: 1, - 36: 1}], - 341: [[[[111, 1], [31, 2]], [[0, 1]], [[49, 1]]], - {6: 1, - 10: 1, - 11: 1, - 12: 1, + 34: 1, + 35: 1, + 39: 1, + 42: 1}], + 341: [[[[17, 1], [111, 2]], [[49, 2]], [[0, 2]]], + {11: 1, 13: 1, + 15: 1, 17: 1, - 18: 1, 19: 1, - 20: 1, + 22: 1, 23: 1, - 27: 1, + 25: 1, + 26: 1, 28: 1, - 31: 1, + 29: 1, + 30: 1, 32: 1, - 33: 1, - 36: 1, - 39: 1}], - 342: [[[[26, 1]], [[174, 2], [0, 1]], [[0, 2]]], {26: 1}], - 343: [[[[62, 1]], [[0, 1]]], {26: 1}]}, + 34: 1, + 35: 1, + 39: 1, + 42: 1}], + 342: [[[[8, 1]], [[174, 2], [0, 1]], [[0, 2]]], {8: 1}], + 343: [[[[61, 1]], [[0, 1]]], {8: 1}]}, states: -[[[[1, 1], [2, 1], [3, 2]], [[0, 1]], [[2, 1]]], +[[[[1, 1], [2, 2], [3, 1]], [[0, 1]], [[3, 1]]], [[[44, 1]], [[45, 0], [0, 1]]], [[[46, 1]], [[47, 0], [0, 1]]], [[[48, 1]], [[49, 2]], [[50, 3], [0, 2]], [[49, 4]], [[0, 4]]], [[[51, 1]], [[52, 2], [0, 1]], [[51, 1], [0, 2]]], - [[[35, 1], [49, 2], [53, 1]], [[49, 3]], [[50, 1], [54, 3], [0, 2]], [[0, 3]]], - [[[55, 1]], [[10, 0], [32, 0], [0, 1]]], - [[[21, 1]], [[49, 2]], [[52, 3], [0, 2]], [[49, 4]], [[0, 4]]], - [[[42, 1]], [[56, 2]], [[0, 2]]], - [[[42, 1]], [[57, 2], [56, 2], [58, 2]], [[0, 2]]], - [[[13, 1], - [17, 2], - [11, 2], - [18, 2], + [[[10, 1], [53, 1], [49, 2]], [[49, 3]], [[50, 1], [54, 3], [0, 2]], [[0, 3]]], + [[[55, 1]], [[26, 0], [19, 0], [0, 1]]], + [[[37, 1]], [[49, 2]], [[52, 3], [0, 2]], [[49, 4]], [[0, 4]]], + [[[18, 1]], [[56, 2]], [[0, 2]]], + [[[18, 1]], [[57, 2], [56, 2], [58, 2]], [[0, 2]]], + [[[35, 1], + [23, 2], + [13, 2], + [25, 3], + [42, 5], [28, 2], - [20, 2], - [23, 4], - [19, 2], - [27, 5], - [33, 3]], - [[59, 2], [60, 6]], + [29, 4], + [32, 2], + [39, 2], + [34, 2]], + [[35, 1], [0, 1]], [[0, 2]], - [[61, 2], [62, 7], [60, 7]], - [[63, 8], [64, 2]], - [[27, 5], [0, 5]], - [[59, 2]], - [[61, 2]], + [[59, 6], [60, 2], [61, 6]], + [[62, 7], [63, 2]], + [[59, 8], [64, 2]], + [[60, 2]], + [[63, 2]], [[64, 2]]], - [[[12, 1], [65, 2]], [[65, 2]], [[66, 2], [0, 2]]], + [[[15, 1], [65, 2]], [[65, 2]], [[66, 2], [0, 2]]], [[[67, 1], [68, 1], [69, 1], @@ -1266,161 +1266,161 @@ states: [78, 1], [79, 1]], [[0, 1]]], - [[[43, 1]], [[0, 1]]], - [[[5, 1]], - [[20, 2]], - [[48, 3], [33, 4]], - [[80, 5]], - [[81, 6], [61, 7]], - [[0, 5]], - [[61, 7]], - [[48, 3]]], - [[[15, 1], [42, 2]], + [[[4, 1]], [[0, 1]]], + [[[43, 1]], + [[28, 2]], + [[48, 4], [25, 3]], + [[80, 5], [60, 6]], + [[81, 7]], + [[60, 6]], + [[48, 4]], + [[0, 7]]], + [[[33, 2], [18, 1]], + [[33, 2]], [[82, 3]], - [[15, 1]], [[83, 4]], [[84, 5]], [[85, 6], [0, 5]], [[0, 6]]], - [[[24, 1]], [[86, 2]], [[85, 3], [0, 2]], [[0, 3]]], + [[[7, 1]], [[86, 2]], [[85, 3], [0, 2]], [[0, 3]]], [[[87, 1], [54, 1]], [[0, 1]]], [[[88, 1], - [6, 2], [89, 1], [90, 1], - [91, 3], - [92, 1], + [91, 1], + [30, 2], [83, 1], - [93, 1], - [90, 1], + [92, 1], + [92, 1], + [93, 3], [94, 1]], [[0, 1]], [[83, 1]], - [[6, 1], [0, 3]]], + [[30, 1], [0, 3]]], [[[95, 1]], [[96, 0], [0, 1]]], - [[[56, 1], + [[[97, 1], [57, 1], - [97, 1], [98, 1], [99, 1], - [58, 1], + [56, 1], [100, 1], [101, 1], + [58, 1], [102, 1]], [[0, 1]]], - [[[38, 1]], [[0, 1]]], - [[[34, 1]], [[0, 1]]], - [[[103, 1]], [[56, 2], [104, 2], [99, 2]], [[0, 2]]], - [[[29, 1]], + [[[14, 1]], [[0, 1]]], + [[[24, 1]], [[0, 1]]], + [[[103, 1]], [[100, 2], [104, 2], [56, 2]], [[0, 2]]], + [[[5, 1]], [[105, 2]], - [[2, 3], [33, 4]], + [[3, 3], [25, 4]], [[0, 3]], - [[81, 5], [61, 6]], - [[61, 6]], - [[2, 3]]], + [[80, 5], [60, 6]], + [[60, 6]], + [[3, 3]]], [[[106, 1]], [[106, 1], [0, 1]]], - [[[14, 1]], [[82, 2]], [[0, 2]]], - [[[53, 3], [49, 2], [107, 1]], - [[54, 5], [52, 4], [0, 1]], - [[54, 5], [48, 6], [52, 4], [0, 2]], - [[95, 7]], - [[49, 8], [107, 8], [0, 4]], - [[0, 5]], - [[49, 7]], - [[52, 9], [54, 5], [0, 7]], - [[52, 4], [0, 8]], - [[53, 10], [49, 11], [0, 9]], + [[[16, 1]], [[82, 2]], [[0, 2]]], + [[[53, 1], [107, 2], [49, 3]], + [[95, 4]], + [[52, 5], [54, 6], [0, 2]], + [[52, 5], [48, 7], [54, 6], [0, 3]], + [[52, 8], [54, 6], [0, 4]], + [[107, 9], [49, 9], [0, 5]], + [[0, 6]], + [[49, 4]], + [[53, 10], [49, 11], [0, 8]], + [[52, 5], [0, 9]], [[95, 12]], [[48, 13]], - [[52, 9], [0, 12]], + [[52, 8], [0, 12]], [[49, 12]]], - [[[105, 1]], [[108, 2], [0, 1]], [[20, 3]], [[0, 3]]], + [[[105, 1]], [[108, 2], [0, 1]], [[28, 3]], [[0, 3]]], [[[109, 1]], [[52, 0], [0, 1]]], - [[[20, 1]], [[110, 0], [0, 1]]], - [[[20, 1]], [[0, 1]]], - [[[111, 1]], [[2, 1], [112, 2]], [[0, 2]]], + [[[28, 1]], [[110, 0], [0, 1]]], + [[[28, 1]], [[0, 1]]], + [[[111, 1]], [[3, 1], [112, 2]], [[0, 2]]], [[[113, 1]], [[49, 2], [0, 1]], - [[108, 3], [52, 3], [0, 2]], + [[52, 3], [108, 3], [0, 2]], [[49, 4]], [[0, 4]]], [[[114, 1]], [[115, 0], [0, 1]]], [[[116, 1]], [[50, 2], [117, 3], [118, 4], [0, 1]], - [[116, 5], [62, 5]], - [[0, 3]], - [[62, 3], [111, 3]], + [[61, 5], [116, 5]], + [[61, 4], [111, 4]], + [[0, 4]], [[50, 2], [0, 5]]], [[[95, 1], [107, 1]], [[52, 2], [0, 1]], [[95, 1], [107, 1], [0, 2]]], - [[[10, 1], [36, 1], [119, 2], [32, 1]], [[120, 2]], [[0, 2]]], - [[[121, 0], [2, 0], [112, 1]], [[0, 1]]], + [[[26, 1], [11, 1], [19, 1], [119, 2]], [[120, 2]], [[0, 2]]], + [[[3, 0], [121, 0], [112, 1]], [[0, 1]]], [[[122, 1], [123, 1], [124, 1], [125, 1], [126, 1]], [[0, 1]]], - [[[15, 1]], + [[[33, 1]], [[82, 2]], [[83, 3]], [[111, 4]], [[48, 5]], - [[80, 6]], + [[81, 6]], [[127, 7], [0, 6]], [[48, 8]], - [[80, 9]], + [[81, 9]], [[0, 9]]], - [[[16, 1]], - [[20, 2]], + [[[12, 1]], + [[28, 2]], [[128, 3]], [[129, 4], [48, 5]], [[49, 6]], - [[80, 7]], + [[81, 7]], [[48, 5]], [[0, 7]]], - [[[25, 1]], [[20, 2]], [[52, 1], [0, 2]]], - [[[24, 1]], + [[[38, 1]], [[28, 2]], [[52, 1], [0, 2]]], + [[[7, 1]], [[49, 2]], [[48, 3]], - [[80, 4]], - [[130, 1], [127, 5], [0, 4]], + [[81, 4]], + [[127, 5], [130, 1], [0, 4]], [[48, 6]], - [[80, 7]], + [[81, 7]], [[0, 7]]], - [[[20, 1]], [[108, 2], [0, 1]], [[20, 3]], [[0, 3]]], + [[[28, 1]], [[108, 2], [0, 1]], [[28, 3]], [[0, 3]]], [[[131, 1]], [[52, 2], [0, 1]], [[131, 1], [0, 2]]], - [[[31, 1]], - [[105, 2], [110, 3], [19, 3]], - [[37, 4]], - [[105, 2], [110, 3], [37, 4], [19, 3]], - [[35, 5], [33, 6], [132, 5]], + [[[17, 1]], + [[105, 2], [110, 3], [39, 3]], + [[27, 4]], + [[105, 2], [27, 4], [39, 3], [110, 3]], + [[10, 5], [25, 6], [132, 5]], [[0, 5]], [[132, 7]], - [[61, 5]]], - [[[37, 1]], [[133, 2]], [[0, 2]]], + [[60, 5]]], + [[[27, 1]], [[133, 2]], [[0, 2]]], [[[134, 1], [135, 1]], [[0, 1]]], - [[[39, 1]], [[48, 2], [136, 3]], [[49, 4]], [[48, 2]], [[0, 4]]], - [[[39, 1]], [[48, 2], [136, 3]], [[86, 4]], [[48, 2]], [[0, 4]]], - [[[41, 1]], [[20, 2]], [[52, 1], [0, 2]]], - [[[6, 1], [137, 2]], [[46, 2]], [[0, 2]]], + [[[22, 1]], [[48, 2], [136, 3]], [[49, 4]], [[48, 2]], [[0, 4]]], + [[[22, 1]], [[48, 2], [136, 3]], [[86, 4]], [[48, 2]], [[0, 4]]], + [[[40, 1]], [[28, 2]], [[52, 1], [0, 2]]], + [[[137, 1], [30, 2]], [[0, 1]], [[46, 1]]], [[[138, 1]], [[139, 0], [0, 1]]], - [[[33, 1]], [[140, 2], [61, 3]], [[61, 3]], [[0, 3]]], - [[[7, 1]], [[0, 1]]], + [[[25, 1]], [[60, 2], [140, 3]], [[0, 2]], [[60, 2]]], + [[[6, 1]], [[0, 1]]], [[[141, 1]], [[53, 2], [0, 1]], [[120, 3]], [[0, 3]]], - [[[4, 1]], - [[49, 2], [142, 3], [0, 1]], - [[52, 4], [0, 2]], - [[49, 5]], - [[49, 2], [0, 4]], - [[52, 6], [0, 5]], + [[[41, 1]], + [[142, 2], [49, 3], [0, 1]], + [[49, 4]], + [[52, 5], [0, 3]], + [[52, 6], [0, 4]], + [[49, 3], [0, 5]], [[49, 7]], [[52, 8], [0, 7]], [[49, 7], [0, 8]]], - [[[40, 1]], + [[[20, 1]], [[49, 2], [0, 1]], - [[52, 3], [31, 3], [0, 2]], + [[52, 3], [17, 3], [0, 2]], [[49, 4]], [[52, 5], [0, 4]], [[49, 6]], [[0, 6]]], - [[[30, 1]], [[111, 2], [0, 1]], [[0, 2]]], + [[[36, 1]], [[111, 2], [0, 1]], [[0, 2]]], [[[143, 1]], [[144, 0], [142, 0], [0, 1]]], - [[[145, 1]], [[146, 2], [2, 3]], [[145, 1], [2, 3]], [[0, 3]]], + [[[145, 1]], [[3, 2], [146, 3]], [[0, 2]], [[3, 2], [145, 1]]], [[[48, 1]], [[49, 2], [0, 1]], [[0, 2]]], [[[147, 1], [148, 1], @@ -1433,57 +1433,57 @@ states: [155, 1], [156, 1]], [[0, 1]]], - [[[35, 1]], [[95, 2]], [[0, 2]]], - [[[1, 1], [3, 1]], [[0, 1]]], + [[[10, 1]], [[95, 2]], [[0, 2]]], + [[[1, 1], [2, 1]], [[0, 1]]], [[[48, 1], [49, 2]], [[157, 3], [49, 4], [0, 1]], [[48, 1], [0, 2]], [[0, 3]], [[157, 3], [0, 4]]], [[[158, 1]], [[52, 2], [0, 1]], [[158, 1], [0, 2]]], - [[[1, 1], [2, 2]], [[0, 1]], [[159, 3]], [[121, 4]], [[160, 1], [121, 4]]], - [[[120, 1]], [[161, 0], [29, 0], [35, 0], [162, 0], [163, 0], [0, 1]]], - [[[164, 1], [84, 2]], + [[[3, 1], [1, 2]], [[159, 3]], [[0, 2]], [[121, 4]], [[160, 2], [121, 4]]], + [[[120, 1]], [[5, 0], [10, 0], [161, 0], [162, 0], [163, 0], [0, 1]]], + [[[84, 2], [164, 1]], [[0, 1]], - [[24, 3], [0, 2]], + [[7, 3], [0, 2]], [[84, 4]], [[127, 5]], [[49, 1]]], [[[165, 1], [84, 1]], [[0, 1]]], [[[49, 1]], [[52, 2], [0, 1]], [[49, 1], [0, 2]]], - [[[49, 1], [107, 1]], - [[54, 3], [52, 2], [0, 1]], - [[49, 4], [107, 4], [0, 2]], + [[[107, 1], [49, 1]], + [[52, 2], [54, 3], [0, 1]], + [[107, 4], [49, 4], [0, 2]], [[0, 3]], [[52, 2], [0, 4]]], - [[[49, 1], [107, 1]], [[52, 2], [0, 1]], [[49, 1], [107, 1], [0, 2]]], - [[[20, 1]], [[48, 2], [0, 1]], [[49, 3]], [[0, 3]]], - [[[13, 1], [110, 2], [33, 3]], - [[166, 4]], - [[20, 5]], - [[81, 6], [61, 5]], - [[59, 5]], + [[[107, 1], [49, 1]], [[52, 2], [0, 1]], [[107, 1], [49, 1], [0, 2]]], + [[[28, 1]], [[48, 2], [0, 1]], [[49, 3]], [[0, 3]]], + [[[25, 1], [42, 3], [110, 2]], + [[80, 4], [60, 5]], + [[28, 5]], + [[166, 6]], + [[60, 5]], [[0, 5]], - [[61, 5]]], - [[[8, 1]], + [[64, 5]]], + [[[21, 1]], [[48, 2]], - [[80, 3]], + [[81, 3]], [[167, 4], [168, 5]], [[48, 6]], [[48, 7]], - [[80, 8]], - [[80, 9]], - [[167, 4], [168, 5], [127, 10], [0, 8]], + [[81, 8]], + [[81, 9]], + [[127, 10], [167, 4], [168, 5], [0, 8]], [[0, 9]], [[48, 11]], - [[80, 12]], + [[81, 12]], [[168, 5], [0, 12]]], - [[[169, 1], [35, 2], [53, 3]], + [[[169, 1], [10, 2], [53, 3]], [[50, 4], [52, 5], [0, 1]], [[169, 6], [52, 7], [0, 2]], [[169, 8]], [[49, 9]], - [[169, 1], [35, 10], [53, 3], [0, 5]], + [[169, 1], [53, 3], [10, 10], [0, 5]], [[52, 7], [0, 6]], [[169, 11], [53, 3], [0, 7]], [[52, 12], [0, 8]], @@ -1496,84 +1496,84 @@ states: [[49, 6]], [[50, 17], [52, 14], [0, 16]], [[49, 13]]], - [[[35, 2], [170, 1], [53, 3]], - [[50, 4], [52, 5], [0, 1]], - [[170, 6], [52, 7], [0, 2]], - [[170, 8]], - [[49, 9]], - [[170, 1], [35, 10], [53, 3], [0, 5]], - [[52, 7], [0, 6]], - [[170, 11], [53, 3], [0, 7]], - [[52, 12], [0, 8]], - [[52, 5], [0, 9]], - [[170, 13], [52, 14], [0, 10]], - [[50, 15], [52, 7], [0, 11]], - [[0, 12]], - [[52, 14], [0, 13]], - [[170, 16], [53, 3], [0, 14]], - [[49, 6]], - [[50, 17], [52, 14], [0, 16]], - [[49, 13]]], - [[[20, 1]], [[0, 1]]], + [[[53, 1], [170, 2], [10, 3]], + [[170, 4]], + [[52, 5], [50, 6], [0, 2]], + [[52, 7], [170, 8], [0, 3]], + [[52, 9], [0, 4]], + [[10, 10], [170, 2], [53, 1], [0, 5]], + [[49, 11]], + [[53, 1], [170, 12], [0, 7]], + [[52, 7], [0, 8]], + [[0, 9]], + [[52, 13], [170, 14], [0, 10]], + [[52, 5], [0, 11]], + [[52, 7], [50, 15], [0, 12]], + [[53, 1], [170, 16], [0, 13]], + [[52, 13], [0, 14]], + [[49, 8]], + [[50, 17], [52, 13], [0, 16]], + [[49, 14]]], + [[[28, 1]], [[0, 1]]], [[[9, 1]], [[49, 2]], [[48, 3]], - [[80, 4]], + [[81, 4]], [[127, 5], [0, 4]], [[48, 6]], - [[80, 7]], + [[81, 7]], [[0, 7]]], [[[49, 1]], [[108, 2], [0, 1]], [[95, 3]], [[0, 3]]], - [[[22, 1]], [[171, 2]], [[48, 3], [52, 1]], [[80, 4]], [[0, 4]]], + [[[31, 1]], [[171, 2]], [[52, 1], [48, 3]], [[81, 4]], [[0, 4]]], [[[172, 1]], [[173, 0], [0, 1]]], - [[[111, 1], [31, 2]], [[0, 1]], [[49, 1]]], - [[[26, 1]], [[174, 2], [0, 1]], [[0, 2]]], - [[[62, 1]], [[0, 1]]]], + [[[17, 1], [111, 2]], [[49, 2]], [[0, 2]]], + [[[8, 1]], [[174, 2], [0, 1]], [[0, 2]]], + [[[61, 1]], [[0, 1]]]], labels: [[0, 'EMPTY'], [317, null], - [4, null], [276, null], - [1, 'print'], - [1, 'class'], - [1, 'not'], + [4, null], + [1, 'break'], + [49, null], [1, 'pass'], - [1, 'try'], + [1, 'if'], + [1, 'yield'], [1, 'while'], - [15, null], - [2, null], + [16, null], + [31, null], + [1, 'def'], + [1, 'null'], + [1, 'continue'], [54, null], - [9, null], [1, 'del'], - [1, 'for'], - [1, 'def'], + [1, 'from'], + [55, null], + [14, null], + [1, 'raise'], + [1, 'try'], + [1, 'lambda'], [1, 'False'], - [1, 'null'], - [52, null], + [1, 'debugger'], + [7, null], + [15, null], + [1, 'import'], [1, null], - [1, 'assert'], - [1, 'with'], [25, null], - [1, 'if'], - [1, 'global'], - [1, 'yield'], - [3, null], + [1, 'not'], + [1, 'with'], [1, 'True'], - [49, null], + [1, 'for'], + [2, null], + [3, null], [1, 'return'], - [1, 'from'], - [14, null], - [7, null], - [1, 'debugger'], - [16, null], - [31, null], - [1, 'import'], - [1, 'continue'], - [1, 'lambda'], - [1, 'raise'], + [1, 'assert'], + [1, 'global'], + [52, null], [1, 'nonlocal'], - [55, null], - [1, 'break'], + [1, 'print'], + [9, null], + [1, 'class'], [316, null], [19, null], [308, null], @@ -1589,50 +1589,50 @@ labels: [297, null], [339, null], [296, null], - [10, null], [329, null], [8, null], [342, null], [283, null], [26, null], + [10, null], [266, null], [332, null], - [38, null], - [46, null], - [44, null], + [45, null], + [40, null], [41, null], + [44, null], [37, null], - [42, null], - [50, null], - [40, null], - [45, null], - [36, null], [39, null], + [36, null], [43, null], + [50, null], + [46, null], + [42, null], + [38, null], [48, null], - [324, null], [260, null], + [324, null], [292, null], [1, 'in'], [309, null], [273, null], [327, null], [272, null], + [29, null], [30, null], [27, null], + [20, null], [28, null], [1, 'is'], [21, null], - [29, null], - [20, null], [290, null], [274, null], + [265, null], + [333, null], [279, null], - [337, null], [270, null], - [333, null], [299, null], - [265, null], + [337, null], [281, null], [264, null], [286, null], @@ -1647,16 +1647,16 @@ labels: [340, null], [18, null], [330, null], - [259, null], [268, null], + [259, null], [312, null], [293, null], [321, null], [315, null], + [343, null], [269, null], - [314, null], [277, null], - [343, null], + [314, null], [1, 'else'], [310, null], [51, null], @@ -1677,23 +1677,23 @@ labels: [33, null], [319, null], [13, null], + [307, null], + [291, null], + [304, null], + [263, null], [278, null], [298, null], - [282, null], - [304, null], [311, null], - [291, null], + [282, null], [295, null], - [263, null], [313, null], - [307, null], [318, null], [322, null], [5, null], [6, null], - [17, null], - [47, null], [24, null], + [47, null], + [17, null], [305, null], [306, null], [323, null], @@ -1706,96 +1706,96 @@ labels: [32, null], [341, null]], keywords: -{'False': 17, - 'null': 18, - 'True': 28, +{'False': 23, + 'null': 13, + 'True': 32, 'and': 47, 'as': 108, - 'assert': 21, - 'break': 43, - 'class': 5, - 'continue': 38, - 'debugger': 34, - 'def': 16, - 'del': 14, + 'assert': 37, + 'break': 4, + 'class': 43, + 'continue': 14, + 'debugger': 24, + 'def': 12, + 'del': 16, 'elif': 130, 'else': 127, 'except': 113, 'finally': 168, - 'for': 15, - 'from': 31, - 'global': 25, - 'if': 24, - 'import': 37, + 'for': 33, + 'from': 17, + 'global': 38, + 'if': 7, + 'import': 27, 'in': 83, - 'is': 91, - 'lambda': 39, - 'nonlocal': 41, - 'not': 6, + 'is': 93, + 'lambda': 22, + 'nonlocal': 40, + 'not': 30, 'or': 139, - 'pass': 7, - 'print': 4, - 'raise': 40, - 'return': 30, - 'try': 8, + 'pass': 6, + 'print': 41, + 'raise': 20, + 'return': 36, + 'try': 21, 'while': 9, - 'with': 22, - 'yield': 26}, + 'with': 31, + 'yield': 8}, tokens: {0: 112, - 1: 20, - 2: 11, - 3: 27, - 4: 2, + 1: 28, + 2: 34, + 3: 35, + 4: 3, 5: 159, 6: 160, - 7: 33, - 8: 61, - 9: 13, - 10: 59, + 7: 25, + 8: 60, + 9: 42, + 10: 64, 11: 48, 12: 52, 13: 146, - 14: 32, - 15: 10, - 16: 35, - 17: 161, + 14: 19, + 15: 26, + 16: 10, + 17: 163, 18: 115, 19: 45, - 20: 94, - 21: 92, + 20: 91, + 21: 94, 22: 50, 23: 110, - 24: 163, - 25: 23, - 26: 64, - 27: 89, - 28: 90, - 29: 93, - 30: 88, - 31: 36, + 24: 161, + 25: 29, + 26: 63, + 27: 90, + 28: 92, + 29: 88, + 30: 89, + 31: 11, 32: 173, 33: 144, 34: 142, 35: 53, - 36: 76, + 36: 73, 37: 71, - 38: 67, - 39: 77, - 40: 74, - 41: 70, - 42: 72, - 43: 78, - 44: 69, - 45: 75, - 46: 68, + 38: 78, + 39: 72, + 40: 68, + 41: 69, + 42: 77, + 43: 74, + 44: 70, + 45: 67, + 46: 76, 47: 162, 48: 79, - 49: 29, - 50: 73, + 49: 5, + 50: 75, 51: 129, - 52: 19, - 54: 12, - 55: 42}, + 52: 39, + 54: 15, + 55: 18}, start: 256 }; diff --git a/src/ast.js b/src/ast.js index 4c1c8caaab..0d33218723 100644 --- a/src/ast.js +++ b/src/ast.js @@ -307,8 +307,8 @@ function copy_location(e, n) if (e) { e.lineno = LINENO(n); e.col_offset = n.col_offset; - e.endlineno = n.endlineno; - e.col_endoffset = n.col_endoffset; + e.end_lineno = n.end_lineno; + e.end_col_offset = n.end_col_offset; } return e; } @@ -389,16 +389,16 @@ function astForExceptClause (c, exc, body) { REQ(exc, SYM.except_clause); REQ(body, SYM.suite); if (NCH(exc) === 1) { - return new Sk.astnodes.ExceptHandler(null, null, astForSuite(c, body), exc.lineno, exc.col_offset, exc.endlineno, exc.col_endoffset); + return new Sk.astnodes.ExceptHandler(null, null, astForSuite(c, body), exc.lineno, exc.col_offset, exc.end_lineno, exc.end_col_offset); } else if (NCH(exc) === 2) { - return new Sk.astnodes.ExceptHandler(ast_for_expr(c, CHILD(exc, 1)), null, astForSuite(c, body), exc.lineno, exc.col_offset, exc.endlineno, exc.col_endoffset); + return new Sk.astnodes.ExceptHandler(ast_for_expr(c, CHILD(exc, 1)), null, astForSuite(c, body), exc.lineno, exc.col_offset, exc.end_lineno, exc.end_col_offset); } else if (NCH(exc) === 4) { var expression = ast_for_expr(c, CHILD(exc, 1)); e = ast_for_expr(c, CHILD(exc, 3)); setContext(c, e, Sk.astnodes.Store, CHILD(exc, 3)); - return new Sk.astnodes.ExceptHandler(ast_for_expr(c, CHILD(exc, 1)), e, astForSuite(c, body), exc.lineno, exc.col_offset, exc.endlineno, exc.col_endoffset); + return new Sk.astnodes.ExceptHandler(ast_for_expr(c, CHILD(exc, 1)), e, astForSuite(c, body), exc.lineno, exc.col_offset, exc.end_lineno, exc.end_col_offset); } Sk.asserts.fail("wrong number of children for except clause"); } @@ -446,7 +446,7 @@ function astForTryStmt (c, n) { } Sk.asserts.assert(!!finally_ || handlers.length != 0); - return new Sk.astnodes.Try(body, handlers, orelse, finally_, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Try(body, handlers, orelse, finally_, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } function astForDottedName (c, n) { @@ -459,10 +459,10 @@ function astForDottedName (c, n) { lineno = n.lineno; col_offset = n.col_offset; id = strobj(CHILD(n, 0).value); - e = new Sk.astnodes.Name(id, Sk.astnodes.Load, lineno, col_offset, n.endlineno, n.col_endoffset); + e = new Sk.astnodes.Name(id, Sk.astnodes.Load, lineno, col_offset, n.end_lineno, n.end_col_offset); for (i = 2; i < NCH(n); i += 2) { id = strobj(CHILD(n, i).value); - e = new Sk.astnodes.Attribute(e, id, Sk.astnodes.Load, lineno, col_offset, n.endlineno, n.col_endoffset); + e = new Sk.astnodes.Attribute(e, id, Sk.astnodes.Load, lineno, col_offset, n.end_lineno, n.end_col_offset); } return e; } @@ -480,7 +480,7 @@ function astForDecorator (c, n) { } else if (NCH(n) === 5) // call with no args { - return new Sk.astnodes.Call(nameExpr, [], [], null, null, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Call(nameExpr, [], [], null, null, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } else { return ast_for_call(c, CHILD(n, 3), nameExpr); @@ -555,9 +555,9 @@ function ast_for_with_stmt(c, n0, is_async) { body = astForSuite(c, CHILD(n, NCH(n) - 1)); if (is_async) { - return new Sk.astnodes.AsyncWith(items, body, LINENO(n0), n0.col_offset, n0.endlineno, n0.col_endoffset); + return new Sk.astnodes.AsyncWith(items, body, LINENO(n0), n0.col_offset, n0.end_lineno, n0.end_col_offset); } else { - return new Sk.astnodes.With(items, body, LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.With(items, body, LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); } } @@ -575,7 +575,7 @@ function astForExecStmt (c, n) { if (nchildren === 6) { locals = ast_for_expr(c, CHILD(n, 5)); } - return new Sk.astnodes.Exec(expr1, globals, locals, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Exec(expr1, globals, locals, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } function astForIfStmt (c, n) { @@ -594,7 +594,7 @@ function astForIfStmt (c, n) { return new Sk.astnodes.If( ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), - [], n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + [], n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } s = CHILD(n, 4).value; @@ -604,7 +604,7 @@ function astForIfStmt (c, n) { ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), astForSuite(c, CHILD(n, 6)), - n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } else if (decider === "i") { nElif = NCH(n) - 4; @@ -628,8 +628,8 @@ function astForIfStmt (c, n) { astForSuite(c, CHILD(n, NCH(n) - 1)), CHILD(n, NCH(n) - 6).lineno, CHILD(n, NCH(n) - 6).col_offset), - CHILD(n, NCH(n) - 6).endlineno, - CHILD(n, NCH(n) - 6).col_endoffset]; + CHILD(n, NCH(n) - 6).end_lineno, + CHILD(n, NCH(n) - 6).end_col_offset]; nElif--; } @@ -642,13 +642,13 @@ function astForIfStmt (c, n) { orelse, CHILD(n, off).lineno, CHILD(n, off).col_offset, - CHILD(n, off).endlineno, - CHILD(n, off).col_endoffset)]; + CHILD(n, off).end_lineno, + CHILD(n, off).end_col_offset)]; } return new Sk.astnodes.If( ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), - orelse, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + orelse, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } Sk.asserts.fail("unexpected token in 'if' statement"); @@ -673,7 +673,7 @@ function ast_for_exprlist (c, n, context) { function astForDelStmt (c, n) { /* del_stmt: 'del' exprlist */ REQ(n, SYM.del_stmt); - return new Sk.astnodes.Delete(ast_for_exprlist(c, CHILD(n, 1), Sk.astnodes.Del), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Delete(ast_for_exprlist(c, CHILD(n, 1), Sk.astnodes.Del), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } function astForGlobalStmt (c, n) { @@ -684,17 +684,17 @@ function astForGlobalStmt (c, n) { for (i = 1; i < NCH(n); i += 2) { s[(i - 1) / 2] = strobj(CHILD(n, i).value); } - return new Sk.astnodes.Global(s, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Global(s, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } function astForAssertStmt (c, n) { /* assert_stmt: 'assert' test [',' test] */ REQ(n, SYM.assert_stmt); if (NCH(n) === 2) { - return new Sk.astnodes.Assert(ast_for_expr(c, CHILD(n, 1)), null, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Assert(ast_for_expr(c, CHILD(n, 1)), null, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } else if (NCH(n) === 4) { - return new Sk.astnodes.Assert(ast_for_expr(c, CHILD(n, 1)), ast_for_expr(c, CHILD(n, 3)), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Assert(ast_for_expr(c, CHILD(n, 1)), ast_for_expr(c, CHILD(n, 3)), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } Sk.asserts.fail("improper number of parts to assert stmt"); } @@ -769,13 +769,13 @@ function astForImportStmt (c, n) { var aliases; var col_offset; var lineno; - var endlineno; - var col_endoffset; + var end_lineno; + var end_col_offset; REQ(n, SYM.import_stmt); lineno = n.lineno; col_offset = n.col_offset; - endlineno = n.endlineno; - col_endoffset = n.col_endoffset + end_lineno = n.end_lineno; + end_col_offset = n.end_col_offset n = CHILD(n, 0); if (n.type === SYM.import_name) { n = CHILD(n, 1); @@ -784,7 +784,7 @@ function astForImportStmt (c, n) { for (i = 0; i < NCH(n); i += 2) { aliases[i / 2] = aliasForImportName(c, CHILD(n, i)); } - return new Sk.astnodes.Import(aliases, lineno, col_offset, endlineno, col_endoffset); + return new Sk.astnodes.Import(aliases, lineno, col_offset, end_lineno, end_col_offset); } else if (n.type === SYM.import_from) { mod = null; @@ -834,7 +834,7 @@ function astForImportStmt (c, n) { } } modname = mod ? mod.name.v : ""; - return new Sk.astnodes.ImportFrom(strobj(modname), aliases, ndots, lineno, col_offset, endlineno, col_endoffset); + return new Sk.astnodes.ImportFrom(strobj(modname), aliases, ndots, lineno, col_offset, end_lineno, end_col_offset); } throw new Sk.builtin.SyntaxError("unknown import statement", c.c_filename, n.lineno); } @@ -886,11 +886,11 @@ function astForFactor (c, n) { expression = ast_for_expr(c, CHILD(n, 1)); switch (CHILD(n, 0).type) { case TOK.T_PLUS: - return new Sk.astnodes.UnaryOp(Sk.astnodes.UAdd, expression, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.UnaryOp(Sk.astnodes.UAdd, expression, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); case TOK.T_MINUS: - return new Sk.astnodes.UnaryOp(Sk.astnodes.USub, expression, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.UnaryOp(Sk.astnodes.USub, expression, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); case TOK.T_TILDE: - return new Sk.astnodes.UnaryOp(Sk.astnodes.Invert, expression, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.UnaryOp(Sk.astnodes.Invert, expression, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } Sk.asserts.fail("unhandled factor"); @@ -912,13 +912,13 @@ function astForForStmt (c, n) { target = _target[0]; } else { - target = new Sk.astnodes.Tuple(_target, Sk.astnodes.Store, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + target = new Sk.astnodes.Tuple(_target, Sk.astnodes.Store, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } return new Sk.astnodes.For(target, ast_for_testlist(c, CHILD(n, 3)), astForSuite(c, CHILD(n, 5)), - seq, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + seq, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } function ast_for_call(c, n, func, allowgen) @@ -1002,7 +1002,7 @@ function ast_for_call(c, n, func, allowgen) return NULL; } starred = new Sk.astnodes.Starred(e, Sk.astnodes.Load, LINENO(chch), - chch.col_offset, chch.endlineno, chch.col_endoffset); + chch.col_offset, chch.end_lineno, chch.end_col_offset); args[nargs++] = starred; } else if (TYPE(chch) == TOK.T_DOUBLESTAR) { /* a keyword argument unpacking */ @@ -1069,7 +1069,7 @@ function ast_for_call(c, n, func, allowgen) } } - return new Sk.astnodes.Call(func, args, keywords, func.lineno, func.col_offset, func.endlineno, func.col_endoffset); + return new Sk.astnodes.Call(func, args, keywords, func.lineno, func.col_offset, func.end_lineno, func.end_col_offset); } function ast_for_trailer(c, n, left_expr) { @@ -1081,7 +1081,7 @@ function ast_for_trailer(c, n, left_expr) { if (TYPE(CHILD(n, 0)) == TOK.T_LPAR) { if (NCH(n) == 2) return new Sk.astnodes.Call(left_expr, NULL, NULL, LINENO(n), - n.col_offset, n.endlineno, n.col_endoffset); + n.col_offset, n.end_lineno, n.end_col_offset); else return ast_for_call(c, CHILD(n, 1), left_expr, true); } @@ -1090,7 +1090,7 @@ function ast_for_trailer(c, n, left_expr) { if (!attr_id) return NULL; return new Sk.astnodes.Attribute(left_expr, attr_id, Sk.astnodes.Load, - LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); + LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); } else { REQ(CHILD(n, 0), TOK.T_LSQB); @@ -1102,7 +1102,7 @@ function ast_for_trailer(c, n, left_expr) { return NULL; } return new Sk.astnodes.Subscript(left_expr, slc, Sk.astnodes.Load, - LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); + LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); } else { /* The grammar is ambiguous here. The ambiguity is resolved @@ -1128,7 +1128,7 @@ function ast_for_trailer(c, n, left_expr) { if (!simple) { return new Sk.astnodes.Subscript(left_expr, new Sk.astnodes.ExtSlice(slices), Sk.astnodes.Load, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } /* extract Index values and put them in a Tuple */ elts = []; @@ -1138,10 +1138,10 @@ function ast_for_trailer(c, n, left_expr) { Sk.asserts.assert(slc.kind == _slice_kind.Index_kind && slc.v.Index.value); elts[j] = slc.v.Index.value; } - e = new Sk.astnodes.Tuple(elts, Sk.astnodes.Load, LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); + e = new Sk.astnodes.Tuple(elts, Sk.astnodes.Load, LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); return new Sk.astnodes.Subscript(left_expr, new Sk.astnodes.Index(e), - Sk.astnodes.Load, LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); + Sk.astnodes.Load, LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); } } } @@ -1165,36 +1165,36 @@ function ast_for_flow_stmt(c, n) switch (TYPE(ch)) { case SYM.break_stmt: return new Sk.astnodes.Break(LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); case SYM.continue_stmt: return new Sk.astnodes.Continue(LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); case SYM.yield_stmt: { /* will reduce to yield_expr */ var exp = ast_for_expr(c, CHILD(ch, 0)); if (!exp) { return null; } return new Sk.astnodes.Expr(exp, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } case SYM.return_stmt: if (NCH(ch) == 1) return new Sk.astnodes.Return(null, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); else { var expression = ast_for_testlist(c, CHILD(ch, 1)); if (!expression) { return null; } return new Sk.astnodes.Return(expression, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } case SYM.raise_stmt: // This is tricky and Skulpt-specific, because we need to handle // both Python 3-style and Python 2-style 'raise' statements if (NCH(ch) == 1) return new Sk.astnodes.Raise(null, null, null, null, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); else if (NCH(ch) >= 2) { var cause = null; var expression = ast_for_expr(c, CHILD(ch, 1)); @@ -1219,7 +1219,7 @@ function ast_for_flow_stmt(c, n) } } return new Sk.astnodes.Raise(expression, cause, inst, tback, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } /* fall through */ default: @@ -1243,7 +1243,7 @@ function astForArg(c, n) annotation = ast_for_expr(c, CHILD(n, 2)); } - return new Sk.astnodes.arg(name, annotation, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.arg(name, annotation, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } /* returns -1 if failed to handle keyword only arguments @@ -1288,7 +1288,7 @@ function handleKeywordonlyArgs(c, n, start, kwonlyargs, kwdefaults) ch = CHILD(ch, 0); forbiddenCheck(c, ch, ch.value, ch.lineno); argname = strobj(ch.value); - kwonlyargs[j++] = new Sk.astnodes.arg(argname, annotation, ch.lineno, ch.col_offset, ch.endlineno, ch.col_endoffset); + kwonlyargs[j++] = new Sk.astnodes.arg(argname, annotation, ch.lineno, ch.col_offset, ch.end_lineno, ch.end_col_offset); i += 2; /* the name and the comma */ break; case TOK.T_DOUBLESTAR: @@ -1492,10 +1492,10 @@ function ast_for_funcdef_impl(c, n0, decorator_seq, is_async) { if (is_async) return new Sk.astnodes.AsyncFunctionDef(name, args, body, decorator_seq, returns, type_comment, - LINENO(n0), n0.col_offset, n0.endlineno, n0.col_endoffset); + LINENO(n0), n0.col_offset, n0.end_lineno, n0.end_col_offset); else return new Sk.astnodes.FunctionDef(name, args, body, decorator_seq, returns, type_comment, - LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); + LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); } function astForClassBases (c, n) { @@ -1523,7 +1523,7 @@ function astForClassdef (c, n, decoratorSeq) { return new Sk.astnodes.ClassDef(classname, [], [], s, decoratorSeq, /*TODO docstring*/null, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } if (TYPE(CHILD(n, 3)) === TOK.T_RPAR) { /* class NAME '(' ')' ':' suite */ @@ -1532,7 +1532,7 @@ function astForClassdef (c, n, decoratorSeq) { forbiddenCheck(c, CHILD(n, 3), classname, CHILD(n, 3).lineno); return new Sk.astnodes.ClassDef(classname, [], [], s, decoratorSeq, /*TODO docstring*/null, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } /* class NAME '(' arglist ')' ':' suite */ @@ -1542,7 +1542,7 @@ function astForClassdef (c, n, decoratorSeq) { var dummy; dummy_name = new_identifier(CHILD(n, 1)); dummy = new Sk.astnodes.Name(dummy_name, Sk.astnodes.Load, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); call = ast_for_call(c, CHILD(n, 3), dummy, false); } s = astForSuite(c, CHILD(n, 6)); @@ -1551,7 +1551,7 @@ function astForClassdef (c, n, decoratorSeq) { return new Sk.astnodes.ClassDef(classname, call.args, call.keywords, s, decoratorSeq, /*TODO docstring*/null, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } function astForLambdef (c, n) { @@ -1566,7 +1566,7 @@ function astForLambdef (c, n) { args = astForArguments(c, CHILD(n, 1)); expression = ast_for_expr(c, CHILD(n, 3)); } - return new Sk.astnodes.Lambda(args, expression, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Lambda(args, expression, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } function astForComprehension(c, n) { @@ -1645,7 +1645,7 @@ function astForComprehension(c, n) { if (NCH(forch) === 1) { comp = new Sk.astnodes.comprehension(t[0], expression, []); } else { - comp = new Sk.astnodes.comprehension(new Sk.astnodes.Tuple(t, Sk.astnodes.Store, n.lineno, n.col_offset, n.endlineno, n.col_endoffset), expression, []); + comp = new Sk.astnodes.comprehension(new Sk.astnodes.Tuple(t, Sk.astnodes.Store, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset), expression, []); } if (NCH(n) === 5) { n = CHILD(n, 4); @@ -1677,9 +1677,9 @@ function astForIterComp(c, n, type) { elt = ast_for_expr(c, CHILD(n, 0)); comps = astForComprehension(c, CHILD(n, 1)); if (type === COMP_GENEXP) { - return new Sk.astnodes.GeneratorExp(elt, comps, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.GeneratorExp(elt, comps, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } else if (type === COMP_SETCOMP) { - return new Sk.astnodes.SetComp(elt, comps, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.SetComp(elt, comps, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } } @@ -1816,7 +1816,7 @@ function ast_for_comprehension(c, n) { comp = new Sk.astnodes.comprehension(first, expression, null, is_async); else comp = new Sk.astnodes.comprehension(new Sk.astnodes.Tuple(t, Sk.astnodes.Store, first.lineno, first.col_offset, - for_ch.endlineno, for_ch.col_endoffset), + for_ch.end_lineno, for_ch.end_col_offset), expression, null, is_async); if (NCH(n) == (5 + is_async)) { @@ -1904,13 +1904,13 @@ function ast_for_itercomp(c, n, type) { if (type == COMP_GENEXP) { return new Sk.astnodes.GeneratorExp(elt, comps, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } else if (type == COMP_LISTCOMP) { return new Sk.astnodes.ListComp(elt, comps, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } else if (type == COMP_SETCOMP) { return new Sk.astnodes.SetComp(elt, comps, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } else { /* Should never happen */ return null; @@ -1959,7 +1959,7 @@ function ast_for_dictcomp(c, n) { key = ast_for_expr(c, CHILD(n, 0)); value = ast_for_expr(c, CHILD(n, 2)); comps = astForComprehension(c, CHILD(n, 3)); - return new Sk.astnodes.DictComp(key, value, comps, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.DictComp(key, value, comps, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } function ast_for_dictdisplay(c, n) @@ -1978,7 +1978,7 @@ function ast_for_dictdisplay(c, n) } return new Sk.astnodes.Dict(keys, values, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } function ast_for_gen_expr(c, n) { @@ -1995,10 +1995,10 @@ function astForWhileStmt (c, n) { /* while_stmt: 'while' test ':' suite ['else' ':' suite] */ REQ(n, SYM.while_stmt); if (NCH(n) === 4) { - return new Sk.astnodes.While(ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), [], n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.While(ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), [], n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } else if (NCH(n) === 7) { - return new Sk.astnodes.While(ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), astForSuite(c, CHILD(n, 6)), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.While(ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), astForSuite(c, CHILD(n, 6)), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } Sk.asserts.fail("wrong number of tokens for 'while' stmt"); } @@ -2051,13 +2051,13 @@ function astForBinop (c, n) { ast_for_expr(c, CHILD(n, 0)), getOperator(CHILD(n, 1)), ast_for_expr(c, CHILD(n, 2)), - n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); var nops = (NCH(n) - 1) / 2; for (i = 1; i < nops; ++i) { nextOper = CHILD(n, i * 2 + 1); newoperator = getOperator(nextOper); tmp = ast_for_expr(c, CHILD(n, i * 2 + 2)); - result = new Sk.astnodes.BinOp(result, newoperator, tmp, nextOper.lineno, nextOper.col_offset, nextOper.endlineno, nextOper.col_endoffset); + result = new Sk.astnodes.BinOp(result, newoperator, tmp, nextOper.lineno, nextOper.col_offset, nextOper.end_lineno, nextOper.end_col_offset); } return result; } @@ -2079,7 +2079,7 @@ function ast_for_testlist (c, n) { return ast_for_expr(c, CHILD(n, 0)); } else { - return new Sk.astnodes.Tuple(seq_for_testlist(c, n), Sk.astnodes.Load, n.lineno, n.col_offset, n.endlineno, n.col_endoffset/*, c.c_arena */); + return new Sk.astnodes.Tuple(seq_for_testlist(c, n), Sk.astnodes.Load, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset/*, c.c_arena */); } } @@ -2103,7 +2103,7 @@ function ast_for_exprStmt (c, n) { test: ... here starts the operator precedence dance */ if (NCH(n) === 1) { - return new Sk.astnodes.Expr(ast_for_testlist(c, CHILD(n, 0)), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Expr(ast_for_testlist(c, CHILD(n, 0)), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } else if (CHILD(n, 1).type === SYM.augassign) { ch = CHILD(n, 0); @@ -2133,7 +2133,7 @@ function ast_for_exprStmt (c, n) { expr2 = ast_for_expr(c, ch); } - return new Sk.astnodes.AugAssign(expr1, astForAugassign(c, CHILD(n, 1)), expr2, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.AugAssign(expr1, astForAugassign(c, CHILD(n, 1)), expr2, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } else if (CHILD(n, 1).type === SYM.annassign) { // TODO translate the relevant section from ast.c @@ -2159,7 +2159,7 @@ function ast_for_exprStmt (c, n) { else { expression = ast_for_expr(c, value); } - return new Sk.astnodes.Assign(targets, expression, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Assign(targets, expression, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } } @@ -2170,7 +2170,7 @@ function astForIfexpr (c, n) { ast_for_expr(c, CHILD(n, 2)), ast_for_expr(c, CHILD(n, 0)), ast_for_expr(c, CHILD(n, 4)), - n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } /** @@ -2429,7 +2429,7 @@ function astForSlice (c, n) { if (ch.type === SYM.sliceop) { if (NCH(ch) === 1) { ch = CHILD(ch, 0); - step = new Sk.astnodes.NameConstant(Sk.builtin.none.none$, Sk.astnodes.Load, ch.lineno, ch.col_offset, ch.endlineno, ch.col_endoffset); + step = new Sk.astnodes.NameConstant(Sk.builtin.none.none$, Sk.astnodes.Load, ch.lineno, ch.col_offset, ch.end_lineno, ch.end_col_offset); } else { ch = CHILD(ch, 1); @@ -2455,21 +2455,21 @@ function ast_for_atom(c, n) var s = STR(ch); if (s.length >= 4 && s.length <= 5) { if (s === "None") { - return new Sk.astnodes.NameConstant(Sk.builtin.none.none$, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.NameConstant(Sk.builtin.none.none$, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } if (s === "True") { - return new Sk.astnodes.NameConstant(Sk.builtin.bool.true$, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.NameConstant(Sk.builtin.bool.true$, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } if (s === "False") { - return new Sk.astnodes.NameConstant(Sk.builtin.bool.false$, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.NameConstant(Sk.builtin.bool.false$, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } } name = new_identifier(s, c); /* All names start in Load context, but may later be changed. */ return new Sk.astnodes.Name(name, Sk.astnodes.Load, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } case TOK.T_STRING: { var str = parsestrplus(c, n); @@ -2497,19 +2497,19 @@ function ast_for_atom(c, n) // } // return NULL; // } - return new Sk.astnodes.Str(str, LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Str(str, LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); } case TOK.T_NUMBER: - return new Sk.astnodes.Num(parsenumber(c, ch.value, n.lineno), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Num(parsenumber(c, ch.value, n.lineno), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); case TOK.T_ELLIPSIS: /* Ellipsis */ return new Sk.astnodes.Ellipsis(LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); case TOK.T_LPAR: /* some parenthesized expressions */ ch = CHILD(n, 1); if (TYPE(ch) == TOK.T_RPAR) return new Sk.astnodes.Tuple([], Sk.astnodes.Load, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); if (TYPE(ch) == SYM.yield_expr) { return ast_for_expr(c, ch); @@ -2531,7 +2531,7 @@ function ast_for_atom(c, n) if (TYPE(ch) == TOK.T_RSQB) return new Sk.astnodes.List([], Sk.astnodes.Load, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); REQ(ch, SYM.testlist_comp); if (NCH(ch) == 1 || TYPE(CHILD(ch, 1)) == TOK.T_COMMA) { @@ -2540,7 +2540,7 @@ function ast_for_atom(c, n) return null; } return new Sk.astnodes.List(elts, Sk.astnodes.Load, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } else { return copy_location(ast_for_listcomp(c, ch), n); @@ -2555,7 +2555,7 @@ function ast_for_atom(c, n) if (TYPE(ch) == TOK.T_RBRACE) { /* It's an empty dict. */ return new Sk.astnodes.Dict(null, null, LINENO(n), n.col_offset, - n.endlineno, n.col_endoffset); + n.end_lineno, n.end_col_offset); } else { var is_dict = (TYPE(CHILD(ch, 0)) == TOK.T_DOUBLESTAR); @@ -2605,7 +2605,7 @@ function ast_for_setdisplay(c, n) { elts[i / 2] = expression; } - return new Sk.astnodes.Set(elts, LINENO(n), n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Set(elts, LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); } @@ -2626,30 +2626,30 @@ function astForAtom(c, n) { // All names start in Load context, but may be changed later if (s.length >= 4 && s.length <= 5) { if (s === "None") { - return new Sk.astnodes.NameConstant(Sk.builtin.none.none$, n.lineno, n.col_offset, n.endlineno, n.col_endoffset /* c.c_arena*/); + return new Sk.astnodes.NameConstant(Sk.builtin.none.none$, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset /* c.c_arena*/); } if (s === "True") { - return new Sk.astnodes.NameConstant(Sk.builtin.bool.true$, n.lineno, n.col_offset, n.endlineno, n.col_endoffset /* c.c_arena*/); + return new Sk.astnodes.NameConstant(Sk.builtin.bool.true$, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset /* c.c_arena*/); } if (s === "False") { - return new Sk.astnodes.NameConstant(Sk.builtin.bool.false$, n.lineno, n.col_offset, n.endlineno, n.col_endoffset /* c.c_arena*/); + return new Sk.astnodes.NameConstant(Sk.builtin.bool.false$, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset /* c.c_arena*/); } } var name = new_identifier(s, c) /* All names start in Load context, but may later be changed. */ - return new Sk.astnodes.Name(name, Sk.astnodes.Load, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Name(name, Sk.astnodes.Load, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); case TOK.T_STRING: - return new Sk.astnodes.Str(parsestrplus(c, n), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Str(parsestrplus(c, n), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); case TOK.T_NUMBER: - return new Sk.astnodes.Num(parsenumber(c, ch.value, n.lineno), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Num(parsenumber(c, ch.value, n.lineno), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); case TOK.T_LPAR: // various uses for parens ch = CHILD(n, 1); if (ch.type === TOK.T_RPAR) { - return new Sk.astnodes.Tuple([], Sk.astnodes.Load, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Tuple([], Sk.astnodes.Load, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } if (ch.type === SYM.yield_expr) { return ast_for_expr(c, ch); @@ -2661,11 +2661,11 @@ function astForAtom(c, n) { case TOK.T_LSQB: // list or listcomp ch = CHILD(n, 1); if (ch.type === TOK.T_RSQB) { - return new Sk.astnodes.List([], Sk.astnodes.Load, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.List([], Sk.astnodes.Load, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } REQ(ch, SYM.listmaker); if (NCH(ch) === 1 || CHILD(ch, 1).type === TOK.T_COMMA) { - return new Sk.astnodes.List(seq_for_testlist(c, ch), Sk.astnodes.Load, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.List(seq_for_testlist(c, ch), Sk.astnodes.Load, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } return ast_for_listcomp(c, ch); @@ -2679,7 +2679,7 @@ function astForAtom(c, n) { ch = CHILD(n, 1); if (n.type === TOK.T_RBRACE) { //it's an empty dict - return new Sk.astnodes.Dict([], null, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Dict([], null, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } else if (NCH(ch) === 1 || (NCH(ch) !== 0 && CHILD(ch, 1).type === TOK.T_COMMA)) { //it's a simple set @@ -2689,7 +2689,7 @@ function astForAtom(c, n) { var expression = ast_for_expr(c, CHILD(ch, i)); elts[i / 2] = expression; } - return new Sk.astnodes.Set(elts, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Set(elts, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } else if (NCH(ch) !== 0 && CHILD(ch, 1).type == SYM.comp_for) { //it's a set comprehension @@ -2705,11 +2705,11 @@ function astForAtom(c, n) { keys[i / 4] = ast_for_expr(c, CHILD(ch, i)); values[i / 4] = ast_for_expr(c, CHILD(ch, i + 2)); } - return new Sk.astnodes.Dict(keys, values, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Dict(keys, values, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } case TOK.T_BACKQUOTE: //throw new Sk.builtin.SyntaxError("backquote not supported, use repr()", c.c_filename, n.lineno); - return new Sk.astnodes.Repr(ast_for_testlist(c, CHILD(n, 1)), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Repr(ast_for_testlist(c, CHILD(n, 1)), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); default: Sk.asserts.fail("unhandled atom", ch.type); @@ -2738,7 +2738,7 @@ function astForAtomExpr(c, n) { } if (start && nch === 2) { - return new Sk.astnodes.Await(e, n.lineno, n.col_offset, n.endlineno, n.col_endoffset /*, c->c_arena*/); + return new Sk.astnodes.Await(e, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset /*, c->c_arena*/); } for (i = start + 1; i < nch; i++) { @@ -2758,7 +2758,7 @@ function astForAtomExpr(c, n) { if (start) { /* there was an AWAIT */ - return new Sk.astnodes.Await(e, n.line, n.col_offset, n.endlineno, n.col_endoffset /*, c->c_arena*/); + return new Sk.astnodes.Await(e, n.line, n.col_offset, n.end_lineno, n.end_col_offset /*, c->c_arena*/); } else { return e; @@ -2780,7 +2780,7 @@ function astForPower (c, n) { } if (CHILD(n, NCH(n) - 1).type === SYM.factor) { f = ast_for_expr(c, CHILD(n, NCH(n) - 1)); - e = new Sk.astnodes.BinOp(e, Sk.astnodes.Pow, f, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + e = new Sk.astnodes.BinOp(e, Sk.astnodes.Pow, f, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } return e; } @@ -2789,7 +2789,7 @@ function astForStarred(c, n) { REQ(n, SYM.star_expr); /* The Load context is changed later */ - return new Sk.astnodes.Starred(ast_for_expr(c, CHILD(n ,1)), Sk.astnodes.Load, n.lineno, n.col_offset, n.endlineno, n.col_endoffset /*, c.c_arena */) + return new Sk.astnodes.Starred(ast_for_expr(c, CHILD(n ,1)), Sk.astnodes.Load, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset /*, c.c_arena */) } function ast_for_expr (c, n) { @@ -2840,17 +2840,17 @@ function ast_for_expr (c, n) { seq[i / 2] = ast_for_expr(c, CHILD(n, i)); } if (CHILD(n, 1).value === "and") { - return new Sk.astnodes.BoolOp(Sk.astnodes.And, seq, n.lineno, n.col_offset, n.endlineno, n.col_endoffset /*, c.c_arena*/); + return new Sk.astnodes.BoolOp(Sk.astnodes.And, seq, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset /*, c.c_arena*/); } Sk.asserts.assert(CHILD(n, 1).value === "or"); - return new Sk.astnodes.BoolOp(Sk.astnodes.Or, seq, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.BoolOp(Sk.astnodes.Or, seq, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); case SYM.not_test: if (NCH(n) === 1) { n = CHILD(n, 0); continue LOOP; } else { - return new Sk.astnodes.UnaryOp(Sk.astnodes.Not, ast_for_expr(c, CHILD(n, 1)), n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.UnaryOp(Sk.astnodes.Not, ast_for_expr(c, CHILD(n, 1)), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } break; case SYM.comparison: @@ -2865,7 +2865,7 @@ function ast_for_expr (c, n) { ops[(i - 1) / 2] = astForCompOp(c, CHILD(n, i)); cmps[(i - 1) / 2] = ast_for_expr(c, CHILD(n, i + 1)); } - return new Sk.astnodes.Compare(ast_for_expr(c, CHILD(n, 0)), ops, cmps, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Compare(ast_for_expr(c, CHILD(n, 0)), ops, cmps, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } break; case SYM.star_expr: @@ -2905,10 +2905,10 @@ function ast_for_expr (c, n) { } if (is_from) { - return new Sk.astnodes.YieldFrom(exp, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.YieldFrom(exp, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } - return new Sk.astnodes.Yield(exp, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Yield(exp, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); case SYM.factor: if (NCH(n) === 1) { n = CHILD(n, 0); @@ -2957,7 +2957,7 @@ function astForPrintStmt (c, n) { seq[j] = ast_for_expr(c, CHILD(n, i)); } nl = (CHILD(n, NCH(n) - 1)).type === TOK.T_COMMA ? false : true; - return new Sk.astnodes.Print(dest, seq, nl, n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Print(dest, seq, nl, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } function astForStmt (c, n) { @@ -2982,7 +2982,7 @@ function astForStmt (c, n) { case SYM.del_stmt: return astForDelStmt(c, n); case SYM.pass_stmt: - return new Sk.astnodes.Pass(n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Pass(n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); case SYM.flow_stmt: return ast_for_flow_stmt(c, n); case SYM.import_stmt: @@ -2996,7 +2996,7 @@ function astForStmt (c, n) { case SYM.print_stmt: return astForPrintStmt(c, n); case SYM.debugger_stmt: - return new Sk.astnodes.Debugger(n.lineno, n.col_offset, n.endlineno, n.col_endoffset); + return new Sk.astnodes.Debugger(n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); default: Sk.asserts.fail("unhandled small_stmt"); } diff --git a/src/compile.js b/src/compile.js index 3c5f30f13e..aa65aa58fa 100644 --- a/src/compile.js +++ b/src/compile.js @@ -982,7 +982,7 @@ Compiler.prototype.caugassign = function (s) { switch (e.constructor) { case Sk.astnodes.Attribute: to = this.vexpr(e.value); - auge = new Sk.astnodes.Attribute(e.value, e.attr, Sk.astnodes.AugLoad, e.lineno, e.col_offset, e.endlineno, e.col_endoffset); + auge = new Sk.astnodes.Attribute(e.value, e.attr, Sk.astnodes.AugLoad, e.lineno, e.col_offset, e.end_lineno, e.end_col_offset); aug = this.vexpr(auge, undefined, to); val = this.vexpr(s.value); res = this._gr("inplbinopattr", "Sk.abstr.numberInplaceBinOp(", aug, ",", val, ",'", s.op.prototype._astname, "')"); @@ -992,7 +992,7 @@ Compiler.prototype.caugassign = function (s) { // Only compile the subscript value once to = this.vexpr(e.value); augsub = this.vslicesub(e.slice); - auge = new Sk.astnodes.Subscript(e.value, augsub, Sk.astnodes.AugLoad, e.lineno, e.col_offset, e.endlineno, e.col_endoffset); + auge = new Sk.astnodes.Subscript(e.value, augsub, Sk.astnodes.AugLoad, e.lineno, e.col_offset, e.end_lineno, e.end_col_offset); aug = this.vexpr(auge, undefined, to, augsub); val = this.vexpr(s.value); res = this._gr("inplbinopsubscr", "Sk.abstr.numberInplaceBinOp(", aug, ",", val, ",'", s.op.prototype._astname, "')"); diff --git a/src/parser.js b/src/parser.js index ef6d5bc53b..124013c263 100644 --- a/src/parser.js +++ b/src/parser.js @@ -195,8 +195,8 @@ Parser.prototype.shift = function (type, value, newstate, context) { value : value, lineno : context[0][0], col_offset: context[0][1], - endlineno : context[1][0], - col_endoffset: context[1][1], + end_lineno : context[1][0], + end_col_offset: context[1][1], children : null }; if (newnode) { @@ -218,8 +218,8 @@ Parser.prototype.push = function (type, newdfa, newstate, context) { value : null, lineno : context[0][0], col_offset: context[0][1], - endlineno : context[1][0], - col_endoffset: context[1][1], + end_lineno : context[1][0], + end_col_offset: context[1][1], children : [] }; this.stack[this.stack.length - 1] = { diff --git a/src/pgen/ast/Python.asdl b/src/pgen/ast/Python.asdl index ae1cd5ea5e..686ce0fc98 100644 --- a/src/pgen/ast/Python.asdl +++ b/src/pgen/ast/Python.asdl @@ -62,7 +62,7 @@ module Python -- XXX Jython will be different -- col_offset is the byte offset in the utf8 string the parser uses - attributes (int lineno, int col_offset, int endlineno, int col_endoffset) + attributes (int lineno, int col_offset, int end_lineno, int end_col_offset) -- BoolOp() can use left & right? expr = BoolOp(boolop op, expr* values) @@ -102,7 +102,7 @@ module Python | Tuple(expr* elts, expr_context ctx) -- col_offset is the byte offset in the utf8 string the parser uses - attributes (int lineno, int col_offset, int endlineno, int col_endoffset) + attributes (int lineno, int col_offset, int end_lineno, int end_col_offset) expr_context = Load | Store | Del | AugLoad | AugStore | Param @@ -122,7 +122,7 @@ module Python comprehension = (expr target, expr iter, expr* ifs, int is_async) excepthandler = ExceptHandler(expr? type, identifier? name, stmt* body) - attributes (int lineno, int col_offset, int endlineno, int col_endoffset) + attributes (int lineno, int col_offset, int end_lineno, int end_col_offset) -- Skulpt: added the underscore because aguments is a reserved word in -- javascript @@ -130,7 +130,7 @@ module Python arg? kwarg, expr* defaults) arg = (identifier arg, expr? annotation) - attributes (int lineno, int col_offset, int endlineno, int col_endoffset) + attributes (int lineno, int col_offset, int end_lineno, int end_col_offset) -- keyword arguments supplied to call (NULL identifier for **kwargs) keyword = (identifier? arg, expr value) From 09257b4a78be10f5ddea667be394572f41945910 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 19 Jun 2019 12:20:05 -0400 Subject: [PATCH 05/68] Fix webpack config, port over timeout improvements, reversed functionality --- src/builtin.js | 4 ++++ src/compile.js | 22 ++++++++++++++-------- src/env.js | 4 ++++ webpack.config.js | 2 +- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/builtin.js b/src/builtin.js index 08a63c2c85..c6b7ddc7ee 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -1264,7 +1264,11 @@ Sk.builtin.reversed = function reversed (seq) { var reverseIter = function (obj) { this.idx = obj.sq$length() - 1; this.myobj = obj; + this.ob$type = Sk.builtin.type.makeTypeObj("reversed", function _reversed(){}); this.getitem = Sk.abstr.lookupSpecial(obj, Sk.builtin.str.$getitem); + this["$r"] = function() { + return new Sk.builtin.str(""); + }, this.tp$iter = function() { return this; }, diff --git a/src/compile.js b/src/compile.js index aa65aa58fa..857ffb95a9 100644 --- a/src/compile.js +++ b/src/compile.js @@ -97,7 +97,7 @@ Compiler.prototype.getSourceLine = function (lineno) { return this.source[lineno - 1]; }; -Compiler.prototype.annotateSource = function (ast) { +Compiler.prototype.annotateSource = function (ast, shouldStep) { var i; var col_offset; var lineno; @@ -111,7 +111,13 @@ Compiler.prototype.annotateSource = function (ast) { out("^\n//\n"); Sk.asserts.assert(ast.lineno !== undefined && ast.col_offset !== undefined); - out("$currLineNo = ", lineno, ";\n$currColNo = ", col_offset, ";\n\n"); + out("\n$currLineNo=Sk.currLineNo=",lineno, ";$currColNo=Sk.currColNo=",col_offset,";"); + out("Sk.currFilename='",this.filename,"';"); + // Do not trace the standard library + if (shouldStep && (!this.filename || + !this.filename.startsWith('src/lib/'))) { + out("Sk.afterSingleExecution($gbl,"+lineno+","+col_offset+","+this.filename+");\n"); + } } }; @@ -318,7 +324,7 @@ Compiler.prototype.outputInterruptTest = function () { // Added by RNL if (Sk.execLimit !== null || Sk.yieldLimit !== null && this.u.canSuspend) { output += "var $dateNow = Date.now();"; if (Sk.execLimit !== null) { - output += "if ($dateNow - Sk.execStart > Sk.execLimit) {throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg())}"; + output += "if ($dateNow - Sk.execStart > Sk.execLimit && Sk.execLimit !== null) {throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg())}"; } if (Sk.yieldLimit !== null && this.u.canSuspend) { output += "if ($dateNow - Sk.lastYield > Sk.yieldLimit) {"; @@ -1126,7 +1132,7 @@ Compiler.prototype.outputSuspensionHelpers = function (unit) { } } - output += "try { $ret=susp.child.resume(); } catch(err) { if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if($exc.length>0) { $err=err; $blk=$exc.pop(); } else { throw err; } }" + + output += "try { $ret=susp.child.resume(); } catch(err) { if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now()} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if($exc.length>0) { $err=err; Sk.err=$err; $blk=$exc.pop(); } else { throw err; } }" + "};"; output += "var $saveSuspension = function($child, $filename, $lineno, $colno) {" + @@ -1263,7 +1269,7 @@ Compiler.prototype.cwhile = function (s) { orelse = s.orelse.length > 0 ? this.newBlock("while orelse") : null; body = this.newBlock("while body"); - this.annotateSource(s); + this.annotateSource(s, true); this._jumpfalse(this.vexpr(s.test), orelse ? orelse : next); this._jump(body); @@ -1982,7 +1988,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal this.u.switchCode = "while(true){try{" this.u.switchCode += this.outputInterruptTest(); this.u.switchCode += "switch($blk){"; - this.u.suffixCode = "} }catch(err){ if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} }});"; + this.u.suffixCode = "} }catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now()} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} }});"; // // jump back to the handler so it can do the main actual work of the @@ -2256,7 +2262,7 @@ Compiler.prototype.cclass = function (s) { this.u.switchCode += "while(true){try{"; this.u.switchCode += this.outputInterruptTest(); this.u.switchCode += "switch($blk){"; - this.u.suffixCode = "}}catch(err){ if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }}}" + this.u.suffixCode = "}}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now()} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }}}" this.u.suffixCode += "}).call(null, $cell);});"; this.u.private_ = s.name; @@ -2704,7 +2710,7 @@ Compiler.prototype.cmod = function (mod) { this.u.switchCode += this.outputInterruptTest(); this.u.switchCode += "switch($blk){"; this.u.suffixCode = "}" - this.u.suffixCode += "}catch(err){ if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} } });"; + this.u.suffixCode += "}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now()} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} } });"; // Note - this change may need to be adjusted for all the other instances of // switchCode and suffixCode in this file. Not knowing how to test those diff --git a/src/env.js b/src/env.js index 9fa5284d7b..8cefaadb8c 100644 --- a/src/env.js +++ b/src/env.js @@ -281,6 +281,10 @@ Sk.syspath = []; Sk.inBrowser = Sk.global["document"] !== undefined; +Sk.afterSingleExecution = function(args) { +}; +Sk.exportSymbol("Sk.afterSingleExecution", Sk.afterSingleExecution); + /** * Internal function used for debug output. * @param {...} args diff --git a/webpack.config.js b/webpack.config.js index ae44234019..ec55c557a6 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -38,7 +38,7 @@ module.exports = (env, argv) => { minimizer: [ new ClosureWebpackPlugin({mode: 'STANDARD'}, { jscomp_error: ['accessControls', 'checkRegExp', 'checkTypes', 'checkVars', - 'deprecated', 'invalidCasts', 'missingProperties', + 'invalidCasts', 'missingProperties', 'nonStandardJsDocs', 'strictModuleDepCheck', 'undefinedVars', 'unknownDefines', 'visibility'], jscomp_off: ['fileoverviewTags', 'deprecated'], From 49ed2ab239b0bb89b3d1041256159972a96a8c6b Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 19 Jun 2019 13:52:35 -0400 Subject: [PATCH 06/68] Port over other old skulpt improvements --- src/errors.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/file.js | 21 ++++----------------- src/import.js | 5 +++++ src/parser.js | 26 ++++++++++++++++++++++++-- src/print.js | 4 ++++ webpack.config.js | 15 +-------------- 6 files changed, 83 insertions(+), 33 deletions(-) diff --git a/src/errors.js b/src/errors.js index 87aecd655a..391c142b89 100644 --- a/src/errors.js +++ b/src/errors.js @@ -537,6 +537,51 @@ Sk.builtin.StopIteration = function (args) { Sk.abstr.setUpInheritance("StopIteration", Sk.builtin.StopIteration, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.StopIteration", Sk.builtin.StopIteration); +/** + * @constructor + * @param {Object} err + */ +Sk.builtin.traceback = function(err) { + if (!(this instanceof Sk.builtin.traceback)) { + return new Sk.builtin.traceback(err); + } + + var lineno = null; + if (err.traceback.length > 0) { + lineno = err.traceback[0].lineno; + } + + this.tb_lineno = new Sk.builtin.int_(lineno) + + //tb_frame, tb_lasti, tb_lineno, tb_next + + this.__class__ = Sk.builtin.traceback; + + return this; +} +Sk.abstr.setUpInheritance("traceback", Sk.builtin.traceback, Sk.builtin.object); +Sk.builtin.traceback.prototype.tp$getattr = function (name) { + if (name != null && (Sk.builtin.checkString(name) || typeof name === "string")) { + var _name = name; + + // get javascript string + if (Sk.builtin.checkString(name)) { + _name = Sk.ffi.remapToJs(name); + } + + if (_name === "tb_lineno") { + return this[_name]; + } + } + + // if we have not returned yet, try the genericgetattr + return Sk.builtin.object.prototype.GenericGetAttr(name); +}; +Sk.builtin.traceback.prototype["$r"] = function () { + return new Sk.builtin.str(""); +}; +Sk.exportSymbol("Sk.builtin.traceback", Sk.builtin.traceback); + // TODO: Extract into sys.exc_info(). Work out how the heck // to find out what exceptions are being processed by parent stack frames... diff --git a/src/file.js b/src/file.js index 8dafa16437..3f7fe929bd 100644 --- a/src/file.js +++ b/src/file.js @@ -26,28 +26,15 @@ Sk.builtin.file = function (name, mode, buffering) { } else { if (Sk.inBrowser) { // todo: Maybe provide a replaceable function for non-import files this.fileno = 10; - elem = document.getElementById(name.v); - if (elem == null) { - if (mode.v == "w" || mode.v == "a") { - this.data$ = ""; - } else { - throw new Sk.builtin.IOError("[Errno 2] No such file or directory: '" + name.v + "'"); - } - } else { - if (elem.nodeName.toLowerCase() == "textarea") { - this.data$ = elem.value; - } else { - this.data$ = elem.textContent; - } - } + this.data$ = Sk.inBrowser(this.name); + this.lineList = this.data$.split("\n"); } else { this.fileno = 11; this.data$ = Sk.read(name.v); + this.lineList = this.data$.split("\n"); + this.lineList = this.lineList.slice(0, -1); } - this.lineList = this.data$.split("\n"); - this.lineList = this.lineList.slice(0, -1); - for (i in this.lineList) { this.lineList[i] = this.lineList[i] + "\n"; } diff --git a/src/import.js b/src/import.js index 5f95ccc57a..af86140072 100644 --- a/src/import.js +++ b/src/import.js @@ -160,6 +160,11 @@ Sk.importSetUpPath = function (canSuspend) { */ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, relativeToPackage, returnUndefinedOnTopLevelNotFound, canSuspend) { //dumpJS = true; + /* TODO: temporary hack, need to delete! */ + if (name == 'pedal.sandbox.sandbox') { + suppliedPyBody= 'class Sandbox: pass\ndef run(): pass\ndef reset(): pass'; + } + /* End hack */ var filename; var prev; var parentModName; diff --git a/src/parser.js b/src/parser.js index 124013c263..dffc75d3a3 100644 --- a/src/parser.js +++ b/src/parser.js @@ -17,6 +17,7 @@ function Parser (filename, grammar) { this.filename = filename; this.grammar = grammar; + this.comments = {}; this.p_flags = 0; return this; } @@ -68,6 +69,11 @@ function findInDfa (a, obj) { return false; } +// Add a comment +Parser.prototype.addcomment = function(value, start, end, line) { + this.comments[start] = value; +}; + // Add a token; return true if we're done Parser.prototype.addtoken = function (type, value, context) { @@ -143,7 +149,7 @@ Parser.prototype.addtoken = function (type, value, context) { //print("findInDfa: " + JSON.stringify(arcs)+" vs. " + tp.state); if (findInDfa(arcs, [0, tp.state])) { - // an accepting state, pop it and try somethign else + // an accepting state, pop it and try something else //print("WAA"); this.pop(); if (this.stack.length === 0) { @@ -280,7 +286,17 @@ function makeParser (filename, style) { return p; } +Sk.parseCache = { + 'lastInput': null, + 'lastParse': null, + 'lastUnit': null +} + Sk.parse = function parse (filename, input) { + if (Sk.parseCache.lastInput == input) { + return Sk.parseCache.lastUnit; + } + var T_COMMENT = Sk.token.tokens.T_COMMENT; var T_NL = Sk.token.tokens.T_NL; var T_OP = Sk.token.tokens.T_OP; @@ -328,6 +344,9 @@ Sk.parse = function parse (filename, input) { lineno += 1; column = 0; } + if (type === T_COMMENT) { + p.addcomment(value, tokenInfo.start, tokenInfo.end, tokenInfo.line); + } } else { if (tokenInfo.type === T_OP) { type = Sk.OpMap[tokenInfo.string]; @@ -348,7 +367,10 @@ Sk.parse = function parse (filename, input) { /** * Small adjustments here in order to return th flags and the cst */ - return {"cst": parser.rootnode, "flags": parser.p_flags}; + var result = {"cst": parser.rootnode, "flags": parser.p_flags, "comments": parser.comments}; + Sk.parseCache.lastUnit = result; + Sk.parseCache.lastInput = input; + return result; }; Sk.parseTreeDump = function parseTreeDump (n, indent) { diff --git a/src/print.js b/src/print.js index 0c5605a14c..54d678ac31 100644 --- a/src/print.js +++ b/src/print.js @@ -81,6 +81,10 @@ var print_f = function function_print(kwa) { }; print_f.co_kwargs = true; +print_f.co_name = new Sk.builtin.str('print'); Sk.builtin.print = new Sk.builtin.func(print_f); Sk.builtin.print.__doc__ = new Sk.builtin.str("print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n\nPrints the values to a stream, or to sys.stdout by default.\nOptional keyword arguments:\nfile: a file-like object (stream); defaults to the current sys.stdout.\nsep: string inserted between values, default a space.\nend: string appended after the last value, default a newline.\nflush: whether to forcibly flush the stream."); + + +Sk.builtin.input.co_name = new Sk.builtin.str('input'); diff --git a/webpack.config.js b/webpack.config.js index ec55c557a6..4941eb0ba0 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -36,7 +36,7 @@ module.exports = (env, argv) => { opt = { noEmitOnErrors: true, minimizer: [ - new ClosureWebpackPlugin({mode: 'STANDARD'}, { + new ClosureWebpackPlugin({mode: 'STANDARD', platform: 'java'}, { jscomp_error: ['accessControls', 'checkRegExp', 'checkTypes', 'checkVars', 'invalidCasts', 'missingProperties', 'nonStandardJsDocs', 'strictModuleDepCheck', 'undefinedVars', @@ -51,19 +51,6 @@ module.exports = (env, argv) => { assertfile = './assert-prod.js'; mod = { rules: [ - { - test: /\.js$/, - enforce: 'pre', - exclude: styleexcludes, - use: [ - { - loader: 'webpack-jshint-loader', - options: { - emitErrors: true - } - } - ] - }, { test: /\.js$/, enforce: 'pre', From b9640e63ab720fffcac65ac7c7cd57fa31ae2bd6 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 20 Jun 2019 08:05:34 -0400 Subject: [PATCH 07/68] Copy over custom libraries (without fixes) --- src/ast.js | 28 + src/builtin/sys.js | 10 + src/lib/ast.js | 374 ++++++++ src/lib/cisc108/__init__.py | 1 + src/lib/cisc108/assertions.py | 294 +++++++ src/lib/inspect.py | 1 + src/lib/json/__init__.js | 116 +++ src/lib/json/__init__.py | 1 - src/lib/matplotlib/__init__.js | 6 + src/lib/matplotlib/pyplot/__init__.js | 1129 ++++++++++++++++++++++++ src/lib/media/__init__.py | 11 + src/lib/numpy/__init__.js | 1134 +++++++++++++++++++++++++ src/lib/pprint.py | 5 +- src/lib/requests/__init__.js | 171 ++++ src/lib/sound/__init__.js | 3 + src/lib/sound/sample.js | 49 ++ src/lib/sound/sound.js | 317 +++++++ test/ast_example.py | 32 + test/exec_test.py | 8 + test/mpl2.py | 27 + test/test_ast.py | 87 ++ test/test_cait.py | 392 +++++++++ test/test_pedal.py | 38 + test/test_tifa.py | 333 ++++++++ 24 files changed, 4565 insertions(+), 2 deletions(-) create mode 100644 src/lib/ast.js create mode 100644 src/lib/cisc108/__init__.py create mode 100644 src/lib/cisc108/assertions.py create mode 100644 src/lib/inspect.py create mode 100644 src/lib/json/__init__.js delete mode 100644 src/lib/json/__init__.py create mode 100644 src/lib/matplotlib/__init__.js create mode 100644 src/lib/matplotlib/pyplot/__init__.js create mode 100644 src/lib/media/__init__.py create mode 100644 src/lib/numpy/__init__.js create mode 100644 src/lib/requests/__init__.js create mode 100644 src/lib/sound/__init__.js create mode 100644 src/lib/sound/sample.js create mode 100644 src/lib/sound/sound.js create mode 100644 test/ast_example.py create mode 100644 test/exec_test.py create mode 100644 test/mpl2.py create mode 100644 test/test_ast.py create mode 100644 test/test_cait.py create mode 100644 test/test_pedal.py create mode 100644 test/test_tifa.py diff --git a/src/ast.js b/src/ast.js index 0d33218723..05de9dfab7 100644 --- a/src/ast.js +++ b/src/ast.js @@ -3152,5 +3152,33 @@ Sk.astDump = function (node) { return _format(node, ""); }; +/* +Sk.INHERITANCE_MAP = { + 'mod': [Module, Interactive, Expression, Suite], + 'stmt': [FunctionDef, ClassDef, Return_, + Delete_, Assign, AugAssign, + For_, While_, If_, With_, + Raise, TryExcept, TryFinally, Assert, + Import_, ImportFrom, Exec, Global, Expr, + Pass, Break_, Continue_, Debugger_], + 'expr': [BoolOp, BinOp, UnaryOp, Lambda, IfExp, + Dict, Set, ListComp, SetComp, DictComp, + GeneratorExp, Yield, Compare, Call, Repr, + Num, Str, Attribute, Subscript, Name, List, Tuple], + 'expr_context': [Load, Store, Del, AugLoad, AugStore, Param], + 'slice': [Ellipsis, Slice, ExtSlice, Index], + 'boolop': [And, Or], + 'operator': [Add, Sub, Mult, Div, Mod, Pow, LShift, + RShift, BitOr, BitXor, BitAnd, FloorDiv], + 'unaryop': [Invert, Not, UAdd, USub], + 'cmpop': [Eq, NotEq, Lt, LtE, Gt, GtE, Is, IsNot, In_, NotIn], + 'comprehension': [], + 'excepthandler': [ExceptHandler], + 'arguments_': [], + 'keyword': [], + 'alias': [] +};*/ + Sk.exportSymbol("Sk.astFromParse", Sk.astFromParse); Sk.exportSymbol("Sk.astDump", Sk.astDump); +goog.exportSymbol("Sk.INHERITANCE_MAP", Sk.INHERITANCE_MAP); diff --git a/src/builtin/sys.js b/src/builtin/sys.js index e0a6a22f70..8b481af9da 100644 --- a/src/builtin/sys.js +++ b/src/builtin/sys.js @@ -69,6 +69,16 @@ var $builtinmodule = function (name) { sys.stdout = sys.__stdout__; sys.stdin = sys.__stdin__; + + sys.exc_info = new Sk.builtin.func(function () { + var type = Sk.err.ob$type; + var value = Sk.builtin.none.none$; + var traceback = new Sk.builtin.traceback(Sk.err); + //print(traceback.tp$setattr) + //traceback.tp$setattr('tb_lineno', traceback.tb_lineno); + var vals = [type, Sk.err, traceback]; + return new Sk.builtin.tuple(vals); + }); return sys; }; diff --git a/src/lib/ast.js b/src/lib/ast.js new file mode 100644 index 0000000000..550a4d10c8 --- /dev/null +++ b/src/lib/ast.js @@ -0,0 +1,374 @@ +var $builtinmodule = function (name) { + var mod = {}; + + /** + * Consumes an AST Node (JS version). Return a list of tuples of + * ``(fieldname, value)`` for each field in ``node._fields`` that is + * present on *node*. + */ + var iter_fieldsJs = function(node) { + var fieldList = []; + for (var i = 0; i < node._fields.length; i += 2) { + var field = node._fields[i]; + if (field in node) { + fieldList.push([field, node[field]]); + } + } + return fieldList; + } + + mod.iter_fields = function(node) { + return node._fields; + }; + + var convertValue = function(value) { + if (value === null) { + return Sk.builtin.none.none$; + } else if (isSpecialPyAst(value)) { + var constructorName = functionName(value); + return Sk.misceval.callsim(mod[constructorName], constructorName, true); + } else if (typeof value == 'number') { + return Sk.builtin.assk$(value); + } else if (Array === value.constructor) { + var subvalues = []; + for (var j = 0; j < value.length; j += 1) { + var subvalue = value[j]; + if (isSpecialPyAst(subvalue)) { + var constructorName = functionName(subvalue); + subvalue = Sk.misceval.callsim(mod[constructorName], constructorName, true); + subvalues.push(subvalue); + } else if (isJsAst(subvalue)) { + var constructorName = functionName(subvalue.constructor); + subvalue = Sk.misceval.callsim(mod[constructorName], subvalue); + subvalues.push(subvalue); + } + // No AST nodes have primitive list values, just + // lists of AST nodes + } + return Sk.builtin.list(subvalues); + } else if (isJsAst(value)) { + var constructorName = functionName(value.constructor) + return Sk.misceval.callsim(mod[constructorName], value); + } else {// Else already a Python value + return value; + } + } + + var isJsAst = function(jsNode) { + return jsNode instanceof Object && "_astname" in jsNode; + } + var isSpecialPyAst = function(val) { + if (typeof val == "function") { + switch (functionName(val)) { + case "Add": case "Add": case "Sub": case "Mult": case "Div": + case "Mod": case "Pow": case "LShift": case "RShift": + case "BitOr": case "BitXor": case "BitAnd": case "FloorDiv": + case "Store": case "Load": case "Del": case "Param": + case "And": case "Or": case "Xor": case "Not": + case "Invert": case "UAdd": case "USub": + case "Lt": case "Gt": case "LtE": case "GtE": + case "NotEq": case "Eq": case "Is": case "IsNot": + case "In": case "NotIn": + return true; + default: return false; + } + } else { + return false; + } + } + var isPyAst = function(pyValue) { + return Sk.misceval.isTrue(Sk.builtin.isinstance(pyValue, mod.AST)); + }; + var isPyList = function(pyValue) { + return Sk.misceval.isTrue(Sk.builtin.isinstance(pyValue, Sk.builtin.list)); + }; + + var iter_child_nodesJs = function(node) { + var fieldList = iter_fieldsJs(node); + var resultList = []; + for (var i = 0; i < fieldList.length; i += 1) { + var field = fieldList[i][0], value = fieldList[i][1]; + if (value === null) { + continue; + } + if ("_astname" in value) { + resultList.push(value); + } else if (value.constructor === Array) { + for (var j = 0; j < value.length; j += 1) { + var subvalue = value[j]; + if ("_astname" in subvalue) { + resultList.push(subvalue); + } + } + } + } + return resultList; + } + + // Python node + mod.iter_child_nodes = function(node) { + var fieldList = node._fields.v; + var childFields = []; + for (var i = 0; i < fieldList.length; i += 1) { + var field = Sk.ffi.remapToJs(fieldList[i].v[0]), + value = fieldList[i].v[1]; + if (isSpecialPyAst(value)) { + childFields.push(value); + } else if (isPyAst(value)) { + childFields.push(value); + } else if (isPyList(value)) { + for (var j = 0; j < value.v.length; j += 1) { + var subvalue = value.v[j]; + if (isPyAst(subvalue)) { + childFields.push(subvalue); + } + } + } + } + return Sk.builtin.list(childFields); + }; + + /** + * Dump the tree in a pretty format + */ + mod.dump = function(node, annotate_fields, include_attributes) { + // Confirm valid arguments + Sk.builtin.pyCheckArgs("dump", arguments, 1, 3); + // node argument + if (!isPyAst(node)) { + throw new Sk.builtin.TypeError("expected AST, got "+Sk.abstr.typeName(node)); + } + // annotate_fields argument + if (annotate_fields === undefined) { + annotate_fields = true; + } else { + Sk.builtin.pyCheckType("annotate_fields", "boolean", Sk.builtin.checkBool(annotate_fields)); + annotate_fields = Sk.ffi.remapToJs(annotate_fields); + } + // include_attributes argument + if (include_attributes === undefined) { + include_attributes = true; + } else { + Sk.builtin.pyCheckType("include_attributes", "boolean", Sk.builtin.checkBool(include_attributes)); + include_attributes = Sk.ffi.remapToJs(include_attributes); + } + // recursive dump + var _format = function(node) { + if (isSpecialPyAst(node)) { + return functionName(node)+"()"; + } else if (isPyAst(node)) { + var rv = node.jsNode._astname+"("; + var fieldList = node._fields.v; + var fieldArgs = []; + for (var i = 0; i < fieldList.length; i += 1) { + var field = Sk.ffi.remapToJs(fieldList[i].v[0]), + value = fieldList[i].v[1]; + value = _format(value); + if (annotate_fields) { + fieldArgs.push(field+"="+value); + } else { + fieldArgs.push(value); + } + } + var attributeList = node._attributes.v; + if (include_attributes) { + for (var i = 0; i < attributeList.length; i += 1) { + var field = Sk.ffi.remapToJs(attributeList[i]); + var value = Sk.ffi.remapToJs(node.jsNode[field]) + fieldArgs.push(field+"="+value); + } + } + fieldArgs = fieldArgs.join(", "); + return rv + fieldArgs + ")"; + } else if (isPyList(node)) { + var nodes = node.v.map(_format); + nodes = nodes.join(', '); + return "["+nodes+"]"; + } else { + return Sk.ffi.remapToJs(node.$r()); + } + } + return Sk.ffi.remapToPy(_format(node, 0)); + } + + var depth = 0; + var NodeVisitor = function($gbl, $loc) { + // Takes in Python Nodes, not JS Nodes + $loc.visit = new Sk.builtin.func(function(self, node) { + depth += 1; + /** Visit a node. **/ + //print(" ".repeat(depth), "VISIT", node.jsNode._astname) + var method_name = 'visit_' + node.jsNode._astname; + //print(" ".repeat(depth), "I'm looking for", method_name) + method_name = Sk.ffi.remapToPy(method_name) + method = Sk.builtin.getattr(self, method_name, $loc.generic_visit) + if (method.im_self) { + //print(method.im_func.func_code) + result = Sk.misceval.callsim(method, node); + depth -= 1; + return result; + }else { + result = Sk.misceval.callsim(method, self, node); + depth -= 1; + return result; + } + + }); + // Takes in Python Nodes, not JS Nodes + $loc.generic_visit = new Sk.builtin.func(function(self, node) { + /** Called if no explicit visitor function exists for a node. **/ + //print(" ".repeat(depth), "Generically checked", node.astname) + var fieldList = mod.iter_fields(node).v; + for (var i = 0; i < fieldList.length; i += 1) { + var field = fieldList[i].v[0].v, value = fieldList[i].v[1]; + if (value === null) { + continue; + } else if (isPyList(value)) { + for (var j = 0; j < value.v.length; j += 1) { + var subvalue = value.v[j]; + if (isPyAst(subvalue)) { + //print(self.visit) + Sk.misceval.callsim(self.visit, self, subvalue); + } + } + } else if (isPyAst(value)) { + //print(self.visit) + Sk.misceval.callsim(self.visit, self, value); + } + } + return Sk.builtin.none.none$; + }); + } + mod.NodeVisitor = Sk.misceval.buildClass(mod, NodeVisitor, "NodeVisitor", []); + + // Python node + mod.walk = function(node) { + if (isSpecialPyAst(node)) { + return Sk.builtin.list([]); + } + var resultList = [node]; + var childList = mod.iter_child_nodes(node); + for (var i = 0; i < childList.v.length; i += 1) { + var child = childList.v[i]; + var children = mod.walk(child); + resultList = resultList.concat(children.v); + } + return Sk.builtin.list(resultList); + } + + /*NodeVisitor.prototype.visitList = function(nodes) { + for (var j = 0; j < nodes.length; j += 1) { + var node = nodes[j]; + if ("_astname" in node) { + this.visit(node); + } + } + } + + NodeVisitor.prototype.recursive_walk = function(node) { + var todo = [node]; + var result = []; + while (todo.length > 0) { + node = todo.shift(); + todo = todo.concat(iter_child_nodes(node)) + result.push(node); + } + return result; + }*/ + + var depth = 0; + AST = function($gbl, $loc) { + var copyFromJsNode = function(self, key, jsNode) { + if (key in self.jsNode) { + Sk.abstr.sattr(self, key, Sk.ffi.remapToPy(jsNode[key]), true); + self._attributes.push(Sk.builtin.str(key)); + } + }; + $loc.__init__ = new Sk.builtin.func(function (self, jsNode, partial) { + depth+=1; + if (partial === true) { + // Alternative constructor for Skulpt's weird nodes + //print(" ".repeat(depth)+"S:", jsNode); + self.jsNode = {'_astname': jsNode}; + self.astname = jsNode; + self._fields = Sk.builtin.list([]); + self._attributes = Sk.builtin.list([]); + Sk.abstr.sattr(self, '_fields', self._fields, true); + Sk.abstr.sattr(self, '_attributes', self._attributes, true); + } else { + //print(" ".repeat(depth)+"P:", jsNode._astname); + self.jsNode = jsNode; + self.astname = jsNode._astname; + var fieldListJs = iter_fieldsJs(jsNode); + self._fields = []; + self._attributes = []; + for (var i = 0; i < fieldListJs.length; i += 1) { + var field = fieldListJs[i][0], value = fieldListJs[i][1]; + value = convertValue(value); + Sk.abstr.sattr(self, field, value, true); + self._fields.push(Sk.builtin.tuple([Sk.builtin.str(field), value])); + } + self._fields = Sk.builtin.list(self._fields) + Sk.abstr.sattr(self, '_fields', self._fields, true); + copyFromJsNode(self, 'lineno', self.jsNode); + copyFromJsNode(self, 'col_offset', self.jsNode); + copyFromJsNode(self, 'endlineno', self.jsNode); + copyFromJsNode(self, 'col_endoffset', self.jsNode); + self._attributes = Sk.builtin.list(self._attributes); + Sk.abstr.sattr(self, '_attributes', self._attributes, true); + } + depth -= 1; + }); + $loc.__str__ = new Sk.builtin.func(function (self) { + return Sk.builtin.str("<_ast."+self.astname+" object>"); + }); + $loc.__repr__ = $loc.__str__; + } + mod.AST = Sk.misceval.buildClass(mod, AST, "AST", []); + + //mod.literal_eval + // Implementation wouldn't be hard, but it does require a lot of Skulpting + + mod.parse = function parse(source, filename) { + if (!(/\S/.test(source))) { + return Sk.misceval.callsim(mod.Module, new Sk.INHERITANCE_MAP.mod[0]([])); + } + if (filename === undefined) { + filename = ''; + } + var parse = Sk.parse(filename, Sk.ffi.remapToJs(source)); + ast = Sk.astFromParse(parse.cst, filename, parse.flags); + return Sk.misceval.callsim(mod.Module, ast); + // Walk tree and create nodes (lazily?) + } + + /* + mod.Module = function ($gbl, $loc) { + Sk.abstr.superConstructor(mod.OrderedDict, this, items); + }*/ + + function functionName(fun) { + var ret = fun.toString(); + ret = ret.substr('function '.length); + ret = ret.substr(0, ret.indexOf('(')); + if (ret == "In_") { + ret = "In"; + } else if (ret == 'Import_') { + ret = 'Import'; + } + return ret; + } + + for (var base in Sk.INHERITANCE_MAP) { + var baseClass = function($gbl, $loc) { return this;}; + mod[base] = Sk.misceval.buildClass(mod, baseClass, base, [mod.AST]); + for (var i=0; i < Sk.INHERITANCE_MAP[base].length; i++) { + var nodeType = Sk.INHERITANCE_MAP[base][i]; + var nodeName = functionName(nodeType); + var nodeClass = function($gbl, $loc) { return this;}; + mod[nodeName] = Sk.misceval.buildClass(mod, nodeClass, nodeName, [mod[base]]) + } + } + + return mod; +}; \ No newline at end of file diff --git a/src/lib/cisc108/__init__.py b/src/lib/cisc108/__init__.py new file mode 100644 index 0000000000..b5f2e5b07e --- /dev/null +++ b/src/lib/cisc108/__init__.py @@ -0,0 +1 @@ +from cisc108.assertions import assert_equal, QUIET, student_tests diff --git a/src/lib/cisc108/assertions.py b/src/lib/cisc108/assertions.py new file mode 100644 index 0000000000..94e18da73a --- /dev/null +++ b/src/lib/cisc108/assertions.py @@ -0,0 +1,294 @@ +''' +CISC106 Module that includes some basic helper functions such as assert_equal(). + +Versions: +0.2.1 - 2019-JAN-23, Austin Cory Bart + + Keep track of tests' counts in student_tests + + Improve make_type_name for BlockPy compatibility +0.2 - 2019-JAN-02, Modified by Austin Cory Bart + + Renamed functions to be in line with common Python convention + + Packaged into an actual library on PyPI, with tests and stuff. + + Replaced type(X) checks with isinstance + + Changed string interpolation to .format + + Extracted out string messages +0.142 - 2014-APR-23, Modified by Jon Leighton + + Modified success and failure messages to print "SUCCESS" and "FAILURE" at the + beginning of each result. This makes it much easier to quickly discern the + outcome visually. +0.141 - 2014-MAR-26, Modified by Jon Leighton + + Removed unused function print_verbose(). + + Appended text to FAILURE message for incompatible types to indicate that types + involved can't be compared. +0.14 - 2014-MAR-26, Modified by Andrew Roosen + + Modified assert_equal() to return False on failure and True on success. + + Introduced QUITE option to supress output on SUCCESS. + + Modified FAILURE message for incompatible data types to be consistent with + + FAILURE message for unequal values. + + Modified names of internal functions isEqual() and isseqtype() to _is_equal() + and _is_seq_type(), respectively +0.13 - 2014-MAR-25, Modified by Jon Leighton + + added elif clause to _is_equal(), to avoid comparing ints and floats to anything + that is not an int or a float, and to return None in this case. The previous + comparison tried to subtract these quantities from each other, causing a + runtime error. + + Modified assert_equal() to check for _is_equal() returning None, which now + indicates an attempt to compare unrelated data types. Failure message is + modified in this case to report the attempt to compare unrelated types. + + Removed unused global variables fail and success. + + Added version numbers to Paul Amer's modifications, and bumped version number to + reflect my modifications. + + Changed version number to string, to match recommended practice. +0.122 - 2012-APR-17, Modified by Paul Amer + + removed graphics stuff; just kept assert_equal +0.121 - 2011-SEP-08, Modified by Paul Amer + +improved success-failure messages +0.12 + + display can be called multiple times + + assert_equal supports PIL.Image.Image +0.1 + + Initial assert_equal, display, animate, bind +''' +__version__ = '0.2.1' + +# Number encapsulates bool, int, float, complex, decimal.Decimal, etc. +try: + from numbers import Number +except: + Number = (bool, int, float, complex) + +try: + bytes +except NameError: + bytes = str + +try: + frozenset() +except: + frozenset = tuple() + +def make_type_name(value): + try: + return type(value).__name__ + except Exception: + return str(type(value))[8:-2] + +def get_line_code(): + # Load in extract_stack, or provide shim for environments without it. + try: + from traceback import extract_stack + trace = extract_stack() + frame = trace[len(trace) - 3] + line = frame[1] + code = frame[3] + return line, code + except Exception: + return None, None + + +# Don't print message from assert_equal on success +QUIET = False + +SET_GENERATOR_TYPES = (type({}.keys()), type({}.values()), type({}.items())) + +LIST_GENERATOR_TYPES = (type(map(bool, [])), type(filter(bool, [])), + type(range(0)), type(reversed([])), type(zip()), + type(enumerate([]))) + +MESSAGE_LINE_CODE = " - [line {line}] {code}" +MESSAGE_UNRELATED_TYPES = ( + "FAILURE{context}, predicted answer was {y!r} ({y_type!r}), " + "computed answer was {x!r} ({x_type!r}). " + "You attempted to compare unrelated data types.") +MESSAGE_GENERIC_FAILURE = ( + "FAILURE{context}, predicted answer was {y!r}, " + "computed answer was {x!r}.") +MESSAGE_GENERIC_SUCCESS = ( + "TEST PASSED{context}") + +class StudentTestReport: + def __init__(self): + self.reset() + def __repr__(self): + return str(self) + def __str__(self): + return ('' + ).format( + failures=self.failures, successes=self.successes, tests=self.tests, + lines=', '.join(self.lines) + ) + def reset(self): + self.failures = 0 + self.successes = 0 + self.tests = 0 + self.lines = [] + +student_tests = StudentTestReport() + +def assert_equal(x, y, precision=4, exact_strings=False, *args): + """ + Checks an expected value using the _is_equal function. + Prints a message if the test case passed or failed. + + Args: + x (Any): Any kind of python value. Should have been computed by + the students' code (their actual answer). + y (Any): Any kind of python value. The expected value to be produced, + precalculated (their expected answer). + precision (int): Optional. Indicates how many decimal places to use + when comparing floating point values. + exact_strings (bool): Whether or not strings should be matched perfectly + character-by-character, or if you should ignore capitalization, + whitespace, and symbols. + Returns: + bool: Whether or not the assertion passed. + """ + + # Can we add in the line number and code? + line, code = get_line_code() + if None in (line, code): + context = "" + else: + context = MESSAGE_LINE_CODE.format(line=line, code=code) + student_tests.lines.append(line) + + result = _is_equal(x, y, precision, exact_strings, *args) + student_tests.tests += 1 + if result is None: + student_tests.failures += 1 + print(MESSAGE_UNRELATED_TYPES.format(context=context, + x=repr(x), x_type=make_type_name(x), + y=repr(y), y_type=make_type_name(y))) + return False + elif not result: + student_tests.failures += 1 + print(MESSAGE_GENERIC_FAILURE.format(context=context, x=repr(x), y=repr(y))) + return False + elif not QUIET: + print(MESSAGE_GENERIC_SUCCESS.format(context=context)) + student_tests.successes += 1 + return True + +# Hack to allow anyone with an assert_equal reference to get the results +# since they are global across all calls. Weird strategy! +assert_equal.student_tests = student_tests + +def _is_equal(x, y, precision, exact_strings, *args): + """ + _is_equal : thing thing -> boolean + _is_equal : number number number -> boolean + Determines whether the two arguments are equal, or in the case of + floating point numbers, within a specified number of decimal points + precision (by default, checks to with 4 decimal points for floating + point numbers). Returns None when attempting to compare ints and floats + to anything other than ints and floats. + + Examples: + >>> _is_equal('ab', 'a'+'b') + True + + >>> _is_equal(12.34, 12.35) + False + + >>> _is_equal(12.3456, 12.34568, 4) + True + + >>> _is_equal(12.3456, 12.34568w5) + False + """ + + # Check if generators + if isinstance(x, SET_GENERATOR_TYPES): + x = set(x) + elif isinstance(x, LIST_GENERATOR_TYPES): + x = list(x) + if isinstance(y, SET_GENERATOR_TYPES): + y = set(y) + elif isinstance(y, LIST_GENERATOR_TYPES): + y = list(y) + + if isinstance(x, float) and isinstance(y, float): + error = 10 ** (-precision) + return abs(x - y) < error + elif isinstance(x, Number) and isinstance(y, Number) and isinstance(x, type(y)): + return x == y + elif ((isinstance(x, str) and isinstance(y, str)) or + (isinstance(x, bytes) and isinstance(y, bytes))): + if exact_strings: + return x == y + else: + return _normalize_string(x) == _normalize_string(y) + elif isinstance(x, list) and isinstance(y, list): + return _are_sequences_equal(x, y, precision, exact_strings) + elif isinstance(x, tuple) and isinstance(y, tuple): + return _are_sequences_equal(x, y, precision, exact_strings) + elif isinstance(x, set) and isinstance(y, set): + return _are_sets_equal(x, y, precision, exact_strings) + elif isinstance(x, frozenset) and isinstance(y, frozenset): + return _are_sets_equal(x, y, precision, exact_strings) + elif isinstance(x, dict) and isinstance(y, dict): + primary_keys = set(x.keys()) + if not _are_sets_equal(primary_keys, set(y.keys()), + precision, exact_strings): + return False + for key in primary_keys: + if not _is_equal(x[key], y[key], precision, exact_strings): + return False + return True + elif not isinstance(x, type(y)): + return None + else: + return x == y + + +def _normalize_string(text): + ''' + For strings: + - strips whitespace from each line + - lower cases + ''' + # Lowercase + text = text.lower() + # Strip whitespace from each line + lines = text.split("\n") + lines = [line.strip() for line in lines if line.strip()] + text = "\n".join(lines) + # Return result + return text + + +def _are_sequences_equal(x, y, precision, exact_strings): + ''' + For sequences that support __len__, __iter__, and should have the same + order. + ''' + if len(x) != len(y): + return False + for x_element, y_element in zip(x, y): + if not _is_equal(x_element, y_element, precision, exact_strings): + return False + return True + + +def _set_contains(needle, haystack, precision, exact_strings): + ''' + Tests if the given needle is one of the elements of haystack, using + the _is_equal function. + ''' + for element in haystack: + if _is_equal(element, needle, precision, exact_strings): + return True + return False + + +def _are_sets_equal(x, y, precision, exact_strings): + ''' + For sequences that support __len__, __iter__, but order does not matter. + ''' + if len(x) != len(y): + return False + for x_element in x: + if not _set_contains(x_element, y, precision, exact_strings): + return False + return True diff --git a/src/lib/inspect.py b/src/lib/inspect.py new file mode 100644 index 0000000000..796862a53e --- /dev/null +++ b/src/lib/inspect.py @@ -0,0 +1 @@ +raise NotImplementedError("inspect is not yet implemented in Skulpt") diff --git a/src/lib/json/__init__.js b/src/lib/json/__init__.js new file mode 100644 index 0000000000..db35839001 --- /dev/null +++ b/src/lib/json/__init__.js @@ -0,0 +1,116 @@ +var $builtinmodule = function(name) { + "use strict"; + var mod = {}; + + // skipkeys=False, + // ensure_ascii=True, + // check_circular=True, + // allow_nan=True, + // cls=None, + // indent=None, + // separators=None, + // encoding="utf-8", + // default=None, + // sort_keys=False, + // **kw + + var dumps_f = function(kwa) { + Sk.builtin.pyCheckArgs("dumps", arguments, 1, Infinity, true, false); + + var args = Array.prototype.slice.call(arguments, 1), + kwargs = new Sk.builtins.dict(kwa), + sort_keys = false, + stringify_opts, default_, jsobj, str; + + // default stringify options + stringify_opts = { + ascii : true, + separators : { + item_separator : ', ', + key_separator : ': ' + } + }; + + kwargs = Sk.ffi.remapToJs(kwargs); + jsobj = Sk.ffi.remapToJs(args[0]); + + // TODO: likely need to go through character by character to enable this + if (typeof(kwargs.ensure_ascii) === "boolean" && kwargs.ensure_ascii === false) { + stringify_opts.ascii = false; + } + + // TODO: javascript sort isn't entirely compatible with python's + if (typeof(kwargs.sort_keys) === "boolean" && kwargs.sort_keys) { + sort_keys = true; + } + + if (!sort_keys) { + // don't do any sorting unless sort_keys is true + // if sort_keys use stringify's default sort, which is alphabetical + stringify_opts.cmp = function(a, b) { + return 0; + }; + } + + // item_separator, key_separator) tuple. The default is (', ', ': '). + if (typeof(kwargs.separators) === "object" && kwargs.separators.length == 2) { + stringify_opts.separators.item_separator = kwargs.separators[0]; + stringify_opts.separators.key_separator = kwargs.separators[1]; + } + + // TODO: if indent is 0 it should add newlines + if (kwargs.indent) { + stringify_opts.space = kwargs.indent; + } + + // Sk.ffi.remapToJs doesn't map functions + if (kwargs.default) { + } + + // may need to create a clone of this to have more control/options + str = JSON.stringify(jsobj, stringify_opts, kwargs.indent || 1); + + return new Sk.builtin.str(str); + }; + + dumps_f.co_kwargs = true; + mod.dumps = new Sk.builtin.func(dumps_f); + + // encoding[, cls[, object_hook[, parse_float[, parse_int[, parse_constant[, object_pairs_hook[, **kw]]]]]]] + var loads_f = function(kwa) { + Sk.builtin.pyCheckArgs("loads", arguments, 1, Infinity, true, false); + + var args = Array.prototype.slice.call(arguments, 1), + kwargs = new Sk.builtins.dict(kwa), + str, obj; + + kwargs = Sk.ffi.remapToJs(kwargs); + str = args[0].v; + obj = JSON.parse(str); + + return Sk.ffi.remapToPy(obj); + }; + + loads_f.co_kwargs = true; + mod.loads = new Sk.builtin.func(loads_f); + + var load_f = function(kwa) { + Sk.builtin.pyCheckArgs("load", arguments, 1, Infinity, true, false); + + var args = Array.prototype.slice.call(arguments, 1), + kwargs = new Sk.builtins.dict(kwa), + str, obj, file; + + kwargs = Sk.ffi.remapToJs(kwargs); + file = args[0]; + str = Sk.misceval.callsim(Sk.builtin.file.prototype['read'], file).v; + obj = JSON.parse(str); + + return Sk.ffi.remapToPy(obj); + } + + load_f.co_kwargs = true; + mod.load = new Sk.builtin.func(load_f); + + return mod; +}; \ No newline at end of file diff --git a/src/lib/json/__init__.py b/src/lib/json/__init__.py deleted file mode 100644 index 8182e6de72..0000000000 --- a/src/lib/json/__init__.py +++ /dev/null @@ -1 +0,0 @@ -raise NotImplementedError("json is not yet implemented in Skulpt") diff --git a/src/lib/matplotlib/__init__.js b/src/lib/matplotlib/__init__.js new file mode 100644 index 0000000000..fe7981ad62 --- /dev/null +++ b/src/lib/matplotlib/__init__.js @@ -0,0 +1,6 @@ +var $builtinmodule = function(name) +{ + var matplotlib = {}; + + return matplotlib; +}; diff --git a/src/lib/matplotlib/pyplot/__init__.js b/src/lib/matplotlib/pyplot/__init__.js new file mode 100644 index 0000000000..0cccd416bb --- /dev/null +++ b/src/lib/matplotlib/pyplot/__init__.js @@ -0,0 +1,1129 @@ +var jsplotlib = {}; + +// Skulpt translation +var $builtinmodule = function(name) { + var mod = {}; + + // Unique ID generator for charts + var chartCounter = 0; + + var chart; // The aggregate object to hold multiple plots + var labels; // Title, X-axis title, Y-axis title + var plots; // All the plots to end up drawing + var extents; // The highest and lowest values across each axis + var colorCycle; + + function resetChart() { + chart = null; + colorCycle = 0; + labels = { + 'title': '', + 'x-axis': '', + 'y-axis': '' + }; + plots = []; + extents = { + 'xMin': null, + 'yMin': null, + 'xMax': null, + 'yMax': null + }; + } + resetChart(); + + // Keep track of any plotted values for later checks + mod.values = []; + + // Creates the aggregate chart object that will hold 1 or more plots + var createChart = function(type) { + if (Sk.console === undefined) { + throw new Sk.builtin.NameError( + "Can not resolve drawing area. Sk.console is undefined!"); + } + + if (!chart) { + // Create a new chart + chartCounter += 1; + chart = {}; + + chart.margin = {'top': 20, 'right': 30, 'bottom': 50, 'left': 40}; + chart.width = Sk.console.width - chart.margin.left - chart.margin.right; + chart.height = Sk.console.height - chart.margin.top - chart.margin.bottom; + chart.id = 'chart' + chartCounter; + chart.type = type; + + if (Sk.console.skipDrawing) { + return chart; + } + + return chart; + } + }; + + function updateMinMax(attr, array) { + if (extents[attr+"Min"] === null) { + extents[attr+"Min"] = d3.min(array); + } else { + extents[attr+"Min"] = Math.min(d3.min(array), extents[attr+"Min"]) + } + if (extents[attr+"Max"] === null) { + extents[attr+"Max"] = d3.max(array); + } else { + extents[attr+"Max"] = Math.max(d3.max(array), extents[attr+"Max"]) + } + } + + function getRandomSubarray(arr, size) { + var shuffled = arr.slice(0), i = arr.length, temp, index; + while (i--) { + index = Math.floor((i + 1) * Math.random()); + temp = shuffled[index]; + shuffled[index] = shuffled[i]; + shuffled[i] = temp; + } + return shuffled.slice(0, size); + } + + // Main plotting function + var plot_f = function(kwa) { + // Parse arguments + Sk.builtin.pyCheckArgs("plotk", arguments, 1, Infinity, true, false); + args = Array.prototype.slice.call(arguments, 1); + kwargs = new Sk.builtins.dict(kwa); // is pretty useless for handling kwargs + kwargs = Sk.ffi.remapToJs(kwargs); // create a proper dict + + // Keep a backup of the arguments for checker + mod.values.push(args); + + // Parse different argument combinations + var xdata = null; + var ydata = null; + var stylestring = null; + if (args.length == 1) { + // ydata + ydata = Sk.ffi.remapToJs(args[0]); + } else if (args.length == 2) { + if (Sk.builtin.checkString(args[1])) { + // ydata, style + ydata = Sk.ffi.remapToJs(args[0]); + stylestring = Sk.ffi.remapToJs(args[1]); + } else { + // xdata, ydata + xdata = Sk.ffi.remapToJs(args[0]); + ydata = Sk.ffi.remapToJs(args[1]); + } + } else if (args.length == 3) { + // xdata, ydata, style + xdata = Sk.ffi.remapToJs(args[0]); + ydata = Sk.ffi.remapToJs(args[1]); + stylestring = Sk.ffi.remapToJs(args[2]); + } + if (xdata === null) { + xdata = []; + for (var i = 0; i < ydata.length; i++) { + xdata.push(i); + } + } + + // empty canvas from previous plots + createChart('line'); + + // Zip up the data + var actualData = d3.zip(xdata, ydata).map(function(e) { + return {'x': e[0], 'y': e[1]} + }); + // Parse formatting, also keep ColorCycler updated + var cycle = jsplotlib.rc["axes.color_cycle"]; + var linestyle = '-', marker= '', + color = cycle[colorCycle % cycle.length]; + if (stylestring !== null) { + var ftm_tuple = jsplotlib._process_plot_format(stylestring); + linestyle = ftm_tuple.linestyle; + marker = jsplotlib.parse_marker(ftm_tuple.marker); + color = ftm_tuple.color; + } else { + colorCycle += 1; + } + // Save + plots.push({ + "data": actualData, + "type": 'line', + 'style': { + 'linestyle': linestyle, + 'marker': marker, + 'color': jsplotlib.color_to_hex(color) + } + }); + // Update min/max + updateMinMax("x", xdata) + updateMinMax("y", ydata) + + if (Sk.console.skipDrawing) { + return; + } + }; + plot_f.co_kwargs = true; + mod.plot = new Sk.builtin.func(plot_f); + + var show_f = function() { + /*if (Sk.console.skipDrawing) { + Sk.console.printHtml([0], plots); + return; + }*/ + + if (!chart) { + createChart('line'); + } + if (chart.type == 'hist' && plots.length < 1) { + resetChart(); + return; + } + if (plots.length == 0) { + return; + } + if (extents['xMin'] === undefined || extents['yMin'] === undefined) { + return; + } + + var yAxisBuffer; + + // Establish x/y scalers and axes + if (chart.type == 'scatter' || chart.type == 'line') { + yAxisBuffer = 5*Math.max(extents['yMin'].toLocaleString().length, + extents['yMax'].toLocaleString().length); + chart.xScale = d3.scale.linear() + .domain([extents['xMin'], extents['xMax']]) + .range([0, chart.width-yAxisBuffer]); + chart.xAxis = d3.svg.axis() + .scale(chart.xScale) + .orient("bottom"); + } else if (chart.type == 'hist') { + yAxisBuffer = 5*Math.max(extents['xMin'].toLocaleString().length, + extents['xMax'].toLocaleString().length); + chart.xScale = d3.scale.linear() + .domain([extents['xMin'], extents['xMax']]) + .range([0, chart.width-yAxisBuffer]); + chart.xAxis = d3.svg.axis() + .scale(chart.xScale) + .orient("bottom"); + var bins = plots[0]['bins']; + var tempScale = d3.scale.linear() + .domain([ + 0, bins + ]) + .range([extents['xMin'], extents['xMax']]); + var tickArray = d3.range(bins+1) + .map(tempScale).map(function(e) { + return e; + }); + // TODO: support multiple histograms + var histMapper = d3.layout.histogram().bins( + tickArray + //chart.xScale.ticks(bins) + )(plots[0]['data']); + } else if (chart.type == 'bar') { + yAxisBuffer = 5*Math.max(extents['yMin'].toLocaleString().length, + extents['yMax'].toLocaleString().length); + chart.xScale = d3.scale.ordinal() + .domain([extents['xMin'], extents['xMax']]) + .rangeBands([0, chart.width-yAxisBuffer]); + chart.xAxis = d3.svg.axis() + .scale(chart.xScale) + .tickFormat(function(d) { return d.index }) + .orient("bottom"); + } + if (chart.type !== 'hist') { + chart.yScale = d3.scale.linear() + .domain([extents['yMin'], extents['yMax']]) + .range([chart.height, 0]); + } else { + chart.yScale = d3.scale.linear() + .domain([0, d3.max(histMapper, function(d) { return d.y; })]) + .range([chart.height, 0]); + } + chart.yAxis = d3.svg.axis() + .scale(chart.yScale) + .orient("left"); + + chart.mapX = function(d) {return chart.xScale(d.x)}; + chart.mapY = function(d) {return chart.yScale(d.y)}; + chart.mapLine = d3.svg.line() + .x(function(d) { return chart.xScale(d.x); }) + .y(function(d) { return chart.yScale(d.y); }) + .interpolate("linear"); + + // set css classes + chart.svg = d3.select(Sk.console.container).append('div').append('svg'); + //$(chart.svg.node()).parent().hide(); + chart.svg.attr('class', 'chart'); + chart.svg.attr('width', Sk.console.width); + chart.svg.attr('height', Sk.console.height); + chart.svg.attr('chartCount', chartCounter); + + var translation = "translate(" + (chart.margin.left + yAxisBuffer) + "," + chart.margin.top + ")"; + chart.canvas = chart.svg.append("g") + .attr("transform", translation); + + chart.canvas.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + chart.height + ")") + .call(chart.xAxis); + chart.canvas.append("g") + .attr("class", "y axis") + .call(chart.yAxis); + chart.canvas.select(".x.axis") + .selectAll("text") + .style("font-size","12px"); + chart.canvas.select(".y.axis") + .selectAll("text") + .style("font-size","12px"); + translation = "translate(" + ( (chart.width-yAxisBuffer) / 2) + " ," + (chart.height + chart.margin.bottom-14) + ")"; + chart.canvas.append("text") // text label for the x axis + .attr("transform", translation) + .attr("class", "x-axis-label") + .style("font-size", "14px") + .text(labels['x-axis']) + .style("text-anchor", "middle"); + chart.canvas.append("text") + .attr("transform", "rotate(-90)") + .attr("class", "y-axis-label") + .attr("y", 0 - chart.margin.left-yAxisBuffer) + .attr("x", 0 - (chart.height / 2)) + .attr("dy", "1em") + .text(labels['y-axis']) + .style("font-size", "14px") + .style("text-anchor", "middle"); + chart.canvas.append("text") + .attr("x", ( (chart.width-yAxisBuffer) / 2)) + .attr("y", 0 - (chart.margin.top / 2)) + .attr("class", "title-text") + .text(labels['title']) + .attr("text-anchor", "middle") + .style("font-size", "14px") + .style("text-decoration", "underline"); + chart.canvas.append("text") + .attr("x", 0) + .attr("y", 0) + .text("BlockPy") + .style("stroke", "#FDFDFD") + .style("font-size", "8px"); + chart.svg.insert('defs', ":first-child") + .append('style') + .attr("type", "text/css") + .text("svg { background-color: white; }\n"+ + ".axis path,.axis line { fill: none; stroke: black; shape-rendering: crispEdges;}\n"+ + ".line { fill: none; stroke-width: 1px;}\n"+ + ".circle { r: 3; shape-rendering: crispEdges; }\n"+ + ".bar { shape-rendering: crispEdges;}\n") + + // Actually draw the chart objects + for (var i = 0; i < plots.length; i += 1) { + var plot = plots[i]; + if (plot['type'] == 'line') { + chart.canvas.append("path") + .style('stroke', plot['style']['color']) + .attr('class', "line") + .data(plot['data']) + .attr("d", chart.mapLine(plot['data'])); + } else if (plot['type'] == 'scatter') { + chart.canvas.append("g") + .attr("class", "series") + .selectAll(".point") + .data(plot['data']) + .enter() + .append('circle') + .style('fill', plot['style']['color']) + .attr("class", "circle") + .attr("cx", chart.mapX) + .attr("cy", chart.mapY) + .attr('r', 2); + } else if (plot['type'] == 'hist') { + chart.canvas.selectAll('.bar') + .data(histMapper) + .enter().append("rect") + .attr("class", "bar") + .style('fill', plot['style']['color']) + .style('stroke', 'black') + .attr("x", function(d) { return chart.xScale(d.x); }) + .attr("width", (chart.width-yAxisBuffer)/(1+histMapper.length)) + .attr("y", function(d) { return chart.yScale(d.y); }) + .attr("height", function(d) { return chart.height - chart.yScale(d.y); }); + } + } + if (Sk.console.pngMode) { + var doctype = '' + '<' + '!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'; + var xml = new XMLSerializer().serializeToString(chart.svg[0][0]); + var blob = new Blob([ doctype + xml], { type: 'image/svg+xml' }); + var url = window.URL.createObjectURL(blob); + //var data = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(xml))); + var img = document.createElement("img"); + img.style.display = 'block'; + var oldChart = chart; + var oldPlots = plots; + Sk.console.printHtml(img, oldPlots); + resetChart(); + oldChart.svg[0][0].parentNode.replaceChild(img, oldChart.svg[0][0]) + img.onload = function() { + img.onload = null; + //TODO: Make this capture a class descendant. Cross the D3/Jquery barrier! + var canvas = document.createElement('canvas'); + canvas.width = Sk.console.width; + canvas.height = Sk.console.height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + var canvasUrl = canvas.toDataURL("image/png"); + img.setAttribute('src', canvasUrl); + // Snip off this chart, we can now start a new one. + } + img.onerror = function() { + + } + img.setAttribute('src', url); + } else { + Sk.console.printHtml(chart.svg, plots); + // Snip off this chart, we can now start a new one. + resetChart(); + } + }; + mod.show = new Sk.builtin.func(show_f); + + var title_f = function(s) { + Sk.builtin.pyCheckArgs("title", arguments, 1, 1); + + if (!Sk.builtin.checkString(s)) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(s) + + "' is not supported for title; should be a string."); + } + + labels['title']= Sk.ffi.remapToJs(s); + }; + mod.title = new Sk.builtin.func(title_f); + + var xlabel_f = function(s) { + Sk.builtin.pyCheckArgs("xlabel", arguments, 1, 1); + + if (!Sk.builtin.checkString(s)) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(s) + + "' is not supported for xlabel; should be a string."); + } + + labels['x-axis']= Sk.ffi.remapToJs(s); + }; + mod.xlabel = new Sk.builtin.func(xlabel_f); + + var ylabel_f = function(s) { + Sk.builtin.pyCheckArgs("ylabel", arguments, 1, 1); + + if (!Sk.builtin.checkString(s)) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(s) + + "' is not supported for ylabel; should be a string."); + } + + labels['y-axis']= Sk.ffi.remapToJs(s); + }; + mod.ylabel = new Sk.builtin.func(ylabel_f); + + // Clear the current figure + var clf_f = function() { + chart = null; + resetChart(); + }; + mod.clf = new Sk.builtin.func(clf_f); + + UNSUPPORTED = ["semilogx", "semilogy", "specgram", "stackplot", "stem", "step", "streamplot", "tricontour", "tricontourf", "tripcolor", "triplot", "vlines", "xcorr", "barbs", "cla", "grid", "table", "text", "annotate", "ticklabel_format", "locator_params", "tick_params", "margins", "autoscale", "autumn", "cool", "copper", "flag", "gray", "hot", "hsv", "jet", "pink", "prism", "spring", "summer", "winter", "spectral", "hlines", "loglog", "magnitude_spectrum", "pcolor", "pcolormesh", "phase_spectrum", "pie", "plot_date", "psd", "quiver", "quiverkey", "findobj", "switch_backend", "isinteractive", "ioff", "ion", "pause", "rc", "rc_context", "rcdefaults", "gci", "sci", "xkcd", "figure", "gcf", "get_fignums", "get_figlabels", "get_current_fig_manager", "connect", "disconnect", "close", "savefig", "ginput", "waitforbuttonpress", "figtext", "suptitle", "figimage", "figlegend", "hold", "ishold", "over", "delaxes", "sca", "gca", "subplot", "subplots", "subplot2grid", "twinx", "twiny", "subplots_adjust", "subplot_tool", "tight_layout", "box", "xlim", "ylim", "xscale", "yscale", "xticks", "yticks", "minorticks_on", "minorticks_off", "rgrids", "thetagrids", "plotting", "get_plot_commands", "colors", "colormaps", "_setup_pyplot_info_docstrings", "colorbar", "clim", "set_cmap", "imread", "imsave", "matshow", "polar", "plotfile", "_autogen_docstring", "acorr", "arrow", "axhline", "axhspan", "axvline", "axvspan", "bar", "barh", "broken_barh", "boxplot", "cohere", "clabel", "contour", "contourf", "csd", "errorbar", "eventplot", "fill", "fill_between", "fill_betweenx", "hexbin", "hist2d", 'axis'] + for (var i = 0; i < UNSUPPORTED.length; i+= 1) { + mod[UNSUPPORTED[i]] = new Sk.builtin.func(function() { + throw new Sk.builtin.NotImplementedError(UNSUPPORTED[i]+" is not yet implemented"); + }); + } + + var legend_f = function() { + return Sk.builtin.none.none$; + } + mod.legend = new Sk.builtin.func(legend_f); + + var hist_f = function(kwa) { + // Parse arguments + Sk.builtin.pyCheckArgs("hist", arguments, 1, Infinity, true, false); + args = Array.prototype.slice.call(arguments, 1); + kwargs = new Sk.builtins.dict(kwa); // is pretty useless for handling kwargs + kwargs = Sk.ffi.remapToJs(kwargs); // create a proper dict + + var bins = 10; + if ("bins" in kwargs) { + bins = kwargs["bins"]; + } + + // Keep a backup of the arguments for checker + mod.values.push(args); + + // Parse different argument combinations + var data = null; + var stylestring = null; + if (args.length == 1) { + // xdata + data = Sk.ffi.remapToJs(args[0]); + } else if (args.length == 2) { + // xdata, style + data = Sk.ffi.remapToJs(args[0]); + stylestring = Sk.ffi.remapToJs(args[1]); + } + + // empty canvas from previous plots + createChart('hist'); + + // Parse formatting, also keep ColorCycler updated + var cycle = jsplotlib.rc["axes.color_cycle"]; + var linestyle = ' ', marker= 'o', + color = cycle[colorCycle % cycle.length]; + if (stylestring !== null) { + var ftm_tuple = jsplotlib._process_plot_format(stylestring); + linestyle = ftm_tuple.linestyle; + marker = jsplotlib.parse_marker(ftm_tuple.marker); + color = ftm_tuple.color; + } else { + colorCycle += 1; + } + // Save + plots.push({ + "data": data, + "type": 'hist', + "bins": bins, + 'style': { + 'linestyle': linestyle, + 'marker': marker, + 'color': jsplotlib.color_to_hex(color) + } + }); + updateMinMax("x", data); + + if (Sk.console.skipDrawing) { + return; + } + } + hist_f.co_kwargs = true; + mod.hist = new Sk.builtin.func(hist_f); + + var scatter_f = function(kwa) { + // Parse arguments + Sk.builtin.pyCheckArgs("scatter", arguments, 1, Infinity, true, false); + args = Array.prototype.slice.call(arguments, 1); + kwargs = new Sk.builtins.dict(kwa); // is pretty useless for handling kwargs + kwargs = Sk.ffi.remapToJs(kwargs); // create a proper dict + + var dot_limit = 256; + if ("dot_limit" in kwargs) { + dot_limit = kwargs["dot_limit"]; + } + + // Keep a backup of the arguments for checker + mod.values.push(args); + + // Parse different argument combinations + var xdata = null; + var ydata = null; + var stylestring = null; + if (args.length == 2) { + // xdata, ydata + xdata = Sk.ffi.remapToJs(args[0]); + ydata = Sk.ffi.remapToJs(args[1]); + } else if (args.length == 3) { + // xdata, ydata, style + xdata = Sk.ffi.remapToJs(args[0]); + ydata = Sk.ffi.remapToJs(args[1]); + stylestring = Sk.ffi.remapToJs(args[2]); + } + + if (xdata.length > dot_limit) { + var xdataSampled = [], ydataSampled = []; + var LENGTH = xdata.length, RATE = LENGTH / dot_limit; + for (var i = 0; i < dot_limit; i+= 1) { + var index = Math.floor((i+Math.random())*RATE); + xdataSampled.push(xdata[index]) + ydataSampled.push(ydata[index]); + } + xdata = xdataSampled; + ydata = ydataSampled; + } + + // empty canvas from previous plots + createChart('scatter'); + + // Zip up the data + var actualData = d3.zip(xdata, ydata).map(function(e) { + return {'x': e[0], 'y': e[1]} + }); + // Parse formatting, also keep ColorCycler updated + var cycle = jsplotlib.rc["axes.color_cycle"]; + var linestyle = ' ', marker= 'o', + color = cycle[colorCycle % cycle.length]; + if (stylestring !== null) { + var ftm_tuple = jsplotlib._process_plot_format(stylestring); + linestyle = ftm_tuple.linestyle; + marker = jsplotlib.parse_marker(ftm_tuple.marker); + color = ftm_tuple.color; + } else { + colorCycle += 1; + } + // Save + plots.push({ + "data": actualData, + "type": 'scatter', + 'style': { + 'linestyle': linestyle, + 'marker': marker, + 'color': jsplotlib.color_to_hex(color) + } + }); + // Update min/max + updateMinMax("x", xdata) + updateMinMax("y", ydata) + if (Sk.console.skipDrawing) { + return; + } + }; + scatter_f.co_kwargs = true; + mod.scatter = new Sk.builtin.func(scatter_f); + + + return mod; +}; + + +jsplotlib.rc = { + "lines.linewidth": 1.0, + "lines.linestyle": "-", + "lines.color": "blue", + "lines.marker": "None", + "lines.markeredgewidth": 0.5, + "lines.markersize": 6, + "lines.dash_joinstyle": "miter", + "lines.dash_capstyle": "butt", + "lines.solid_jointyle": "miter", + "lines.solid_capstyle": "projecting", + "lines.antialiased": true, + "patch.linewidth": 1.0, + "patch.facecolor": "blue", + "patch.edgecolor": "black", + "patch.antialiased": true, + "text.color": "black", + "axes.hold": true, // whether to clear the axes by default on + "axes.facecolor": "white", // axes background color + "axes.edgecolor": "black", // axes edge color + "axes.grid": false, + "axes.titlesize": "large", + "axes.labelsize": "medium", + "axes.labelweigth": "normal", + "axes.labelcolor": "black", + "axes.axisbelow": false, + "axes.color_cycle": ["b", "g", "r", "c", "m", "y", "k"] +}; + +var chart_counter = 0; // for creating unique ids +jsplotlib._line_counter = 0; + +/** List of all supported line styles **/ +jsplotlib.lineStyles = { + '-': '_draw_solid', + '--': '_draw_dashed', + '-.': '_draw_dash_dot', + ':': '_draw_dotted', + 'None': '_draw_nothing', + ' ': '_draw_nothing', + '': '_draw_nothing', +}; + +jsplotlib.lineMarkers = { + '.': 'point', + ',': 'pixel', + 'o': 'circle', + 'v': 'triangle_down', + '^': 'triangle_up', + '<': 'triangle_left', + '>': 'triangle_right', + '1': 'tri_down', + '2': 'tri_up', + '3': 'tri_left', + '4': 'tri_right', + '8': 'octagon', + 's': 'square', + 'p': 'pentagon', + '*': 'star', + 'h': 'hexagon1', + 'H': 'hexagon2', + '+': 'plus', + 'x': 'x', + 'D': 'diamond', + 'd': 'thin_diamond', + '|': 'vline', + '_': 'hline', + //TICKLEFT: 'tickleft', + //TICKRIGHT: 'tickright', + //TICKUP: 'tickup', + //TICKDOWN: 'tickdown', + //CARETLEFT: 'caretleft', + //CARETRIGHT: 'caretright', + //CARETUP: 'caretup', + //CARETDOWN: 'caretdown', + "None": 'nothing', + //Sk.builtin.none.none$: 'nothing', + ' ': 'nothing', + '': 'nothing' +}; + +/** + Color short keys +**/ +jsplotlib.colors = { + 'b': 'blue', + 'g': 'green', + 'r': 'red', + 'c': 'cyan', + 'm': 'magenta', + 'y': 'yellow', + 'k': 'black', + 'w': 'white' +}; + +/** + Mapping of all possible CSS colors, that are supported by matplotlib +**/ +jsplotlib.cnames = { + 'aliceblue': '#F0F8FF', + 'antiquewhite': '#FAEBD7', + 'aqua': '#00FFFF', + 'aquamarine': '#7FFFD4', + 'azure': '#F0FFFF', + 'beige': '#F5F5DC', + 'bisque': '#FFE4C4', + 'black': '#000000', + 'blanchedalmond': '#FFEBCD', + 'blue': '#0000FF', + 'blueviolet': '#8A2BE2', + 'brown': '#A52A2A', + 'burlywood': '#DEB887', + 'cadetblue': '#5F9EA0', + 'chartreuse': '#7FFF00', + 'chocolate': '#D2691E', + 'coral': '#FF7F50', + 'cornflowerblue': '#6495ED', + 'cornsilk': '#FFF8DC', + 'crimson': '#DC143C', + 'cyan': '#00FFFF', + 'darkblue': '#00008B', + 'darkcyan': '#008B8B', + 'darkgoldenrod': '#B8860B', + 'darkgray': '#A9A9A9', + 'darkgreen': '#006400', + 'darkkhaki': '#BDB76B', + 'darkmagenta': '#8B008B', + 'darkolivegreen': '#556B2F', + 'darkorange': '#FF8C00', + 'darkorchid': '#9932CC', + 'darkred': '#8B0000', + 'darksage': '#598556', + 'darksalmon': '#E9967A', + 'darkseagreen': '#8FBC8F', + 'darkslateblue': '#483D8B', + 'darkslategray': '#2F4F4F', + 'darkturquoise': '#00CED1', + 'darkviolet': '#9400D3', + 'deeppink': '#FF1493', + 'deepskyblue': '#00BFFF', + 'dimgray': '#696969', + 'dodgerblue': '#1E90FF', + 'firebrick': '#B22222', + 'floralwhite': '#FFFAF0', + 'forestgreen': '#228B22', + 'fuchsia': '#FF00FF', + 'gainsboro': '#DCDCDC', + 'ghostwhite': '#F8F8FF', + 'gold': '#FFD700', + 'goldenrod': '#DAA520', + 'gray': '#808080', + 'green': '#008000', + 'greenyellow': '#ADFF2F', + 'honeydew': '#F0FFF0', + 'hotpink': '#FF69B4', + 'indianred': '#CD5C5C', + 'indigo': '#4B0082', + 'ivory': '#FFFFF0', + 'khaki': '#F0E68C', + 'lavender': '#E6E6FA', + 'lavenderblush': '#FFF0F5', + 'lawngreen': '#7CFC00', + 'lemonchiffon': '#FFFACD', + 'lightblue': '#ADD8E6', + 'lightcoral': '#F08080', + 'lightcyan': '#E0FFFF', + 'lightgoldenrodyellow': '#FAFAD2', + 'lightgreen': '#90EE90', + 'lightgray': '#D3D3D3', + 'lightpink': '#FFB6C1', + 'lightsage': '#BCECAC', + 'lightsalmon': '#FFA07A', + 'lightseagreen': '#20B2AA', + 'lightskyblue': '#87CEFA', + 'lightslategray': '#778899', + 'lightsteelblue': '#B0C4DE', + 'lightyellow': '#FFFFE0', + 'lime': '#00FF00', + 'limegreen': '#32CD32', + 'linen': '#FAF0E6', + 'magenta': '#FF00FF', + 'maroon': '#800000', + 'mediumaquamarine': '#66CDAA', + 'mediumblue': '#0000CD', + 'mediumorchid': '#BA55D3', + 'mediumpurple': '#9370DB', + 'mediumseagreen': '#3CB371', + 'mediumslateblue': '#7B68EE', + 'mediumspringgreen': '#00FA9A', + 'mediumturquoise': '#48D1CC', + 'mediumvioletred': '#C71585', + 'midnightblue': '#191970', + 'mintcream': '#F5FFFA', + 'mistyrose': '#FFE4E1', + 'moccasin': '#FFE4B5', + 'navajowhite': '#FFDEAD', + 'navy': '#000080', + 'oldlace': '#FDF5E6', + 'olive': '#808000', + 'olivedrab': '#6B8E23', + 'orange': '#FFA500', + 'orangered': '#FF4500', + 'orchid': '#DA70D6', + 'palegoldenrod': '#EEE8AA', + 'palegreen': '#98FB98', + 'paleturquoise': '#AFEEEE', + 'palevioletred': '#DB7093', + 'papayawhip': '#FFEFD5', + 'peachpuff': '#FFDAB9', + 'peru': '#CD853F', + 'pink': '#FFC0CB', + 'plum': '#DDA0DD', + 'powderblue': '#B0E0E6', + 'purple': '#800080', + 'red': '#FF0000', + 'rosybrown': '#BC8F8F', + 'royalblue': '#4169E1', + 'saddlebrown': '#8B4513', + 'salmon': '#FA8072', + 'sage': '#87AE73', + 'sandybrown': '#FAA460', + 'seagreen': '#2E8B57', + 'seashell': '#FFF5EE', + 'sienna': '#A0522D', + 'silver': '#C0C0C0', + 'skyblue': '#87CEEB', + 'slateblue': '#6A5ACD', + 'slategray': '#708090', + 'snow': '#FFFAFA', + 'springgreen': '#00FF7F', + 'steelblue': '#4682B4', + 'tan': '#D2B48C', + 'teal': '#008080', + 'thistle': '#D8BFD8', + 'tomato': '#FF6347', + 'turquoise': '#40E0D0', + 'violet': '#EE82EE', + 'wheat': '#F5DEB3', + 'white': '#FFFFFF', + 'whitesmoke': '#F5F5F5', + 'yellow': '#FFFF00', + 'yellowgreen': '#9ACD32' +}; + +jsplotlib.color_to_hex = function(color) { + // is color a shortcut? + if (jsplotlib.colors[color]) + color = jsplotlib.colors[color]; + + // is inside cnames array? + if (jsplotlib.cnames[color]) + return jsplotlib.cnames[color]; + + // check if it is already a hex value + if (typeof color == "string") { + var match = color.match(/^#(?:[0-9a-fA-F]{3}){1,2}$/); + if (match && match.length === 1) + return match[0]; + } + + // add rgb colors here + if (Array.isArray(color) && color.length === 3) { + return jsplotlib.rgb2hex(color); + } + + // back to default + return jsplotlib.cnames[jsplotlib.rc['lines.color']]; +}; + +jsplotlib.get_color = function(cs) { + return jsplotlib.colors[cs] ? jsplotlib.colors[cs] : jsplotlib.colors.b; +}; + +jsplotlib.parse_marker = function(style) { + if (!style) return "x"; + switch (style) { + case '.': + return "."; + case ',': + return "x"; + case 'o': + return "o"; + case 'v': + return "x"; + case '^': + return "x"; + case '<': + return "x"; + case '>': + return "x"; + case '1': + return "x"; + case '2': + return "x"; + case '3': + return "x"; + case '4': + return "x"; + case 's': + return "s"; + case 'p': + return "x"; + case '*': + return "x"; + case 'h': + return "x"; + case 'H': + return "x"; + case '+': + return "x"; + case 'x': + return "x"; + case 'D': + return "x"; + case 'd': + return "x"; + case '|': + return "x"; + case '_': + return "x"; + default: + return ""; + } +}; + +/** +Process a MATLAB style color/line style format string. Return a +(*linestyle*, *color*) tuple as a result of the processing. Default +values are ('-', 'b'). Example format strings include: + +* 'ko': black circles +* '.b': blue dots +* 'r--': red dashed lines + +.. seealso:: + + :func:`~matplotlib.Line2D.lineStyles` and + :func:`~matplotlib.pyplot.colors` + for all possible styles and color format string. +**/ +jsplotlib._process_plot_format = function(fmt) { + var linestyle = null; + var marker = null; + var color = null; + + // Is fmt just a colorspec + try { + color = jsplotlib.to_rgb(fmt); + if (color) { + return { + 'linestyle': linestyle, + 'marker': marker, + 'color': color + }; + } + } catch (e) {} + + // handle the multi char special cases and strip them for the string + if (fmt.search(/--/) >= 0) { + linestyle = '--'; + fmt = fmt.replace(/--/, ''); + } + if (fmt.search(/-\./) >= 0) { + linestyle = '-.'; + fmt = fmt.replace(/-\./, ''); + } + if (fmt.search(/ /) >= 0) { + linestyle = ''; + fmt = fmt.replace(/ /, ''); + } + + var i; + for (i = 0; i < fmt.length; i++) { + var c = fmt.charAt(i); + if (jsplotlib.lineStyles[c]) { + if (linestyle) { + throw new Sk.builtin.ValueError('Illegal format string "' + fmt + + '"; two linestyle symbols'); + } + linestyle = c; + } else if (jsplotlib.lineMarkers[c]) { + if (marker) { + throw new Sk.builtin.ValueError('Illegal format string "' + fmt + + '"; two marker symbols'); + } + marker = c; + } else if (jsplotlib.colors[c]) { + if (color) { + throw new Sk.builtin.ValueError('Illegal format string "' + fmt + + '"; two color symbols'); + } + color = c; + } else { + throw new Sk.builtin.ValueError('Unrecognized character ' + c + + ' in format string'); + } + } + + if (!linestyle && !marker) { + // use defaults --> rcParams['lines.linestyle'] + linestyle = '-'; + } + if (!linestyle) { + linestyle = ' '; + } + if (!marker) { + marker = ''; + } + + return { + 'linestyle': linestyle, + 'marker': marker, + 'color': color + }; +}; + +/** + https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/colors.py + http://matplotlib.org/api/colors_api.html + + Returns an *RGB* tuple of three floats from 0-1. + + *arg* can be an *RGB* or *RGBA* sequence or a string in any of + several forms: + + 1) a letter from the set 'rgbcmykw' + 2) a hex color string, like '#00FFFF' + 3) a standard name, like 'aqua' + 4) a string representation of a float, like '0.4', + indicating gray on a 0-1 scale + + if *arg* is *RGBA*, the *A* will simply be discarded. +**/ +jsplotlib.to_rgb = function(fmt) { + if (!fmt) return null; + + var color = null; + + if (typeof fmt == "string") { + fmt_lower = fmt.toLowerCase(); + + if (jsplotlib.colors[fmt_lower]) + return jsplotlib.hex2color(jsplotlib.cnames[jsplotlib.colors[fmt_lower]]); + + // is inside cnames array? + if (jsplotlib.cnames[fmt_lower]) + return jsplotlib.hex2color(jsplotlib.cnames[fmt_lower]); + + if (fmt_lower.indexOf('#') === 0) { + return jsplotlib.hex2color(fmt_lower); + } + + // is it simple grey shade? + var fl = parseFloat(fmt_lower); + if (isNaN(fl)) { + throw new Sk.builtin.ValueError('cannot convert argument to rgb sequence'); + } + + if (fl < 0 || fl > 1) { + throw new Sk.builtin.ValueError('gray (string) must be in range 0-1'); + } + + return [fl, fl, fl]; + } + + // check if its a color tuple [r,g,b, [a]] with values from [0-1] + if (Array.isArray(fmt)) { + if (fmt.length > 4 || fmt.length < 3) + throw new Sk.builtin.ValueError('sequence length is ' + fmt.length + + '; must be 3 or 4'); + + color = fmt.slice(0, 3); + var i; + + for (i = 0; i < 3; i++) { + var fl_rgb = parseFloat(fmt); + + if (fl_rgb < 0 || fl_rgb > 1) + throw new Sk.builtin.ValueError( + 'number in rbg sequence outside 0-1 range'); + } + } + + return color; +}; + +/** + Take a hex string *s* and return the corresponding rgb 3-tuple + Example: #efefef -> (0.93725, 0.93725, 0.93725) +**/ +jsplotlib.hex2color = function(s) { + if (!s || typeof s != "string") { + throw new Sk.builtin.TypeError("hex2color requires a string argument"); + } + // check if it is a hex value + var i; + var s_copy = s; + var hex_tuple = []; + for (i = 0; i < 3; i++) { + var match = s_copy.match(/(?:[0-9a-fA-F]){1,2}$/); + if (match && match.length === 1) { + hex_tuple.push(match[0]); + s_copy = s_copy.substring(0, match.index); + } + } + //var match = s.match(/^#(?:[0-9a-fA-F]{3}){1,2}$/); + if (hex_tuple.length === 3) { + // yeah positiv --> convert into right color spec + var color = []; + color[0] = parseInt(hex_tuple[0], 16) / 255.0; + color[1] = parseInt(hex_tuple[1], 16) / 255.0; + color[2] = parseInt(hex_tuple[2], 16) / 255.0; + + return color.reverse(); + } else { + throw new Sk.builtin.ValueError('invalid hex color string "' + s + '"'); + } +}; + +/** + Expects and rgb tuple with values [0,1] +**/ +jsplotlib.rgb2hex = function(rgb) { + if (!rgb) return null; + + if (rgb.length && rgb.length >= 3) { + var i; + // some hacky code to rebuild string format :( + var hex_str = '#'; + for (i = 0; i < 3; i++) { + var val = Math.round(rgb[i] * 255).toString(16); + hex_str += val.length == 2 ? val : '0' + val; + } + + return hex_str; + } +}; diff --git a/src/lib/media/__init__.py b/src/lib/media/__init__.py new file mode 100644 index 0000000000..1831a6d44c --- /dev/null +++ b/src/lib/media/__init__.py @@ -0,0 +1,11 @@ +# Order is important here + +from image.style import * +from image.color import * +from image.pixel import * +from image.picture import * +from image import * + +from sound import * +from sound.sample import * +from sound.sound import * diff --git a/src/lib/numpy/__init__.js b/src/lib/numpy/__init__.js new file mode 100644 index 0000000000..3a6994cc75 --- /dev/null +++ b/src/lib/numpy/__init__.js @@ -0,0 +1,1134 @@ +/** + Made by Michael Ebert for https://github.com/skulpt/skulpt + ndarray implementation inspired by https://github.com/geometryzen/davinci-dev (not compatible with skulpt) + + Some methods are based on the original numpy implementation. + + See http://waywaaard.github.io/skulpt/ for more information. +**/ +var numpy = function () { + if (typeof mathjs == 'function') { + // load mathjs instance + this.math = mathjs; + } else { + Sk.debugout("mathjs not included and callable"); + } +}; + +numpy.prototype.wrapasfloats = function (values) { + var i; + for (i = 0; i < values.length; i++) { + values[i] = new Sk.builtin.nmber(values[i], Sk.builtin.nmber.float$); + } + + return values; +}; + +numpy.prototype.arange = function (start, stop, step) { + if (step === undefined) + step = 1.0; + + start *= 1.0; + stop *= 1.0; + step *= 1.0; + + var res = []; + for (var i = start; i < stop; i += step) { + res.push(i); + } + + return res; +}; + +var $builtinmodule = function (name) { + var np = new numpy(); + + var mod = {}; + + /** + Class for numpy.ndarray + **/ + var CLASS_NDARRAY = "numpy.ndarray"; + + function remapToJs_shallow(obj, shallow) { + var _shallow = shallow || true; + if (obj instanceof Sk.builtin.list) { + if (!_shallow) { + var ret = []; + for (var i = 0; i < obj.v.length; ++i) { + ret.push(Sk.ffi.remapToJs(obj.v[i])); + } + return ret; + } else { + return obj.v; + } + } else if (obj instanceof Sk.builtin.float_) { + return Sk.builtin.asnum$nofloat(obj); + } else { + return Sk.ffi.remapToJs(obj); + } + } + + /** + Unpacks in any form fo nested Lists + **/ + function unpack(py_obj, buffer, state) { + if (py_obj instanceof Sk.builtin.list || py_obj instanceof Sk.builtin.tuple) { + var py_items = remapToJs_shallow(py_obj); + state.level += 1; + + if (state.level > state.shape.length) { + state.shape.push(py_items.length); + } + var i; + var len = py_items.length; + for (i = 0; i < len; i++) { + unpack(py_items[i], buffer, state); + } + state.level -= 1; + } else { + buffer.push(py_obj); + } + } + + /** + Computes the strides for columns and rows + **/ + function computeStrides(shape) { + var strides = shape.slice(0); + strides.reverse(); + var prod = 1; + var temp; + for (var i = 0, len = strides.length; i < len; i++) { + temp = strides[i]; + strides[i] = prod; + prod *= temp; + } + + return strides.reverse(); + } + + /** + Computes the offset for the ndarray for given index and strides + [1, ..., n] + **/ + function computeOffset(strides, index) { + var offset = 0; + for (var k = 0, len = strides.length; k < len; k++) { + offset += strides[k] * index[k]; + } + return offset; + } + + /** + Calculates the size of the ndarray, dummy + **/ + function prod(numbers) { + var size = 1; + var i; + for (i = 0; i < numbers.length; i++) { + size *= numbers[i]; + } + return size; + } + + /** + Creates a string representation for given buffer and shape + buffer is an ndarray + **/ + function stringify(buffer, shape, dtype) { + var emits = shape.map(function (x) { + return 0; + }); + var uBound = shape.length - 1; + var idxLevel = 0; + var str = "["; + var i = 0; + while (idxLevel !== -1) { + if (emits[idxLevel] < shape[idxLevel]) { + if (emits[idxLevel] !== 0) { + str += ", "; + } + + if (idxLevel < uBound) { + str += "["; + idxLevel += 1; + } else { + if (dtype === Sk.builtin.float_) + str += Sk.ffi.remapToJs(Sk.builtin.str(new Sk.builtin.float_(buffer[ + i++]))); + else + str += Sk.ffi.remapToJs(Sk.builtin.str(buffer[i++])); + emits[idxLevel] += 1; + } + } else { + emits[idxLevel] = 0; + str += "]"; + idxLevel -= 1; + if (idxLevel >= 0) { + emits[idxLevel] += 1; + } + } + } + return str; + } + + /* + http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.tolist.html?highlight=tolist#numpy.ndarray.tolist + */ + function tolistrecursive(buffer, shape, strides, startdim, dtype) { + var i, n, stride; + var arr, item; + + /* Base case */ + if (startdim >= shape.length) { + if (dtype && dtype === Sk.builtin.float_) { + return new Sk.builtin.float_(buffer[0]); // handle float special case + } else { + return Sk.ffi.remapToPy(buffer[0]); + } + } + + n = shape[startdim]; + stride = strides[startdim]; + + arr = []; + + for (i = 0; i < n; i++) { + item = tolistrecursive(buffer, shape, strides, startdim + 1, dtype); + arr.push(item); + + buffer = buffer.slice(stride); + } + + return new Sk.builtin.list(arr); + } + + /** + internal tolist interface + **/ + function tolist(buffer, shape, strides, dtype) { + var buffer_copy = buffer.slice(0); + return tolistrecursive(buffer_copy, shape, strides, 0, dtype); + } + + /** + Updates all attributes of the numpy.ndarray + **/ + function updateAttributes(self, ndarrayJs) { + Sk.abstr.sattr(self, 'ndmin', new Sk.builtin.int_(ndarrayJs.shape.length)); + Sk.abstr.sattr(self, 'dtype', ndarrayJs.dtype); + Sk.abstr.sattr(self, 'shape', new Sk.builtin.tuple(ndarrayJs.shape.map( + function (x) { + return new Sk.builtin.int_(x); + }))); + Sk.abstr.sattr(self, 'strides', new Sk.builtin.tuple(ndarrayJs.strides.map( + function (x) { + return new Sk.builtin.int_(x); + }))); + Sk.abstr.sattr(self, 'size', new Sk.builtin.int_(prod(ndarrayJs.shape))); + Sk.abstr.sattr(self, 'data', new Sk.ffi.remapToPy(ndarrayJs.buffer)); + } + + /** + An array object represents a multidimensional, homogeneous array of fixed-size items. + An associated data-type object describes the format of each element in the array + (its byte-order, how many bytes it occupies in memory, whether it is an integer, a + floating point number, or something else, etc.) + + Arrays should be constructed using array, zeros or empty (refer to the See Also + section below). The parameters given here refer to a low-level method (ndarray(...)) for + instantiating an array. + + For more information, refer to the numpy module and examine the the methods and + attributes of an array. + **/ + var ndarray_f = function ($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self, shape, dtype, buffer, + offset, strides, order) { + var ndarrayJs = {}; // js object holding the actual array + ndarrayJs.shape = Sk.ffi.remapToJs(shape); + + ndarrayJs.strides = computeStrides(ndarrayJs.shape); + ndarrayJs.dtype = dtype || Sk.builtin.none.none$; + + if (buffer && buffer instanceof Sk.builtin.list) { + ndarrayJs.buffer = Sk.ffi.remapToJs(buffer); + } + + self.v = ndarrayJs; // value holding the actual js object and array + self.tp$name = CLASS_NDARRAY; // set class name + + updateAttributes(self, ndarrayJs); + }); + + $loc.tp$getattr = Sk.builtin.object.prototype.GenericGetAttr; + + // ToDo: setAttribute should be implemented, change of shape causes resize + // ndmin cannot be set, etc... + $loc.tp$setattr = Sk.builtin.object.prototype.GenericSetAttr; + + /* + Return the array as a (possibly nested) list. + + Return a copy of the array data as a (nested) Python list. Data items are + converted to the nearest compatible Python type. + */ + $loc.tolist = new Sk.builtin.func(function (self) { + var ndarrayJs = Sk.ffi.remapToJs(self); + var list = tolist(ndarrayJs.buffer, ndarrayJs.shape, ndarrayJs.strides, + ndarrayJs.dtype); + + return list; + }); + + $loc.reshape = new Sk.builtin.func(function (self, shape, order) { + Sk.builtin.pyCheckArgs("reshape", arguments, 2, 3); + var ndarrayJs = Sk.ffi.remapToJs(self); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, ndarrayJs.dtype, + new Sk.builtin.list(ndarrayJs.buffer)); + }); + + $loc.copy = new Sk.builtin.func(function (self, order) { + Sk.builtin.pyCheckArgs("copy", arguments, 1, 2); + var ndarrayJs = Sk.ffi.remapToJs(self); + var buffer = ndarrayJs.buffer.map(function (x) { + return x; + }); + var shape = new Sk.builtin.tuplePy(ndarrayJs.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, ndarrayJs.dtype, + new Sk.builtin.list(buffer)); + }); + + /** + Fill the array with a scalar value. + Parameters: value: scalar + All elements of a will be assigned this value + **/ + $loc.fill = new Sk.builtin.func(function (self, value) { + Sk.builtin.pyCheckArgs("fill", arguments, 2, 2); + var ndarrayJs = Sk.ffi.remapToJs(self); + var buffer = ndarrayJs.buffer.map(function (x) { + return x; + }); + var i; + for (i = 0; i < ndarrayJs.buffer.length; i++) { + if (ndarrayJs.dtype) { + ndarrayJs.buffer[i] = Sk.misceval.callsim(ndarrayJs.dtype, + value); + } + } + }); + + $loc.__getitem__ = new Sk.builtin.func(function (self, index) { + Sk.builtin.pyCheckArgs("[]", arguments, 2, 2); + var ndarrayJs = Sk.ffi.remapToJs(self); + var _index; // current index + var _buffer; // buffer as python type + var buffer_internal; // buffer als js array + var _stride; // stride + var _shape; // shape as js + var i; + + // single index e.g. [3] + if (Sk.builtin.checkInt(index)) { + var offset = Sk.ffi.remapToJs(index); + + if (ndarrayJs.shape.length > 1) { + _stride = ndarrayJs.strides[0]; + buffer_internal = []; + _index = 0; + + for (i = offset * _stride, ubound = (offset + 1) * _stride; i < + ubound; i++) { + buffer_internal[_index++] = ndarrayJs.buffer[i]; + } + + _buffer = new Sk.builtin.list(buffer_internal); + _shape = new Sk.builtin.tuple(Array.prototype.slice.call( + ndarrayJs.shape, + 1) + .map(function (x) { + return new Sk.builtin.int_(x); + })); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], _shape, + undefined, + _buffer); + } else { + if (offset >= 0 && offset < ndarrayJs.buffer.length) { + return ndarrayJs.buffer[offset]; + } else { + throw new Sk.builtin.IndexError("array index out of range"); + } + } + } else if (index instanceof Sk.builtin.tuple) { + // index like [1,3] + var keyJs = Sk.ffi.remapToJs(index); + return ndarrayJs.buffer[computeOffset(ndarrayJs.strides, keyJs)]; + } else if (index instanceof Sk.builtin.slice) { + // support for slices e.g. [1:4] + var indices = index.indices(); + var start = typeof indices[0] !== 'undefined' ? indices[0] : 0; + var stop = typeof indices[1] !== 'undefined' ? indices[1] : + ndarrayJs + .buffer.length; + stop = stop > ndarrayJs.buffer.length ? ndarrayJs.buffer.length : + stop; + var step = typeof indices[2] !== 'undefined' ? indices[2] : 1; + buffer_internal = []; + _index = 0; + if (step > 0) { + for (i = start; i < stop; i += step) { + buffer_internal[_index++] = ndarrayJs.buffer[i]; + } + } + _buffer = new Sk.builtin.list(buffer_internal); + _shape = new Sk.builtin.tuple([buffer_internal.length].map( + function ( + x) { + return new Sk.builtin.int_(x); + })); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], _shape, undefined, + _buffer); + } else { + throw new Sk.builtin.ValueError('Index "' + index + + '" must be int, slice or tuple'); + } + }); + + $loc.__setitem__ = new Sk.builtin.func(function (self, index, value) { + var ndarrayJs = Sk.ffi.remapToJs(self); + Sk.builtin.pyCheckArgs("[]", arguments, 3, 3); + if (index instanceof Sk.builtin.int_) { + var _offset = Sk.ffi.remapToJs(index); + if (ndarrayJs.shape.length > 1) { + var _value = Sk.ffi.remapToJs(value); + var _stride = ndarrayJs.strides[0]; + var _index = 0; + + var _ubound = (_offset + 1) * _stride; + var i; + for (i = _offset * _stride; i < _ubound; i++) { + ndarrayJs.buffer[i] = _value.buffer[_index++]; + } + } else { + if (_offset >= 0 && _offset < ndarrayJs.buffer.length) { + ndarrayJs.buffer[_offset] = value; + } else { + throw new Sk.builtin.IndexError("array index out of range"); + } + } + } else if (index instanceof Sk.builtin.tuple) { + _key = Sk.ffi.remapToJs(index); + ndarrayJs.buffer[computeOffset(ndarrayJs.strides, _key)] = value; + } else { + throw new Sk.builtin.TypeError( + 'argument "index" must be int or tuple'); + } + }); + + $loc.__len__ = new Sk.builtin.func(function (self) { + var ndarrayJs = Sk.ffi.remapToJs(self); + return new Sk.builtin.int_(ndarrayJs.shape[0]); + }); + + $loc.__iter__ = new Sk.builtin.func(function (self) { + var ndarrayJs = Sk.ffi.remapToJs(self); + var ret = { + tp$iter: function () { + return ret; + }, + $obj: ndarrayJs, + $index: 0, + tp$iternext: function () { + if (ret.$index >= ret.$obj.buffer.length) return undefined; + return ret.$obj.buffer[ret.$index++]; + } + }; + return ret; + }); + + $loc.__str__ = new Sk.builtin.func(function (self) { + var ndarrayJs = remapToJs_shallow(self, false); + return new Sk.builtin.str(stringify(ndarrayJs.buffer, + ndarrayJs.shape, ndarrayJs.dtype)); + }); + + $loc.__repr__ = new Sk.builtin.func(function (self) { + var ndarrayJs = Sk.ffi.remapToJs(self); + return new Sk.builtin.str("array(" + stringify(ndarrayJs.buffer, + ndarrayJs.shape, ndarrayJs.dtype) + ")"); + }); + + /** + Creates left hand side operations for given binary operator + **/ + function makeNumericBinaryOpLhs(operation) { + return function (self, other) { + var lhs; + var rhs; + var buffer; // external + var _buffer; // internal use + var shape; // new shape of returned ndarray + var i; + + + var ndarrayJs = Sk.ffi.remapToJs(self); + + if (Sk.abstr.typeName(other) === CLASS_NDARRAY) { + lhs = ndarrayJs.buffer; + rhs = Sk.ffi.remapToJs(other) + .buffer; + _buffer = []; + for (i = 0, len = lhs.length; i < len; i++) { + //_buffer[i] = operation(lhs[i], rhs[i]); + _buffer[i] = Sk.abstr.binary_op_(lhs[i], rhs[i], operation); + } + } else { + lhs = ndarrayJs.buffer; + _buffer = []; + for (i = 0, len = lhs.length; i < len; i++) { + _buffer[i] = Sk.abstr.numberBinOp(lhs[i], other, operation); + } + } + + // create return ndarray + shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); + buffer = new Sk.builtin.list(_buffer); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, + buffer); + }; + } + + function makeNumericBinaryOpRhs(operation) { + return function (self, other) { + var ndarrayJs = Sk.ffi.remapToJs(self); + var rhsBuffer = ndarrayJs.buffer; + var _buffer = []; + for (var i = 0, len = rhsBuffer.length; i < len; i++) { + _buffer[i] = Sk.abstr.numberBinOp(other, rhsBuffer[i], operation); + } + var shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); + buffer = new Sk.builtin.list(_buffer); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, buffer); + }; + } + + /* + Applies given operation on each element of the ndarray. + */ + function makeUnaryOp(operation) { + return function (self) { + var ndarrayJs = Sk.ffi.remapToJs(self); + var _buffer = ndarrayJs.buffer.map(function (value) { + return Sk.abstr.numberUnaryOp(Sk.ffi.remapToPy(value), operation); + }); + var shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); + buffer = new Sk.builtin.list(_buffer); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, buffer); + }; + } + + $loc.__add__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Add")); + $loc.__radd__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Add")); + + $loc.__sub__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Sub")); + $loc.__rsub__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Sub")); + + $loc.__mul__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Mult")); + $loc.__rmul__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Mult")); + + $loc.__div__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Div")); + $loc.__rdiv__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Div")); + + $loc.__mod__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Mod")); + $loc.__rmod__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Mod")); + + $loc.__xor__ = new Sk.builtin.func(makeNumericBinaryOpLhs("BitXor")); + $loc.__rxor__ = new Sk.builtin.func(makeNumericBinaryOpRhs("BitXor")); + + $loc.__lshift__ = new Sk.builtin.func(makeNumericBinaryOpLhs("LShift")); + $loc.__rlshift__ = new Sk.builtin.func(makeNumericBinaryOpRhs("LShift")); + + $loc.__rshift__ = new Sk.builtin.func(makeNumericBinaryOpLhs("RShift")); + $loc.__rrshift__ = new Sk.builtin.func(makeNumericBinaryOpRhs("RShift")); + + $loc.__pos__ = new Sk.builtin.func(makeUnaryOp("UAdd")); + $loc.__neg__ = new Sk.builtin.func(makeUnaryOp("USub")); + + /** + Simple pow implementation that faciliates the pow builtin + **/ + $loc.__pow__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__pow__", arguments, 2, 2); + var ndarrayJs = Sk.ffi.remapToJs(self); + var _buffer = ndarrayJs.buffer.map(function (value) { + return Sk.builtin.pow(Sk.ffi.remapToPy(value), other); + }); + var shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); + buffer = new Sk.builtin.list(_buffer); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, buffer); + }); + + // end of ndarray_f + }; + + mod[CLASS_NDARRAY] = Sk.misceval.buildClass(mod, ndarray_f, + CLASS_NDARRAY, []); + + /** + Trigonometric functions, all element wise + **/ + mod.pi = Sk.builtin.assk$(np.math ? np.math.PI : Math.PI, Sk.builtin.nmber.float$); + mod.e = Sk.builtin.assk$(np.math ? np.math.E : Math.E, Sk.builtin.nmber.float$); + /** + Trigonometric sine, element-wise. + **/ + + function callTrigonometricFunc(x, op) { + var res; + var num; + if (x instanceof Sk.builtin.list || x instanceof Sk.builtin.tuple) { + x = Sk.misceval.callsim(mod.array, x); + } + + if (Sk.abstr.typeName(x) === CLASS_NDARRAY) { + var ndarrayJs = Sk.ffi.remapToJs(x); + + var _buffer = ndarrayJs.buffer.map(function (value) { + num = Sk.builtin.asnum$(value); + res = op.call(null, num); + return new Sk.builtin.nmber(res, Sk.builtin.nmber + .float$); + }); + + var shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); + + buffer = new Sk.builtin.list(_buffer); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, buffer); + } else if (Sk.builtin.checkNumber(x)) { + num = Sk.builtin.asnum$(x); + res = op.call(null, num); + return new Sk.builtin.nmber(res, Sk.builtin.nmber + .float$); + } + + throw new Sk.builtin.TypeError('Unsupported argument type for "x"'); + } + + // Sine, element-wise. + var sin_f = function (x, out) { + Sk.builtin.pyCheckArgs("sin", arguments, 1, 2); + return callTrigonometricFunc(x, np.math ? np.math.sin : Math.sin); + }; + sin_f.co_varnames = ['x', 'out']; + sin_f.$defaults = [0, new Sk.builtin.list([])]; + mod.sin = new Sk.builtin.func(sin_f); + + // Hyperbolic sine, element-wise. + var sinh_f = function (x, out) { + Sk.builtin.pyCheckArgs("sinh", arguments, 1, 2); + if (!np.math) throw new Sk.builtin.OperationError("sinh requires mathjs"); + return callTrigonometricFunc(x, np.math.sinh); + }; + sinh_f.co_varnames = ['x', 'out']; + sinh_f.$defaults = [0, new Sk.builtin.list([])]; + mod.sinh = new Sk.builtin.func(sinh_f); + + // Inverse sine, element-wise. + var arcsin_f = function (x, out) { + Sk.builtin.pyCheckArgs("arcsin", arguments, 1, 2); + return callTrigonometricFunc(x, np.math ? np.math.asin : Math.asin); + }; + arcsin_f.co_varnames = ['x', 'out']; + arcsin_f.$defaults = [0, new Sk.builtin.list([])]; + mod.arcsin = new Sk.builtin.func(arcsin_f); + + // Cosine, element-wise. + var cos_f = function (x, out) { + Sk.builtin.pyCheckArgs("cos", arguments, 1, 2); + return callTrigonometricFunc(x, np.math ? np.math.cos : Math.cos); + }; + cos_f.co_varnames = ['x', 'out']; + cos_f.$defaults = [0, new Sk.builtin.list([])]; + mod.cos = new Sk.builtin.func(cos_f); + + // Hyperbolic cosine, element-wise. + var cosh_f = function (x, out) { + Sk.builtin.pyCheckArgs("cosh", arguments, 1, 2); + if (!np.math) throw new Sk.builtin.OperationError("cosh requires mathjs"); + return callTrigonometricFunc(x, np.math.cosh); + }; + cosh_f.co_varnames = ['x', 'out']; + cosh_f.$defaults = [0, new Sk.builtin.list([])]; + mod.cosh = new Sk.builtin.func(cosh_f); + + // Inverse cosine, element-wise. + var arccos_f = function (x, out) { + Sk.builtin.pyCheckArgs("arccos", arguments, 1, 2); + return callTrigonometricFunc(x, np.math ? np.math.acos : Math.acos); + }; + arccos_f.co_varnames = ['x', 'out']; + arccos_f.$defaults = [0, new Sk.builtin.list([])]; + mod.arccos = new Sk.builtin.func(arccos_f); + + // Inverse tangens, element-wise. + var arctan_f = function (x, out) { + Sk.builtin.pyCheckArgs("arctan", arguments, 1, 2); + return callTrigonometricFunc(x, np.math ? np.math.atan : Math.atan); + }; + arctan_f.co_varnames = ['x', 'out']; + arctan_f.$defaults = [0, new Sk.builtin.list([])]; + mod.arctan = new Sk.builtin.func(arctan_f); + + // Tangens, element-wise. + var tan_f = function (x, out) { + Sk.builtin.pyCheckArgs("tan", arguments, 1, 2); + return callTrigonometricFunc(x, np.math ? np.math.tan : Math.tan); + }; + tan_f.co_varnames = ['x', 'out']; + tan_f.$defaults = [0, new Sk.builtin.list([])]; + mod.tan = new Sk.builtin.func(tan_f); + + // Hyperbolic cosine, element-wise. + var tanh_f = function (x, out) { + Sk.builtin.pyCheckArgs("tanh", arguments, 1, 2); + if (!np.math) throw new Sk.builtin.OperationError("tanh requires mathjs"); + return callTrigonometricFunc(x, np.math.tanh); + }; + tanh_f.co_varnames = ['x', 'out']; + tanh_f.$defaults = [0, new Sk.builtin.list([])]; + mod.tanh = new Sk.builtin.func(tanh_f); + + /* Simple reimplementation of the linspace function + * http://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html + */ + var linspace_f = function (start, stop, num, endpoint, retstep) { + Sk.builtin.pyCheckArgs("linspace", arguments, 3, 5); + Sk.builtin.pyCheckType("start", "number", Sk.builtin.checkNumber( + start)); + Sk.builtin.pyCheckType("stop", "number", Sk.builtin.checkNumber( + stop)); + if (num === undefined) { + num = 50; + } + var num_num = Sk.builtin.asnum$(num); + var endpoint_bool; + + if (endpoint === undefined) { + endpoint_bool = true; + } else if (endpoint.constructor === Sk.builtin.bool) { + endpoint_bool = endpoint.v; + } + + var retstep_bool; + if (retstep === undefined) { + retstep_bool = false; + } else if (retstep.constructor === Sk.builtin.bool) { + retstep_bool = retstep.v; + } + + var samples; + var step; + + start_num = Sk.builtin.asnum$(start) * 1.0; + stop_num = Sk.builtin.asnum$(stop) * 1.0; + + if (num_num <= 0) { + samples = []; + } else { + + var samples_array; + if (endpoint_bool) { + if (num_num == 1) { + samples = [start_num]; + } else { + step = (stop_num - start_num) / (num_num - 1); + samples_array = np.arange(0, num_num); + samples = samples_array.map(function (v) { + return v * step + start_num; + }); + samples[samples.length - 1] = stop_num; + } + } else { + step = (stop_num - start_num) / num_num; + samples_array = np.arange(0, num_num); + samples = samples_array.map(function (v) { + return v * step + start_num; + }); + } + } + + //return as ndarray! dtype:float + var dtype = Sk.builtin.float_; + for (i = 0; i < samples.length; i++) { + samples[i] = Sk.misceval.callsim(dtype, samples[i]); + } + + var buffer = Sk.builtin.list(samples); + var shape = new Sk.builtin.tuple([samples.length]); + var ndarray = Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, dtype, + buffer); + + if (retstep_bool === true) + return new Sk.builtin.tuple([ndarray, step]); + else + return ndarray; + }; + + // this should allow for named parameters + linspace_f.co_varnames = ['start', 'stop', 'num', 'endpoint', + 'retstep' + ]; + linspace_f.$defaults = [0, 0, 50, true, false]; + mod.linspace = + new Sk.builtin.func(linspace_f); + + /* Simple reimplementation of the arange function + * http://docs.scipy.org/doc/numpy/reference/generated/numpy.arange.html#numpy.arange + */ + var arange_f = function (start, stop, step, dtype) { + Sk.builtin.pyCheckArgs("arange", arguments, 1, 4); + Sk.builtin.pyCheckType("start", "number", Sk.builtin.checkNumber( + start)); + var start_num; + var stop_num; + var step_num; + + if (stop === undefined && step === undefined) { + start_num = Sk.builtin.asnum$(0); + stop_num = Sk.builtin.asnum$(start); + step_num = Sk.builtin.asnum$(1); + } else if (step === undefined) { + start_num = Sk.builtin.asnum$(start); + stop_num = Sk.builtin.asnum$(stop); + step_num = Sk.builtin.asnum$(1); + } else { + start_num = Sk.builtin.asnum$(start); + stop_num = Sk.builtin.asnum$(stop); + step_num = Sk.builtin.asnum$(step); + } + + // set to float + if (!dtype || dtype == Sk.builtin.none.none$) { + if (Sk.builtin.checkInt(start)) + dtype = Sk.builtin.int_; + else + dtype = Sk.builtin.float_; + } + + // return ndarray + var arange_buffer = np.arange(start_num, stop_num, step_num); + // apply dtype casting function, if it has been provided + if (dtype && Sk.builtin.checkClass(dtype)) { + for (i = 0; i < arange_buffer.length; i++) { + arange_buffer[i] = Sk.misceval.callsim(dtype, arange_buffer[i]); + } + } + + buffer = Sk.builtin.list(arange_buffer); + var shape = new Sk.builtin.tuple([arange_buffer.length]); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, dtype, + buffer); + }; + + arange_f.co_varnames = ['start', 'stop', 'step', 'dtype']; + arange_f + .$defaults = [0, 1, 1, Sk.builtin.none.none$]; + mod.arange = new Sk.builtin + .func(arange_f); + + /* implementation for numpy.array + ------------------------------------------------------------------------------------------------ + http://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html#numpy.array + + object : array_like + An array, any object exposing the array interface, an object whose __array__ method returns an array, or any (nested) sequence. + + dtype : data-type, optional + The desired data-type for the array. If not given, then the type will be determined as the minimum type required to hold the objects in the sequence. This argument can only be used to ‘upcast’ the array. For downcasting, use the .astype(t) method. + + copy : bool, optional + If true (default), then the object is copied. Otherwise, a copy will only be made if __array__ returns a copy, if obj is a nested sequence, or if a copy is needed to satisfy any of the other requirements (dtype, order, etc.). + + order : {‘C’, ‘F’, ‘A’}, optional + Specify the order of the array. If order is ‘C’ (default), then the array will be in C-contiguous order (last-index varies the fastest). If order is ‘F’, then the returned array will be in Fortran-contiguous order (first-index varies the fastest). If order is ‘A’, then the returned array may be in any order (either C-, Fortran-contiguous, or even discontiguous). + + subok : bool, optional + If True, then sub-classes will be passed-through, otherwise the returned array will be forced to be a base-class array (default). + + ndmin : int, optional + Specifies the minimum number of dimensions that the resulting array should have. Ones will be pre-pended to the shape as needed to meet this requirement. + + Returns : + out : ndarray + An array object satisfying the specified requirements + */ + // https://github.com/geometryzen/davinci-dev/blob/master/src/stdlib/numpy.js + // https://github.com/geometryzen/davinci-dev/blob/master/src/ffh.js + // http://docs.scipy.org/doc/numpy/reference/arrays.html + var array_f = function (object, dtype, copy, order, subok, ndmin) { + Sk.builtin.pyCheckArgs("array", arguments, 1, 6); + + if (object === undefined) + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(object) + + "' object is undefined"); + + var elements = []; + var state = {}; + state.level = 0; + state.shape = []; + + unpack(object, elements, state); + + var i; + // apply dtype casting function, if it has been provided + if (dtype && Sk.builtin.checkClass(dtype)) { + for (i = 0; i < elements.length; i++) { + elements[i] = Sk.misceval.callsim(dtype, elements[i]); + } + } else { + // check elements and find first usable type + // should be refactored + for (i = 0; i < elements.length; i++) { + if (elements[i] && isNaN(elements[i])) { + dtype = Sk.builtin.float_; + break; + } else if (typeof elements[i] == 'string') { + dtype = Sk.builtin.str; + } else { + dtype = Sk.builtin.float_; + } + } + } + + // check for ndmin param + if (ndmin) { + if (Sk.builtin.checkNumber(ndmin)) { + var _ndmin = Sk.builtin.asnum$(ndmin); + if (_ndmin >= 0 && _ndmin > state.shape.length) { + var _ndmin_array = []; + for (i = 0; i < _ndmin - state.shape.length; i++) { + _ndmin_array.push(1); + } + state.shape = _ndmin_array.concat(state.shape); + } + } else { + throw new Sk.builtin.TypeError( + 'Parameter "ndmin" must be of type "int"'); + } + } + + var _shape = new Sk.builtin.tuple(state.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); + + var _buffer = new Sk.builtin.list(elements); + // create new ndarray instance + return Sk.misceval.callsim(mod[CLASS_NDARRAY], _shape, dtype, + _buffer); + }; + + array_f.co_varnames = ['object', 'dtype', 'copy', 'order', + 'subok', 'ndmin' + ]; + array_f.$defaults = [null, Sk.builtin.none.none$, true, new Sk.builtin.str( + 'C'), false, new Sk.builtin.int_(0)]; + mod.array = new Sk.builtin.func(array_f); + + /** + Return a new array of given shape and type, filled with zeros. + **/ + var zeros_f = function (shape, dtype, order) { + Sk.builtin.pyCheckArgs("zeros", arguments, 1, 3); + Sk.builtin.pyCheckType("shape", "tuple", shape instanceof Sk.builtin.tuple); + if (dtype instanceof Sk.builtin.list) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(dtype) + + "' is not supported for dtype."); + } + + var _zero = new Sk.builtin.float_(0.0); + + return Sk.misceval.callsim(mod.full, shape, _zero, dtype, order); + }; + zeros_f.co_varnames = ['shape', 'dtype', 'order']; + zeros_f.$defaults = [ + new Sk.builtin.tuple([]), Sk.builtin.none.none$, new Sk.builtin.str('C') + ]; + mod.zeros = new Sk.builtin.func(zeros_f); + + /** + Return a new array of given shape and type, filled with `fill_value`. + **/ + var full_f = function (shape, fill_value, dtype, order) { + Sk.builtin.pyCheckArgs("full", arguments, 2, 4); + Sk.builtin.pyCheckType("shape", "tuple", shape instanceof Sk.builtin.tuple); + if (dtype instanceof Sk.builtin.list) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(dtype) + + "' is currently not supported for dtype."); + } + + // generate an array of the dimensions for the generic array method + var _shape = Sk.ffi.remapToJs(shape); + var _size = prod(_shape); + var _buffer = []; + var _fill_value = fill_value; + var i; + + for (i = 0; i < _size; i++) { + _buffer[i] = _fill_value; + } + + // if no dtype given and type of fill_value is numeric, assume float + if (!dtype && Sk.builtin.checkNumber(fill_value)) { + dtype = Sk.builtin.float_; + } + + // apply dtype casting function, if it has been provided + if (Sk.builtin.checkClass(dtype)) { + for (i = 0; i < _buffer.length; i++) { + _buffer[i] = Sk.misceval.callsim(dtype, _buffer[i]); + } + } + + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, dtype, new Sk.builtin + .list( + _buffer)); + }; + full_f.co_varnames = ['shape', 'fill_value', 'dtype', 'order']; + full_f.$defaults = [ + new Sk.builtin.tuple([]), Sk.builtin.none.none$, Sk.builtin.none.none$, new Sk + .builtin + .str('C') + ]; + mod.full = new Sk.builtin.func(full_f); + + + /** + Return a new array of given shape and type, filled with ones. + **/ + var ones_f = function (shape, dtype, order) { + Sk.builtin.pyCheckArgs("ones", arguments, 1, 3); + Sk.builtin.pyCheckType("shape", "tuple", shape instanceof Sk.builtin.tuple); + if (dtype instanceof Sk.builtin.list) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(dtype) + + "' is not supported for dtype."); + } + + var _one = new Sk.builtin.float_(1.0); + return Sk.misceval.callsim(mod.full, shape, _one, dtype, order); + }; + ones_f.co_varnames = ['shape', 'dtype', 'order']; + ones_f.$defaults = [ + new Sk.builtin.tuple([]), Sk.builtin.none.none$, new Sk.builtin.str('C') + ]; + mod.ones = new Sk.builtin.func(ones_f); + + + /** + Dot product + **/ + var dot_f = function (a, b) { + Sk.builtin.pyCheckArgs("dot", arguments, 2, 2); + + // ToDo: add support for ndarray args + + if (!(a instanceof Sk.builtin.list) && !Sk.builtin.checkNumber( + a) && (Sk.abstr.typeName(a) !== CLASS_NDARRAY)) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(a) + + "' is not supported for a."); + } + + if (!(b instanceof Sk.builtin.list) && !Sk.builtin.checkNumber( + b) && (Sk.abstr.typeName(b) !== CLASS_NDARRAY)) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(b) + + "' is not supported for b."); + } + + var res; + + var b_matrix; + var a_matrix; + + if (Sk.abstr.typeName(a) === CLASS_NDARRAY) { + a_matrix = a.v.buffer; + a_matrix = a_matrix.map(function (x) { + return Sk.ffi.remapToJs(x); + }); + } else { + a_matrix = Sk.ffi.remapToJs(a); + } + + if (Sk.abstr.typeName(b) === CLASS_NDARRAY) { + b_matrix = b.v.buffer; + b_matrix = b_matrix.map(function (x) { + return Sk.ffi.remapToJs(x); + }); + } else { + b_matrix = Sk.ffi.remapToJs(b); + } + + var a_size = np.math.size(a_matrix); + var b_size = np.math.size(b_matrix); + + + if (a_size.length >= 1 && b_size.length > 1) { + if (a_size[a_size.length - 1] != b_size[b_size - 2]) { + throw new Sk.builtin.ValueError( + "The last dimension of a is not the same size as the second-to-last dimension of b." + ); + } + } + + res = np.math.multiply(a_matrix, b_matrix); + + if (!Array.isArray(res)) { // if result + return Sk.ffi.remapToPy(res); + } + + // return ndarray + buffer = new Sk.builtin.list(res); + return Sk.misceval.callsim(mod.array, buffer, Sk.builtin.float_); + }; + dot_f.co_varnames = ['a', 'b']; + dot_f.$defaults = [Sk.builtin.none.none$, + Sk.builtin.none.none$ + ]; + mod.dot = new Sk.builtin.func(dot_f); + + /* not implemented methods */ + mod.ones_like = new Sk.builtin.func(function () { + throw new Sk.builtin.NotImplementedError( + "ones_like is not yet implemented"); + }); + mod.empty_like = new Sk.builtin.func(function () { + throw new Sk.builtin.NotImplementedError( + "empty_like is not yet implemented"); + }); + mod.ones_like = new Sk.builtin.func(function () { + throw new Sk.builtin.NotImplementedError( + "ones_like is not yet implemented"); + }); + mod.empty = new Sk.builtin.func(function () { + throw new Sk.builtin.NotImplementedError( + "empty is not yet implemented"); + }); + mod.arctan2 = new Sk.builtin.func(function () { + throw new Sk.builtin.NotImplementedError( + "arctan2 is not yet implemented"); + }); + mod.asarray = new Sk.builtin.func(array_f); + return mod; +}; diff --git a/src/lib/pprint.py b/src/lib/pprint.py index c9710bd35f..f852e95d51 100644 --- a/src/lib/pprint.py +++ b/src/lib/pprint.py @@ -1 +1,4 @@ -raise NotImplementedError("pprint is not yet implemented in Skulpt") +import json + +def pprint(obj, indent=1): + print(json.dumps(obj)) \ No newline at end of file diff --git a/src/lib/requests/__init__.js b/src/lib/requests/__init__.js new file mode 100644 index 0000000000..89cef1c41c --- /dev/null +++ b/src/lib/requests/__init__.js @@ -0,0 +1,171 @@ +var $builtinmodule = function (name) { + var request = {}; + + + //~ Classes ................................................................. + + // Response class + // + // Response objects are returned by the request, get, post, etc. + // methods, allowing the user to access the response text, status + // code, and other information. + + // ------------------------------------------------------------ + var response = function ($gbl, $loc) { + + // ------------------------------------------------------------ + $loc.__init__ = new Sk.builtin.func(function (self, data) { + self.data$ = data; + self.lineList = self.data$.split("\n"); + self.lineList = self.lineList.slice(0, -1); + for (var i = 0; i < self.lineList.length; i++) { + self.lineList[i] = self.lineList[i] + '\n'; + } + self.currentLine = 0; + self.pos$ = 0; + Sk.abstr.sattr(self, 'text', Sk.ffi.remapToPy(self.data$), true); + }); + + + // ------------------------------------------------------------ + $loc.__str__ = new Sk.builtin.func(function (self) { + return Sk.ffi.remapToPy(''); + }); + + $loc.__repr__ = $loc.__str__; + + // ------------------------------------------------------------ + $loc.__iter__ = new Sk.builtin.func(function (self) { + var allLines = self.lineList; + + return Sk.builtin.makeGenerator(function () { + if (this.$index >= this.$lines.length) { + return undefined; + } + return new Sk.builtin.str(this.$lines[this.$index++]); + }, { + $obj : self, + $index: 0, + $lines: allLines + }); + }); + + + // ------------------------------------------------------------ + $loc.read = new Sk.builtin.func(function (self, size) { + if (self.closed) { + throw new Sk.builtin.ValueError("I/O operation on closed file"); + } + var len = self.data$.length; + if (size === undefined) { + size = len; + } + var ret = new Sk.builtin.str(self.data$.substr(self.pos$, size)); + self.pos$ += size; + if (self.pos$ >= len) { + self.pos$ = len; + } + return ret; + }); + + + // ------------------------------------------------------------ + $loc.readline = new Sk.builtin.func(function (self, size) { + var line = ""; + if (self.currentLine < self.lineList.length) { + line = self.lineList[self.currentLine]; + self.currentLine++; + } + return new Sk.builtin.str(line); + }); + + + // ------------------------------------------------------------ + $loc.readlines = new Sk.builtin.func(function (self, sizehint) { + var arr = []; + for (var i = self.currentLine; i < self.lineList.length; i++) { + arr.push(new Sk.builtin.str(self.lineList[i])); + } + return new Sk.builtin.list(arr); + }); + + // ------------------------------------------------------------ + $loc.json = new Sk.builtin.func(function (self) { + return Sk.ffi.remapToPy(JSON.parse(self.data$)); + }); + + }; + + request.Response = + Sk.misceval.buildClass(request, response, 'Response', []); + + + //~ Module functions ........................................................ + + // ------------------------------------------------------------ + /** + * Constructs and sends a Request. Returns Response object. + * + * http://docs.python-requests.org/en/latest/api/#requests.request + * + * For now, this implementation doesn't actually construct a Request + * object; it just makes the request through jQuery.ajax and then + * constructs a Response. + */ + request.get = new Sk.builtin.func(function (url, data, timeout) { + var prom = new Promise(function(resolve, reject) { + if (Sk.requestsGet) { + Sk.requestsGet(Sk.ffi.remapToJs(url), data, timeout).then(function(result) { + resolve(Sk.misceval.callsim(request.Response, result)); + }, function(err) { + reject(err); + //resolve(Sk.misceval.callsim(request.Response, err)); + }); + } else { + var xmlhttp = new XMLHttpRequest(); + + xmlhttp.addEventListener("loadend", function (e) { + resolve(Sk.misceval.callsim(request.Response, xmlhttp.responseText)); + }); + + if (!data) { + xmlhttp.open("GET", url.v); + xmlhttp.send(null); + } else { + xmlhttp.open("POST", url.v); + xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + xmlhttp.setRequestHeader("Content-length", data.v.length); + xmlhttp.send(data.v); + } + } + }); + + var susp = new Sk.misceval.Suspension(); + + susp.resume = function() { + if (susp.data["error"]) { + //throw new Sk.builtin.IOError(susp.data["error"].message); + throw susp.data["error"]; + } else { + return resolution; + } + }; + + susp.data = { + type: "Sk.promise", + promise: prom.then(function(value) { + resolution = value; + return value; + }, function(err) { + resolution = ""; + throw err; + //return err; + }) + }; + + return susp; + }); + + + return request; +}; \ No newline at end of file diff --git a/src/lib/sound/__init__.js b/src/lib/sound/__init__.js new file mode 100644 index 0000000000..d0dbafc09a --- /dev/null +++ b/src/lib/sound/__init__.js @@ -0,0 +1,3 @@ +var $builtinmodule = function() { + return {}; +}; diff --git a/src/lib/sound/sample.js b/src/lib/sound/sample.js new file mode 100644 index 0000000000..83b1ac1796 --- /dev/null +++ b/src/lib/sound/sample.js @@ -0,0 +1,49 @@ +var $builtinmodule = function() { + var mod, sampleWrapper; + + mod = {}; + + sampleWrapper = { + getSound : new Sk.builtin.func(function (sample) { + Sk.builtin.pyCheckArgs('getSound', arguments, 1); + return sample._sound; + }), + + getSampleValue : new Sk.builtin.func(function (sample) { + Sk.builtin.pyCheckArgs('getSampleValue', arguments, 1); + return new Sk.builtin.float_(sample._internalSound.getLeftSample(sample._index)); + }), + + setSampleValue : new Sk.builtin.func(function (sample, value) { + Sk.builtin.pyCheckArgs('setSampleValue', arguments, 2); + sample._internalSound.setLeftSample(sample._index, Sk.ffi.unwrapo(value)); + }), + }; + + mod.Sample = Sk.misceval.buildClass(mod, function ($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self, sound, index) { + Sk.builtin.pyCheckArgs('__init__', arguments, 3); + self._sound = sound; + self._internalSound = sound._sound; + self._index = Sk.ffi.unwrapo(index); + }); + + $loc.__str__ = new Sk.builtin.func(function (self) { + Sk.builtin.pyCheckArgs('__str__', arguments, 1); + return new Sk.builtin.str('Sample at ' + self._index + ' with value ' + + self._internalSound.getLeftSample(self._index)); + }); + + $loc.__repr__ = new Sk.builtin.func(function (self) { + Sk.builtin.pyCheckArgs('__repr__', arguments, 1); + return new Sk.builtin.str('Sample at ' + self._index + ' with value ' + + self._internalSound.getLeftSample(self._index)); + }); + + goog.object.extend($loc, sampleWrapper); + }, 'Sample', []); + + goog.object.extend(mod, sampleWrapper); + + return mod; +}; diff --git a/src/lib/sound/sound.js b/src/lib/sound/sound.js new file mode 100644 index 0000000000..171a7c42bc --- /dev/null +++ b/src/lib/sound/sound.js @@ -0,0 +1,317 @@ +// Do not include this module directly as it has dependencies +var $builtinmodule = function() { + var soundWrapper, mod, Sample; + + mod = {}; + + // Dependency + Sample = Sk.sysmodules.mp$subscript('sound.sample').$d.Sample; + + soundWrapper = { + stopPlaying: new Sk.builtin.func(function (sound) { + Sk.builtin.pyCheckArgs('stopPlaying', arguments, 1); + sound._sound.stop(); + }), + + play : new Sk.builtin.func(function(sound) { + Sk.builtin.pyCheckArgs('play', arguments, 1); + sound._sound.play(); + }), + + blockingPlay : new Sk.builtin.func(function(sound) { + Sk.builtin.pyCheckArgs('blockingPlay', arguments, 1); + Sk.future(function (continueWith) { + sound._sound.play(continueWith); + }); + }), + + getDuration : new Sk.builtin.func(function(sound) { + Sk.builtin.pyCheckArgs('getDuration', arguments, 1); + return new Sk.builtin.float_(sound._sound.getDuration()); + }), + + getNumSamples : new Sk.builtin.func(function(sound) { + Sk.builtin.pyCheckArgs('getNumSamples', arguments, 1); + return new Sk.builtin.int_(sound._sound.getLength()); + }), + + getLength : new Sk.builtin.func(function(sound) { + Sk.builtin.pyCheckArgs('getLength', arguments, 1); + return new Sk.builtin.int_(sound._sound.getLength()); + }), + + getSamplingRate : new Sk.builtin.func(function(sound) { + Sk.builtin.pyCheckArgs('getSamplingRate', arguments, 1); + return new Sk.builtin.int_(sound._sound.getSamplingRate()); + }), + + setSampleValueAt : new Sk.builtin.func(function(sound, index, value) { + var length; + + Sk.builtin.pyCheckArgs('setSampleValueAt', arguments, 3); + + length = sound._sound.getLength(); + + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); + } + + if(!(value instanceof Sk.builtin.int_)) { + throw new Sk.builtin.TypeError('Value must be an integer'); + } + + value = Sk.ffi.unwrapo(value); + + if(value < -32768) { value = -32768; } + if(value > 32767) { value = 32767; } + + sound._sound.setLeftSample(Sk.ffi.unwrapo(index), pythy.Sound.map16BitIntToFloat(value)); + }), + + setLeftSample : new Sk.builtin.func(function(sound, index, value) { + var length; + + Sk.builtin.pyCheckArgs('setLeftSample', arguments, 3); + + length = sound._sound.getLength(); + + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); + } + + if(!(value instanceof Sk.builtin.int_)) { + throw new Sk.builtin.TypeError('Value must be an integer'); + } + + value = Sk.ffi.unwrapo(value); + + if(value < -32768) { value = -32768; } + if(value > 32767) { value = 32767; } + + sound._sound.setLeftSample(Sk.ffi.unwrapo(index), pythy.Sound.map16BitIntToFloat(value)); + }), + + setRightSample : new Sk.builtin.func(function(sound, index, value) { + var length; + + Sk.builtin.pyCheckArgs('setRightSample', arguments, 3); + + length = sound._sound.getLength(); + + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); + } + + if(!(value instanceof Sk.builtin.int_)) { + throw new Sk.builtin.TypeError('Value must be an integer'); + } + + value = Sk.ffi.unwrapo(value); + + if(value < -32768) { value = -32768; } + if(value > 32767) { value = 32767; } + + sound._sound.setRightSample(Sk.ffi.unwrapo(index), pythy.Sound.map16BitIntToFloat(value)); + }), + + getSampleValueAt : new Sk.builtin.func(function(sound, index) { + var length; + + Sk.builtin.pyCheckArgs('getSampleValueAt', arguments, 2); + + length = sound._sound.getLength(); + + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); + } + + return new Sk.builtin.int_(pythy.Sound.mapFloatTo16BitInt(sound._sound.getLeftSample(Sk.ffi.unwrapo(index)))); + }), + + getLeftSample : new Sk.builtin.func(function(sound, index) { + var length; + + Sk.builtin.pyCheckArgs('getLeftSample', arguments, 2); + + length = sound._sound.getLength(); + + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); + } + + return new Sk.builtin.int_(pythy.Sound.mapFloatTo16BitInt(sound._sound.getLeftSample(Sk.ffi.unwrapo(index)))); + }), + + getRightSample : new Sk.builtin.func(function(sound, index) { + var length; + + Sk.builtin.pyCheckArgs('getRightSample', arguments, 2); + + length = sound._sound.getLength(); + + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); + } + + return new Sk.builtin.int_(pythy.Sound.mapFloatTo16BitInt(sound._sound.getRightSample(Sk.ffi.unwrapo(index)))); + }), + + getSampleObjectAt : new Sk.builtin.func(function (sound, index) { + var length; + + Sk.builtin.pyCheckArgs('getSampleObjectAt', arguments, 2); + + length = sound._sound.getLength(); + + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); + } + + return Sk.misceval.callsim(Sample, sound, index); + }), + + getSamples : new Sk.builtin.func(function (sound) { + var samples, len; + + Sk.builtin.pyCheckArgs('getSamples', arguments, 1); + + samples = []; + len = sound._sound.getLength(); + + for(var i = 0; i < len; i++) { + samples.push(Sk.misceval.callsim(Sample, sound, Sk.builtin.int_(i))); + } + + return new Sk.builtin.list(samples); + }) + }; + + mod.Sound = Sk.misceval.buildClass(mod, function ($gbl, $loc) { + var onError; + + onError = function (continueWith) { + return function (errorMsg) { + if(errorMsg.indexOf('File') !== -1) { + continueWith(new Sk.builtin.ValueError(errorMsg + '. Is the URL incorrect?')); + } else { + continueWith(new Sk.builtin.ValueError(errorMsg)); + } + } + }; + + $loc.__init__ = new Sk.builtin.func(function (sound) { + var arg0, res, arg1, arg2; + + Sk.builtin.pyCheckArgs('__init__', arguments, [2, 3]); + + arg0 = arguments[1]; + + if(arg0 instanceof Sk.builtin.str) { + arg0 = Sk.ffi.unwrapo(arg0); //url + res = Sk.future(function (continueWith) { + new window.pythy.Sound(continueWith, onError(continueWith), arg0); + }); + } else if(arg0.tp$name === 'Sound') { + res = Sk.future(function (continueWith) { + new window.pythy.Sound(continueWith, onError(continueWith), arg0._sound); + }); + } else { + arg1 = Sk.ffi.unwrapo(arguments[1]); //numSamples + arg2 = Sk.ffi.unwrapo(arguments[2]); //samplingRate + res = Sk.future(function (continueWith) { + new window.pythy.Sound(continueWith, onError(continueWith), arg1, arg2); + }); + } + + if(res instanceof window.pythy.Sound) { + sound._sound = res; + } else if(res) { + throw res; + } + }); + + $loc.__str__ = new Sk.builtin.func(function(sound) { + var str; + + Sk.builtin.pyCheckArgs('__str__', arguments, 1); + + str = 'Sound, '; + + if(sound._sound.url) { + str += 'File: ' + sound._sound.url + ', '; + } + + return new Sk.builtin.str(str + 'Number of samples: ' + sound._sound.getLength()); + }); + + $loc.__repr__ = new Sk.builtin.func(function(sound) { + var str; + + Sk.builtin.pyCheckArgs('__repr__', arguments, 1); + + str = 'Sound, '; + + if(sound._sound.url) { + str += 'File: ' + sound._sound.url + ', '; + } + + return new Sk.builtin.str(str + 'Number of samples: ' + sound._sound.getLength()); + }); + + $loc.writeToFile = new Sk.builtin.func(function(sound, path) { + Sk.builtin.pyCheckArgs('writeToFile', arguments, 2); + sound._sound.save(Sk.ffi.unwrapo(path)); + }); + + $loc.duplicate = new Sk.builtin.func(function (sound) { + Sk.builtin.pyCheckArgs('duplicate', arguments, 1); + return Sk.misceval.callsim(mod.Sound, sound); + }); + + goog.object.extend($loc, soundWrapper); + + }, 'Sound', []); + + goog.object.extend(mod, soundWrapper); + + goog.object.extend(mod, { + duplicateSound: new Sk.builtin.func(function (sound) { + Sk.builtin.pyCheckArgs('duplicateSound', arguments, 1); + return Sk.misceval.callsim(mod.Sound, sound); + }), + + makeSound: new Sk.builtin.func(function (url) { + Sk.builtin.pyCheckArgs('makeSound', arguments, 1); + return Sk.misceval.callsim(mod.Sound, url); + }), + + makeEmptySound: new Sk.builtin.func(function (numSamples, samplingRate) { + Sk.builtin.pyCheckArgs('makeEmptySound', arguments, [1, 2]); + return Sk.misceval.callsim(mod.Sound, numSamples, samplingRate); + }), + + makeEmptySoundBySeconds: new Sk.builtin.func(function (seconds, samplingRate) { + var numSamples; + + Sk.builtin.pyCheckArgs('makeEmptySoundBySeconds', arguments, [1, 2]); + + if(Sk.ffi.unwrapo(seconds) < 0) { + throw new Sk.builtin.ValueError('Duration can not be negative'); + } + numSamples = Sk.ffi.unwrapo(seconds) * (Sk.ffi.unwrapo(samplingRate) || window.pythy.Sound.SAMPLE_RATE); + return Sk.misceval.callsim(mod.Sound, new Sk.builtin.int_(numSamples), samplingRate); + }), + + openSoundTool: new Sk.builtin.func(function (sound) { + Sk.builtin.pyCheckArgs('openSoundTool', arguments, 1); + window.pythy.soundTool.start(sound._sound); + }), + + writeSoundTo : new Sk.builtin.func(function(sound, path) { + Sk.builtin.pyCheckArgs('writeSoundTo', arguments, 2); + sound._sound.save(Sk.ffi.unwrapo(path)); + }) + }); + + return mod; +}; diff --git a/test/ast_example.py b/test/ast_example.py new file mode 100644 index 0000000000..c6eee8431c --- /dev/null +++ b/test/ast_example.py @@ -0,0 +1,32 @@ +class NodeVisitor(object): + """ + A node visitor base class that walks the abstract syntax tree and calls a + visitor function for every node found. This function may return a value + which is forwarded by the `visit` method. + This class is meant to be subclassed, with the subclass adding visitor + methods. + Per default the visitor functions for the nodes are ``'visit_'`` + + class name of the node. So a `TryFinally` node visit function would + be `visit_TryFinally`. This behavior can be changed by overriding + the `visit` method. If no visitor function exists for a node + (return value `None`) the `generic_visit` visitor is used instead. + Don't use the `NodeVisitor` if you want to apply changes to nodes during + traversing. For this a special visitor exists (`NodeTransformer`) that + allows modifications. + """ + + def visit(self, node): + """Visit a node.""" + method = 'visit_' + node.__class__.__name__ + visitor = getattr(self, method, self.generic_visit) + return visitor(node) + + def generic_visit(self, node): + """Called if no explicit visitor function exists for a node.""" + for field, value in iter_fields(node): + if isinstance(value, list): + for item in value: + if isinstance(item, AST): + self.visit(item) + elif isinstance(value, AST): + self.visit(value) \ No newline at end of file diff --git a/test/exec_test.py b/test/exec_test.py new file mode 100644 index 0000000000..089349ec23 --- /dev/null +++ b/test/exec_test.py @@ -0,0 +1,8 @@ +a = 3 +dummy_space = {'a': 5} +assert callable(execf) +execf('a=6', dummy_space) +assert a == 3, 'Make sure that the actual a is unchanged from 3' +assert dummy_space['a'] != 5, 'Make sure that the dummy a was not left at 5' +assert dummy_space['a'] == 6, 'Make sure that the dummy a is changed to 6' +print("All done") \ No newline at end of file diff --git a/test/mpl2.py b/test/mpl2.py new file mode 100644 index 0000000000..f40b248509 --- /dev/null +++ b/test/mpl2.py @@ -0,0 +1,27 @@ +import matplotlib.pyplot as plt +import random + + +xs = [random.randint(0, 100) for x in xrange(1000)] +ys = [random.randint(0, 100) for x in xrange(1000)] + +plt.scatter(xs, ys) +plt.show() + + +''' +plt.hist([1,1,1,3,3,5,8,8,10]) +plt.show() + +plt.plot([1,4,8,4,1]) +plt.plot([1,2,3,4,5], 'r') +plt.plot([1,2,3,4,5], [6,5,4,3,1]) +plt.xlabel("Time (Years)") +plt.ylabel("Distance (miles)") +plt.title("Change in distance over Time") +plt.show() + +plt.scatter([1,2,3,4,5], [6,5,4,3,1]) +plt.scatter([1,2,3,4,5], [1,2,3,4,5]) +plt.show() +''' diff --git a/test/test_ast.py b/test/test_ast.py new file mode 100644 index 0000000000..78eaa38e36 --- /dev/null +++ b/test/test_ast.py @@ -0,0 +1,87 @@ +import ast + +print(dir(ast)) + +addition = ast.parse("1 + 2") + +assert isinstance(addition, ast.Module), "Unable to parse addition" +assert hasattr(addition, 'body'), "Module has no body" +print(addition) +print(addition._fields) +print(addition.body) +assert addition.body[0].value.left.n == 1, "Parsed Addition wasn't correct" +print("lineno", addition.body[0].lineno) +assert addition.body[0].lineno == 1, "Addition's lineno was not 1" +assert addition.body[0].col_offset == 0, "Addition's col_offset was not 0" +assert addition.body[0].col_endoffset == 1, "Addition's col_endoffset was not 1" +assert addition.body[0].endlineno == 1, "Addition's endlineno was not 1" + +print("1 + 2") +#print(ast.dump(addition) + +comparison = ast.parse('1 < 1') +assert isinstance(comparison, ast.Module), "Unable to parse comparison" +assert isinstance(comparison.body[0].value, ast.Compare), "Could not access Compare object" +print(comparison.body[0].value.ops) +assert len(comparison.body[0].value.ops) == 1, "Did not retrieve operations from comparison" + +FOR_CODE = "for x in y:\n a = 0" +for_loop = ast.parse(FOR_CODE) +print("*"*20) +print(FOR_CODE) +print("FOR", for_loop, "should be Module") +print("FOR", for_loop.body[0], "should be For") +print("FOR", for_loop.body[0].body[0], "should be Assign") +#print("FOR", for_loop.body[0].body[0].id, "should not be a") +print(ast.dump(for_loop)) + + +multiline = ast.parse("""for x in y: + a + 1 - 3 +if x: + try: + a = 7 + except: + False +def T(): + while Banana(): + return 7 +class X(basic): + def define(apple, banana): + this.__init__(7, 3, 4) +'''HEY''' or (1 and 2) +assert EXPLODE() +one += one +one -= one +one | one +a[0] += 100 +5 < 3 +not True +del apple["Hearted"] +import garbage +8 is 7 +""") + +print("iter_fields:", ast.iter_fields(multiline.body[0])) + +print("iter_child_nodes:", ast.iter_child_nodes(multiline)) + +print("walk:", ast.walk(multiline)) + +print(ast.dump(multiline, True, False)) +print("*"*40) +class VisitStuff(ast.NodeVisitor): + def __init__(self): + self.nums = 0 + def visit_Num(self, node): + print("Found a ", node.n) + self.nums += 1 +vs = VisitStuff() +p = ast.parse('''a = 0\nprint(a+5) or 5''') +vs.generic_visit(p) +assert vs.nums == 3, "Did not find 3 nums, only: "+str(vs.nums) + + +print("All tests complete.") + +assert ast.In, "Could not load In ast element" \ No newline at end of file diff --git a/test/test_cait.py b/test/test_cait.py new file mode 100644 index 0000000000..4593c32aae --- /dev/null +++ b/test/test_cait.py @@ -0,0 +1,392 @@ +import unittest +import ast +import sys + +from pedal.cait.stretchy_tree_matching import * +from pedal.cait.easy_node import * +from pedal.source import set_source +from pedal.tifa import tifa_analysis +from pedal.report import MAIN_REPORT, clear_report +from pedal.cait.cait_api import * + +''' +_accu_ = 0 +_iList_ = [1,2,3,4] +for _item_ in _iList_: + _accu_ = _accu_ + _item_ +print(_accu_) +''' + + +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + + +def parse_code(student_code): + """ + parses code and returns a new EasyNode that is the root of the student + """ + return EasyNode(ast.parse(student_code)) + + +class CaitTests(unittest.TestCase): + + def setUp(self): + MAIN_REPORT.clear() + + def test_var_match(self): + # print("TESTING VAR MATCH") + # tests whether variables are stored correctly + test_tree = StretchyTreeMatcher("_accu_ = 0") + + # extracting instrutor name node + ins_ast = test_tree.rootNode + ins_name_node = ins_ast.children[0].children[0] + + # creating student code + student_code = "accumulator = 0" + std_ast = parse_code(student_code) + # extracting student name node + std_name_node = std_ast.children[0].children[0] + mapping = test_tree.shallow_match(ins_name_node, std_name_node) + self.assertTrue(mapping, "match not found") + + if mapping: + mapping = mapping[0] + self.assertTrue(mapping.mappings.keys[0].astNode == + ins_name_node.astNode, "ins node match not correct in _var_ case") + self.assertTrue(mapping.mappings.values[0].astNode == + std_name_node.astNode, "std node match not correct in _var_ case") + debug_print = False # TODO: debug flag + if debug_print: + print(test_tree) + print(ins_name_node) + print(std_name_node) + print(mapping) + + def test_expresssion_match(self): + # tests whether expressions are stored correctly + # print("TESTING EXPRESSION MATCH") + test_tree = StretchyTreeMatcher("__exp__ = 0") + + # extracting instrutor name node + ins_ast = test_tree.rootNode + ins_name_node = ins_ast.children[0].children[0] + + # creating student code + student_code = "accumulator = 0\nfor item in myList:\n accumulator += item" + std_ast = parse_code(student_code) + # extracting body node + std_for_loop = std_ast.children[1] + + mapping = test_tree.shallow_match(ins_name_node, std_for_loop, False) + self.assertTrue(mapping, "match not found") + if mapping: + mapping = mapping[0] + if mapping: + self.assertTrue(mapping.exp_table.keys[0] == "__exp__", "symbol match not found") + self.assertTrue(mapping.exp_table.values[0].astNode == std_for_loop.astNode, + "did not match to correct ast node") + debug_print = False # TODO: debug flag + if debug_print: + print(test_tree) + print(ins_name_node) + print(std_for_loop) + print(mapping) + + def test_wild_card_match(self): + # tests whether Wild matches are stored correctly + # print("TESTING WILD CARD MATCH") + test_tree = StretchyTreeMatcher("___ = 0") + + # extracting instrutor name node + ins_ast = test_tree.rootNode + ins_name_node = ins_ast.children[0].children[0] + + # creating student code + student_code = "accumulator = 0\nfor item in myList:\n accumulator += item\nfor item in myList:" \ + "\n accumulator += item" + std_ast = parse_code(student_code) + # extracting body node + std_for_loop = std_ast.children[1] + mapping = test_tree.shallow_match(ins_name_node, std_for_loop, False) + self.assertTrue(mapping, "match not found") + if mapping: + mapping = mapping[0] + self.assertTrue(mapping.mappings.keys[0].astNode == ins_name_node.astNode and + mapping.mappings.values[0].astNode == std_for_loop.astNode, "wild card didn't match") + debug_print = False # TODO: debug flag + if debug_print: + print(test_tree) + print(ins_name_node) + print(std_for_loop) + print(mapping) + + def test_match_all(self): + # print("TESTING MATCH ALL") + test_tree = StretchyTreeMatcher("___ = 0") + ins_ast = test_tree.rootNode + ins_num_node = ins_ast.children[0].children[1] + + # creating student code + student_code = "billy = 0" + std_ast = parse_code(student_code) + # extracting body node + std_num_node = std_ast.children[0].children[1] + + mapping = test_tree.shallow_match(ins_num_node, std_num_node) + self.assertTrue(mapping, "match not found") + if mapping: + mapping = mapping[0] + if mapping: + self.assertTrue(mapping.mappings.keys[0].astNode == ins_num_node.astNode, + "ins node not matched correctly") + self.assertTrue(mapping.mappings.values[0].astNode == std_num_node.astNode, + "student node not matched correctly") + debug_print = False # TODO: debug print + if debug_print: + print(test_tree) + print(std_num_node) + print(mapping) + + def test_generic_match(self): + # print("TESTING SAME ORDER") + std_code = "_sum = 0\n_list = [1,2,3,4]\nfor item in _list:\n _sum = _sum + item\nprint(_sum)" + ins_code = "_accu_ = 0\n_iList_ = __listInit__\nfor _item_ in _iList_:\n _accu_ = _accu_ + _item_\nprint(_accu_)" + # var std_code = "_sum = 12 + 13" + # var ins_code = "_accu_ = 12 + 11" + ins_tree = StretchyTreeMatcher(ins_code) + ins_ast = ins_tree.rootNode + std_ast = parse_code(std_code) + + mappings_array = ins_tree.find_matches(std_ast.astNode) + self.assertTrue(mappings_array, "matches not found") + if mappings_array: + mappings = mappings_array[0] + self.assertTrue( + mappings.mappings.size() == len(ins_ast.linear_tree) - 1, # -1 is because expression subsumes load + "incorrect number of mappings found {} instead of {}".format(mappings.mappings.size(), + len(ins_ast.linear_tree) - 1)) + self.assertTrue(mappings.symbol_table.size() == 3 and + len(mappings.symbol_table.values[0]) == 4 and + len(mappings.symbol_table.values[1]) == 2 and + len(mappings.symbol_table.values[2]) == 2, "inconsistent symbol matching") + debug_print = False # TODO: debug print + if debug_print: + print(mappings) + print(ins_ast.astNode) + print(std_ast.astNode) + + def test_many_to_one(self): + # print("TESTING MANY TO ONE") + std_code = "_sum = 0\nlist = [1,2,3,4]\nfor item in list:\n _sum = _sum + item\nprint(_sum)" + ins_code = "_accu_ = 0\n_iList_ = __listInit__\nfor _item_ in _iList_:\n _accu_ = _accu2_ + _item_\nprint(_accu_)" + ins_tree = StretchyTreeMatcher(ins_code) + ins_ast = ins_tree.rootNode + std_ast = parse_code(std_code) + + mappings = ins_tree.find_matches(std_ast.astNode) + self.assertTrue(mappings, "no matches found") + if mappings: + mappings = mappings[0] + if mappings: + self.assertTrue(len(mappings.conflict_keys) == 0, "Conflicting keys when there shouldn't be") + self.assertTrue( + mappings.symbol_table.size() == 4 and + len(mappings.symbol_table.values[0]) == 3 and + len(mappings.symbol_table.values[1]) == 2 and + len(mappings.symbol_table.values[2]) == 2 and + len(mappings.symbol_table.values[3]) == 1, "inconsistent symbol matching") + debug_print = False + if debug_print: + print(ins_ast.astNode) + print(std_ast.astNode) + print(mappings) + + def test_commutativity(self): + # print("TESTING COMMUTATIVITY ADDITION") + fail_count = 0 + success_count = 0 + std_code = "_sum = 0\nlist = [1,2,3,4]\nfor item in list:\n _sum = item + _sum\nprint(_sum)" + ins_code = "_accu_ = 0\n_iList_ = __listInit__\nfor _item_ in _iList_:\n _accu_ = _accu_ + _item_\nprint(_accu_)" + ins_tree = StretchyTreeMatcher(ins_code) + ins_ast = ins_tree.rootNode + std_ast = parse_code(std_code) + + mappings = ins_tree.find_matches(std_ast.astNode) + self.assertTrue(mappings, "mapping not found") + if mappings: + mappings = mappings[0] + self.assertTrue(mappings.conflict_keys != 0, "Conflicting keys in ADDITION when there shouldn't be") + debug_print = False # TODO: debug print + if debug_print: + print(ins_ast.astNode) + print(std_ast.astNode) + print(mappings) + + def test_one_to_many(self): + # print("TESTING ONE TO MANY") + std_code = "_sum = 0\nlist = [1,2,3,4]\nfor item in list:\n _sum = _sum + _sum\nprint(_sum)" + ins_code = "_accu_ = 0\n_iList_ = __listInit__\nfor _item_ in _iList_:\n _accu_ = _accu_ + _item_\nprint(_accu_)" + ins_tree = StretchyTreeMatcher(ins_code) + ins_ast = ins_tree.rootNode + std_ast = parse_code(std_code) + mappings = ins_tree.find_matches(std_ast.astNode) + self.assertFalse(mappings, "found match when match shouldn't be found") + debug_print = False # TODO: debug print + if debug_print: + print(ins_ast.astNode) + print(std_ast.astNode) + print(mappings) + + def test_multimatch_false(self): + # print("TESTING MULTI-MATCH") + # var std_code = "_sum = 0\ncount = 0\nlist = [1,2,3,4]\nfor item in list:\n _sum = _sum + item\n count" \ + # " = count + 1\nprint(_sum)" + std_code = "_sum = 0\ncount = 0\n_list = [1,2,3,4]\nfor item in _list:\n _sum = _sum + count\n count = _sum " \ + "+ count\nprint(_sum) " + ins_code = "_accu_ = 0\n_iList_ = __listInit__\nfor _item_ in _iList_:\n _accu_ = _accu_ + __exp__\nprint(_accu_)" + ins_tree = StretchyTreeMatcher(ins_code) + ins_ast = ins_tree.rootNode + std_ast = parse_code(std_code) + mappings = ins_tree.find_matches(std_ast.astNode) + self.assertFalse(len(mappings) > 1, "too many matchings found") + + debug_print = False + if debug_print: + print(ins_ast) + print(std_ast) + print(mappings) + + def test_multimatch_true(self): + std_code = "_sum = 0\ncount = 0\n_list = [1,2,3,4]\nfor item in _list:\n _sum = _sum + count\n count = _sum " \ + "+ count\nprint(_sum) " + ins_code = "_accu_ = 0\n_iList_ = __listInit__\nfor _item_ in _iList_:\n _accu_ = _accu_ + __exp__" + ins_tree = StretchyTreeMatcher(ins_code) + ins_ast = ins_tree.rootNode + std_ast = parse_code(std_code) + mappings = ins_tree.find_matches(std_ast.astNode) + self.assertTrue(len(mappings) >= 2, "Not enough mappings found") + debug_print = False + if debug_print: + print(ins_ast) + print(std_ast) + print(mappings) + + def test_pass(self): + # print("TESTING PASS MATCH") + std_code = 'import matplotlib.pyplot as plt\nquakes = [1,2,3,4]\nquakes_in_miles = []\nfor quakes in quakes:' \ + '\n quakes_in_miles.append(quake * 0.62)\nplt.hist(quakes_in_miles)\nplt.xlabel("Depth in Miles")' \ + '\nplt.ylabel("Number of Earthquakes")\nplt.title("Distribution of Depth in Miles of Earthquakes")' \ + '\nplt.show()' + ins_code = "for _item_ in _item_:\n pass" + ins_tree = StretchyTreeMatcher(ins_code) + ins_ast = ins_tree.rootNode + std_ast = parse_code(std_code) + mappings = ins_tree.find_matches(std_ast.astNode) + self.assertTrue(mappings, "mapping should have been found") + debug_print = False + if debug_print: + print(ins_ast) + print(std_ast) + print(mappings) + + def test_meta_stretch(self): + # print("TESTING META STRETCH") + std_code = ''.join([ + 'steps_hiked_list = [1,2,3,4]\n', + 'total = 0\n', + 'for steps_hiked in steps_hiked_list:\n', + ' total = steps_hiked + total\n', + ' steps = steps + 1']) + ins_code = "for ___ in ___:\n" + " ___ = _sum_ + ___" + ins_tree = StretchyTreeMatcher(ins_code) + ins_ast = ins_tree.rootNode + std_ast = parse_code(std_code) + mappings = ins_tree.find_matches(std_ast.astNode) + self.assertTrue(mappings, "mapping should have been found") + if mappings: + self.assertTrue(len(mappings) == 3, + "Should find exactly 3 matches, instead found {count}".format( + count=len(mappings).__str__())) + debug_print = False + if debug_print: + print(ins_ast) + print(std_ast) + print(mappings) + + def test_parse_program(self): + # noinspection PyBroadException + try: + parse_program() + self.assertTrue(False, "Program did not throw exception") + except Exception: + self.assertTrue(True, "Should NOT FAIL") + set_source("fun = 1 + 0") + parse_program() + self.assertTrue('cait' in MAIN_REPORT, "No parsing happened") + ''' + def test_def_use_error(self): + set_source("fun = fun + 1") + parse_program() + name_node = MAIN_REPORT["source"]["ast"].body[0].easy_node.target + self.assertTrue(def_use_error(name_node), "def_use error should have been found but wasn't") + self.assertTrue(def_use_error("fun"), "def_use error should have been found but wasn't") + self.assertFalse(def_use_error("gitax"), "variable doesn't exist") + + def test_data_type(self): + set_source("fun = 1 + 1") + parse_program() + name_node = MAIN_REPORT["source"]["ast"].body[0].easy_node.target + self.assertTrue(data_type(name_node).is_equal(int), "Data type not successfully found from name node") + self.assertTrue(data_type("fun").is_equal(int), "Data type not successfully found from str name")''' + + def test_find_match(self): + set_source("fun = 1 + 1") + parse_program() + match = find_match("_var_ = __expr__") + self.assertTrue(type(match) == AstMap, "Match not found") + + def test_find_matches(self): + set_source("fun = 1 + 1\nfun2 = 2 + 2") + parse_program() + matches = find_matches("_var_ = __expr__") + self.assertTrue(type(matches) == list, "find_matches did not return a list") + self.assertTrue(type(matches[0]) == AstMap, "find_matches does not contain an AstMap") + self.assertTrue(len(matches) == 2, "find_matches does not return the correct number of matches") + + def test_invalid_code(self): + set_source("float('0') + 1") + parse_program() + matches = find_match("_var_ = __expr__") + self.assertIsNone(matches) + + + def test_old_style_api(self): + set_source("a = open('file.txt')") + ast = parse_program() + calls = ast.find_all("Call") + self.assertEqual(len(calls), 1) + self.assertEqual(calls[0].func.ast_name, 'Name') + self.assertEqual(calls[0].func.id, 'open') + self.assertEqual(len(calls[0].args), 1) + + clear_report() + set_source("def a():\n pass\na()") + ast = parse_program() + defs = ast.find_all("FunctionDef") + self.assertEqual(len(defs), 1) + print(defs[0].body) + print(defs[0].name) + print(defs[0].args) + print(defs[0].__getattr__('name')) + self.assertEqual(defs[0]._name, "a") + print("SUCCESS") + + +ct = CaitTests() +for l in dir(ct)[:-2]: + if l.startswith('test'): + MAIN_REPORT.clear() + x = getattr(ct, l)() + \ No newline at end of file diff --git a/test/test_pedal.py b/test/test_pedal.py new file mode 100644 index 0000000000..af6862cd7a --- /dev/null +++ b/test/test_pedal.py @@ -0,0 +1,38 @@ +import time +stopwatch = time.time() +def click(phase): + global stopwatch + diff = time.time() - stopwatch + print("Phase {}: {} secs".format(phase, round(diff, 2))) + stopwatch = time.time() + +import pedal +click("Imported pedal") + +from pedal.source import set_source +click("Imported source") + +set_source("a = 0") +click("Set source") + +from pedal.tifa import tifa_analysis +click("Imported Tifa") + +tifa_analysis() +click("Ran Tifa") + +from pedal.cait import parse_program +click("Imported cait") + +ast = parse_program() +click("Parsed program") + +if ast.find_all("Assign"): + print(ast.find_all("Assign")) +click("Found assignments") + +from pedal.resolvers import simple +click("Imported resolver") + +print(simple.resolve()) +click("Resolved") \ No newline at end of file diff --git a/test/test_tifa.py b/test/test_tifa.py new file mode 100644 index 0000000000..8a97fbf869 --- /dev/null +++ b/test/test_tifa.py @@ -0,0 +1,333 @@ +from pedal.tifa.tifa import Tifa +import sys +import unittest + +unit_tests = { + # Source Code, Shouldn't catch this, Should catch this + 'builtin_True': ['print(True)', ['Initialization Problem'], []], + 'unread_variable': ['a = 0', [], ['Unused Variable']], + 'undefined_variable': ['print(a)', [], ['Initialization Problem']], + 'defined_read_variable': ['a = 0\nprint(a)', ['Initialization Problem'], []], + 'overwritten_variable': ['a = 0\na = 5', [], ['Overwritten Variable']], + 'unread_variables': ['a = 0\nb = 5', ['Overwritten Variable'], ['Unused Variable']], + 'wrwr_variable': ['a = [1]\nprint(a)\na = [1]\nprint(a)', [], []], + # Unconnected blocks + 'unconnected_assign': ['a = ___', [], ['Unconnected blocks']], + 'unconnected_print': ['print(___)', [], ['Unconnected blocks']], + + 'literal_in_call': ['print("dog" in input("test"))', [], []], + 'wrong_method_for_type': ['[].replace(",","")', [], []], + + # Double call + 'double_call': ['def x(a):\n return a\nx(5)\nx(3)', ['Read out of scope'], []], + + # Chained functions + 'chained_functions': ['def x():\n return 0\ndef y():\n x()\ny()', ['Read out of scope', 'Initialization Problem'], []], + + # String indexing and slicing + 'string_indexing_slicing': ['("a"[0] + ""[:])[:][0]', ['Incompatible types'], []], + # List indexing and slicing + 'list_indexing_slicing': ['([0][0] + [1,2,3][:][2])', ['Incompatible types'], []], + + 'returned_string': + ['def pluralize(a_word):\n return a_word+"s"\nnoun = pluralize("Dog")\nprint(noun + " can pet other " + noun)', ['Incompatible types'], []], + 'update_without_read': + ['a = 0\na+= 1', ['Initialization Problem'], ['Unused Variable']], + 'update_and_read': + ['a = 0\na+= 1\nprint(a)', ['Initialization Problem', 'Unused Variable'], []], + 'iterate_through_non_existing_list': + ['for x in y:\n\tpass', ['Unused Variable'], ['Initialization Problem']], + 'iterate_through_list': + ['y = [1,2,3]\nfor x in y:\n\tpass', ['Unused Variable', 'Initialization Problem'], []], + 'iterate_through_empty_list': + ['y = []\nfor x in y:\n\tpass', ['Unused Variable', 'Initialization Problem'], ['Iterating over empty list']], + 'double_iterate_through_strings': + ['ss = ["Testing", "Here"]\nfor a in ss:\n print(a)\nfor b in a:\n print(b)', ['Iterating over non-list', 'Iterating over empty list'], []], + 'iterate_through_number': + ['y = 5\nfor x in y:\n\tpass', ['Unused Variable', 'Initialization Problem'], ['Iterating over non-list']], + 'iterate_over_iteration_variable': + ['y = [1,2,3]\nfor y in y:\n\tpass', [], ['Iteration Problem']], + 'type_change': + ['a = 0\nprint(a)\na="T"\nprint(a)', [], ['Type changes']], + 'defined_in_if_root_but_not_other': + ['if True:\n\ta = 0\nprint(a)', [], ['Possible Initialization Problem']], + 'defined_in_both_branches': + ['if True:\n\ta = 0\nelse:\n\ta = 1\nprint(a)', ['Possible Initialization Problem'], []], + 'defined_in_else_root_but_not_other': + ['if True:\n\tpass\nelse:\n\ta = 1\nprint(a)', [], ['Possible Initialization Problem']], + 'defined_in_if_branch_but_others': + ['if True:\n\tif False:\n\t\ta = 0\nprint(a)', [], ['Possible Initialization Problem']], + 'defined_before_if_branch_but_not_others': + ['if True:\n\ta = 0\nif False:\t\tpass\nprint(a)', [], ['Possible Initialization Problem']], + 'defined_after_if_branch_but_not_others': + ['if True:\n\tif False:\n\t\tpass\n\ta = 0\nprint(a)', [], ['Possible Initialization Problem']], + 'defined_within_both_if_branches_but_not_others': + ['if True:\n\tif False:\n\t\ta=0\n\telse:\n\t\ta = 0\nprint(a)', [], ['Possible Initialization Problem']], + 'defined_in_all_branches': + ['if True:\n\tif False:\n\t\ta=0\n\telse:\n\t\ta = 0\nelse:\n\ta=3\nprint(a)', ['Possible Initialization Problem'], []], + 'read_in_if_branch_but_unset': + ['if True:\n\tprint(a)', [], ['Initialization Problem']], + 'read_in_else_branch_but_unset': + ['if True:\n\tpass\nelse:\n\tprint(a)', [], ['Initialization Problem']], + 'read_in_both_branches_but_unset': + ['if True:\n\tprint(a)\nelse:\n\tprint(a)', [], ['Initialization Problem']], + 'overwritten_in_both_branches': + ['a = 0\nif True:\n\ta = 0\nelse:\n\ta = 1', [], ['Overwritten Variable']], + 'overwritten_in_one_branch': + ['a = 0\nif True:\n\tpass\nelse:\n\ta = 1', ['Overwritten Variable'], []], + 'overwritten_in_inner_branch': + ['a = 0\nif True:\n\tif False:\n\t\ta = 0\nelse:\n\ta = 1', ['Overwritten Variable'], []], + 'overwritten_in_all_branch': + ['a = 0\nif True:\n\tif False:\n\t\ta = 0\n\telse:\n\t\ta = 2\nelse:\n\ta = 1', [], ['Overwritten Variable']], + 'overwritten_in_all_branches2': + ['a = 0\nif True:\n\tprint(a)\n\tif False:\n\t\ta = 0\n\telse:\n\t\ta = 2\nelse:\n\ta = 1', ['Overwritten Variable'], []], + + # Iterating over the result of a builtin + 'print_range': + ['x = range(100)\nprint(x)', ['Iterating over non-list'], []], + 'iterate_range': + ['x = range(100)\nfor y in x:\n print(y)', ['Iterating over non-list'], []], + 'iterate_over_ranges_atomic_subtype': + ['x = range(100)\nfor y in x:\n pass\nfor z in y:\n print(z)', [], ['Iterating over non-list']], + 'iterate_over_split': + ['for x in "a,b,c".split(","):\n x+""', ['Iterating over non-list', 'Iterating over empty list'], []], + 'iterate_over_string_upper': + ['for l in "abc".upper():\n l+""', ['Iterating over non-list', 'Iterating over empty list'], []], + + # Incompatible types + 'add_int_str': + ['a = 5 + "ERROR"', [], ['Incompatible types']], + 'multiply_str_int': + ['a = "ERROR" * 5', ['Incompatible types'], []], + 'unary_and_sub_int_int': + ['-(5)+0', ['Incompatible types'], []], + 'iadd_int_int': + ['a=0\na+=5\na', ['Incompatible types', 'Unused Variable', 'Initialization Problem', 'Overwritten Variable'], []], + 'iadd_str_int': + ['a=""\na+=5\na', ['Unused Variable', 'Initialization Problem', 'Overwritten Variable'], ['Incompatible types']], + 'iadd_undefined': + ['a+=5\na', ['Unused Variable', 'Overwritten Variable'], ['Initialization Problem']], + 'iadd_unread': + ['a=0\na+=5', ['Initialization Problem', 'Overwritten Variable'], ['Unused Variable']], + + # Lambda + 'lambda_add': + ['a = lambda: 0\nb=a()\nb+5', ['Incompatible types'], []], + + # Handle function definitions + 'uncalled_function': + ['def named(x):\n\tprint(x)\n', ['Initialization Problem'], ['Unused Variable']], + 'called_int_function': + ['def int_func(x):\n\treturn 5\nint_func(10)', [], []], + 'called_constant_function': + ['def x():\n return 4\nx()', ['Unused Variable'], []], + # Actions after returning + 'return_after_return': + ['def x():\n return 5\n return 4\nx()', [], ['Action after return']], + 'action_after_return_on_both_branches': + ['def x():\n if True:\n return 4\n else:\n return 3\n a = 0\n print(a)\nx()', [], ['Action after return']], + # Function with subtypes + 'function_with_subtypes_add_int_list_int': + ['def add_first(a_list):\n for element in a_list:\n return element + 5\nprint(add_first([1]))', ['Incompatible types'], []], + 'function_with_subtypes_add_int_list_str': + ['def add_first(a_list):\n for element in a_list:\n return element + 5\nprint(add_first(["1"]))', [], ['Incompatible types']], + 'function_with_subtypes_add_int_primitive_int': + ['def add_first(a_list):\n for element in a_list:\n return element + 5\nprint(add_first(1))', [], ['Iterating over non-list']], + 'function_with_subtypes_add_int_primitive_str': + ['def add_first(a_list):\n for element in a_list:\n return element + 5\nprint(add_first("1"))', [], ['Incompatible types']], + # Out of scope + 'read_out_of_scope': + ['def x(parameter):\n return parameter\nx(0)\nparameter', [], ['Read out of scope']], + 'read_inside_of_scope': + ['def x(parameter):\n return parameter\nx(0)', ['Read out of scope'], []], + 'read_not_out_of_scope': + ['def x():\n if 1:\n y=0\n else:\n y=1\n y\nx()\nx()', ['Read out of scope'], []], + + 'append_to_empty_list': + ['a = []\na.append(1)\nprint(a)', ['Initialization Problem', 'Unused Variable'], []], + 'append_to_non_empty_list': + ['a = [1]\na.append(1)\nprint(a)', ['Initialization Problem', 'Unused Variable'], []], + 'append_to_undefined': + ['a.append(1)\nprint(a)', ['Unused Variable'], ['Initialization Problem']], + 'append_to_unread': + ['a=[]\na.append(1)', ['Initialization Problem'], ['Unused Variable']], + 'append_to_number': + ['a=1\na.append(1)\nprint(a)', [], ['Append to non-list']], + + 'append_and_index': + ['x=[]\nx.append(1)\nx[0]+1', ['Incompatible types'], []], + 'indexing_used': + ['mag1 = 0\nmercalli = [0]\nmag1 = mercalli[mag1]\nmag1', ['Initialization Problem', 'Unused Variable', 'Overwritten Variable'], []], + + 'created_list_but_unread': + ['old = [1,2,3]\nnew=[]\nfor x in old:\n\tnew.append(x)', [], ['Unused Variable']], + 'created_list_but_undefined': + ['old = [1,2,3]\nfor x in old:\n\tnew.append(x)\nprint(new)', [], ['Initialization Problem']], + + 'builtin_float_converter': + ['a = float(5)\nb = "test"\nprint(a+b)', [], ['Incompatible types']], + + # Double iteration + 'iterate_over_list_of_tuples': + ['for x,y in [(1,2), (3,4)]:\n x, y', ['Initialization Problem'], []], + 'iterate_over_items': + ['record = {"A": 5, "B": 6}\nfor x,y in record.items():\n x, y', ['Initialization Problem'], []], + 'iterate_over_items_add': + ['record = {"A": 5, "B": 6}\nfor x,y in record.items():\n x+"", y+0', ['Initialization Problem', "Incompatible types"], []], + + # Tuple, Multiple Assignment + 'multiple_assignment': ['a,b = 1,2\n1+a\nb', ['Incompatible types'], []], + 'tuple_index': ['tuple_box = (6, 8, 4)\nprint(tuple_box[0])', [], []], + + # Sets + 'set_creation': ['a = set([1,2,3])\nprint(a)', ['Initialization Problem'], []], + + # Dictionaries + 'set_key_in_dict': ['a = {}\na[1] = 0', [], []], + 'use_key_in_dict': ['a = {"x": 5, "y": "T"}\na["x"]+5', ['Incompatible types'], []], + 'use_key_in_lod': ['x=[{"a": 0, "b": True}, {"a": 1, "b": False}]\ny=x[0]\nz=y["a"]+0', ['Incompatible types'], []], + 'use_chained_key_in_lod': ['x=[{"a": 0, "b": True}, {"a": 1, "b": False}]\nnot x[1]["b"]', ['Incompatible types'], []], + 'iterate_over_lod': ['ls=[{"a": 0, "b": True}, {"a": 1, "b": False}]\nfor x in ls:\n x["a"]+0', ['Incompatible types'], []], + # TODO: Add stronger assertion this one, it shouldn't be a good one + 'incorrect_dict_iteration': ['dict = {"T": "V"}\nfor key,value in dict:\n print(key)', [], []], + 'incorrect_dict_iteration2': ['dict = {"T": 0}\nfor i in dict:\n print(i, dict[i])', [], []], + + # While + 'while_until_input': ['user = input("Give a word.")\nwhile user:\n print(user)\n user = input("Give another word.")', ['Unused Variable'], []], + + # With + 'with_open': + ['with open("A") as a:\n print(a)', ['Initialization Problem'], []], + + # List comprehensions + 'list_comprehension': + ['a = [5 for x in range(100)]\nfor i in a:\n 5+i', ['Iterating over non-list', 'Incompatible types'], []], + + # Return outside function + 'no_return_outside_function': + ['def x():\n return 5\nx()', ['Return outside function'], []], + 'return_outside_function': + ['def x():\n pass\nreturn 5\nx()', [], ['Return outside function']], + + # Classes + 'class_definition': + ['class A:\n y = 0\n def __init__(self, x):\n self.x = 0\n self.test()\n def test(self):\n self.x = 5\nA()', [], []], + + # Mutable Types + 'mutable_list_in_function': + ['def t():\n x = []\n x.append(1)\n return x\nfor x in t():\n x + 1', ['Incompatible types'], []], + # Importing + 'import_matplotlib': + ['import matplotlib.pyplot as plt\nplt.hist([1,2,3])\nplt.show()', ['Initialization Problem'], []], + 'import_random': + ['from random import randint\na=randint(1,2)\n1+a', ['Initialization Problem', 'Incompatible types'], []], + + 'import_state_demographics': + ["import state_demographics\n\n\nincome_list = state_demographics.get(\"Per Capita Income\",\"(None)\",'')\nfilter_income = []\nfor income in income_list:\n if income > 28000:\n income_list.append(filter_income)\nprint(filter_income)\n", [], []], + 'import_state_demographics2': + ["import state_demographics\n\n\nincome_list = state_demographics.get(\"Per Capita Income\",\"(None)\",'')\nnew_income_list = 0\nfor income in income_list:\n if income > 28000:\n new_income_list = new_income_list + 1\nprint(new_income_list)\n", [], []], + 'filter_pattern': + ['l = []\nfor x in l:\n if x > 0:\n x', [], []], + 'append_iter_var_to_list': + ['x = []\nx.append(x)\nx', [], []], + 'function_with_parameter': + ['def x(y):\n y\nx()', [], []], + 'function_returns_None': + ['def x():\n return\nx()', [], []], + 'mutually_recursive_call': + ['def y():\n x()\ndef x():\n y()\nx()', [], ['Recursive Call']], + 'recursive_call': + ['def x():\n x()\nx()', [], ['Recursive Call']], + 'overwritten_double_nested_branch': + ['b= 0\nif True:\n if True:\n b=0\nb', ['Possible Initialization Problem'], []], + # Overwritten in one branches + 'overwritten_in_one_branch': + ['a = 0\nif True:\n\ta = 1\na', ['Possible Initialization Problem'], []], + 'filter_pattern2': + ["t = 0\nfor x in []:\n if x:\n t = t + 1\nprint(t)", ['Possible Initialization Problem'], []], + 'read_out_scope2': + ["x = ''\ndef y():\n return x\ny()", ['Unused Variable'], []], + + 'read_out_scope_double_branch': + ["def x():\n if True:\n y=0\n else:\n y=1\n y\nx()", + ['Unused Variable', 'Read out of scope'], []], + + # Calling functions from within functions + 'call_function_within_function': + ['def z():\n return b\ndef y():\n b = 0\n z()\n return b\ndef x():\n y()\nx()', ['Unused Variable'], ['Read out of scope']], + + # While loop with possibly unused body + 'while_body_and_conditional_uses_variable': + ['a = 10\nwhile a:\n a -= 1', ['Unused Variable'], []], + 'while_body_uses_variable': + ['a = 10\nwhile True:\n a -= 1', [], ['Unused Variable']], + 'while_body_possibly_defines_variable': + ['while True:\n a=0\na', [], ['Possible Initialization Problem']], + 'while_body_defined_variable': + ['a=0\nwhile True:\n a=0\na', ['Possible Initialization Problem'], []], + + # Generators + 'add_list_to_list_comprehension': + ['[1]+[a for a in [1,2,3]]', ['Unused Variable', "Incompatible types"], []], + 'add_set_to_set_comprehension': + ['{4}+{a for a in [1,2,3]}', ['Unused Variable', "Incompatible types"], []], + 'int_membership_in_dictionary': + ['3 in {a:a for a in [1,2,3]}', ['Unused Variable', "Incompatible types"], []], + 'int_membership_in_comprehension': + ['4 in (a for a in [1,2,3])', ['Unused Variable', "Incompatible types"], []], + + 'prevent_empty_iteration_in_appended_list': + ['eles = [1,2,3]\nx = []\nfor ele in eles:\n x.append(ele)\nfor e2 in x:\n e2+1', + ['Iterating over empty list'], []], + + 'prevent_empty_iteration_dict': + ['x={"A":5}\nfor y in x:\n y', ['Iterating over empty list'], []], + + # Built-in modules + 'import_string_letters': + ['import string\nstring.letters+""', ['Incompatible types'], []] +} + +class TestCode(unittest.TestCase): + pass + +SILENCE_EXCEPT = None +class o: + def fail(s, message): + print(message) +self = o() + +def make_tester(name, code, nones, somes): + def test_code(self): + tifa = Tifa(False) + try: + tifa.process_code(code) + except Exception as e: + raise type(e)(str(e) + + ' in code:\n%s' % code) + if not tifa.report['tifa']['success']: + self.fail(name+": Error message in\n"+code+"\n"+str(tifa.report['tifa']['error'])) + for none in nones: + if tifa.report['tifa']['issues'].get(none, []): + self.fail(name+": Incorrectly detected "+none+"\n"+code+"\n") + for some in somes: + if not tifa.report['tifa']['issues'].get(some, []): + self.fail(name+": Failed to detect "+some+"\n"+code+"\n") + return test_code +for name, (code, nones, somes) in unit_tests.items(): + if SILENCE_EXCEPT is None or SILENCE_EXCEPT == name: + #setattr(TestCode, 'test_code_{:02}'.format(i), make_tester(code, nones, somes)) + make_tester(name, code, nones, somes)(self) + +#if __name__ == '__main__': + #unittest.main() +tifa = Tifa(False) +tifa.process_code(''' +a = 0 +for y in [1,2,3]: + print(a + y) +with open("lacrimosa.txt") as inp: + for line in inp: + print(line.strip()) +''') +print(tifa.report) \ No newline at end of file From 5510ab5c7219083b12dfb7658770af419245714f Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 20 Jun 2019 08:10:19 -0400 Subject: [PATCH 08/68] Make linter happy for new files --- src/errors.js | 5 +++-- src/import.js | 4 ++-- src/parser.js | 8 ++++---- src/print.js | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/errors.js b/src/errors.js index 391c142b89..051ad4f662 100644 --- a/src/errors.js +++ b/src/errors.js @@ -551,14 +551,15 @@ Sk.builtin.traceback = function(err) { lineno = err.traceback[0].lineno; } - this.tb_lineno = new Sk.builtin.int_(lineno) + this.tb_lineno = new Sk.builtin.int_(lineno); //tb_frame, tb_lasti, tb_lineno, tb_next this.__class__ = Sk.builtin.traceback; return this; -} +}; + Sk.abstr.setUpInheritance("traceback", Sk.builtin.traceback, Sk.builtin.object); Sk.builtin.traceback.prototype.tp$getattr = function (name) { if (name != null && (Sk.builtin.checkString(name) || typeof name === "string")) { diff --git a/src/import.js b/src/import.js index af86140072..bed0e3a166 100644 --- a/src/import.js +++ b/src/import.js @@ -161,8 +161,8 @@ Sk.importSetUpPath = function (canSuspend) { Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, relativeToPackage, returnUndefinedOnTopLevelNotFound, canSuspend) { //dumpJS = true; /* TODO: temporary hack, need to delete! */ - if (name == 'pedal.sandbox.sandbox') { - suppliedPyBody= 'class Sandbox: pass\ndef run(): pass\ndef reset(): pass'; + if (name == "pedal.sandbox.sandbox") { + suppliedPyBody= "class Sandbox: pass\ndef run(): pass\ndef reset(): pass"; } /* End hack */ var filename; diff --git a/src/parser.js b/src/parser.js index dffc75d3a3..f7b7e15012 100644 --- a/src/parser.js +++ b/src/parser.js @@ -287,10 +287,10 @@ function makeParser (filename, style) { } Sk.parseCache = { - 'lastInput': null, - 'lastParse': null, - 'lastUnit': null -} + "lastInput": null, + "lastParse": null, + "lastUnit": null +}; Sk.parse = function parse (filename, input) { if (Sk.parseCache.lastInput == input) { diff --git a/src/print.js b/src/print.js index 54d678ac31..f179bc58a3 100644 --- a/src/print.js +++ b/src/print.js @@ -81,10 +81,10 @@ var print_f = function function_print(kwa) { }; print_f.co_kwargs = true; -print_f.co_name = new Sk.builtin.str('print'); +print_f.co_name = new Sk.builtin.str("print"); Sk.builtin.print = new Sk.builtin.func(print_f); Sk.builtin.print.__doc__ = new Sk.builtin.str("print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n\nPrints the values to a stream, or to sys.stdout by default.\nOptional keyword arguments:\nfile: a file-like object (stream); defaults to the current sys.stdout.\nsep: string inserted between values, default a space.\nend: string appended after the last value, default a newline.\nflush: whether to forcibly flush the stream."); -Sk.builtin.input.co_name = new Sk.builtin.str('input'); +Sk.builtin.input.co_name = new Sk.builtin.str("input"); From 0822fbb84d8cae61e4c16a30c0a3e904a0d71936 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 20 Jun 2019 09:38:00 -0400 Subject: [PATCH 09/68] Minor ast/parser fixes, attempts to fix webpack config --- src/ast.js | 8 +++++--- src/parser.js | 2 +- webpack.config.js | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ast.js b/src/ast.js index 05de9dfab7..494a5909e5 100644 --- a/src/ast.js +++ b/src/ast.js @@ -3152,8 +3152,9 @@ Sk.astDump = function (node) { return _format(node, ""); }; -/* + Sk.INHERITANCE_MAP = { +/* 'mod': [Module, Interactive, Expression, Suite], 'stmt': [FunctionDef, ClassDef, Return_, Delete_, Assign, AugAssign, @@ -3177,8 +3178,9 @@ Sk.INHERITANCE_MAP = { 'arguments_': [], 'keyword': [], 'alias': [] -};*/ +*/ +}; Sk.exportSymbol("Sk.astFromParse", Sk.astFromParse); Sk.exportSymbol("Sk.astDump", Sk.astDump); -goog.exportSymbol("Sk.INHERITANCE_MAP", Sk.INHERITANCE_MAP); +Sk.exportSymbol("Sk.INHERITANCE_MAP", Sk.INHERITANCE_MAP); diff --git a/src/parser.js b/src/parser.js index f7b7e15012..0a58d1ad65 100644 --- a/src/parser.js +++ b/src/parser.js @@ -345,7 +345,7 @@ Sk.parse = function parse (filename, input) { column = 0; } if (type === T_COMMENT) { - p.addcomment(value, tokenInfo.start, tokenInfo.end, tokenInfo.line); + parser.addcomment(tokenInfo.string, tokenInfo.start, tokenInfo.end, tokenInfo.line); } } else { if (tokenInfo.type === T_OP) { diff --git a/webpack.config.js b/webpack.config.js index 4941eb0ba0..2077a2d20b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -68,6 +68,7 @@ module.exports = (env, argv) => { filename: outfile }, devtool: 'source-map', + module: mod, plugins: [ new CleanWebpackPlugin(), new CopyWebpackPlugin([ @@ -89,8 +90,7 @@ module.exports = (env, argv) => { alias: { 'assert': assertfile } - }, - module: mod + } }; return config; From b996de737b31cf79073aa07e5142101f68fa35fc Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 1 Jul 2019 11:49:08 -0400 Subject: [PATCH 10/68] Fixed AST parser module --- example/calling_from_js-dev.html | 41 ++++++++++ example/parser_test.html | 22 ++++++ example/simple.html | 34 +++++++++ package.json | 5 +- src/ast.js | 124 +++++++++++++++++++++++++------ src/lib/ast.js | 36 +++++---- src/lib/ast.py | 1 - support/run/runfile.js | 12 ++- test/astppdump.py | 2 +- test/test_ast.py | 6 +- 10 files changed, 239 insertions(+), 44 deletions(-) create mode 100644 example/calling_from_js-dev.html create mode 100644 example/parser_test.html create mode 100644 example/simple.html delete mode 100644 src/lib/ast.py diff --git a/example/calling_from_js-dev.html b/example/calling_from_js-dev.html new file mode 100644 index 0000000000..baacd24ffa --- /dev/null +++ b/example/calling_from_js-dev.html @@ -0,0 +1,41 @@ + + + +
+
+ +
+ +

diff --git a/example/parser_test.html b/example/parser_test.html
new file mode 100644
index 0000000000..fd2d5bf4db
--- /dev/null
+++ b/example/parser_test.html
@@ -0,0 +1,22 @@
+
+  
+    
+    
+    
+  
+
+  
+
+
+
+
+ + + diff --git a/example/simple.html b/example/simple.html new file mode 100644 index 0000000000..ee3a6409c4 --- /dev/null +++ b/example/simple.html @@ -0,0 +1,34 @@ + + + + + + + + + + + +
+ + + diff --git a/package.json b/package.json index b8fb55bb00..2340b3d08b 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "test": "node test/testwrapper.js && node test/testunit.js && node test/testunit.js --python3", "start": "node support/run/runfile.js", "profile": "node --prof --no-logfile-per-isolate --log-internal-timer-events support/run/runfile.js -o", - "postprofile": "node --prof-process v8.log" + "postprofile": "node --prof-process v8.log", + "standaloneparser": "webpack src/standalone_parser.js -o dist/python_parser.js" }, "repository": { "type": "git", @@ -66,7 +67,7 @@ "shelljs": "^0.8.3", "strftime": "^0.10.0", "webpack": "^4.32.0", - "webpack-cli": "^3.3.2" + "webpack-cli": "^3.3.4" }, "engines": { "node": ">=8.10" diff --git a/src/ast.js b/src/ast.js index 494a5909e5..229db144ff 100644 --- a/src/ast.js +++ b/src/ast.js @@ -3154,31 +3154,111 @@ Sk.astDump = function (node) { Sk.INHERITANCE_MAP = { -/* - 'mod': [Module, Interactive, Expression, Suite], - 'stmt': [FunctionDef, ClassDef, Return_, - Delete_, Assign, AugAssign, - For_, While_, If_, With_, - Raise, TryExcept, TryFinally, Assert, - Import_, ImportFrom, Exec, Global, Expr, - Pass, Break_, Continue_, Debugger_], - 'expr': [BoolOp, BinOp, UnaryOp, Lambda, IfExp, - Dict, Set, ListComp, SetComp, DictComp, - GeneratorExp, Yield, Compare, Call, Repr, - Num, Str, Attribute, Subscript, Name, List, Tuple], - 'expr_context': [Load, Store, Del, AugLoad, AugStore, Param], - 'slice': [Ellipsis, Slice, ExtSlice, Index], - 'boolop': [And, Or], - 'operator': [Add, Sub, Mult, Div, Mod, Pow, LShift, - RShift, BitOr, BitXor, BitAnd, FloorDiv], - 'unaryop': [Invert, Not, UAdd, USub], - 'cmpop': [Eq, NotEq, Lt, LtE, Gt, GtE, Is, IsNot, In_, NotIn], + 'mod': [Sk.astnodes.Module, + Sk.astnodes.Interactive, + Sk.astnodes.Expression, + Sk.astnodes.Suite], + 'stmt': [Sk.astnodes.FunctionDef, + Sk.astnodes.AsyncFunctionDef, + Sk.astnodes.ClassDef, + Sk.astnodes.Return, + Sk.astnodes.Delete, + Sk.astnodes.Assign, + Sk.astnodes.AugAssign, + Sk.astnodes.AnnAssign, + Sk.astnodes.For, + Sk.astnodes.AsyncFor, + Sk.astnodes.While, + Sk.astnodes.If, + Sk.astnodes.With, + Sk.astnodes.AsyncWith, + Sk.astnodes.Raise, + Sk.astnodes.Try, + Sk.astnodes.Assert, + Sk.astnodes.Import, + Sk.astnodes.ImportFrom, + Sk.astnodes.Global, + Sk.astnodes.Nonlocal, + Sk.astnodes.Expr, + Sk.astnodes.Pass, + Sk.astnodes.Break, + Sk.astnodes.Continue, + Sk.astnodes.Print, + Sk.astnodes.Debugger], + 'expr': [Sk.astnodes.BoolOp, + Sk.astnodes.BinOp, + Sk.astnodes.UnaryOp, + Sk.astnodes.Lambda, + Sk.astnodes.IfExp, + Sk.astnodes.Dict, + Sk.astnodes.Set, + Sk.astnodes.ListComp, + Sk.astnodes.SetComp, + Sk.astnodes.DictComp, + Sk.astnodes.GeneratorExp, + Sk.astnodes.Await, + Sk.astnodes.Yield, + Sk.astnodes.YieldFrom, + Sk.astnodes.Compare, + Sk.astnodes.Call, + Sk.astnodes.Num, + Sk.astnodes.Str, + Sk.astnodes.FormattedValue, + Sk.astnodes.JoinedStr, + Sk.astnodes.Bytes, + Sk.astnodes.Ellipsis, + Sk.astnodes.NameConstant, + Sk.astnodes.Constant, + Sk.astnodes.Attribute, + Sk.astnodes.Subscript, + Sk.astnodes.Starred, + Sk.astnodes.Name, + Sk.astnodes.List, + Sk.astnodes.Tuple], + 'expr_context': [Sk.astnodes.Load, + Sk.astnodes.Store, + Sk.astnodes.Del, + Sk.astnodes.AugLoad, + Sk.astnodes.AugStore, + Sk.astnodes.Param], + 'slice': [Sk.astnodes.Slice, + Sk.astnodes.ExtSlice, + Sk.astnodes.Index], + 'boolop': [Sk.astnodes.And, Sk.astnodes.Or], + 'operator': [Sk.astnodes.Add, + Sk.astnodes.Sub, + Sk.astnodes.Mult, + Sk.astnodes.MatMult, + Sk.astnodes.Div, + Sk.astnodes.Mod, + Sk.astnodes.Pow, + Sk.astnodes.LShift, + Sk.astnodes.RShift, + Sk.astnodes.BitOr, + Sk.astnodes.BitXor, + Sk.astnodes.BitAnd, + Sk.astnodes.FloorDiv], + 'unaryop': [Sk.astnodes.Invert, + Sk.astnodes.Not, + Sk.astnodes.UAdd, + Sk.astnodes.USub], + 'cmpop': [Sk.astnodes.Eq, + Sk.astnodes.NotEq, + Sk.astnodes.Lt, + Sk.astnodes.LtE, + Sk.astnodes.Gt, + Sk.astnodes.GtE, + Sk.astnodes.Is, + Sk.astnodes.IsNot, + Sk.astnodes.In, + Sk.astnodes.NotIn], 'comprehension': [], - 'excepthandler': [ExceptHandler], + 'excepthandler': [Sk.astnodes.ExceptHandler], 'arguments_': [], + 'arg': [], 'keyword': [], - 'alias': [] -*/ + 'alias': [], + 'withitem': [] }; Sk.exportSymbol("Sk.astFromParse", Sk.astFromParse); diff --git a/src/lib/ast.js b/src/lib/ast.js index 550a4d10c8..2947ca8960 100644 --- a/src/lib/ast.js +++ b/src/lib/ast.js @@ -154,6 +154,7 @@ var $builtinmodule = function (name) { } // recursive dump var _format = function(node) { + //console.log(node.astname, node); if (isSpecialPyAst(node)) { return functionName(node)+"()"; } else if (isPyAst(node)) { @@ -280,7 +281,7 @@ var $builtinmodule = function (name) { AST = function($gbl, $loc) { var copyFromJsNode = function(self, key, jsNode) { if (key in self.jsNode) { - Sk.abstr.sattr(self, key, Sk.ffi.remapToPy(jsNode[key]), true); + Sk.abstr.sattr(self, Sk.builtin.str(key), Sk.ffi.remapToPy(jsNode[key]), true); self._attributes.push(Sk.builtin.str(key)); } }; @@ -288,34 +289,43 @@ var $builtinmodule = function (name) { depth+=1; if (partial === true) { // Alternative constructor for Skulpt's weird nodes - //print(" ".repeat(depth)+"S:", jsNode); + //console.log(" ".repeat(depth)+"S:", jsNode); self.jsNode = {'_astname': jsNode}; self.astname = jsNode; self._fields = Sk.builtin.list([]); self._attributes = Sk.builtin.list([]); - Sk.abstr.sattr(self, '_fields', self._fields, true); - Sk.abstr.sattr(self, '_attributes', self._attributes, true); + Sk.abstr.sattr(self, Sk.builtin.str('_fields'), self._fields, true); + Sk.abstr.sattr(self, Sk.builtin.str('_attributes'), self._attributes, true); + //console.log(" ".repeat(depth)+"--", jsNode); } else { - //print(" ".repeat(depth)+"P:", jsNode._astname); + //console.log(" ".repeat(depth)+"P:", jsNode._astname); self.jsNode = jsNode; self.astname = jsNode._astname; var fieldListJs = iter_fieldsJs(jsNode); self._fields = []; self._attributes = []; + //console.log(" ".repeat(depth)+"FIELDS"); for (var i = 0; i < fieldListJs.length; i += 1) { var field = fieldListJs[i][0], value = fieldListJs[i][1]; - value = convertValue(value); - Sk.abstr.sattr(self, field, value, true); + //console.log(" ".repeat(depth+1)+field, value) + if (field === 'docstring' && value === undefined) { + value = Sk.builtin.str(''); + } else { + value = convertValue(value); + } + Sk.abstr.sattr(self, Sk.builtin.str(field), value, true); self._fields.push(Sk.builtin.tuple([Sk.builtin.str(field), value])); } + //console.log(" ".repeat(depth)+"FIELDS") self._fields = Sk.builtin.list(self._fields) - Sk.abstr.sattr(self, '_fields', self._fields, true); + Sk.abstr.sattr(self, Sk.builtin.str('_fields'), self._fields, true); copyFromJsNode(self, 'lineno', self.jsNode); copyFromJsNode(self, 'col_offset', self.jsNode); - copyFromJsNode(self, 'endlineno', self.jsNode); - copyFromJsNode(self, 'col_endoffset', self.jsNode); + copyFromJsNode(self, 'end_lineno', self.jsNode); + copyFromJsNode(self, 'end_col_offset', self.jsNode); self._attributes = Sk.builtin.list(self._attributes); - Sk.abstr.sattr(self, '_attributes', self._attributes, true); + Sk.abstr.sattr(self, Sk.builtin.str('_attributes'), self._attributes, true); + //console.log(" ".repeat(depth)+"--", jsNode._astname); } depth -= 1; }); @@ -334,7 +344,7 @@ var $builtinmodule = function (name) { return Sk.misceval.callsim(mod.Module, new Sk.INHERITANCE_MAP.mod[0]([])); } if (filename === undefined) { - filename = ''; + filename = Sk.builtin.str(''); } var parse = Sk.parse(filename, Sk.ffi.remapToJs(source)); ast = Sk.astFromParse(parse.cst, filename, parse.flags); @@ -364,7 +374,7 @@ var $builtinmodule = function (name) { mod[base] = Sk.misceval.buildClass(mod, baseClass, base, [mod.AST]); for (var i=0; i < Sk.INHERITANCE_MAP[base].length; i++) { var nodeType = Sk.INHERITANCE_MAP[base][i]; - var nodeName = functionName(nodeType); + var nodeName = nodeType.prototype._astname; var nodeClass = function($gbl, $loc) { return this;}; mod[nodeName] = Sk.misceval.buildClass(mod, nodeClass, nodeName, [mod[base]]) } diff --git a/src/lib/ast.py b/src/lib/ast.py deleted file mode 100644 index 8db810e2e5..0000000000 --- a/src/lib/ast.py +++ /dev/null @@ -1 +0,0 @@ -raise NotImplementedError("ast is not yet implemented in Skulpt") diff --git a/support/run/runfile.js b/support/run/runfile.js index 36f62251a8..2876824118 100644 --- a/support/run/runfile.js +++ b/support/run/runfile.js @@ -42,8 +42,16 @@ function run (python3, opt, filename) { elapsed = (endtime - starttime) / 1000; console.log("Run time: " + elapsed.toString() + "s"); }, function(e) { - console.log("UNCAUGHT EXCEPTION: " + e); - console.log(e.stack); + if (e.message) { + console.log(e.message + "\n"); + console.log(e.stack); + } else if (e.nativeError) { + console.log(e.nativeError.message + "\n"); + console.log(e.nativeError.stack); + } else { + console.log(e.toString()); + console.log(e.stack) + } }); } diff --git a/test/astppdump.py b/test/astppdump.py index 579b018c24..e442ebd1b2 100644 --- a/test/astppdump.py +++ b/test/astppdump.py @@ -24,4 +24,4 @@ def _format(node, indent): if __name__ == "__main__": - print astppdump(ast.parse(open(sys.argv[1]).read(), sys.argv[1])) + print(astppdump(ast.parse(open(sys.argv[1]).read(), sys.argv[1]))) diff --git a/test/test_ast.py b/test/test_ast.py index 78eaa38e36..9b19a51a56 100644 --- a/test/test_ast.py +++ b/test/test_ast.py @@ -13,8 +13,8 @@ print("lineno", addition.body[0].lineno) assert addition.body[0].lineno == 1, "Addition's lineno was not 1" assert addition.body[0].col_offset == 0, "Addition's col_offset was not 0" -assert addition.body[0].col_endoffset == 1, "Addition's col_endoffset was not 1" -assert addition.body[0].endlineno == 1, "Addition's endlineno was not 1" +assert addition.body[0].end_col_offset == 1, "Addition's end_col_offset was not 1" +assert addition.body[0].end_lineno == 1, "Addition's end_lineno was not 1" print("1 + 2") #print(ast.dump(addition) @@ -35,7 +35,7 @@ #print("FOR", for_loop.body[0].body[0].id, "should not be a") print(ast.dump(for_loop)) - +print("MULTILINE") multiline = ast.parse("""for x in y: a + 1 - 3 if x: From 84eb3cd4a8ee06105a0cd55f49b2b5bb3593e291 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 1 Jul 2019 14:20:33 -0400 Subject: [PATCH 11/68] Annotated assignment --- src/ast.js | 53 +++++++++++++++++++++++++++++++++++++++++-- src/compile.js | 5 ++++ src/symtable.js | 24 ++++++++++++++++++++ test/run/t905.py | 1 + test/run/t905.py.real | 0 test/run/t905.trans | 3 +++ test/test_ast.py | 6 +++++ 7 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 test/run/t905.py create mode 100644 test/run/t905.py.real create mode 100644 test/run/t905.trans diff --git a/src/ast.js b/src/ast.js index 229db144ff..bfef0ff915 100644 --- a/src/ast.js +++ b/src/ast.js @@ -2093,6 +2093,10 @@ function ast_for_exprStmt (c, n) { var varName; var expr1; var ch; + var deep; + var ann; + var simple; + var expr3; REQ(n, SYM.expr_stmt); /* expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | ('=' (yield_expr|testlist_star_expr))*) @@ -2136,8 +2140,53 @@ function ast_for_exprStmt (c, n) { return new Sk.astnodes.AugAssign(expr1, astForAugassign(c, CHILD(n, 1)), expr2, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } else if (CHILD(n, 1).type === SYM.annassign) { - // TODO translate the relevant section from ast.c - throw new Sk.builtin.SyntaxError("Skulpt does not yet support type assignments", c.c_filename, n.lineno); + // annotated assignment + ch = CHILD(n, 0); + ann = CHILD(n, 1); + simple = 1; + deep = ch; + while (NCH(deep) == 1) { + deep = CHILD(deep, 0); + } + if (NCH(deep) > 0 && TYPE(CHILD(deep, 0)) == TOK.T_LPAR) { + simple = 0; + } + expr1 = ast_for_testlist(c, ch); + switch (expr1.constructor) { + case Sk.astnodes.Name: + varName = expr1.id; + forbiddenCheck(c, ch, varName, n.lineno); + setContext(c, expr1, Sk.astnodes.Store, ch); + break; + case Sk.astnodes.Attribute: + varName = expr1.attr; + forbiddenCheck(c, ch, varName, n.lineno); + setContext(c, expr1, Sk.astnodes.Store, ch); + break; + case Sk.astnodes.Subscript: + setContext(c, expr1, Sk.astnodes.Store, ch); + break; + case Sk.astnodes.List: + throw new Sk.builtin.SyntaxError("only single target (not list) can be annotated", c.c_filename, n.lineno); + case Sk.astnodes.Tuple: + throw new Sk.builtin.SyntaxError("only single target (not tuple) can be annotated", c.c_filename, n.lineno); + default: + throw new Sk.builtin.SyntaxError("illegal target for annotation", c.c_filename, n.lineno); + } + + if (expr1.constructor != Sk.astnodes.Name) { + simple = 0; + } + + ch = CHILD(ann, 1); + expr2 = ast_for_expr(c, ch); + if (NCH(ann) == 2) { + return new Sk.astnodes.AnnAssign(expr1, expr2, null, simple, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); + } else { + ch = CHILD(ann, 3); + expr3 = ast_for_expr(c, ch); + return new Sk.astnodes.AnnAssign(expr1, expr2, expr3, simple, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); + } } else { // normal assignment diff --git a/src/compile.js b/src/compile.js index 857ffb95a9..082fbaba4b 100644 --- a/src/compile.js +++ b/src/compile.js @@ -2370,6 +2370,11 @@ Compiler.prototype.vstmt = function (s, class_for_super) { this.vexpr(s.targets[i], val); } break; + case Sk.astnodes.AnnAssign: + val = this.vexpr(s.value); + this.vexpr(s.target, val); + this.vexpr(s.annotation); + break; case Sk.astnodes.AugAssign: return this.caugassign(s); case Sk.astnodes.Print: diff --git a/src/symtable.js b/src/symtable.js index 22f9c1c91e..e1b13b96e7 100644 --- a/src/symtable.js +++ b/src/symtable.js @@ -520,6 +520,30 @@ SymbolTable.prototype.visitStmt = function (s) { this.SEQExpr(s.targets); this.visitExpr(s.value); break; + case Sk.astnodes.AnnAssign: + if (s.target.constructor == Sk.astnodes.Name) { + e_name = s.target; + name = Sk.mangleName(this.curClass, e_name.id).v; + name = Sk.fixReservedNames(name); + cur = this.cur.symFlags[name]; + if ((cur & (DEF_GLOBAL | DEF_NONLOCAL) ) + && (this.global != this.cur.symFlags) // TODO + && (s.simple)) { + throw new Sk.builtin.SyntaxError("annotated name '"+ name +"' can't be global", this.filename, s.lineno); + } + if (s.simple) { + this.addDef(new Sk.builtin.str(name), DEF_ANNOT | DEF_LOCAL, s.lineno); + } else if (s.value) { + this.addDef(new Sk.builtin.str(name), DEF_LOCAL, s.lineno); + } + } else { + this.visitExpr(s.target); + } + this.visitExpr(s.annotation); + if (s.value) { + this.visitExpr(s.value); + } + break; case Sk.astnodes.AugAssign: this.visitExpr(s.target); this.visitExpr(s.value); diff --git a/test/run/t905.py b/test/run/t905.py new file mode 100644 index 0000000000..74a493e08d --- /dev/null +++ b/test/run/t905.py @@ -0,0 +1 @@ +a: int = 0 \ No newline at end of file diff --git a/test/run/t905.py.real b/test/run/t905.py.real new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/run/t905.trans b/test/run/t905.trans new file mode 100644 index 0000000000..360575f2fc --- /dev/null +++ b/test/run/t905.trans @@ -0,0 +1,3 @@ +Module(body=[AnnAssign(target=Name(id='a', ctx=Store()), + annotation=Name(id='int', ctx=Load()), value=Num(n=0), simple=1) + ]) \ No newline at end of file diff --git a/test/test_ast.py b/test/test_ast.py index 9b19a51a56..02e04d06e4 100644 --- a/test/test_ast.py +++ b/test/test_ast.py @@ -25,6 +25,12 @@ print(comparison.body[0].value.ops) assert len(comparison.body[0].value.ops) == 1, "Did not retrieve operations from comparison" +print("*"*20) + +ANNASSIGN_CODE = 'a: int = 0' +ann_assign = ast.parse(ANNASSIGN_CODE) +print(ast.dump(ann_assign)) + FOR_CODE = "for x in y:\n a = 0" for_loop = ast.parse(FOR_CODE) print("*"*20) From 63171730fa57f62f8418f9c2cef05df2a3eee7d8 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 2 Jul 2019 00:26:24 -0400 Subject: [PATCH 12/68] Fix dictcomp, AOT compilation, various other fixes --- package.json | 4 ++-- src/ast.js | 8 ++++---- src/env.js | 14 +++++++++++--- src/lib/StringIO.py | 2 +- src/lib/test/test_support.py | 4 ++-- src/method.js | 2 +- src/symtable.js | 13 +++++++++++++ support/build/wrapmodules.js | 29 +++++++++++++++++++++++++++++ support/run/runfile.js | 1 + webpack.config.js | 5 +++-- 10 files changed, 67 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 2340b3d08b..c133fbe7dc 100644 --- a/package.json +++ b/package.json @@ -66,8 +66,8 @@ "setimmediate": "^1.0.5", "shelljs": "^0.8.3", "strftime": "^0.10.0", - "webpack": "^4.32.0", - "webpack-cli": "^3.3.4" + "webpack": "^4.35.2", + "webpack-cli": "^3.3.5" }, "engines": { "node": ">=8.10" diff --git a/src/ast.js b/src/ast.js index bfef0ff915..f3fab2ae1f 100644 --- a/src/ast.js +++ b/src/ast.js @@ -627,9 +627,9 @@ function astForIfStmt (c, n) { astForSuite(c, CHILD(n, NCH(n) - 4)), astForSuite(c, CHILD(n, NCH(n) - 1)), CHILD(n, NCH(n) - 6).lineno, - CHILD(n, NCH(n) - 6).col_offset), + CHILD(n, NCH(n) - 6).col_offset, CHILD(n, NCH(n) - 6).end_lineno, - CHILD(n, NCH(n) - 6).end_col_offset]; + CHILD(n, NCH(n) - 6).end_col_offset)]; nElif--; } @@ -642,8 +642,8 @@ function astForIfStmt (c, n) { orelse, CHILD(n, off).lineno, CHILD(n, off).col_offset, - CHILD(n, off).end_lineno, - CHILD(n, off).end_col_offset)]; + CHILD(n, NCH(n) - 6).end_lineno, + CHILD(n, NCH(n) - 6).end_col_offset)]; } return new Sk.astnodes.If( ast_for_expr(c, CHILD(n, 1)), diff --git a/src/env.js b/src/env.js index 8cefaadb8c..1d77e406a4 100644 --- a/src/env.js +++ b/src/env.js @@ -253,12 +253,20 @@ Sk.yieldLimit = Number.POSITIVE_INFINITY; Sk.output = function (x) { }; +/* By default, you can put any modules you like into this object. */ +Sk.builtinFiles = {}; +Sk.exportSymbol("Sk.builtinFiles", Sk.builtinFiles); + /* - * Replacable function to load modules with (called via import, etc.) - * todo; this should be an async api + * Replaceable function to load modules with (called via import, etc.). + * May return a suspension. */ Sk.read = function (x) { - throw "Sk.read has not been implemented"; + if (Sk.builtinFiles === undefined || Sk.builtinFiles["files"][x] === undefined) { + throw "File not found: '" + x + "'"; + } + + return Sk.builtinFiles["files"][x]; }; /* diff --git a/src/lib/StringIO.py b/src/lib/StringIO.py index 6166584388..6a336d0692 100644 --- a/src/lib/StringIO.py +++ b/src/lib/StringIO.py @@ -33,7 +33,7 @@ def _complain_ifclosed(closed): if closed: - raise ValueError, "I/O operation on closed file" + raise ValueError("I/O operation on closed file") class StringIO: """class StringIO([buffer]) diff --git a/src/lib/test/test_support.py b/src/lib/test/test_support.py index 25c9671108..32018a9e76 100644 --- a/src/lib/test/test_support.py +++ b/src/lib/test/test_support.py @@ -25,8 +25,8 @@ def run_unittest(*classes): """Run tests from unittest.TestCase-derived classes.""" for cls in classes: - print cls + print(cls) if issubclass(cls, unittest.TestCase): cls().main() else: - print "Don't know what to do with ", cls + print("Don't know what to do with ", cls) diff --git a/src/method.js b/src/method.js index 83e6490029..0cb1239486 100644 --- a/src/method.js +++ b/src/method.js @@ -10,7 +10,7 @@ */ Sk.builtin.method = function (func, self, klass, builtin) { if (!(this instanceof Sk.builtin.method)) { - Sk.builtin.pyCheckArgsLen("method", arguments.length, 3, 3); + Sk.builtin.pyCheckArgsLen("method", arguments.length, 2, 3); if (!Sk.builtin.checkCallable(func)) { throw new Sk.builtin.TypeError("First argument must be callable"); } diff --git a/src/symtable.js b/src/symtable.js index e1b13b96e7..d19b9fdb46 100644 --- a/src/symtable.js +++ b/src/symtable.js @@ -22,6 +22,11 @@ var DEF_FREE_CLASS = 2 << 8; /* free variable from class's method */ var DEF_IMPORT = 2 << 9; /* assignment occurred via import */ +var DEF_NONLOCAL = 2 << 10; +/* nonlocal stmt */ +var DEF_ANNOT = 2 << 11; +/* this name is annotated */ + var DEF_BOUND = (DEF_LOCAL | DEF_PARAM | DEF_IMPORT); @@ -477,6 +482,7 @@ SymbolTable.prototype.visitStmt = function (s) { var i; var nameslen; var tmp; + var e_name; Sk.asserts.assert(s !== undefined, "visitStmt called with undefined"); switch (s.constructor) { case Sk.astnodes.FunctionDef: @@ -696,7 +702,14 @@ SymbolTable.prototype.visitExpr = function (e) { this.SEQExpr(e.values); break; case Sk.astnodes.DictComp: + this.newTmpname(e.lineno); + this.visitExpr(e.key); + this.visitExpr(e.value); + this.visitComprehension(e.generators, 0); + break; case Sk.astnodes.SetComp: + this.newTmpname(e.lineno); + this.visitExpr(e.elt); this.visitComprehension(e.generators, 0); break; case Sk.astnodes.ListComp: diff --git a/support/build/wrapmodules.js b/support/build/wrapmodules.js index 664d150568..b3e8b99ba4 100644 --- a/support/build/wrapmodules.js +++ b/support/build/wrapmodules.js @@ -2,6 +2,17 @@ const fs = require('fs'); const path = require('path'); const minify = require('babel-minify'); +const reqskulpt = require('../run/require-skulpt').requireSkulpt; +var skulpt = reqskulpt(); +Sk.configure({__future__: Sk.python3}); + +var WHITE_LIST = ['tifa.py', 'builtin_definitions.py', 'type_definitions.py']; +function endsWithAny(string, suffixes) { + return suffixes.some(function (suffix) { + return string.endsWith(suffix); + }); +} + /** * If this optional file exists in the top level directory, it will be * used to exclude libraries from the standard library file. @@ -42,6 +53,24 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { let result = minify(contents); contents = result.code; } + // AOT compilation + /* + else if (ext == '.py' && + !endsWithAny(fullname, WHITE_LIST) && + !fullname.startsWith('src/builtin/')) { + var co; + try { + co = Sk.compile(contents, fullname, 'exec', true); + console.log("Compiled: "+fullname); + } catch (e) { + console.log("Failed to compile: "+fullname); + console.log(e); + console.log(e.stack); + console.error(e.args); + } + fullname = fullname.replace(/\.py$/, ".js"); + contents = co.code + '\nvar $builtinmodule = ' + co.funcname + ';'; + }*/ ret.files[fullname] = contents; } } diff --git a/support/run/runfile.js b/support/run/runfile.js index 2876824118..2f44c8dd5f 100644 --- a/support/run/runfile.js +++ b/support/run/runfile.js @@ -42,6 +42,7 @@ function run (python3, opt, filename) { elapsed = (endtime - starttime) / 1000; console.log("Run time: " + elapsed.toString() + "s"); }, function(e) { + console.error(e); if (e.message) { console.log(e.message + "\n"); console.log(e.stack); diff --git a/webpack.config.js b/webpack.config.js index 2077a2d20b..c6eb27e55c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -51,12 +51,12 @@ module.exports = (env, argv) => { assertfile = './assert-prod.js'; mod = { rules: [ - { + /*{ test: /\.js$/, enforce: 'pre', exclude: styleexcludes, loader: 'eslint-loader' - } + }*/ ] }; } @@ -65,6 +65,7 @@ module.exports = (env, argv) => { entry: './src/main.js', output: { path: path.resolve(__dirname, 'dist'), + devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]', filename: outfile }, devtool: 'source-map', From d131e333ee4111519325f500f130d1e2d7363149 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 10 Jul 2019 12:49:38 -0400 Subject: [PATCH 13/68] Filling in some libraries, partially --- src/builtin/sys.js | 2 + src/import.js | 2 +- src/lib/StringIO.py | 271 +--- src/lib/_thread.py | 2 + src/lib/functools.py | 76 +- src/lib/genericpath.py | 152 +- src/lib/io.py | 271 +++- src/lib/itertools.py | 14 + src/lib/linecache.py | 178 ++- src/lib/os.py | 7 +- src/lib/pedal/__init__.py | 31 + src/lib/pedal/assertions/__init__.py | 12 + src/lib/pedal/assertions/assertions.py | 645 +++++++++ src/lib/pedal/assertions/organizers.py | 155 ++ src/lib/pedal/assertions/setup.py | 111 ++ src/lib/pedal/cait/__init__.py | 16 + src/lib/pedal/cait/ast_helpers.py | 59 + src/lib/pedal/cait/ast_map.py | 270 ++++ src/lib/pedal/cait/cait_api.py | 272 ++++ src/lib/pedal/cait/cait_node.py | 503 +++++++ src/lib/pedal/cait/stretchy_tree_matching.py | 658 +++++++++ src/lib/pedal/mistakes/__init__.py | 0 src/lib/pedal/mistakes/feedback_mod.py | 15 + src/lib/pedal/mistakes/instructor_append.py | 131 ++ src/lib/pedal/mistakes/instructor_filter.py | 50 + .../pedal/mistakes/instructor_histogram.py | 119 ++ .../pedal/mistakes/instructor_iteration.py | 135 ++ src/lib/pedal/mistakes/iteration_context.py | 1204 ++++++++++++++++ src/lib/pedal/plugins/__init__.py | 13 + .../pedal/plugins/blockpy_compatibility.py | 106 ++ src/lib/pedal/plugins/grade_magic.py | 389 ++++++ .../pedal/plugins/test_reference_solution.py | 138 ++ src/lib/pedal/plugins/vpl.py | 148 ++ src/lib/pedal/plugins/vpl_safe_runner.py | 10 + src/lib/pedal/plugins/vpl_unittest.py | 112 ++ src/lib/pedal/questions/__init__.py | 88 ++ src/lib/pedal/questions/design.md | 92 ++ src/lib/pedal/questions/graders.py | 105 ++ src/lib/pedal/questions/setup.py | 39 + src/lib/pedal/report/__init__.py | 8 + src/lib/pedal/report/feedback.py | 102 ++ src/lib/pedal/report/imperative.py | 80 ++ src/lib/pedal/report/report.py | 156 +++ src/lib/pedal/resolvers/__init__.py | 19 + src/lib/pedal/resolvers/core.py | 22 + src/lib/pedal/resolvers/readme.md | 3 + src/lib/pedal/resolvers/sectional.py | 77 + src/lib/pedal/resolvers/simple.py | 156 +++ src/lib/pedal/sandbox/__init__.py | 34 + src/lib/pedal/sandbox/compatibility.py | 121 ++ src/lib/pedal/sandbox/exceptions.py | 169 +++ src/lib/pedal/sandbox/messages.py | 61 + src/lib/pedal/sandbox/mocked.py | 271 ++++ src/lib/pedal/sandbox/result.py | 369 +++++ src/lib/pedal/sandbox/sandbox.py | 688 +++++++++ src/lib/pedal/sandbox/timeout.py | 155 ++ src/lib/pedal/sandbox/tracer.py | 108 ++ src/lib/pedal/sk_mod_instructor_list.txt | 40 + src/lib/pedal/source/__init__.py | 109 ++ src/lib/pedal/source/sections.py | 132 ++ src/lib/pedal/tifa/.gitignore | 1 + src/lib/pedal/tifa/__init__.py | 105 ++ src/lib/pedal/tifa/builtin_definitions.py | 231 +++ src/lib/pedal/tifa/identifier.py | 24 + src/lib/pedal/tifa/messages.py | 154 ++ src/lib/pedal/tifa/readme.md | 5 + src/lib/pedal/tifa/state.py | 77 + src/lib/pedal/tifa/tifa.py | 1243 +++++++++++++++++ src/lib/pedal/tifa/type_definitions.py | 514 +++++++ src/lib/pedal/tifa/type_operations.py | 136 ++ src/lib/pedal/toolkit/__init__.py | 0 src/lib/pedal/toolkit/files.py | 56 + src/lib/pedal/toolkit/functions.py | 343 +++++ src/lib/pedal/toolkit/imports.py | 25 + src/lib/pedal/toolkit/plotting.py | 129 ++ src/lib/pedal/toolkit/printing.py | 22 + src/lib/pedal/toolkit/signatures.py | 344 +++++ src/lib/pedal/toolkit/upload.py | 54 + src/lib/pedal/toolkit/utilities.py | 337 +++++ src/lib/posixpath.py | 528 ++++++- src/lib/reprlib.py | 161 +++ src/lib/stat.py | 180 ++- src/lib/traceback.py | 623 ++++++++- src/lib/unittest/mock.py | 5 + src/symtable.js | 4 + support/build/wrapmodules.js | 3 +- test/test_pedal.py | 2 + 87 files changed, 14507 insertions(+), 280 deletions(-) create mode 100644 src/lib/_thread.py create mode 100644 src/lib/itertools.py create mode 100644 src/lib/pedal/__init__.py create mode 100644 src/lib/pedal/assertions/__init__.py create mode 100644 src/lib/pedal/assertions/assertions.py create mode 100644 src/lib/pedal/assertions/organizers.py create mode 100644 src/lib/pedal/assertions/setup.py create mode 100644 src/lib/pedal/cait/__init__.py create mode 100644 src/lib/pedal/cait/ast_helpers.py create mode 100644 src/lib/pedal/cait/ast_map.py create mode 100644 src/lib/pedal/cait/cait_api.py create mode 100644 src/lib/pedal/cait/cait_node.py create mode 100644 src/lib/pedal/cait/stretchy_tree_matching.py create mode 100644 src/lib/pedal/mistakes/__init__.py create mode 100644 src/lib/pedal/mistakes/feedback_mod.py create mode 100644 src/lib/pedal/mistakes/instructor_append.py create mode 100644 src/lib/pedal/mistakes/instructor_filter.py create mode 100644 src/lib/pedal/mistakes/instructor_histogram.py create mode 100644 src/lib/pedal/mistakes/instructor_iteration.py create mode 100644 src/lib/pedal/mistakes/iteration_context.py create mode 100644 src/lib/pedal/plugins/__init__.py create mode 100644 src/lib/pedal/plugins/blockpy_compatibility.py create mode 100644 src/lib/pedal/plugins/grade_magic.py create mode 100644 src/lib/pedal/plugins/test_reference_solution.py create mode 100644 src/lib/pedal/plugins/vpl.py create mode 100644 src/lib/pedal/plugins/vpl_safe_runner.py create mode 100644 src/lib/pedal/plugins/vpl_unittest.py create mode 100644 src/lib/pedal/questions/__init__.py create mode 100644 src/lib/pedal/questions/design.md create mode 100644 src/lib/pedal/questions/graders.py create mode 100644 src/lib/pedal/questions/setup.py create mode 100644 src/lib/pedal/report/__init__.py create mode 100644 src/lib/pedal/report/feedback.py create mode 100644 src/lib/pedal/report/imperative.py create mode 100644 src/lib/pedal/report/report.py create mode 100644 src/lib/pedal/resolvers/__init__.py create mode 100644 src/lib/pedal/resolvers/core.py create mode 100644 src/lib/pedal/resolvers/readme.md create mode 100644 src/lib/pedal/resolvers/sectional.py create mode 100644 src/lib/pedal/resolvers/simple.py create mode 100644 src/lib/pedal/sandbox/__init__.py create mode 100644 src/lib/pedal/sandbox/compatibility.py create mode 100644 src/lib/pedal/sandbox/exceptions.py create mode 100644 src/lib/pedal/sandbox/messages.py create mode 100644 src/lib/pedal/sandbox/mocked.py create mode 100644 src/lib/pedal/sandbox/result.py create mode 100644 src/lib/pedal/sandbox/sandbox.py create mode 100644 src/lib/pedal/sandbox/timeout.py create mode 100644 src/lib/pedal/sandbox/tracer.py create mode 100644 src/lib/pedal/sk_mod_instructor_list.txt create mode 100644 src/lib/pedal/source/__init__.py create mode 100644 src/lib/pedal/source/sections.py create mode 100644 src/lib/pedal/tifa/.gitignore create mode 100644 src/lib/pedal/tifa/__init__.py create mode 100644 src/lib/pedal/tifa/builtin_definitions.py create mode 100644 src/lib/pedal/tifa/identifier.py create mode 100644 src/lib/pedal/tifa/messages.py create mode 100644 src/lib/pedal/tifa/readme.md create mode 100644 src/lib/pedal/tifa/state.py create mode 100644 src/lib/pedal/tifa/tifa.py create mode 100644 src/lib/pedal/tifa/type_definitions.py create mode 100644 src/lib/pedal/tifa/type_operations.py create mode 100644 src/lib/pedal/toolkit/__init__.py create mode 100644 src/lib/pedal/toolkit/files.py create mode 100644 src/lib/pedal/toolkit/functions.py create mode 100644 src/lib/pedal/toolkit/imports.py create mode 100644 src/lib/pedal/toolkit/plotting.py create mode 100644 src/lib/pedal/toolkit/printing.py create mode 100644 src/lib/pedal/toolkit/signatures.py create mode 100644 src/lib/pedal/toolkit/upload.py create mode 100644 src/lib/pedal/toolkit/utilities.py create mode 100644 src/lib/reprlib.py create mode 100644 src/lib/unittest/mock.py diff --git a/src/builtin/sys.js b/src/builtin/sys.js index 8b481af9da..7a0889712d 100644 --- a/src/builtin/sys.js +++ b/src/builtin/sys.js @@ -23,6 +23,8 @@ var $builtinmodule = function (name) { sys.modules = Sk.sysmodules; sys.path = Sk.realsyspath; + + sys.platform = Sk.builtin.str('skulpt'); sys.getExecutionLimit = new Sk.builtin.func(function () { if (Sk.execLimit === null) { diff --git a/src/import.js b/src/import.js index bed0e3a166..7532b4a552 100644 --- a/src/import.js +++ b/src/import.js @@ -465,7 +465,7 @@ Sk.importBuiltinWithBody = function (name, dumpJS, body, canSuspend) { }; Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { - //print("Importing: ", JSON.stringify(name), JSON.stringify(fromlist), level); + //console.log("Importing: ", JSON.stringify(name), JSON.stringify(fromlist), level); //if (name == "") { debugger; } // Save the Sk.globals variable importModuleInternal_ may replace it when it compiles diff --git a/src/lib/StringIO.py b/src/lib/StringIO.py index 6a336d0692..0c4550cd09 100644 --- a/src/lib/StringIO.py +++ b/src/lib/StringIO.py @@ -1,270 +1 @@ -r"""File-like objects that read from or write to a string buffer. - -This implements (nearly) all stdio methods. - -f = StringIO() # ready for writing -f = StringIO(buf) # ready for reading -f.close() # explicitly release resources held -flag = f.isatty() # always false -pos = f.tell() # get current position -f.seek(pos) # set current position -f.seek(pos, mode) # mode 0: absolute; 1: relative; 2: relative to EOF -buf = f.read() # read until EOF -buf = f.read(n) # read up to n bytes -buf = f.readline() # read until end of line ('\n') or EOF -list = f.readlines()# list of f.readline() results until EOF -f.truncate([size]) # truncate file at to at most size (default: current pos) -f.write(buf) # write at current position -f.writelines(list) # for line in list: f.write(line) -f.getvalue() # return whole file's contents as a string - -Notes: -- Using a real file is often faster (but less convenient). -- There's also a much faster implementation in C, called cStringIO, but - it's not subclassable. -- fileno() is left unimplemented so that code which uses it triggers - an exception early. -- Seeking far beyond EOF and then writing will insert real null - bytes that occupy space in the buffer. -- There's a simple test set (see end of this file). -""" - -__all__ = ["StringIO"] - -def _complain_ifclosed(closed): - if closed: - raise ValueError("I/O operation on closed file") - -class StringIO: - """class StringIO([buffer]) - - When a StringIO object is created, it can be initialized to an existing - string by passing the string to the constructor. If no string is given, - the StringIO will start empty. - - The StringIO object can accept either Unicode or 8-bit strings, but - mixing the two may take some care. If both are used, 8-bit strings that - cannot be interpreted as 7-bit ASCII (that use the 8th bit) will cause - a UnicodeError to be raised when getvalue() is called. - """ - def __init__(self, buf = ''): - # Force self.buf to be a string or unicode - if not isinstance(buf, str): - buf = str(buf) - self.buf = buf - self.len = len(buf) - self.buflist = [] - self.pos = 0 - self.closed = False - self.softspace = 0 - - def __iter__(self): - return self - - def next(self): - """A file object is its own iterator, for example iter(f) returns f - (unless f is closed). When a file is used as an iterator, typically - in a for loop (for example, for line in f: print line), the next() - method is called repeatedly. This method returns the next input line, - or raises StopIteration when EOF is hit. - """ - _complain_ifclosed(self.closed) - r = self.readline() - if not r: - raise StopIteration - return r - - def close(self): - """Free the memory buffer. - """ - if not self.closed: - self.closed = True - self.buf = None - self.pos = None - - def isatty(self): - """Returns False because StringIO objects are not connected to a - tty-like device. - """ - _complain_ifclosed(self.closed) - return False - - def seek(self, pos, mode = 0): - """Set the file's current position. - - The mode argument is optional and defaults to 0 (absolute file - positioning); other values are 1 (seek relative to the current - position) and 2 (seek relative to the file's end). - - There is no return value. - """ - _complain_ifclosed(self.closed) - if self.buflist: - self.buf += ''.join(self.buflist) - self.buflist = [] - if mode == 1: - pos += self.pos - elif mode == 2: - pos += self.len - self.pos = max(0, pos) - - def tell(self): - """Return the file's current position.""" - _complain_ifclosed(self.closed) - return self.pos - - def read(self, n = -1): - """Read at most size bytes from the file - (less if the read hits EOF before obtaining size bytes). - - If the size argument is negative or omitted, read all data until EOF - is reached. The bytes are returned as a string object. An empty - string is returned when EOF is encountered immediately. - """ - _complain_ifclosed(self.closed) - if self.buflist: - self.buf += ''.join(self.buflist) - self.buflist = [] - if n is None or n < 0: - newpos = self.len - else: - newpos = min(self.pos+n, self.len) - r = self.buf[self.pos:newpos] - self.pos = newpos - return r - - def readline(self, length=None): - r"""Read one entire line from the file. - - A trailing newline character is kept in the string (but may be absent - when a file ends with an incomplete line). If the size argument is - present and non-negative, it is a maximum byte count (including the - trailing newline) and an incomplete line may be returned. - - An empty string is returned only when EOF is encountered immediately. - - Note: Unlike stdio's fgets(), the returned string contains null - characters ('\0') if they occurred in the input. - """ - _complain_ifclosed(self.closed) - if self.buflist: - self.buf += ''.join(self.buflist) - self.buflist = [] - i = self.buf.find('\n', self.pos) - if i < 0: - newpos = self.len - else: - newpos = i+1 - if length is not None and length >= 0: - if self.pos + length < newpos: - newpos = self.pos + length - r = self.buf[self.pos:newpos] - self.pos = newpos - return r - - def readlines(self, sizehint = 0): - """Read until EOF using readline() and return a list containing the - lines thus read. - - If the optional sizehint argument is present, instead of reading up - to EOF, whole lines totalling approximately sizehint bytes (or more - to accommodate a final whole line). - """ - total = 0 - lines = [] - line = self.readline() - while line: - lines.append(line) - total += len(line) - if 0 < sizehint <= total: - break - line = self.readline() - return lines - - def truncate(self, size=None): - """Truncate the file's size. - - If the optional size argument is present, the file is truncated to - (at most) that size. The size defaults to the current position. - The current file position is not changed unless the position - is beyond the new file size. - - If the specified size exceeds the file's current size, the - file remains unchanged. - """ - _complain_ifclosed(self.closed) - if size is None: - size = self.pos - elif size < 0: - raise IOError(22, "Negative size not allowed") - elif size < self.pos: - self.pos = size - self.buf = self.getvalue()[:size] - self.len = size - - def write(self, s): - """Write a string to the file. - - There is no return value. - """ - _complain_ifclosed(self.closed) - if not s: return - # Force s to be a string or unicode - if not isinstance(s, str): - s = str(s) - spos = self.pos - slen = self.len - if spos == slen: - self.buflist.append(s) - self.len = self.pos = spos + len(s) - return - if spos > slen: - self.buflist.append('\0'*(spos - slen)) - slen = spos - newpos = spos + len(s) - if spos < slen: - if self.buflist: - self.buf += ''.join(self.buflist) - self.buflist = [self.buf[:spos], s, self.buf[newpos:]] - self.buf = '' - if newpos > slen: - slen = newpos - else: - self.buflist.append(s) - slen = newpos - self.len = slen - self.pos = newpos - - def writelines(self, iterable): - """Write a sequence of strings to the file. The sequence can be any - iterable object producing strings, typically a list of strings. There - is no return value. - - (The name is intended to match readlines(); writelines() does not add - line separators.) - """ - write = self.write - for line in iterable: - write(line) - - def flush(self): - """Flush the internal buffer - """ - _complain_ifclosed(self.closed) - - def getvalue(self): - """ - Retrieve the entire contents of the "file" at any time before - the StringIO object's close() method is called. - - The StringIO object can accept either Unicode or 8-bit strings, - but mixing the two may take some care. If both are used, 8-bit - strings that cannot be interpreted as 7-bit ASCII (that use the - 8th bit) will cause a UnicodeError to be raised when getvalue() - is called. - """ - _complain_ifclosed(self.closed) - if self.buflist: - self.buf += ''.join(self.buflist) - self.buflist = [] - return self.buf +from io import StringIO \ No newline at end of file diff --git a/src/lib/_thread.py b/src/lib/_thread.py new file mode 100644 index 0000000000..b10987a39d --- /dev/null +++ b/src/lib/_thread.py @@ -0,0 +1,2 @@ +def get_ident(): + return 1 \ No newline at end of file diff --git a/src/lib/functools.py b/src/lib/functools.py index 7a39b03f7f..b3eaa455bf 100644 --- a/src/lib/functools.py +++ b/src/lib/functools.py @@ -1 +1,75 @@ -raise NotImplementedError("functools is not yet implemented in Skulpt") +from reprlib import recursive_repr + +# Purely functional, no descriptor behaviour +class partial: + """New function with partial application of the given arguments + and keywords. + """ + + __slots__ = "func", "args", "keywords", "__dict__", "__weakref__" + + def __new__(cls, func, *args, **keywords): + if not callable(func): + raise TypeError("the first argument must be callable") + + if hasattr(func, "func"): + args = func.args + args + keywords = keywords.copy() + keywords.update(func.keywords) + #keywords = {**func.keywords, **keywords} + func = func.func + + self = super(partial, cls).__new__(cls) + + self.func = func + self.args = args + self.keywords = keywords + return self + + def __call__(self, *args, **keywords): + #keywords = {**self.keywords, **keywords} + keywords = keywords.copy() + keywords.update(self.keywords) + return self.func(*self.args, *args, **keywords) + + @recursive_repr + def __repr__(self): + qualname = type(self).__qualname__ + args = [repr(self.func)] + args.extend(repr(x) for x in self.args) + args.extend("{k}={v!r}".format(k=k,v=v) for (k, v) in self.keywords.items()) + if type(self).__module__ == "functools": + return "functools.{qualname}({j_args})".format( + qualname=qualname, j_args=', '.join(args) + ) + return "{qualname}({j_args})".format( + qualname=qualname, j_args=', '.join(args) + ) + + def __reduce__(self): + return type(self), (self.func,), (self.func, self.args, + self.keywords or None, self.__dict__ or None) + + def __setstate__(self, state): + if not isinstance(state, tuple): + raise TypeError("argument to __setstate__ must be a tuple") + if len(state) != 4: + raise TypeError("expected 4 items in state, got {l_state}".format(l_state=len(state))) + func, args, kwds, namespace = state + if (not callable(func) or not isinstance(args, tuple) or + (kwds is not None and not isinstance(kwds, dict)) or + (namespace is not None and not isinstance(namespace, dict))): + raise TypeError("invalid partial state") + + args = tuple(args) # just in case it's a subclass + if kwds is None: + kwds = {} + elif type(kwds) is not dict: # XXX does it need to be *exactly* dict? + kwds = dict(kwds) + if namespace is None: + namespace = {} + + self.__dict__ = namespace + self.func = func + self.args = args + self.keywords = kwds \ No newline at end of file diff --git a/src/lib/genericpath.py b/src/lib/genericpath.py index 4dce51f324..ba1f9cee74 100644 --- a/src/lib/genericpath.py +++ b/src/lib/genericpath.py @@ -1 +1,151 @@ -raise NotImplementedError("genericpath is not yet implemented in Skulpt") +""" +Path operations common to more than one OS +Do not use directly. The OS specific modules import the appropriate +functions from this module themselves. +""" +import os +import stat + +__all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime', + 'getsize', 'isdir', 'isfile', 'samefile', 'sameopenfile', + 'samestat'] + + +# Does a path exist? +# This is false for dangling symbolic links on systems that support them. +def exists(path): + """Test whether a path exists. Returns False for broken symbolic links""" + try: + os.stat(path) + except (OSError, ValueError): + return False + return True + + +# This follows symbolic links, so both islink() and isdir() can be true +# for the same path on systems that support symlinks +def isfile(path): + """Test whether a path is a regular file""" + try: + st = os.stat(path) + except (OSError, ValueError): + return False + return stat.S_ISREG(st.st_mode) + + +# Is a path a directory? +# This follows symbolic links, so both islink() and isdir() +# can be true for the same path on systems that support symlinks +def isdir(s): + """Return true if the pathname refers to an existing directory.""" + try: + st = os.stat(s) + except (OSError, ValueError): + return False + return stat.S_ISDIR(st.st_mode) + + +def getsize(filename): + """Return the size of a file, reported by os.stat().""" + return os.stat(filename).st_size + + +def getmtime(filename): + """Return the last modification time of a file, reported by os.stat().""" + return os.stat(filename).st_mtime + + +def getatime(filename): + """Return the last access time of a file, reported by os.stat().""" + return os.stat(filename).st_atime + + +def getctime(filename): + """Return the metadata change time of a file, reported by os.stat().""" + return os.stat(filename).st_ctime + + +# Return the longest prefix of all list elements. +def commonprefix(m): + "Given a list of pathnames, returns the longest common leading component" + if not m: return '' + # Some people pass in a list of pathname parts to operate in an OS-agnostic + # fashion; don't try to translate in that case as that's an abuse of the + # API and they are already doing what they need to be OS-agnostic and so + # they most likely won't be using an os.PathLike object in the sublists. + if not isinstance(m[0], (list, tuple)): + m = tuple(map(os.fspath, m)) + s1 = min(m) + s2 = max(m) + for i, c in enumerate(s1): + if c != s2[i]: + return s1[:i] + return s1 + +# Are two stat buffers (obtained from stat, fstat or lstat) +# describing the same file? +def samestat(s1, s2): + """Test whether two stat buffers reference the same file""" + return (s1.st_ino == s2.st_ino and + s1.st_dev == s2.st_dev) + + +# Are two filenames really pointing to the same file? +def samefile(f1, f2): + """Test whether two pathnames reference the same actual file""" + s1 = os.stat(f1) + s2 = os.stat(f2) + return samestat(s1, s2) + + +# Are two open files really referencing the same file? +# (Not necessarily the same file descriptor!) +def sameopenfile(fp1, fp2): + """Test whether two open file objects reference the same file""" + s1 = os.fstat(fp1) + s2 = os.fstat(fp2) + return samestat(s1, s2) + + +# Split a path in root and extension. +# The extension is everything starting at the last dot in the last +# pathname component; the root is everything before that. +# It is always true that root + ext == p. + +# Generic implementation of splitext, to be parametrized with +# the separators +def _splitext(p, sep, altsep, extsep): + """Split the extension from a pathname. + + Extension is everything from the last dot to the end, ignoring + leading dots. Returns "(root, ext)"; ext may be empty.""" + # NOTE: This code must work for text and bytes strings. + + sepIndex = p.rfind(sep) + if altsep: + altsepIndex = p.rfind(altsep) + sepIndex = max(sepIndex, altsepIndex) + + dotIndex = p.rfind(extsep) + if dotIndex > sepIndex: + # skip all leading dots + filenameIndex = sepIndex + 1 + while filenameIndex < dotIndex: + if p[filenameIndex:filenameIndex+1] != extsep: + return p[:dotIndex], p[dotIndex:] + filenameIndex += 1 + + return p, p[:0] + +def _check_arg_types(funcname, *args): + hasstr = hasbytes = False + for s in args: + if isinstance(s, str): + hasstr = True + elif isinstance(s, bytes): + hasbytes = True + else: + raise TypeError('%s() argument must be str or bytes, not %r' % + (funcname, s.__class__.__name__)) from None + if hasstr and hasbytes: + raise TypeError("Can't mix strings and bytes in path components") from None \ No newline at end of file diff --git a/src/lib/io.py b/src/lib/io.py index 6513bf6270..6a336d0692 100644 --- a/src/lib/io.py +++ b/src/lib/io.py @@ -1 +1,270 @@ -raise NotImplementedError("io is not yet implemented in Skulpt") +r"""File-like objects that read from or write to a string buffer. + +This implements (nearly) all stdio methods. + +f = StringIO() # ready for writing +f = StringIO(buf) # ready for reading +f.close() # explicitly release resources held +flag = f.isatty() # always false +pos = f.tell() # get current position +f.seek(pos) # set current position +f.seek(pos, mode) # mode 0: absolute; 1: relative; 2: relative to EOF +buf = f.read() # read until EOF +buf = f.read(n) # read up to n bytes +buf = f.readline() # read until end of line ('\n') or EOF +list = f.readlines()# list of f.readline() results until EOF +f.truncate([size]) # truncate file at to at most size (default: current pos) +f.write(buf) # write at current position +f.writelines(list) # for line in list: f.write(line) +f.getvalue() # return whole file's contents as a string + +Notes: +- Using a real file is often faster (but less convenient). +- There's also a much faster implementation in C, called cStringIO, but + it's not subclassable. +- fileno() is left unimplemented so that code which uses it triggers + an exception early. +- Seeking far beyond EOF and then writing will insert real null + bytes that occupy space in the buffer. +- There's a simple test set (see end of this file). +""" + +__all__ = ["StringIO"] + +def _complain_ifclosed(closed): + if closed: + raise ValueError("I/O operation on closed file") + +class StringIO: + """class StringIO([buffer]) + + When a StringIO object is created, it can be initialized to an existing + string by passing the string to the constructor. If no string is given, + the StringIO will start empty. + + The StringIO object can accept either Unicode or 8-bit strings, but + mixing the two may take some care. If both are used, 8-bit strings that + cannot be interpreted as 7-bit ASCII (that use the 8th bit) will cause + a UnicodeError to be raised when getvalue() is called. + """ + def __init__(self, buf = ''): + # Force self.buf to be a string or unicode + if not isinstance(buf, str): + buf = str(buf) + self.buf = buf + self.len = len(buf) + self.buflist = [] + self.pos = 0 + self.closed = False + self.softspace = 0 + + def __iter__(self): + return self + + def next(self): + """A file object is its own iterator, for example iter(f) returns f + (unless f is closed). When a file is used as an iterator, typically + in a for loop (for example, for line in f: print line), the next() + method is called repeatedly. This method returns the next input line, + or raises StopIteration when EOF is hit. + """ + _complain_ifclosed(self.closed) + r = self.readline() + if not r: + raise StopIteration + return r + + def close(self): + """Free the memory buffer. + """ + if not self.closed: + self.closed = True + self.buf = None + self.pos = None + + def isatty(self): + """Returns False because StringIO objects are not connected to a + tty-like device. + """ + _complain_ifclosed(self.closed) + return False + + def seek(self, pos, mode = 0): + """Set the file's current position. + + The mode argument is optional and defaults to 0 (absolute file + positioning); other values are 1 (seek relative to the current + position) and 2 (seek relative to the file's end). + + There is no return value. + """ + _complain_ifclosed(self.closed) + if self.buflist: + self.buf += ''.join(self.buflist) + self.buflist = [] + if mode == 1: + pos += self.pos + elif mode == 2: + pos += self.len + self.pos = max(0, pos) + + def tell(self): + """Return the file's current position.""" + _complain_ifclosed(self.closed) + return self.pos + + def read(self, n = -1): + """Read at most size bytes from the file + (less if the read hits EOF before obtaining size bytes). + + If the size argument is negative or omitted, read all data until EOF + is reached. The bytes are returned as a string object. An empty + string is returned when EOF is encountered immediately. + """ + _complain_ifclosed(self.closed) + if self.buflist: + self.buf += ''.join(self.buflist) + self.buflist = [] + if n is None or n < 0: + newpos = self.len + else: + newpos = min(self.pos+n, self.len) + r = self.buf[self.pos:newpos] + self.pos = newpos + return r + + def readline(self, length=None): + r"""Read one entire line from the file. + + A trailing newline character is kept in the string (but may be absent + when a file ends with an incomplete line). If the size argument is + present and non-negative, it is a maximum byte count (including the + trailing newline) and an incomplete line may be returned. + + An empty string is returned only when EOF is encountered immediately. + + Note: Unlike stdio's fgets(), the returned string contains null + characters ('\0') if they occurred in the input. + """ + _complain_ifclosed(self.closed) + if self.buflist: + self.buf += ''.join(self.buflist) + self.buflist = [] + i = self.buf.find('\n', self.pos) + if i < 0: + newpos = self.len + else: + newpos = i+1 + if length is not None and length >= 0: + if self.pos + length < newpos: + newpos = self.pos + length + r = self.buf[self.pos:newpos] + self.pos = newpos + return r + + def readlines(self, sizehint = 0): + """Read until EOF using readline() and return a list containing the + lines thus read. + + If the optional sizehint argument is present, instead of reading up + to EOF, whole lines totalling approximately sizehint bytes (or more + to accommodate a final whole line). + """ + total = 0 + lines = [] + line = self.readline() + while line: + lines.append(line) + total += len(line) + if 0 < sizehint <= total: + break + line = self.readline() + return lines + + def truncate(self, size=None): + """Truncate the file's size. + + If the optional size argument is present, the file is truncated to + (at most) that size. The size defaults to the current position. + The current file position is not changed unless the position + is beyond the new file size. + + If the specified size exceeds the file's current size, the + file remains unchanged. + """ + _complain_ifclosed(self.closed) + if size is None: + size = self.pos + elif size < 0: + raise IOError(22, "Negative size not allowed") + elif size < self.pos: + self.pos = size + self.buf = self.getvalue()[:size] + self.len = size + + def write(self, s): + """Write a string to the file. + + There is no return value. + """ + _complain_ifclosed(self.closed) + if not s: return + # Force s to be a string or unicode + if not isinstance(s, str): + s = str(s) + spos = self.pos + slen = self.len + if spos == slen: + self.buflist.append(s) + self.len = self.pos = spos + len(s) + return + if spos > slen: + self.buflist.append('\0'*(spos - slen)) + slen = spos + newpos = spos + len(s) + if spos < slen: + if self.buflist: + self.buf += ''.join(self.buflist) + self.buflist = [self.buf[:spos], s, self.buf[newpos:]] + self.buf = '' + if newpos > slen: + slen = newpos + else: + self.buflist.append(s) + slen = newpos + self.len = slen + self.pos = newpos + + def writelines(self, iterable): + """Write a sequence of strings to the file. The sequence can be any + iterable object producing strings, typically a list of strings. There + is no return value. + + (The name is intended to match readlines(); writelines() does not add + line separators.) + """ + write = self.write + for line in iterable: + write(line) + + def flush(self): + """Flush the internal buffer + """ + _complain_ifclosed(self.closed) + + def getvalue(self): + """ + Retrieve the entire contents of the "file" at any time before + the StringIO object's close() method is called. + + The StringIO object can accept either Unicode or 8-bit strings, + but mixing the two may take some care. If both are used, 8-bit + strings that cannot be interpreted as 7-bit ASCII (that use the + 8th bit) will cause a UnicodeError to be raised when getvalue() + is called. + """ + _complain_ifclosed(self.closed) + if self.buflist: + self.buf += ''.join(self.buflist) + self.buflist = [] + return self.buf diff --git a/src/lib/itertools.py b/src/lib/itertools.py new file mode 100644 index 0000000000..ee1486720b --- /dev/null +++ b/src/lib/itertools.py @@ -0,0 +1,14 @@ +def islice(iterable, limit): + # islice('ABCDEFG', 2) --> A B + # islice('ABCDEFG', 2, 4) --> C D + # islice('ABCDEFG', 2, None) --> C D E F G + # islice('ABCDEFG', 0, None, 2) --> A C E G + it = iter(range(0, limit, 1)) + try: + nexti = next(it) + except StopIteration: + return + for i, element in enumerate(iterable): + if i == nexti: + yield element + nexti = next(it) \ No newline at end of file diff --git a/src/lib/linecache.py b/src/lib/linecache.py index 255c992b8f..cafa1c4015 100644 --- a/src/lib/linecache.py +++ b/src/lib/linecache.py @@ -1 +1,177 @@ -raise NotImplementedError("linecache is not yet implemented in Skulpt") +"""Cache lines from Python source files. + +This is intended to read lines from modules imported -- hence if a filename +is not found, it will look down the module search path for a file by +that name. +""" + +import functools +import sys +import os +import tokenize + +__all__ = ["getline", "clearcache", "checkcache"] + +def getline(filename, lineno, module_globals=None): + lines = getlines(filename, module_globals) + if 1 <= lineno <= len(lines): + return lines[lineno-1] + else: + return '' + + +# The cache + +# The cache. Maps filenames to either a thunk which will provide source code, +# or a tuple (size, mtime, lines, fullname) once loaded. +cache = {} + + +def clearcache(): + """Clear the cache entirely.""" + + global cache + cache = {} + + +def getlines(filename, module_globals=None): + """Get the lines for a Python source file from the cache. + Update the cache if it doesn't contain an entry for this file already.""" + + if filename in cache: + entry = cache[filename] + if len(entry) != 1: + return cache[filename][2] + + try: + return updatecache(filename, module_globals) + except MemoryError: + clearcache() + return [] + + +def checkcache(filename=None): + """Discard cache entries that are out of date. + (This is not checked upon each call!)""" + + if filename is None: + filenames = list(cache.keys()) + else: + if filename in cache: + filenames = [filename] + else: + return + + for filename in filenames: + entry = cache[filename] + if len(entry) == 1: + # lazy cache entry, leave it lazy. + continue + size, mtime, lines, fullname = entry + if mtime is None: + continue # no-op for files loaded via a __loader__ + try: + stat = os.stat(fullname) + except OSError: + del cache[filename] + continue + if size != stat.st_size or mtime != stat.st_mtime: + del cache[filename] + + +def updatecache(filename, module_globals=None): + """Update a cache entry and return its list of lines. + If something's wrong, print a message, discard the cache entry, + and return an empty list.""" + + if filename in cache: + if len(cache[filename]) != 1: + del cache[filename] + if not filename or (filename.startswith('<') and filename.endswith('>')): + return [] + + fullname = filename + try: + stat = os.stat(fullname) + except OSError: + basename = filename + + # Realise a lazy loader based lookup if there is one + # otherwise try to lookup right now. + if lazycache(filename, module_globals): + try: + data = cache[filename][0]() + except (ImportError, OSError): + pass + else: + if data is None: + # No luck, the PEP302 loader cannot find the source + # for this module. + return [] + cache[filename] = ( + len(data), None, + [line+'\n' for line in data.splitlines()], fullname + ) + return cache[filename][2] + + # Try looking through the module search path, which is only useful + # when handling a relative filename. + if os.path.isabs(filename): + return [] + + for dirname in sys.path: + try: + fullname = os.path.join(dirname, basename) + except (TypeError, AttributeError): + # Not sufficiently string-like to do anything useful with. + continue + try: + stat = os.stat(fullname) + break + except OSError: + pass + else: + return [] + try: + with tokenize.open(fullname) as fp: + lines = fp.readlines() + except OSError: + return [] + if lines and not lines[-1].endswith('\n'): + lines[-1] += '\n' + size, mtime = stat.st_size, stat.st_mtime + cache[filename] = size, mtime, lines, fullname + return lines + + +def lazycache(filename, module_globals): + """Seed the cache for filename with module_globals. + + The module loader will be asked for the source only when getlines is + called, not immediately. + + If there is an entry in the cache already, it is not altered. + + :return: True if a lazy load is registered in the cache, + otherwise False. To register such a load a module loader with a + get_source method must be found, the filename must be a cachable + filename, and the filename must not be already cached. + """ + if filename in cache: + if len(cache[filename]) == 1: + return True + else: + return False + if not filename or (filename.startswith('<') and filename.endswith('>')): + return False + # Try for a __loader__, if available + if module_globals and '__loader__' in module_globals: + name = module_globals.get('__name__') + loader = module_globals['__loader__'] + get_source = getattr(loader, 'get_source', None) + + if name and get_source: + get_lines = functools.partial(get_source, name) + cache[filename] = (get_lines,) + return True + return False \ No newline at end of file diff --git a/src/lib/os.py b/src/lib/os.py index e47c9738e5..0adef78d92 100644 --- a/src/lib/os.py +++ b/src/lib/os.py @@ -1 +1,6 @@ -raise NotImplementedError("os is not yet implemented in Skulpt") +import sys + +import posixpath as path +sys.modules['os.path'] = path + +#raise NotImplementedError("os is not yet implemented in Skulpt") diff --git a/src/lib/pedal/__init__.py b/src/lib/pedal/__init__.py new file mode 100644 index 0000000000..26439398c7 --- /dev/null +++ b/src/lib/pedal/__init__.py @@ -0,0 +1,31 @@ +""" +A package for analyzing student code. +""" + +# Probably want to import useful stuff from: +# report +# source +# sandbox +# tifa +# cait +# resolver +# etc. + +import time; stopwatch = time.time(); + +from pedal.cait import (find_match, find_matches, + parse_program, + find_submatches, find_expr_sub_matches, + def_use_error, data_state, data_type, + expire_cait_cache) +print("Phase {}: {} secs".format("Cait loaded", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() +from pedal.report.imperative import (suppress, explain, compliment, + give_partial, gently, set_success) +print("Phase {}: {} secs".format("Imperative loaded", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() +from pedal.sandbox.sandbox import run, reset +print("Phase {}: {} secs".format("Sandbox loaded", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() +from pedal.tifa import tifa_analysis +print("Phase {}: {} secs".format("Tifa loaded", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() +from pedal.source import (set_source, check_section_exists, next_section, + set_source_file) +print("Phase {}: {} secs".format("Source loaded", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() diff --git a/src/lib/pedal/assertions/__init__.py b/src/lib/pedal/assertions/__init__.py new file mode 100644 index 0000000000..a4fc75249d --- /dev/null +++ b/src/lib/pedal/assertions/__init__.py @@ -0,0 +1,12 @@ +from pedal.report.imperative import MAIN_REPORT + +from pedal.assertions.setup import _setup_assertions, resolve_all +from pedal.assertions.assertions import * +from pedal.assertions.organizers import * + +def set_assertion_mode(exceptions=True, report=None): + if report is None: + report = MAIN_REPORT + _setup_assertions(report) + + report['assertions']['exceptions'] = exceptions diff --git a/src/lib/pedal/assertions/assertions.py b/src/lib/pedal/assertions/assertions.py new file mode 100644 index 0000000000..f916c01970 --- /dev/null +++ b/src/lib/pedal/assertions/assertions.py @@ -0,0 +1,645 @@ +import string +import re + +from pedal.report.imperative import MAIN_REPORT +from pedal.sandbox.result import SandboxResult +from pedal.sandbox.exceptions import SandboxException +from pedal.sandbox.sandbox import DataSandbox +from pedal.assertions.setup import _setup_assertions, AssertionException + +iterable = lambda obj: hasattr(obj,'__iter__') or hasattr(obj,'__getitem__') + +_MAX_LENGTH = 80 + +def _escape_curly_braces(result): + return result.replace("{", "{{").replace("}", "}}") + +def safe_repr(obj, short=False): + try: + result = repr(obj) + except Exception: + result = object.__repr__(obj) + if short and len(result) >= _MAX_LENGTH: + result = result[:_MAX_LENGTH] + ' [truncated]...' + result = result + return result + + +punctuation_table = str.maketrans(string.punctuation, ' ' * len(string.punctuation)) + + +def _normalize_string(a_string, numeric_endings=False): + # Lower case + a_string = a_string.lower() + # Remove trailing decimals (TODO: How awful!) + if numeric_endings: + a_string = re.sub(r"(\s*[0-9]+)\.[0-9]+(\s*)", r"\1\2", a_string) + # Remove punctuation + a_string = a_string.translate(punctuation_table) + # Split lines + lines = a_string.split("\n") + normalized = [[piece + for piece in line.split()] + for line in lines] + normalized = [[piece for piece in line if piece] + for line in normalized + if line] + return sorted(normalized) + + +def equality_test(actual, expected, _exact_strings, _delta, _test_output): + # Float comparison + if (isinstance(expected, float) and + isinstance(actual, (float, int)) and + abs(actual - expected) < _delta): + return True + # Exact Comparison + if actual == expected: + return True + # Inexact string comparison + if (_exact_strings and isinstance(expected, str) and + isinstance(actual, str) and + _normalize_string(actual) == _normalize_string(expected)): + return True + # Output comparison + if _test_output: + # Inexact output comparison + normalized_actual = [_normalize_string(line) for line in actual] + if (isinstance(expected, str) and + _normalize_string(expected) in normalized_actual): + return True + # Exact output comparison + normalized_expected = [_normalize_string(line) for line in expected] + if (isinstance(expected, list) and + normalized_expected == normalized_actual): + return True + # Else + return False + + +# Unittest Asserts +DELTA = .001 + + +def _fail(code_message, actual_message, expected_message, + show_expected_value, modify_right, *values): + normal_values = [] + sandboxed_values = [] + sandboxed_results = [] + if modify_right and values: + values = values[:-1] + (modify_right(values[-1]), ) + for value in values: + if is_sandbox_result(value): + sandboxed_results.append(value) + value = value._actual_value + sandboxed_values.append(safe_repr(value)) + else: + normal_values.append(safe_repr(value)) + if sandboxed_results: + code_message = _build_context(sandboxed_results, actual_message, + expected_message, show_expected_value) + return AssertionException(code_message.format(*(sandboxed_values + normal_values))) + + +def _build_result_from_target(target, index, quantity): + if target == "_": + if quantity == 1: + return "the result" + elif index == 0: + return "the first result" + else: + return "the second result" + return ""+target+"" + +def _build_context(sandboxed_results, actual_message, expected_message, + show_expected_value): + context = [] + calls = [] + inputs = [] + outputs = [] + targets = [] + for result in sandboxed_results: + # Look up info + call_id = result._actual_call_id + sandbox = result._actual_sandbox + outputs.extend(sandbox.output_contexts[call_id]) + calls.extend(sandbox.call_contexts[call_id]) + inputs.extend(sandbox.input_contexts[call_id]) + targets.append(sandbox.target_contexts[call_id]) + # Actual rendering of text + if calls: + calls = [_escape_curly_braces(str(call)) for call in calls] + context.append("I ran:
"+ "\n".join(calls)+ "
") + if inputs: + inputs = [_escape_curly_braces(str(inp)) for inp in inputs] + context.append("I entered as input:
"+ "\n".join(inputs)+ "
") + actual_message += ":
{}
" + for i, target in enumerate(targets): + named_target = _build_result_from_target(target, i, len(targets)) + if target == '_': + context.append(named_target.capitalize() + " "+actual_message) + else: + context.append("The value of "+named_target+" "+actual_message) + expected_context = "But I expected " + if len(targets) == 2: + expected_context += _build_result_from_target(targets[0], 0, 2) + expected_context += " " +expected_message + " " + expected_context += _build_result_from_target(targets[1], 1, 2) + else: + expected_context += _build_result_from_target(targets[0], 0, 1) + expected_context += " " + expected_message + if show_expected_value: + expected_context += ":
{}
" + context.append(expected_context) + return "\n".join(context) + + +def is_sandbox_result(value): + if hasattr(value, "__actual_class__"): + if value.__actual_class__ == SandboxResult: + return True + return False + + +def _basic_assertion(left, right, operator, code_comparison_message, + hc_message, hc_message_past, message, report, contextualize, + show_expected_value=True, modify_right=None): + if report is None: + report = MAIN_REPORT + _setup_assertions(report) + context = "" + if message: + message = "\n"+message + else: + message = "" + # TODO: Handle right-side sandbox result + #if is_sandbox_result(right): + # right = right._actual_value + if isinstance(left, Exception): + return False + if isinstance(right, Exception): + return False + if not operator(left, right): + failure = _fail(code_comparison_message, hc_message, hc_message_past, + show_expected_value, modify_right, left, right) + report['assertions']['collected'].append(failure) + report.attach('Instructor Test', category='student', tool='Assertions', + mistake={'message': "Student code failed instructor test.
\n"+ + context+str(failure)+message}) + report['assertions']['failures'] += 1 + if report['assertions']['exceptions']: + raise failure + else: + return False + return True + + +PRE_VAL = "" + + +def assertEqual(left, right, score=None, message=None, report=None, + contextualize=True, exact=False, compare_lengths=None): + if compare_lengths is None: + compare_lengths = (iterable(left) and isinstance(right, (int, float))) + if _basic_assertion(left, right, + lambda l, r: + equality_test(len(l), r, False, DELTA, False) if + compare_lengths else + equality_test(l, r, False, DELTA, False), + "len({}) != {}" if compare_lengths else "{} != {}", + "was"+PRE_VAL, + "to have its length equal to" + if compare_lengths else "to be equal to", + message, report, contextualize): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + + +assert_equal = assertEqual + + +def assertNotEqual(left, right, score=None, message=None, report=None, + contextualize=True, exact=False): + if _basic_assertion(left, right, + lambda l, r: not equality_test(l, r, False, DELTA, False), + "{} == {}", + "was"+PRE_VAL, + "to not be equal to", + message, report, contextualize): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + + +def assertTrue(something, score=None, message=None, report=None, + contextualize=True): + if _basic_assertion(something, True, + lambda l, r: bool(l), + "{} is true", + "was false"+PRE_VAL, + "to be true", + message, report, contextualize, + show_expected_value=False): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + + +def assertFalse(something, score=None, message=None, report=None, + contextualize=True): + if _basic_assertion(something, False, + lambda l, r: not bool(l), + "{} is false", + "was true"+PRE_VAL, + "to be false", + message, report, contextualize, + show_expected_value=False): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + + +def assertIs(left, right, score=None, message=None): + pass + + +def assertIsNot(left, right, score=None, message=None): + pass + +def _actually_is_none(l, r): + if is_sandbox_result(l): + return l._actual_value is None + return l is None + +def assertIsNone(something, score=None, message=None, report=None, + contextualize=True): + if _basic_assertion(something, None, + _actually_is_none, + "{} is none", + "was"+PRE_VAL, + "to be none", + message, report, contextualize, + show_expected_value=False): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + +def _actually_is_not_none(l, r): + if is_sandbox_result(l): + return l._actual_value is not None + return l is not None + +def assertIsNotNone(something, score=None, message=None, report=None, + contextualize=True): + if _basic_assertion(something, None, + _actually_is_not_none, + "{} is not none", + "was"+PRE_VAL, + "to not be none", + message, report, contextualize, + show_expected_value=False): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + + +def assertIn(needle, haystack, score=None, message=None, report=None, + contextualize=True): + expected_message = "to be in" + if not is_sandbox_result(needle) and is_sandbox_result(haystack): + expected_message = "to contain" + if _basic_assertion(needle, haystack, + lambda n, h: n in h, + "{} not in {}", + "was"+PRE_VAL, + expected_message, + message, report, contextualize): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + + +def assertNotIn(needle, haystack, score=None, message=None, report=None, + contextualize=True): + expected_message = "to not be in" + if not is_sandbox_result(needle) and is_sandbox_result(haystack): + expected_message = "to not contain" + if _basic_assertion(needle, haystack, + lambda n, h: n not in h, + "{} in {}", + "was"+PRE_VAL, + expected_message, + message, report, contextualize): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + +def _humanize_types(types): + if isinstance(types, tuple): + return ', '.join([t.__name__ for t in types]) + return types.__name__ + +def assertIsInstance(value, types, score=None, message=None, report=None, + contextualize=True): + if _basic_assertion(value, types, + lambda v, t: isinstance(v, t), + "isinstance({}, {})", + "was"+PRE_VAL, + "to be of type", + message, report, contextualize, + modify_right=_humanize_types): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + + +def assertNotIsInstance(value, types): + pass + + +def assertRaises(exception): + pass + + +def assertRaisesRegexp(exception): + pass + + +def assertAlmostEqual(left, right): + pass + + +def assertNotAlmostEqual(left, right): + pass + + +def assertGreater(left, right, score=None, message=None, report=None, + contextualize=True, compare_lengths=None): + if compare_lengths is None: + compare_lengths = (iterable(left) and isinstance(right, (int, float))) + if _basic_assertion(left, right, + lambda l, r: + len(l) > r if + compare_lengths else + l > r, + "len({}) <= {}" if compare_lengths else "{} <= {}", + "was"+PRE_VAL, + "to have its length greater than" + if compare_lengths else + "to be greater than", + message, report, contextualize): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + + +def assertGreaterEqual(left, right, score=None, message=None, report=None, + contextualize=True, compare_lengths=None): + if compare_lengths is None: + compare_lengths = (iterable(left) and isinstance(right, (int, float))) + if _basic_assertion(left, right, + lambda l, r: + len(l) >= r if + compare_lengths else + l >= r, + "len({}) < {}" if compare_lengths else "{} < {}", + "was"+PRE_VAL, + "to have its length greater than or equal to" if compare_lengths else + "to be greater than or equal to", + message, report, contextualize): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + + +def assertLess(left, right, score=None, message=None, report=None, + contextualize=True, compare_lengths=None): + if compare_lengths is None: + compare_lengths = (iterable(left) and isinstance(right, (int, float))) + if _basic_assertion(left, right, + lambda l, r: + len(l) < r if + compare_lengths else + l < r, + "len({}) >= {}" if compare_lengths else "{} >= {}", + "was"+PRE_VAL, + "to have its length less than" + if compare_lengths else + "to be less than", + message, report, contextualize): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + + +def assertLessEqual(left, right, score=None, message=None, report=None, + contextualize=True, compare_lengths=None): + if compare_lengths is None: + compare_lengths = (iterable(left) and isinstance(right, (int, float))) + if _basic_assertion(left, right, + lambda l, r: + len(l) <= r if + compare_lengths else + l <= r, + "len({}) > {}" if compare_lengths else "{} > {}", + "was"+PRE_VAL, + "to have its length less than or equal to" if compare_lengths else + "to be less than or equal to", + message, report, contextualize): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + + +def assertRegexpMatches(text, pattern): + pass + + +def assertNotRegexpMatches(text, pattern): + pass + + +def assertItemsEqual(left, right): + pass + + +def assertDictContainsSubset(left, right): + pass + + +def assertMultiLineEqual(left, right): + pass + + +def assertSequenceEqual(left, right): + pass + + +# Speciality Asserts +def assertPrints(result, expected_output, args=None, returns=None, + score=None, message=None, report=None, + contextualize=True, exact=False): + if not isinstance(result, SandboxResult): + return False + raise TypeError("You must pass in a SandboxResult (e.g., using `call`) to assertPrints") + if report is None: + report = MAIN_REPORT + _setup_assertions(report) + call_id = result._actual_call_id + sandbox = result._actual_sandbox + calls = sandbox.call_contexts[call_id] + inputs = sandbox.input_contexts[call_id] + actual_output = sandbox.output_contexts[call_id] + if not equality_test(actual_output, expected_output, exact, DELTA, True): + context= [] + if calls: + context.append("I ran:
"+
+                           "\n".join(map(str, calls))+
+                           "
") + if inputs: + context.append("I entered as input:
"+
+                           "\n".join(map(str, inputs))+
+                           "
") + if actual_output: + context.append("The function printed:
"+
+                           "\n".join(map(str, actual_output))+
+                           "
") + else: + context.append("The function printed nothing.") + context.append("But I expected the output:
"+ "\n".join(map(str, expected_output))+ "
") + failure = AssertionException("\n".join(context)) + report['assertions']['collected'].append(failure) + report.attach('Instructor Test', category='student', tool='Assertions', + mistake={'message': "Student code failed instructor test.
\n"+ + str(failure)}) + report['assertions']['failures'] += 1 + if report['assertions']['exceptions']: + raise failure + else: + return False + report.give_partial(score) + return True + +def assertHasFunction(obj, function, args=None, returns=None, + score=None, message=None, report=None, + contextualize=True, exact=False): + # If object is a sandbox, will check the .data[variable] attribute + # Otherwise, check it directly + if isinstance(obj, DataSandbox): + comparison = lambda o, f: f in o.data + else: + comparison = lambda o, f: f in hasattr(o, f) + if not _basic_assertion(obj, function, + comparison, + "Could not find function {}{}", + "was"+PRE_VAL, + "to have the function", + message, report, contextualize): + return False + if isinstance(obj, DataSandbox): + student_function = obj.data[function] + else: + student_function = getattr(obj, function) + if _basic_assertion(student_function, function, + lambda l, r: callable(l), + "The value {} is in the variable {}, and that value is not a callable function.", + "was callable"+PRE_VAL, + "to be callable", + message, report, contextualize, + show_expected_value=False): + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + return False + +def assertHasClass(sandbox, class_name, attrs=None): + pass + + +def assertHas(obj, variable, types=None, value=None, score=None, + message=None, report=None, contextualize=True): + # If object is a sandbox, will check the .data[variable] attribute + # Otherwise, check it directly + if isinstance(obj, DataSandbox): + comparison = lambda o, v: v in o.data + else: + comparison = lambda o, v: v in hasattr(o, v) + if not _basic_assertion(obj, variable, + comparison, + "Could not find variable {}{}", + "was"+PRE_VAL, + "to have the variable", + message, report, contextualize): + return False + if isinstance(obj, DataSandbox): + student_variable = obj.data[variable] + else: + student_variable = getattr(obj, variable) + if types is not None: + if not _basic_assertion(student_variable, types, + lambda v, t: isinstance(v, t), + "isinstance({}, {})", + "was"+PRE_VAL, + "to be of type", + message, report, contextualize, + modify_right=_humanize_types): + return False + if value is not None: + if not _basic_assertion(student_variable, value, + lambda l, r: equality_test(l, r, False, DELTA, False), + "{} != {}", + "was"+PRE_VAL, + "to be equal to", + message, report, contextualize, + show_expected_value=False): + return False + if report is None: + report = MAIN_REPORT + report.give_partial(score) + return True + +def assertGenerally(expression, score=None, message=None, report=None, + contextualize=True): + if report is None: + report = MAIN_REPORT + _setup_assertions(report) + if expression: + report.give_partial(score) + return True + else: + report['assertions']['failures'] += 1 + if report['assertions']['exceptions']: + raise AssertionException("General assertion") + else: + return False + +# Allow addition of new assertions +# e.g., assertGraphType, assertGraphValues diff --git a/src/lib/pedal/assertions/organizers.py b/src/lib/pedal/assertions/organizers.py new file mode 100644 index 0000000000..074b748e6a --- /dev/null +++ b/src/lib/pedal/assertions/organizers.py @@ -0,0 +1,155 @@ +''' + +Sections are a way to separate the pieces of a file such that the pieces do not +interfere with each other. + +Phases are a way to chunk a collection of functions together. If one of these +functions fails, the other functions in the phase will continue to be evaluated. +However, that phase will still have failed. You can establish that one phase +comes before or after another phase; if a precondition phase fails, then the +subsequent phase will not run. + +Example: + Students are working on a text adventure game and have to implement a + function named create_world(). The grading for portion of the assignment + has three phases: + 'create_world_exists' which confirms that the function was defined + 'create_world_returns' which confirms that calling the function + produces the right result. + 'create_world_complete' which confirms that the previous phase + terminated in order to give some partial credit. + + Although the 'create_world_exists' phase is composed of one function, the + 'create_world_returns' phase is actually composed of several functions that + check the components of the function. + + @phase('create_world_exists') + + @phase('create_world_returns', after='create_world_exists') + +Phases are reset between sections. + +''' + + +from pedal.report.imperative import MAIN_REPORT +from pedal.assertions.setup import (_setup_assertions, AssertionException, + _add_relationships, _add_phase) +from functools import wraps + +def contextualize_calls(): + pass + + +class _finish_section: + def __init__(self, number, *functions): + if isinstance(number, int): + self.number = number + else: + self.number = -1 + functions = [number] + list(functions) + self.functions = functions + for function in functions: + self(function, False) + + def __call__(self, f=None, quiet=True): + if f is not None: + f() + if quiet: + print("\tNEXT SECTION") + + def __enter__(self): + pass + + def __exit__(self, x, y, z): + print("\tNEXT SECTION") + # return wrapped_f + + +def finish_section(number, *functions, next_section=False): + if len(functions) == 0: + x = _finish_section(number, *functions) + x() + else: + result = _finish_section(number, *functions) + if next_section: + print("\tNEXT SECTION") + return result + +def section(*args): + ''' + TODO: Deprecate? + ''' + _setup_assertions(MAIN_REPORT) + def wrap(f): + _add_phase(phase_name, _handle_entry) + MAIN_REPORT['assertions']['phases'].append((section_number, f)) + return f + section_number = -1 + if len(args) >= 1 and callable(args[0]): + if len(args) >= 2: + section_number = args[1] + return wrap(args[0]) + elif len(args) >= 1: + section_number = args[0] + return wrap + +def phase(phase_name, before=None, after=None): + ''' + + Args: + phase_name (str): The name of the phase this function will belong to. + before (list[str] or str): the name(s) of any phases that this phase + should be before. + after (list[str] or str): the name(s) of any phases that this phase + should be after. + ''' + _setup_assertions(MAIN_REPORT) + def wrap(f): + @wraps(f) + def _handle_entry(*args, **kwargs): + old_exception_state = MAIN_REPORT['assertions']['exceptions'] + MAIN_REPORT['assertions']['exceptions'] = True + value = f(*args, **kwargs) + MAIN_REPORT['assertions']['exceptions'] = old_exception_state + return value + _add_phase(phase_name, _handle_entry) + _add_relationships(phase_name, before) + _add_relationships(after, phase_name) + return _handle_entry + return wrap + +def stop_on_failure(f): + _setup_assertions(MAIN_REPORT) + @wraps(f) + def wrapped(*args, **kwargs): + old_exception_state = MAIN_REPORT['assertions']['exceptions'] + MAIN_REPORT['assertions']['exceptions'] = True + value = None + try: + value = f(*args, **kwargs) + except AssertionException: + pass + MAIN_REPORT['assertions']['exceptions'] = old_exception_state + return value + return wrapped + + +def try_all(): + _setup_assertions(MAIN_REPORT) + @wraps(f) + def wrapped(*args, **kwargs): + old_exception_state = MAIN_REPORT['assertions']['exceptions'] + MAIN_REPORT['assertions']['exceptions'] = False + value = f(*args, **kwargs) + MAIN_REPORT['assertions']['exceptions'] = old_exception_state + return value + return wrapped + + +def precondition(function): + pass + + +def postcondition(function): + pass diff --git a/src/lib/pedal/assertions/setup.py b/src/lib/pedal/assertions/setup.py new file mode 100644 index 0000000000..609fbffaa1 --- /dev/null +++ b/src/lib/pedal/assertions/setup.py @@ -0,0 +1,111 @@ +import sys + +from pedal.report.imperative import MAIN_REPORT +from pedal.sandbox.exceptions import SandboxStudentCodeException + +class AssertionException(Exception): + pass + +def _topological_sort(names, orderings): + visited = set() + stack = [] + + def dfs(name): + visited.add(name) + if name in orderings: + for neighbor in orderings[name]: + if neighbor not in visited: + dfs(neighbor) + stack.insert(0, name) + + for name in names[::-1]: + if name not in visited: + dfs(name) + return stack + + +def resolve_all(set_success=False, report=None): + from pprint import pprint + if report is None: + report = MAIN_REPORT + _setup_assertions(report) + orderings = report['assertions']['relationships'] + phase_functions = report['assertions']['phase_functions'] + phase_names = report['assertions']['phases'] + phase_names = _topological_sort(phase_names, orderings) + #pprint(orderings) + phase_success = False + for phase_name in phase_names: + phase_success = True + for function in phase_functions[phase_name]: + try: + phase_success = phase_success and (function() is not False) + except AssertionException: + phase_success = False + except SandboxStudentCodeException: + phase_success = False + if not phase_success: + break + + #for f in report.feedback: + # print("\t", f, f.mistake, f.misconception) + if not report['assertions']['failures'] and phase_success and set_success: + report.set_success() + + _reset_phases(report) + +def _add_phase(phase_name, function, report=None): + if report is None: + report = MAIN_REPORT + phase_functions = report['assertions']['phase_functions'] + phases = report['assertions']['phases'] + if phase_name not in phase_functions: + phase_functions[phase_name] = [] + phases.append(phase_name) + phase_functions[phase_name].append(function) + +def _add_relationships(befores, afters, report=None): + if report is None: + report = MAIN_REPORT + relationships = report['assertions']['relationships'] + if None in (befores, afters): + return + if not isinstance(befores, (list, tuple)): + befores = [befores] + if not isinstance(afters, (list, tuple)): + afters = [afters] + for before in befores: + if not isinstance(before, str): + before = before.__name__ + if before not in relationships: + relationships[before] = [] + for after in afters: + if not isinstance(after, str): + after = after.__name__ + relationships[before].append(after) + + +def _reset_phases(report=None): + if report is None: + report = MAIN_REPORT + report['assertions']['relationships'].clear() + report['assertions']['phases'].clear() + report['assertions']['phase_functions'].clear() + report['assertions']['failures'] = 0 + + +def _setup_assertions(report): + if 'assertions' not in report: + report['assertions'] = { + 'phases': [], + 'phase_functions': {}, + 'relationships': {}, + 'exceptions': False, + 'failures': 0, + 'collected': [], + # Should we batch up multiple assertion failures? + # The grouping mechanism is try_all + 'tabular_output': False, + } + report.add_hook('source.next_section.before', resolve_all) + report.add_hook('pedal.resolvers.resolve', resolve_all) diff --git a/src/lib/pedal/cait/__init__.py b/src/lib/pedal/cait/__init__.py new file mode 100644 index 0000000000..a01b744ec1 --- /dev/null +++ b/src/lib/pedal/cait/__init__.py @@ -0,0 +1,16 @@ +""" +A package of tools for capturing student code by matching it against patterns. +""" + +NAME = 'CAIT' +SHORT_DESCRIPTION = "Captures instructor code patterns within student code." +DESCRIPTION = ''' +''' +REQUIRES = ['Source'] +OPTIONALS = ['TIFA'] + +from pedal.cait.cait_api import (find_match, find_matches, + parse_program, + find_submatches, find_expr_sub_matches, + def_use_error, data_state, data_type, + expire_cait_cache) diff --git a/src/lib/pedal/cait/ast_helpers.py b/src/lib/pedal/cait/ast_helpers.py new file mode 100644 index 0000000000..2adc042b31 --- /dev/null +++ b/src/lib/pedal/cait/ast_helpers.py @@ -0,0 +1,59 @@ +""" +A pretty-printing dump function for the ast module. The code was copied from +the ast.dump function and modified slightly to pretty-print. + +Alex Leone (acleone ~AT~ gmail.com), 2010-01-30 + +From http://alexleone.blogspot.co.uk/2010/01/python-ast-pretty-printer.html +""" + +from ast import AST, iter_fields, parse + + +def dump(node, annotate_fields=True, include_attributes=False, indent=' '): + """ + Return a formatted dump of the tree in *node*. This is mainly useful for + debugging purposes. The returned string will show the names and the values + for fields. This makes the code impossible to evaluate, so if evaluation is + wanted *annotate_fields* must be set to False. Attributes such as line + numbers and column offsets are not dumped by default. If this is wanted, + *include_attributes* can be set to True. + """ + + def _format(_node, level=0): + if isinstance(_node, AST): + fields = [(a, _format(b, level)) for a, b in iter_fields(_node)] + if include_attributes and _node._attributes: + fields.extend([(a, _format(getattr(_node, a), level)) + for a in _node._attributes]) + return ''.join([ + _node.__class__.__name__, + '(', + ', '.join(('%s=%s' % field for field in fields) + if annotate_fields else + (b for a, b in fields)), + ')']) + elif isinstance(_node, list): + lines = ['['] + lines.extend((indent * (level + 2) + _format(x, level + 2) + ',' + for x in _node)) + if len(lines) > 1: + lines.append(indent * (level + 1) + ']') + else: + lines[-1] += ']' + return '\n'.join(lines) + return repr(_node) + + if not isinstance(node, AST): + raise TypeError('expected AST, got %r' % node.__class__.__name__) + return _format(node) + + +def parseprint(code, filename="", mode="exec", **kwargs): + """Parse some code from a string and pretty-print it.""" + node = parse(code, mode=mode) # An ode to the code + print(dump(node, **kwargs)) + + +# Short name: pdp = parse, dump, print +pdp = parseprint diff --git a/src/lib/pedal/cait/ast_map.py b/src/lib/pedal/cait/ast_map.py new file mode 100644 index 0000000000..fb8d51095e --- /dev/null +++ b/src/lib/pedal/cait/ast_map.py @@ -0,0 +1,270 @@ +from pedal.cait.cait_node import CaitNode + + +class AstSymbol: + """ + This represents an Ast symbol, whether it be a variable (name node) or a function name + for place holders used in instructor patterns + + Notes: + Also has the attributes of the relevant Name node from the ast class. + + Attributes: + id (str): the name of the variable place holder used by the instructor + ast_node (cait_node): the ast node of the variable + """ + + def __init__(self, _id="", _node=None): + self.id = _id + self.astNode = _node + self.ast_node = _node + + def __getattr__(self, attr): + return getattr(self.astNode, attr) + + def __str__(self): + # return ''.join(["id = ", self.id.__str__(), ", astNode = ", type(self.astNode).__name__]) + return self.id + + def __repr__(self): + return ''.join(["id = ", self.id.__str__(), ", astNode = ", type(self.astNode).__name__]) + + +class AstSymbolList: + """ + This class is a wrapper for a list of AstSymbols for ease of access + If accessed as a list, manipulable as a list, otherwise, acts as the first AstSymbol in the list + """ + + def __init__(self): + self.my_list = [] + + def __getitem__(self, item): + return self.my_list.__getitem__(item) + + def append(self, item): + self.my_list.append(item) + + def __getattr__(self, attr): + return getattr(self.my_list[0], attr) + + def __len__(self): + return self.my_list.__len__() + + +class AstMap: + def __init__(self): + self.mappings = {} + self.symbol_table = {} + self.exp_table = {} + self.func_table = {} + self.conflict_keys = [] + self.match_root = None + self.diagnosis = "" + + def add_func_to_sym_table(self, ins_node, std_node): + """ + Adds ins_node.name to the symbol table if it doesn't already exist, mapping it to a set of ins_node. Updates a + second dictionary that maps ins_node to an std_node, and overwrites the current std_node since there should only + be one mapping. + + Args: + ins_node: instructor node or str representing a function name + std_node: student node representing function + + Returns: + int: number of conflicts generated + + """ + if not isinstance(std_node, CaitNode): + raise TypeError + if isinstance(ins_node, str): + key = ins_node + else: + try: + if ins_node.ast_name == "FunctionDef": + key = ins_node.astNode.name + else: # TODO: Little skulpt artifact that doesn't raise Attribute Errors... + key = ins_node._id + raise AttributeError + except AttributeError: + key = ins_node.astNode._id + + try: + if std_node.ast_name == "FunctionDef": + value = AstSymbol(std_node.astNode.name, std_node) + else: # TODO: Little skulpt artifact that doesn't raise Attribute Errors... + raise AttributeError +# value = AstSymbol(std_node.astNode.name, std_node) + except AttributeError: + node = std_node + if type(node.astNode).__name__ != "Call": + node = node.parent + node._id = std_node._id + value = AstSymbol(std_node._id, node) + if key in self.func_table: + new_list = self.func_table[key] + if value not in new_list: + new_list.append(value) + if not (key in self.conflict_keys): + for other in new_list: + if value.id != other.id: + self.conflict_keys.append(key) + break + else: + new_list = AstSymbolList() + new_list.append(value) + + self.func_table[key] = new_list + return len(self.conflict_keys) + + def add_var_to_sym_table(self, ins_node, std_node): + """ + Adds ins_node._id to the symbol table if it doesn't already exist, mapping it to a set of ins_node. Updates a + second dictionary that maps ins_node to an std_node, and overwrites the current std_node since there should only + be one mapping. + + Args: + ins_node: instructor node or str representing variable + std_node: student node representing variable + + Returns: + int: number of conflicts generated + + """ + if not isinstance(std_node, CaitNode): + raise TypeError + if isinstance(ins_node, str): + key = ins_node + else: + key = ins_node.astNode._id + value = AstSymbol(std_node.astNode._id, std_node) + if key in self.symbol_table: + new_list = self.symbol_table[key] + new_list.append(value) + if not (key in self.conflict_keys): + for other in new_list: + if value._id != other._id: + self.conflict_keys.append(key) + break + else: + new_list = AstSymbolList() + new_list.append(value) + + self.symbol_table[key] = new_list + return len(self.conflict_keys) + + def add_exp_to_sym_table(self, ins_node, std_node): + """ + Adds mapping of expression symbol to student node + This function does NOT check for conflicts at the moment and probably should at some point. + TODO: Check for conflicts + Args: + ins_node: Instructor node representing an expression + std_node: student ast subtree corresponding to the symbol + + Returns: + None + """ + if not isinstance(std_node, CaitNode): + raise TypeError + self.exp_table[ins_node.astNode.id] = std_node + + def add_node_pairing(self, ins_node, std_node): + """ + Adds a mapping of instructor ast node to a specific student ast node + Args: + ins_node: instructor pattern ast node + std_node: student ast node + + Returns: + None + """ + if not isinstance(std_node, CaitNode): + raise TypeError + self.mappings[ins_node] = std_node + + def has_conflicts(self): + """ + + Returns: + bool: True if number of conflicts is greater than 0 + """ + return len(self.conflict_keys) > 0 + + def new_merged_map(self, other): + """ + Returns a newly merged map consisting of this and other + without modifying self. + Args: + other (AstMap): the other AstMap to be merged with + + Returns: + AstMap: self modified by adding the contents of other + """ + new_map = AstMap() + new_map.merge_map_with(self) + new_map.merge_map_with(other) + return new_map + + def merge_map_with(self, other): + """ + Returns a newly merged map consisting of this and other + by modifying self + Args: + other (AstMap): the other AstMap to be merged with + + Returns: + AstMap: self modified by adding the contents of other + """ + if not isinstance(other, type(self)): + raise TypeError + + # merge all mappings + self.mappings.update(other.mappings) + + # merge all expressions + self.exp_table.update(other.exp_table) + + # merge all symbols + for key, value in other.symbol_table.items(): + for sub_value in value: + self.add_var_to_sym_table(key, sub_value.astNode) + + # merge all functions + for key, value in other.func_table.items(): + for sub_value in value: + self.add_func_to_sym_table(str(key), sub_value.astNode) + + @property + def match_lineno(self): + """ + + Returns: + int: the line number this match started on + """ + values = [v.lineno for v in self.mappings.values() + if v.lineno is not None] + if not values: + return -1 + else: + return min(values) + + def __getitem__(self, id_n): + if id_n.startswith('__'): + return self.exp_table[id_n] + else: + if id_n in self.symbol_table: + return self.symbol_table[id_n] + else: + return self.func_table[id_n] + + def __contains__(self, id_n): + if id_n.startswith('__'): + return id_n in self.exp_table + else: + exists = id_n in self.symbol_table + if exists: + return exists + else: + return id_n in self.func_table diff --git a/src/lib/pedal/cait/cait_api.py b/src/lib/pedal/cait/cait_api.py new file mode 100644 index 0000000000..115c770e02 --- /dev/null +++ b/src/lib/pedal/cait/cait_api.py @@ -0,0 +1,272 @@ +from pedal.report import MAIN_REPORT +from pedal.cait.stretchy_tree_matching import StretchyTreeMatcher +from pedal.cait.cait_node import CaitNode +import ast + + +class CaitException(Exception): + pass + + +""" +CaitReport: + A collection of information from the latest CAIT run. + + Attrs: + ast: The CaitNode tree that was most recently parsed out. + cache[str:CaitNode]: A dictionary mapping student code (str) to + parsed representations. + success: Whether there have been any errors so far. + error: The exception that occurred, or None if no exception so far. +""" + + +def _parse_source(code, cait_report): + """ + Parses the given code and returns its Cait representation. If the parse was + unsuccessful, it attaches the error to the report. + + Args: + code (str): A string of Python code. + cait_report (dict): A Cait Report to store information in. + Returns: + AstNode: The parsed AST reprensetation, or None + """ + try: + parsed = ast.parse(code) + except SyntaxError as e: + cait_report['success'] = False + cait_report['error'] = e + return ast.parse("") + return parsed + + +def _load_cait(student_code, report): + """ + Retrieves the current report for CAIT. If there is no CAIT report, it will + generate one. If source code is given, that will be used instead of the + report's source code. + + Args: + student_code (str): The code to parse into the a CaitNode tree. If + None, then it will use the code in the report's Source tool. + report (Report): The report to attach data to. + + Returns: + dict: Returns the Cait Report + """ + if 'cait' not in report: + report['cait'] = {'success': True, 'error': None, + 'ast': None, 'cache': {}} + cait = report['cait'] + if student_code is not None: + if student_code in cait['cache']: + cait['ast'] = cait['cache'][student_code] + return cait + else: + student_ast = _parse_source(student_code, cait) + elif report['source']['success']: + student_code = report['source']['code'] + if student_code in cait['cache']: + cait['ast'] = cait['cache'][student_code] + return cait + else: + student_ast = report['source']['ast'] + else: + report.attach("No source code found", tool='cait', + category='analyzer') + cait['success'] = False + cait['ast'] = CaitNode(ast.parse(""), report=report) + return cait + cait['ast'] = cait['cache'][student_code] = CaitNode(student_ast, report=report) + return cait + + +def require_tifa(self): + """ + Confirms that TIFA was run successfully, otherwise raises a + CaitException. + """ + if not self.report['tifa']['success']: + raise CaitException("TIFA was not run prior to CAIT.") + + +# noinspection PyBroadException +def parse_program(student_code=None, report=None): + """ + Parses student code and produces a CAIT representation. + + Args: + student_code (str): The student source code to parse. If None, defaults + to the code within the Source tool of the given Report. + report (Report): The report to attach data to. Defaults to MAIN_REPORT. + + Returns: + CaitNode: A CAIT-enhanced representation of the root Node. + """ + if report is None: + report = MAIN_REPORT + cait_report = _load_cait(student_code, report) + return cait_report['ast'] + + +def expire_cait_cache(report=None): + """ + Deletes the most recent CAIT run and any cached CAIT parses. + + Args: + report (Report): The report to attach data to. Defaults to MAIN_REPORT. + """ + if report is None: + report = MAIN_REPORT + report['cait']['ast'] = None + report['cait']['cache'] = {} + + +def def_use_error(node, report=None): + """ + Checks if node is a name and has a def_use_error + + Args: + node (str or AstNode or CaitNode): The Name node to look up. + report (Report): The report to attach data to. Defaults to MAIN_REPORT. + Returns: + True if the given name has a def_use_error + """ + if report is None: + report = MAIN_REPORT + if not isinstance(node, str) and node.ast_name != "Name": + raise TypeError + try: + def_use_vars = report['tifa']['issues']['Initialization Problem'] + except KeyError: + return False + if not isinstance(node, str): + node_id = node.id + else: + node_id = node + has_error = False + for issue in def_use_vars: + name = issue['name'] + if name == node_id: + has_error = True + break + return has_error + + +# noinspection PyBroadException +def data_state(node, report=None): + """ + Determines the Tifa State of the given node. + + Args: + node (str or AstNode or CaitNode): The Name node to look up in TIFA. + report (Report): The report to attach data to. Defaults to MAIN_REPORT. + Returns: + The State of the object (Tifa State) or None if it doesn't exist + """ + if report is None: + report = MAIN_REPORT + if not isinstance(node, str) and node.ast_name != "Name": + raise TypeError + if isinstance(node, str): + node_id = node + else: + node_id = node.id + try: + return report['tifa']["top_level_variables"][node_id] + except KeyError: + return None + + +def data_type(node, report=None): + """ + Looks up the type of the node using Tifa's analysis. + + Args: + node (str or AstNode or CaitNode): The Name node to look up in TIFA. + report (Report): The report to attach data to. Defaults to MAIN_REPORT. + Returns: + The type of the object (Tifa type) or None if a type doesn't exist + """ + state = data_state(node, report=report) + if state is not None: + return state.type + return None + + +def find_match(pattern, student_code=None, report=None, cut=False): + """ + Apply Tree Inclusion and return the first match of the `pattern` in the + `student_code`. + + Args: + pattern (str): The CaitExpression to match against. + student_code (str): The string of student code to check against. + Defaults to the code of the Source tool in the Report. + report (Report): The report to attach data to. + cut (bool): Set to true to trim root to first branch + Returns: + CaitNode or None: The first matching node for the given pattern, or + None if nothing was found. + """ + matches = find_matches(pattern=pattern, student_code=student_code, + report=report, cut=cut) + if matches: + return matches[0] + else: + return None + + +def find_matches(pattern, student_code=None, report=None, cut=False): + """ + Apply Tree Inclusion and return all matches of the `pattern` in the + `student_code`. + + Args: + pattern (str): The CaitExpression to match against. + student_code (str): The string of student code to check against. + report (Report): The report to attach data to. + cut (bool): Set to true to trim root to first branch + Returns: + List[CaitNode]: All matching nodes for the given pattern. + """ + if report is None: + report = MAIN_REPORT + cait_report = _load_cait(student_code, report) + if not cait_report['success']: + return [] + student_ast = cait_report['ast'] + matcher = StretchyTreeMatcher(pattern, report=report) + return matcher.find_matches(student_ast) + + +def find_submatches(pattern, student_code, is_mod=False): + """ + Incomplete. + """ + return find_expr_sub_matches(pattern, student_code, is_mod) + + +def find_expr_sub_matches(pattern, student_code, is_mod=False, report=None): + """ + Finds pattern in student_code + # TODO: Add code to make pattern accept CaitNodes + # TODO: Make this function without so much meta knowledge + Args: + pattern: the expression to find (str that MUST evaluate to a Module node with a single child or an AstNode) + student_code: student subtree + is_mod (bool): currently hack for multiline sub matches + report: defaults to MAIN_REPORT unless another one exists + Returns: + a list of matches or False if no matches found + """ + if report is None: + report = MAIN_REPORT + is_node = isinstance(pattern, CaitNode) + if not isinstance(pattern, str) and not is_node: + raise TypeError("pattern expected str or CaitNode, found {0}".format(type(pattern))) + matcher = StretchyTreeMatcher(pattern, report=report) + if (not is_node and not is_mod) and len(matcher.root_node.children) != 1: + raise ValueError("pattern does not evaluate to a singular statement") + return matcher.find_matches(student_code, check_meta=False) diff --git a/src/lib/pedal/cait/cait_node.py b/src/lib/pedal/cait/cait_node.py new file mode 100644 index 0000000000..50c83409a3 --- /dev/null +++ b/src/lib/pedal/cait/cait_node.py @@ -0,0 +1,503 @@ +import ast +from pedal.cait.ast_helpers import dump +from types import MethodType +from pedal.report import MAIN_REPORT + + +class CaitNode: + """ + A wrapper class for AST nodes. Linearizes access to the children of the ast + node and saves the field this AST node + originated from. + + Attributes: + ast_name (str): The name of the original AstNode (e.g., "Name" or + "FunctionDef") + + TODO: May want to just add fields and methods to the existing AST nodes and + use a production pattern instead. + """ + + def __init__(self, ast_node, my_field='', tid=0, lin_tree=None, + ancestor=None, report=None): + """ + + Args: + ast_node (ast_node): The AST node to be wrapped + my_field (str): the field of the parent node that produced this child. + tid (int): the tree id + lin_tree list of cait_node: A linear version of the tree + ancestor (cait_node): The parent of this node + report: The report associated with this particular match. + """ + if report is None: + report = MAIN_REPORT + self.report = report + self.children = [] + self.astNode = ast_node + self.field = my_field + self.tree_id = tid + self.parent = ancestor + if lin_tree is None: + self.linear_tree = [self] + else: + lin_tree.append(self) + self.linear_tree = lin_tree + + # reference to the easy node wrapping the ast_node + setattr(ast_node, 'cait_node', self) + + tid_count = tid + + my_field_generator = ast.iter_fields(self.astNode) + for item in my_field_generator: + field, value = item + # if the field doesn't have a value, no child exists + if value is None: + continue + + # If the children are not in an array, wrap it in an array for + # consistency in the code the follows + if not isinstance(value, list): + value = [value] + + # Reference ast_node_visitor.js for the original behavior and keep note of it for the purposes of handling + # the children noting the special case when the nodes of the array are actually parameters of the node + # (e.g. a load function) instead of a child node + for sub_value in value: + if isinstance(sub_value, ast.AST): + new_child = CaitNode(sub_value, my_field=field, + tid=tid_count + 1, + lin_tree=self.linear_tree, + ancestor=self, + report=self.report) + self.children.append(new_child) + tid_count = len(self.linear_tree) - 1 + + def __str__(self): + return ''.join([self.field, "\n", dump(self.astNode)]) + + def numeric_logic_check(self, mag, expr): + """ + If this node is a Compare or BoolOp node, sees if the logic in expr (a javascript string being a logical + statement) matches the logic of self. This assumes that we are only comparing numerical values to a single + variable + TODO: modify this to take multiple variables + TODO: modify to support more than +, -, *, and / BinOps + TODO: modify to support unary operators other than USub and Not + TODO: This is very finicky and buggy, try not to use it + Args: + mag (float): the order of magnitude that should be added to numbers to check logic, 1 is usually a good value, + especially when working with the set of integers. + expr (Compare or BoolOp): the "Compare" or "BoolOp" tree to check self against + + Returns: + bool: True if self (typically student node) and expr are equivalent boolean expressions + """ + + def eval_unop(unop_num, unop_node): + operand = eval_selector(unop_num, unop_node.operand) + op = unop_node.op_name + + return {"USub": -operand, + "Not": not operand}[op] + + def eval_binop(binop_num, binop_node): + left = eval_selector(binop_num, binop_node.left) + right = eval_selector(binop_num, binop_node.right) + op = binop_node.op_name + + return { + "Add": left + right, + "Sub": left - right, + "Mult": left * right, + "Div": left / right}[op] + + def eval_selector(op_num, op_expr): + op_expr = op_num if op_expr.ast_name == "Name" else op_expr + if isinstance(op_expr, (int, float)): + return op_expr + if op_expr.ast_name == "BinOp": + return eval_binop(op_num, op_expr) + if op_expr.ast_name == "UnaryOp": + return eval_unop(op_num, op_expr) + if op_expr.ast_name == "Num": + return op_expr.n + raise NotImplementedError + + def eval_bool_comp(num_list, comp_ast): + ops = comp_ast.ops_names + comps = comp_ast.comparators + results = [] + current = comp_ast.left + left = current + + for num_i in num_list: + result = True + for op, comp in zip(ops, comps): + current = eval_selector(num_i, current) + comp_p = eval_selector(num_i, comp) + + res = { + "Eq": current == comp_p, + "NotEq": current != comp_p, + "Lt": current < comp_p, + "LtE": current <= comp_p, + "Gt": current > comp_p, + "GtE": current >= comp_p, + }[op] + current = comp + result = result and res + if not result: + break + results.append(result) + current = left + return results + + def eval_boolop(num_list, boolop_ast): + boolop = boolop_ast.op_name + values = boolop_ast.values + results_c = None + is_and = boolop == "And" + for value in values: + if value.ast_name == "Compare": + results = eval_bool_comp(num_list, value) + else: # should be boolop + results = eval_boolop(num_list, value) + if results_c is None: + results_c = results + else: # compile results + new_result = [] + for result1, result2 in zip(results_c, results): + if is_and: + new_result.append(result1 and result2) + else: + new_result.append(result1 or result2) + results_c = new_result + return results_c + + try: + ins_expr = CaitNode(ast.parse(expr), report=self.report).body[0].value + ins_nums = ins_expr.find_all("Num") + std_nums = self.find_all("Num") + test_nums = [] + for num in ins_nums: + raw_num = num.n + test_nums.append(raw_num) + test_nums.append(raw_num + mag) + test_nums.append(raw_num - mag) + for num in std_nums: + raw_num = num.n + test_nums.append(raw_num) + test_nums.append(raw_num + mag) + test_nums.append(raw_num - mag) + + if self.ast_name == "Compare": + std_res = eval_bool_comp(test_nums, self) + elif self.ast_name == "BoolOp": + std_res = eval_boolop(test_nums, self) + else: + return False + + if ins_expr.ast_name == "Compare": + ins_res = eval_bool_comp(test_nums, ins_expr) + elif ins_expr.ast_name == "BoolOp": + ins_res = eval_boolop(test_nums, ins_expr) + else: + raise TypeError + return ins_res == std_res + except Exception: + return False + + def get_next_tree(self): + """Gets the next tree in the AST + This method gets the next AST node that is of equal or higher level than self. Returns None if the end of the + tree is reached + TODO: Create a get sibling method. + + Returns: + cait_node: The next tree in the AST + + """ + + # adding function to track tree ids + def visit_counter(self, node): + self.counter += 1 + self.generic_visit(node) + + node_counter = ast.NodeVisitor() + setattr(node_counter, 'counter', self.tree_id) + node_counter.visit = MethodType(visit_counter, node_counter) + + # getting ids + node_counter.visit(self.astNode) + out_of_tree = node_counter.counter >= len(self.linear_tree) # check if out of bounds + # len(self.children) > 0 and self.children[-1] == node_counter + if out_of_tree: + return None + return self.linear_tree[node_counter.counter] + + def get_child(self, node): + """ + + Args: + node: a non-CaitNode ast node + + Returns: + cait_node: the corresponding cait_node to the child + """ + if isinstance(node, ast.AST): + for child in self.children: + if child.astNode == node: + return child + elif isinstance(node, int): + return self.children(node) + return None + + @staticmethod + def get_ast_name(node): + return type(node).__name__ + + def get_clashing_attr(self, key): + if key == "value": + return self.get_value() + + def __getattr__(self, item): + key = item + """ + Non-ast node attributes based on ast_node attributes + """ + node_name = CaitNode.get_ast_name(self.astNode) + if node_name == "Assign" and key == "target": + key = "targets" + if item in AST_SINGLE_FUNCTIONS: + key = item[:-5] # strip suffix '_name' + if item in AST_ARRAYS_OF_FUNCTIONS: + key = item[:-6] # strip suffix '_names' + + """ + Non-ast node attributes + """ + if key == 'next_tree': + return self.get_next_tree() + if key == 'ast_name': + return node_name + elif key == '_name': + return self.astNode.name + elif key == 'ast_node': + return self.astNode + else: # ast node attributes or derivative attributes + if hasattr(self.astNode, key): + # noinspection PyBroadException + try: + field = self.astNode.__getattribute__(key) + except Exception: + field = None + if node_name == "Assign" and item != key: + if item == "target": + return field[0].cait_node # Get's the relevant ast node + elif item == "targets" and isinstance(field, list): + easy_array = [] + for node in field: + easy_array.append(node.cait_node) + return easy_array + else: + return field + elif item in AST_SINGLE_FUNCTIONS: + return type(field).__name__ + elif item in AST_ARRAYS_OF_FUNCTIONS: + str_ops_list = [] + for op in field: + str_ops_list.append(type(op).__name__) + return str_ops_list + elif isinstance(field, ast.AST): + return field.cait_node + elif isinstance(field, list): + try: + return [f.cait_node for f in field] + except AttributeError: + # This can only happen in NonLocals, which has a list + # of raw strings in the `names` property + return field + else: + return field + else: # get added field that may have existed for different node types + return self.get_clashing_attr(key) + + def find_matches(self, pattern, is_mod=False, check_meta=True): + """ + Retrieves any patterns that match against this CaitNode. Expected to be + used for subpattern matching. + """ + # Avoid circular import + import pedal.cait.stretchy_tree_matching as stm + is_node = isinstance(pattern, CaitNode) + if not isinstance(pattern, str) and not is_node: + raise TypeError("pattern expected str or CaitNode, found {0}".format(type(pattern))) + matcher = stm.StretchyTreeMatcher(pattern, report=self.report) + if (not is_node and not is_mod) and len(matcher.root_node.children) != 1: + raise ValueError("pattern does not evaluate to a singular statement") + return matcher.find_matches(self, check_meta=check_meta) + + def find_match(self, pattern, is_mod=False): + matches = self.find_matches(pattern, is_mod) + if len(matches) != 0: + return matches[0] + return None + + def find_all(self, node_type): + """Finds all nodes defined by string node_type + + Args: + node_type: the string representing the "type" of node to look for + + Returns: + a list of Ast Nodes (cait_nodes) of self that are of the specified type (including self if self + meets that criteria) + """ + items = [] + visitor = ast.NodeVisitor() + # setattr(visitor, "current_id", self.tree_id - 1) + setattr(visitor, "items", items) + func_name = 'visit_' + node_type + + def main_visit(self, node): + self.items.append(node.cait_node) + return self.generic_visit(node) + + func_ref = main_visit + setattr(visitor, func_name, MethodType(func_ref, visitor)) + visitor.visit(self.astNode) + return visitor.items + + def has(self, node): + """ + Determine if this node has the given `node`. + """ + if isinstance(node, (int, float)): + visitor = ast.NodeVisitor() + has_num = [] + + def visit_Num(self, potential): + has_num.append(node == potential.n) + return self.generic_visit(potential) + + visitor.visit_Num = MethodType(visit_Num, visitor) + visitor.visit(self.astNode) + return any(has_num) + elif node.ast_name != "Name": + return False + visitor = ast.NodeVisitor() + has_name = [] + + def visit_Name(self, potential): + has_name.append(node.id == potential.id) + return self.generic_visit(potential) + + visitor.visit_Name = MethodType(visit_Name, visitor) + visitor.visit(self.astNode) + return any(has_name) + + def is_before(self, other): + """ + Uses tree id to check if self node came before other. + Args: + other (cait_node): the other node to compare to + + Returns: + bool: True if self is before other + """ + try: + return self.tree_id < other.tree_id and self.linear_tree == other.linear_tree + except Exception: + raise TypeError + + def is_ast(self, ast_name): + """ + Checks self is the type of the specified ast node + Args: + ast_name (str): The name of the ast node type + + Returns: + bool: True if this node's ast name matches the specified one + """ + if not isinstance(ast_name, str): + ast_name = CaitNode.get_ast_name(ast_name.astNode) + return CaitNode.get_ast_name(self.astNode).lower() == ast_name.lower() + + def is_method(self): + """ + Checks if self is a method + + Returns: + bool: True if I'm a FunctionDef, and if any of my parents are ClassDef. + """ + # Check if I'm a FunctionDef, and if any of my parents are ClassDef. + if self.ast_name != "FunctionDef": + return False + current = self.parent + while current is not None: + if current.ast_name == "ClassDef": + return True + # Don't treat closures as methods + elif current.ast_name == "FunctionDef": + return False + current = current.parent + return False + + def get_data_state(self): + """ + Gets the data_state object of self + + Returns: + data_state or None: returns data_state if self is a name and exists, otherwise None + """ + if self.ast_name != "Name": + return None + try: + return self.report['tifa']["top_level_variables"][self.id] + except KeyError: + return None + + def get_data_type(self): + """ + + Returns: + type of the variable associated with this node if it's a name node, otherwise None. + """ + state = self.get_data_state() + if state is None: + return None + else: + return state.type + + def was_type(self, tp): + """ + + Returns: + type of the variable associated with this node if it's a name node, otherwise None. + """ + state = self.get_data_state() + if state is None: + return None + else: + return state.was_type(tp) + + def get_value(self): + """" + Returns: + Value of node if Num or Str, and get_data_state if Name + """ + value = None + if self.is_ast("Num"): + value = self.n + elif self.is_ast("Str"): + value = self.s + elif self.is_ast("Name"): + # TODO: Decide on what this should return... + value = self.id + return value + + +AST_SINGLE_FUNCTIONS = ["ctx_name", "op_name"] +AST_ARRAYS_OF_FUNCTIONS = ["ops_names"] diff --git a/src/lib/pedal/cait/stretchy_tree_matching.py b/src/lib/pedal/cait/stretchy_tree_matching.py new file mode 100644 index 0000000000..989c1942d9 --- /dev/null +++ b/src/lib/pedal/cait/stretchy_tree_matching.py @@ -0,0 +1,658 @@ +import ast +import re +from pedal.cait.ast_map import AstMap +from pedal.cait.cait_node import CaitNode + +# "Enums" for _name_regex +_VAR = "var" +_EXP = "exp" +_WILD = "wild" +_NONE_FIELD = "none" + + +def is_primitive(item): + """ + Determines if the given item is a primitive value (either an int, float, + str, bool, or None). + + Args: + item (any): Any value + Returns: + bool: Whether the item is a primitive value. + """ + return isinstance(item, (int, float, str, bool)) or item is None + + +def _name_regex(name_id): + var_match = re.compile('^_[^_].*_$') # /regex + exp_match = re.compile('^__.*__$') # /regex + wild_card = re.compile('^___$') # /regex + return {_VAR: var_match.match(name_id), + _EXP: exp_match.match(name_id), + _WILD: wild_card.match(name_id)} + + +class StretchyTreeMatcher: + def __init__(self, ast_or_code, report, filename="__main__"): + """ + The StretchyTreeMatcher is used to compare a pattern against some + student code. It produces a set of potential mappings between them. + + Args: + ast_or_code (str or AstNode): The students' code or a valid AstNode from + `ast.parse`. If the code has invalid syntax, a SyntaxError + will be raised. + filename (str): The filename to parse with - only used for error + reporting. + report (Report): A report to obtain data from. + """ + self.report = report + if isinstance(ast_or_code, str): + ast_node = ast.parse(ast_or_code, filename) + else: + ast_node = ast_or_code + # Build up root + if ast_node is None: + self.root_node = None + elif isinstance(ast_node, CaitNode): + self.root_node = ast_node + else: + self.root_node = CaitNode(ast_node, _NONE_FIELD, report=self.report) + + def find_matches(self, ast_or_code, filename="__main__", check_meta=True): + """ + Args: + ast_or_code (str or AstNode): The students' code or a valid AstNode from + `ast.parse`. If the code has invalid syntax, a SyntaxError + will be raised. + filename (str): The filename to parse with - only used for error + reporting. + check_meta (bool): Determine if the nodes came from the same AST + field. + Returns: + list[AstMap]: A list of AstMaps that are suitable matches. + """ + if isinstance(ast_or_code, str): + other_tree = CaitNode(ast.parse(ast_or_code, filename), report=self.report) + elif isinstance(ast_or_code, CaitNode): + other_tree = ast_or_code + else: + other_tree = CaitNode(ast_or_code, _NONE_FIELD, report=self.report) + explore_root = self.root_node + trim_set = ["Expr", "Module"] + explore_root_old_field = explore_root.field + if self.root_node is not None: # Trimming ins_node + while (len(explore_root.children) == 1 and + explore_root.ast_name in trim_set): + explore_root.field = explore_root_old_field + explore_root = explore_root.children[0] + explore_root_old_field = explore_root.field + explore_root.field = _NONE_FIELD + other_root = other_tree + other_root_old_field = other_root.field + if other_root is not None: # Trimming std_node + while len(other_root.children) == 1 and other_root.ast_name in trim_set: + other_root.field = other_root_old_field + other_root = other_root.children[0] + other_root_old_field = other_root.field + other_root.field = _NONE_FIELD + matches = self.any_node_match(explore_root, other_root, + check_meta=check_meta) + explore_root.field = explore_root_old_field + other_root.field = other_root_old_field + return matches + + def any_node_match(self, ins_node, std_node, check_meta=True, cut=False): + """ + Finds whether ins_node can be matched to some node in the tree std_node + + Args: + ins_node: + std_node: + check_meta: + cut: + + Returns: + list of AstMaps: a mapping of nodes and a symbol table mapping ins_node to + some node in the tree std_node or False if such a matching does not + exist + """ + # @TODO: create a more public function that converts ins_node and std_node into CaitNodes + # TODO: Create exhaustive any_node_match + # matching: an object representing the mapping and the symbol table + matching = self.deep_find_match(ins_node, std_node, check_meta) + # if a direct matching is found + if matching: + for match in matching: + match.match_root = std_node + else: + matching = [] + # return matching # return it + # if not matching or exhaust: # otherwise + # try to matching ins_node to each child of std_node, recursively + for std_child in std_node.children: + matching_c = self.any_node_match(ins_node, std_child, check_meta=check_meta, cut=cut) + if matching_c: + for match in matching_c: + match.match_root = std_child + # return matching + matching = matching + matching_c + if len(matching) > 0: + return matching + return [] + + def deep_find_match(self, ins_node, std_node, check_meta=True): + """ + Finds whether ins_node and matches std_node and whether ins_node's children flexibly match std_node's children + in order + Args: + ins_node: The instructor ast that should be included in the student AST + std_node: The student AST that we are searching for the included tree + check_meta: Flag, if True, check whether the two nodes originated from the same ast field + + Returns: + a mapping of nodes and a symbol table mapping ins_node to std_node, or [] if no mapping was found + """ + method_name = "deep_find_match_" + type(ins_node.astNode).__name__ + target_func = getattr(self, method_name, self.deep_find_match_generic) + return target_func(ins_node, std_node, check_meta) + + # noinspection PyPep8Naming + def deep_find_match_Name(self, ins_node, std_node, check_meta=True): + name_id = ins_node.astNode.id + match = _name_regex(name_id) + mapping = AstMap() + matched = False + meta_matched = self.metas_match(ins_node, std_node, check_meta) + if match[_VAR] and meta_matched: # if variable + # This if body is probably unnecessary. + if type(std_node.astNode).__name__ == "Name": + return self.deep_find_match_generic(ins_node, std_node, check_meta=check_meta, ignores=["ctx"]) + # could else return False, but shallow_match_generic should do this as well + elif match[_EXP]: # and meta_matched: # if expression + # terminate recursion, the whole subtree should match since expression nodes match to anything + mapping.add_exp_to_sym_table(ins_node, std_node) + matched = True + elif match[_WILD] and meta_matched: # if wild card, don't care + # terminate the recursion, the whole subtree should match since wild cards match to anything + matched = True + + if matched: + mapping.add_node_pairing(ins_node, std_node) + return [mapping] + # else + return self.deep_find_match_generic(ins_node, std_node, check_meta=check_meta, ignores=["ctx"]) + + # noinspection PyPep8Naming + def deep_find_match_BinOp(self, ins_node, std_node, check_meta=True): + op = ins_node.astNode.op + op = type(op).__name__ + is_generic = not (op == "Mult" or op == "Add") + if is_generic: + return self.deep_find_match_generic(ins_node, std_node, check_meta) + else: # this means that the node is clearly commutative + return self.deep_find_match_binflex(ins_node, std_node, False) + + # noinspection PyMethodMayBeStatic + def binflex_helper(self, case_left, case_right, new_mappings, base_mappings): + """ + adds to new_mappings (return/modify by argument) the mappings for both the left and right subtrees as denoted by + case_left and case_right + Args: + case_left: The mappings for the left opperand + case_right: The mappings for the right opperand + new_mappings: The new set of mappings to generate + base_mappings: The original mappings of the binop node + + Returns: + None + """ + if case_left and case_right: + for case_l in case_left: + new_map = base_mappings[0].new_merged_map(case_l) + for case_r in case_right: + both = new_map.new_merged_map(case_r) + if not both.has_conflicts(): + new_mappings.append(both) + + def deep_find_match_binflex(self, ins_node, std_node, check_meta=False): + base_mappings = self.shallow_match(ins_node, std_node, check_meta) + if not base_mappings: + return [] + op_mappings = self.shallow_match(ins_node.children[1], std_node.children[1], check_meta=True) + if not op_mappings: + return [] + base_mappings = [base_mappings[0].new_merged_map(op_mappings[0])] + + if base_mappings: + ins_left = ins_node.children[0] # instructor left ast node + ins_right = ins_node.children[2] # instructor right ast node + std_left = std_node.children[0] # student left ast node + std_right = std_node.children[2] # student right ast node + new_mappings = [] + # case 1: ins_left->std_left and ins_right->std_right + case_left = self.deep_find_match(ins_left, std_left, False) + case_right = self.deep_find_match(ins_right, std_right, False) + self.binflex_helper(case_left, case_right, new_mappings, base_mappings) + # case 2: ins_left->std_right and ins_right->std_left + case_left = self.deep_find_match(ins_left, std_right, False) + case_right = self.deep_find_match(ins_right, std_left, False) + self.binflex_helper(case_left, case_right, new_mappings, base_mappings) + if len(new_mappings) == 0: + return [] + return new_mappings + return [] + + def deep_find_match_Expr(self, ins_node, std_node, check_meta=True): + """ + An Expression node (not to be confused with expressions denoted by the instructor nodes in Name ast nodes) + checks whether it should be generic, or not + Args: + ins_node: Instructor ast to find in the student ast + std_node: Student AST to search for the instructor ast in + check_meta: flag to check whether the fields of the instructor node and the student node should match + + Returns: + AstMap: a mapping between the instructor and student asts, or False if such a mapping doesn't exist + """ + # if check_meta and ins_node.field != std_node.field: + if not self.metas_match(ins_node, std_node, check_meta): + return [] + mapping = AstMap() + value = ins_node.value + ast_type = type(value.astNode).__name__ + if ast_type == "Name": + name_id = value.astNode.id + exp_match = re.compile('^__.*__$') # /regex + wild_card = re.compile('^___$') # /regex + matched = False + meta_matched = self.metas_match(ins_node, std_node, check_meta) + if exp_match.match(name_id): # and meta_matched: # if expression + # terminate recursion, the whole subtree should match since expression nodes match to anything + mapping.add_exp_to_sym_table(value, std_node) + matched = True + elif wild_card.match(name_id) and meta_matched: # if wild card, don't care + # terminate the recursion, the whole subtree should match since wild cards match to anything + matched = True + if matched: + mapping.add_node_pairing(ins_node, std_node) + return [mapping] + return self.deep_find_match_generic(ins_node, std_node, check_meta) + + def deep_find_match_generic(self, ins_node, std_node, check_meta=True, ignores=None): + """ + This first uses shallow match to find a base map (match) from which to + build off. The algorithm then tracks all the possible mappings that + match a given child node in the instructor AST, keeping track of which + siblings have been visited. + + For each instructor child, when all children of the student node have + been iterated through recursively, a helper function is called. This + helper function determines which possible children validly can extend + the base match to create a set of new base maps through use of the + indicies of the sibilings. + + The process repeats itself until no matches can be grown or until each + instructor child node has been visited + + Args: + ins_node: Instructor ast to find in the student ast + std_node: Student AST to search for the instructor ast in + check_meta: flag to check whether the fields of the instructor node and the student node should match + ignores: List of fields to ignore in the field match + + Returns: + a mapping between the isntructor and student asts, or [] if such a mapping doesn't exist + """ + if ignores is None: + ignores = [] + base_mappings = self.shallow_match(ins_node, std_node, check_meta) + if base_mappings: + # base case this runs 0 times because no children + # find each child of ins_node that matches IN ORDER + base_sibs = [-1] + youngest_sib = 0 + # for each child + for i, insChild in enumerate(ins_node.children): + # make a new set of maps + running_maps = [] + running_sibs = [] + if insChild.field in ignores: + continue + # accumulate all potential matches for current child + for j, std_child in enumerate(std_node.children[youngest_sib:], youngest_sib): + std_child = std_node.children[j] + new_mapping = self.deep_find_match(insChild, std_child, check_meta) + if new_mapping: + running_maps.append(new_mapping) + running_sibs.append(j) + map_update = self.map_merge(base_mappings, base_sibs, running_maps, running_sibs) + if map_update is None: + return [] + base_mappings = map_update['new_maps'] + base_sibs = map_update['new_sibs'] + youngest_sib = map_update['youngest_sib'] + 1 + return base_mappings + return [] + + # noinspection PyMethodMayBeStatic + def map_merge(self, base_maps, base_sibs, run_maps, run_sibs): + """ + Merges base_maps with the current possible maps. Helper method to deep_find_match_generic. checks whether each + mapping in run_maps can extend the match to any possible mapping in base_maps. + + Args: + base_maps: The original mappings + base_sibs: The corresponding siblings for each mapping in base_maps + run_maps: The set of maps to merge into the current base_maps + run_sibs: The corresponding siblings for each mapping in run_maps + + Returns: + A new set of maps for all valid extensions of base_maps with running maps + """ + # no matching nodes were found + if len(run_maps) == 0: + return None + new_maps = [] + new_sibs = [] + youngest_sib = run_sibs[0] + for baseMap, base_sib in zip(base_maps, base_sibs): + for run_map, runSib in zip(run_maps, run_sibs): + if runSib > base_sib: + for run_mapsub in run_map: + new_map = baseMap.new_merged_map(run_mapsub) + if not new_map.has_conflicts(): # if it's a valid mapping + new_maps.append(new_map) + new_sibs.append(runSib) + if len(new_maps) == 0: + return None + return { + 'new_maps': new_maps, + 'new_sibs': new_sibs, + 'youngest_sib': youngest_sib + } + + # noinspection PyMethodMayBeStatic,PyPep8Naming,PyUnusedLocal + def shallow_match_Module(self, ins_node, std_node, check_meta=True): + """ + Flexibly matches a module node to a module or a body + Args: + ins_node: + std_node: + check_meta: + + Returns: + a mapping of ins_node to std_node, or False if doesn't match + """ + if type(std_node.astNode).__name__ == "Module" or std_node.field == "body": + mapping = AstMap() + mapping.add_node_pairing(ins_node, std_node) + return [mapping] + return [] + + def shallow_symbol_handler(self, ins_node, std_node, id_val, check_meta=True): + """ + TODO: Make this handle the func field to handle functions + Matches ins_node to std_node for different cases of encountering a name node in ins_node + case 1: _var_ matches if std_node is a name node and automatically returns a mapping and symbol table + case 2: __exp__ matches to any subtree and automatically returns a mapping and symbol table + case 3: ___ matches to any subtree and automatically returns a mapping + case 4: matches only if the exact names are the same (falls through to shallow_match_generic) + Args: + ins_node: + std_node: + id_val: + check_meta: + + Returns: + list of AstMap: a mapping of ins_node to std_node and possibly a symbol_table, or False if it doesn't match + """ + name_id = ins_node.astNode.__getattribute__(id_val) + match = _name_regex(name_id) + mapping = AstMap() + matched = False + # TODO: add functionality to add function references to func_table? + meta_matched = self.metas_match(ins_node, std_node, check_meta) + if match[_VAR] and meta_matched: # variable + if type(std_node.astNode).__name__ == "Name" or id_val in ["attr", "arg"]: + if id_val in ["attr", "arg"]: + std_node.astNode._id = std_node.astNode.__getattribute__(id_val) + if std_node.field == "func" and ins_node.field != _NONE_FIELD: + # TODO: This 'ins_node.field != _NONE_FIELD' code is for an obscure edge case where the + # instructor code is only _var_ + std_node.astNode._id = std_node.astNode.__getattribute__(id_val) + mapping.add_func_to_sym_table(ins_node, std_node) + else: + std_node.astNode._id = std_node.astNode.__getattribute__(id_val) + mapping.add_var_to_sym_table(ins_node, std_node) # TODO: Capture result? + matched = True + # could else return False, but shallow_match_generic should do this as well + elif match[_EXP] and meta_matched: + mapping.add_exp_to_sym_table(ins_node, std_node) + matched = True + elif match[_WILD] and meta_matched: + matched = True + + if matched: + mapping.add_node_pairing(ins_node, std_node) + return [mapping] + # else + return self.shallow_match_main(ins_node, std_node, check_meta=check_meta, ignores=["ctx"]) + + # noinspection PyPep8Naming,PyMethodMayBeStatic + def shallow_match_arg(self, ins_node, std_node, check_meta=True): + ins_node.astNode._id = ins_node.arg + # TODO: annotations are currently ignored because shallow_symbol_handler doesn't handle them, feature? or + # should we fix this. Although this should actually be toggleable? + return self.shallow_symbol_handler(ins_node, std_node, "arg", check_meta=check_meta) + + def shallow_match_arguments(self, ins_node, std_node, check_meta=True): + # TODO: do we ignore default values? Currently not ignored + return self.shallow_match_generic(ins_node, std_node, check_meta=check_meta) + + # noinspection PyPep8Naming,PyMethodMayBeStatic + def shallow_func_handle(self, ins_node, std_node, check_meta=True): + if ins_node.field == "func" and std_node.field == "func": + ins_node.astNode._id = ins_node.astNode.attr + return self.shallow_symbol_handler(ins_node, std_node, "attr", check_meta) + return self.shallow_match_generic(ins_node, std_node, check_meta) + + def shallow_match_Attribute(self, ins_node, std_node, check_meta=True): + if ins_node.field == "func" and std_node.ast_name == "Attribute": + return self.shallow_func_handle(ins_node, std_node, check_meta) + elif std_node.ast_name == "Attribute": + ins_node.astNode._id = ins_node.attr # TODO: Fix this hack more gracefully + # add_var_to_sym_table in ast_map needs the id attribute to make the map + return self.shallow_symbol_handler(ins_node, std_node, "attr", check_meta) + else: + return self.shallow_match_generic(ins_node, std_node, check_meta) + + # noinspection PyPep8Naming + def shallow_match_Name(self, ins_node, std_node, check_meta=True): + """ + TODO: Make this handle the func field to handle functions + Matches ins_node to std_node for different cases of encountering a name node in ins_node + case 1: _var_ matches if std_node is a name node and automatically returns a mapping and symbol table + case 2: __exp__ matches to any subtree and automatically returns a mapping and symbol table + case 3: ___ matches to any subtree and automatically returns a mapping + case 4: matches only if the exact names are the same (falls through to shallow_match_generic) + Args: + ins_node: + std_node: + check_meta: + + Returns: + list of AstMap: a mapping of ins_node to std_node and possibly a symbol_table, or False if it doesn't match + """ + ins_node.ast_node._id = ins_node.id + return self.shallow_symbol_handler(ins_node, std_node, "id", check_meta) + + # noinspection PyPep8Naming,PyMethodMayBeStatic + def shallow_match_Pass(self, ins_node, std_node, check_meta=True): + """ + An empty body should match to anything + Args: + ins_node: Instructor ast to find in the student ast + std_node: Student AST to search for the instructor ast in + check_meta: flag to check whether the fields of the instructor node and the student node should match + + Returns: + list of AstMap: a mapping between the isntructor and student asts, or False if such a mapping doesn't exist + """ + # if check_meta and ins_node.field != std_node.field: + if not self.metas_match(ins_node, std_node, check_meta): + return [] + mapping = AstMap() + mapping.add_node_pairing(ins_node, std_node) + return [mapping] + + # noinspection PyPep8Naming,PyMethodMayBeStatic + def shallow_match_Expr(self, ins_node, std_node, check_meta=True): + """ + An Expression node (not to be confused with expressions denoted by the instructor nodes in Name ast nodes) + should match to anything + Args: + ins_node: Instructor ast to find in the student ast + std_node: Instructor ast to find in the student ast + check_meta: flag to check whether the fields of the instructor node and the student node should match + + Returns: + a mapping between the instructor and student asts, or False if such a mapping doesn't exist + """ + # if check_meta and ins_node.field != std_node.field: + if not self.metas_match(ins_node, std_node, check_meta): + return [] + mapping = AstMap() + mapping.add_node_pairing(ins_node, std_node) + return [mapping] + + def shallow_match_Call(self, ins_node, std_node, check_meta=True): + return self.shallow_match_main(ins_node, std_node, check_meta, ignores=None) + # matches = self.shallow_match_main(ins_node, std_node, check_meta, ignores=["func"]) + # if matches: + # pass + # return None + # TODO: Make this handle Calls more intelligently + + # noinspection PyPep8Naming + def shallow_match_FunctionDef(self, ins_node, std_node, check_meta=True): + ins = ins_node.astNode + std = std_node.astNode + meta_matched = self.metas_match(ins_node, std_node, check_meta) + is_match = type(ins).__name__ == type(std).__name__ and meta_matched + mapping = self.shallow_match_main(ins_node, std_node, check_meta, ignores=['name', 'args']) + matched = False + if is_match and mapping: + name = ins.name + match = _name_regex(name) + if match[_VAR] and meta_matched: # variable + ins._id = name + std._id = std.name + mapping[0].add_func_to_sym_table(ins_node, std_node) # TODO: Capture result? + matched = True + elif match[_WILD] and meta_matched: + matched = True + elif name == std.name and meta_matched: + matched = True + if matched: + return mapping + else: + return [] + + # noinspection PyMethodMayBeStatic + def shallow_match_generic(self, ins_node, std_node, check_meta=True): + """ + Checks that all non astNode attributes are equal between ins_node and std_node + Args: + ins_node: Instructor ast root node + std_node: Student AST root node + check_meta: flag to check whether the fields of the instructor node and the student node should match + + Returns: + list of AstMap: a mapping between the instructor and student root nodes (potentially empty) + """ + return self.shallow_match_main(ins_node, std_node, check_meta=check_meta) + + def shallow_match_main(self, ins_node, std_node, check_meta=True, ignores=None): + """ + Checks that all non astNode attributes are equal between ins_node and std_node + Args: + ins_node: Instructor ast root node + std_node: Student AST root node + check_meta: flag to check whether the fields of the instructor node and the student node should match + ignores: a mapping between the instructor and student root nodes, or False if such a mapping doesn't exist + + Returns: + + """ + if ignores is None: + ignores = [] + ignores.append("_id") # special exception for symbols in lookup tables + ins = ins_node.astNode + std = std_node.astNode + ins_field_list = list(ast.iter_fields(ins)) + std_field_list = list(ast.iter_fields(std)) + meta_matched = self.metas_match(ins_node, std_node, check_meta) + is_match = len(ins_field_list) == len(std_field_list) and type(ins).__name__ == type( + std).__name__ and meta_matched + for insTup, stdTup in zip(ins_field_list, std_field_list): + if not is_match: + break + + ins_field = insTup[0] + ins_value = insTup[1] + std_field = stdTup[0] + std_value = stdTup[1] + + if ins_value is None: + continue + + ignore_field = ins_field in ignores + + is_match = (ins_field == std_field) or ignore_field + + if not isinstance(ins_value, list): + ins_value = [ins_value] + + if not isinstance(std_value, list): + std_value = [std_value] + + # is_match = len(ins_value) == len(std_value)# for stretchy matching this isn't True + # Reference ast_node_visitor.js for the original behavior and keep note of it for the purposes of handling + # the children noting the special case when the nodes of the array are actually parameters of the node + # (e.g. a load function) instead of a child node + if not ignore_field: + for inssub_value, stdsub_value in zip(ins_value, std_value): + if not is_match: + break + # TODO: make this a smarter comparison, maybe handle dictionaries, f-strings, tuples, etc. + if is_primitive(inssub_value): + is_match = inssub_value == stdsub_value + if is_match: + mapping = AstMap() # return MAPPING + mapping.add_node_pairing(ins_node, std_node) + return [mapping] + else: + return [] + + # filter function for various types of nodes + def shallow_match(self, ins_node, std_node, check_meta=True): + method_name = 'shallow_match_' + type(ins_node.astNode).__name__ + target_func = getattr(self, method_name, self.shallow_match_generic) + return target_func(ins_node, std_node, check_meta=check_meta) + + @staticmethod + def metas_match(ins_node, std_node, check_meta=True): + """ + Args: + ins_node: + std_node: + check_meta: + + Returns: + + """ + return ((check_meta and ins_node.field == std_node.field) or + not check_meta + # or std_node.field == _NONE_FIELD + or ins_node.field == _NONE_FIELD) diff --git a/src/lib/pedal/mistakes/__init__.py b/src/lib/pedal/mistakes/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/lib/pedal/mistakes/feedback_mod.py b/src/lib/pedal/mistakes/feedback_mod.py new file mode 100644 index 0000000000..d7f6b67461 --- /dev/null +++ b/src/lib/pedal/mistakes/feedback_mod.py @@ -0,0 +1,15 @@ +from pedal.report.imperative import explain, gently + + +def gently_r(message, code, line=None, tldr="explain"): + gently(message + "

({})

".format(code), line, label=tldr) + return message + + +def explain_r(message, code, priority='medium', line=None, tldr="explain"): + explain(message + "

({})

".format(code), priority, line, label=tldr) + return message + + +def codify(code): + return "" + code + "" diff --git a/src/lib/pedal/mistakes/instructor_append.py b/src/lib/pedal/mistakes/instructor_append.py new file mode 100644 index 0000000000..aa69495f6a --- /dev/null +++ b/src/lib/pedal/mistakes/instructor_append.py @@ -0,0 +1,131 @@ +from pedal.cait.cait_api import find_matches, find_expr_sub_matches, data_state +from pedal.mistakes.feedback_mod import * + + +def append_group_on_change(): + wrong_not_append_to_list() + + +def append_group(): + missing_append_in_iteration() + missing_append_list_initialization() + wrong_append_list_initialization() + wrong_not_append_to_list() + append_list_wrong_slot() + # TODO: add app_assign on next iteration of experiment! + # app_assign() + + +def find_append_in(node): + append_list = [] + calls = node.find_all("Call") + for node in calls: + if node.func.attr == "append": + append_list.append(node) + return append_list + + +""" +def missing_append_in_iteration(): + std_ast = parse_program() + for_loops = std_ast.find_all("For") + for loop in for_loops: + if len(find_append_in(loop)): + return False + explain("You must construct a list by appending values one at a time to the list.

(app_in_iter)
") + return True +""" + + +def missing_append_in_iteration(): + matches = find_matches("for ___ in ___:\n" + " __expr__") + if matches: + for match in matches: + __expr__ = match["__expr__"] + submatch = __expr__.find_matches("___.append(___)") + if submatch: + return False + explain("You must construct a list by appending values one at a time to the list." + "

(app_in_iter)
") + return True + return False + + +def wrong_not_append_to_list(): + matches = find_matches("for ___ in ___:\n" + " __expr__") + for match in matches: + __expr__ = match["__expr__"] + submatches = __expr__.find_matches("_target_.append(___)") + for submatch in submatches: + _target_ = submatch["_target_"] + if not data_state(_target_).was_type('list'): + explain("Values can only be appended to a list. The variable {0!s} is either " + "not initialized, not initialized correctly, or is confused with another variable." + "

(app_not_list)
".format(_target_)) + return True + return False + + +def missing_append_list_initialization(): + matches = find_matches("for ___ in ___:\n" + " __expr__") + for match in matches: + __expr__ = match["__expr__"] + submatches = __expr__.find_matches("_new_list_.append(___)", ) + for submatch in submatches: + _new_list_ = submatch["_new_list_"].astNode + matches02 = find_matches("{} = []\n" + "for ___ in ___:\n" + " __expr__".format(_new_list_.id)) + if not matches02: + explain("The list variable {0!s} must be initialized.

" + "(no_app_list_init)
".format(_new_list_.id)) + return True + return False + + +def wrong_append_list_initialization(): + matches = find_matches("_list_ = __expr1__\n" + "for ___ in ___:\n" + " __expr2__") + for match in matches: + _list_ = match["_list_"].astNode + __expr1__ = match["__expr1__"] + __expr2__ = match["__expr2__"] + submatch = find_expr_sub_matches("{}.append(___)".format(_list_.id), __expr2__) + if submatch and (__expr1__.ast_name == "List" and + len(__expr1__.elts) != 0 or + __expr1__.ast_name != "List"): + explain("The list variable {0!s} is either not " + "initialized correctly or mistaken for" + " another variable. The list you append to should be " + "initialized to an empty list.

" + "(app_list_init)
".format(_list_.id)) + return True + return False + + +def append_list_wrong_slot(): + matches = find_matches("_target_.append(_item_)") + if matches: + for match in matches: + _item_ = match["_item_"].astNode + _target_ = match["_target_"].astNode + if data_state(_item_).was_type('list'): + explain("You should not append a list ({0!s}) to {1!s}.

" + "(app_list_slot)
".format(_item_.id, _target_.id)) + return True + return False + + +def app_assign(): + message = ("Appending modifies the list, so unlike addition," + " an assignment statement is not needed when using append.") + code = "app_asgn" + + matches = find_matches("_sum_ = _sum_.append(__exp__)") + if matches: + return explain_r(message, code) + return False diff --git a/src/lib/pedal/mistakes/instructor_filter.py b/src/lib/pedal/mistakes/instructor_filter.py new file mode 100644 index 0000000000..6fe25d4806 --- /dev/null +++ b/src/lib/pedal/mistakes/instructor_filter.py @@ -0,0 +1,50 @@ +from pedal.cait.cait_api import find_match, find_matches +from pedal.report.imperative import explain + + +def filter_group(): + missing_if_in_for() + append_not_in_if() + + +def missing_if_in_for(): + """ + Name: missing_if_in_for + Pattern: + missing + for in ___ : + if ... ... : + + Feedback: The arrangement of decision and iteration is not correct for the filter pattern. + Returns: + + """ + matches = find_matches("for _item_ in ___:\n" + " if __expr__:\n" + " pass") + if not matches: + explain("The arrangement of decision and iteration is not correct for the filter pattern.

" + "(missing_if_in_for)

") + return True + return False + + +def append_not_in_if(): + """ + Name: append_not_in_if + Pattern: + missing + if ... : + ___.append(___) + + Feedback: Only items satisfying some condition should be appended to the list. + + Returns: + """ + match = find_match("if ___:\n" + " ___.append(___)") + if not match: + explain( + "Only items satisfying some condition should be appended to the list.

(app_not_in_if)
") + return True + return False diff --git a/src/lib/pedal/mistakes/instructor_histogram.py b/src/lib/pedal/mistakes/instructor_histogram.py new file mode 100644 index 0000000000..139737daaf --- /dev/null +++ b/src/lib/pedal/mistakes/instructor_histogram.py @@ -0,0 +1,119 @@ +from pedal.cait.cait_api import find_match, find_matches, data_state +from pedal.report.imperative import explain + + +def histogram_group(): + histogram_argument_not_list() + histogram_wrong_list() + histogram_missing() + plot_show_missing() + + +def histogram_missing(): + """ + Name: histogram_missing + Pattern: + + Missing + plt.hist(___) + + Feedback: The program should display a histogram. + + Returns: + """ + match = find_match("plt.hist(___)") + if not match: + explain("The program should display a histogram.

(histo_missing)
") + return True + return False + + +def plot_show_missing(): + """ + Name: plot_show_missing + Pattern: + Missing + plt.show() + + Feedback: The plot must be explicitly shown to appear in the Printer area. + + Returns: + """ + match = find_match("plt.show()") + if not match: + explain("The plot must be explicitly shown to appear in the Printer area." + "

(plot_show_missing)
") + return True + return False + + +def histogram_argument_not_list(): + """ + + Name: histogram_argument_not_list + Pattern: + plt.hist() + Where type() is not "list" + + Feedback: Making a histogram requires a list; is not a list. + + + Returns: + """ + matches = find_matches("plt.hist(_argument_)") + if matches: + for match in matches: + _argument_ = match["_argument_"].astNode + if not data_state(_argument_).was_type('list'): + explain("Making a histogram requires a list; {0!s} is not a list.

" + "(hist_arg_not_list)
".format(_argument_.id)) + return True + return False + + +def histogram_wrong_list(): + """ + + Name: histogram_wrong_list + Pattern: + + for ___ in ___: + .append(___) + plt.hist() + + where name() != name() + + Feedback: The list created in the iteration is not the list being used to create the histogram. + + Returns: + """ + matches = find_matches("for ___ in ___:\n" + " __expr__\n" + "plt.hist(_list_)") + if matches: + for match in matches: + _list_ = match["_list_"].astNode + __expr__ = match["__expr__"] + submatches = __expr__.find_matches("{}.append(___)".format(_list_.id)) + if submatches: + return False + explain( + "The list created in the iteration is not the list being used to create the histogram.

" + "(histo_wrong_list)
") + return True + return False + + +def histogram_wrong_placement(): + matches = find_matches("for ___ in ___:\n" + " pass\n") + if matches: + matches02 = find_matches("plt.hist(___)") + for match in matches: + if matches02: + for match02 in matches02: + if match02.match_lineno > match.match_lineno: + return False + explain("The histogram should be plotted only once, after the new list has been created" + "

(histo_wrong_place)
") + return True diff --git a/src/lib/pedal/mistakes/instructor_iteration.py b/src/lib/pedal/mistakes/instructor_iteration.py new file mode 100644 index 0000000000..3a02e0c2c1 --- /dev/null +++ b/src/lib/pedal/mistakes/instructor_iteration.py @@ -0,0 +1,135 @@ +from pedal.cait.cait_api import (parse_program, find_match, find_matches, + find_expr_sub_matches, data_state, + def_use_error) +from pedal.report.imperative import explain + + +def iteration_group(): + list_initialization_misplaced() + wrong_target_is_list() + wrong_list_repeated_in_for() + missing_iterator_initialization() + list_not_initialized_on_run() + wrong_iterator_not_list() + missing_target_slot_empty() + missing_for_slot_empty() + wrong_target_reassigned() + + +def iteration_group_on_change(): + wrong_target_is_list() + wrong_list_repeated_in_for() + wrong_iterator_not_list() + + +def all_for_loops(): + std_ast = parse_program() + return std_ast.find_all("For") + + +# this conflics with list_repeated_in_for +def wrong_target_is_list(): + match = find_match("for _item_ in ___:\n pass") + if match: + _item_ = match["_item_"].astNode + if data_state(_item_).was_type('list'): + explain('The variable {0!s} is a list and should not be placed in the iteration variable slot' + ' of the "for" block

(target_is_list)
.'.format(_item_.id)) + return True + return False + + +# this conflicts with list_in_wrong_slot_in_for +def wrong_list_repeated_in_for(): + match = find_match("for _item_ in _item_:\n pass") + if match: + _item_ = match["_item_"].astNode + if data_state(_item_).was_type('list'): + explain('The {0!s} variable can only appear once in the "for" block

' + '(list_repeat)
'.format(_item_.id)) + return True + return False + + +# this isn't consistent with the pattern you wrote +def missing_iterator_initialization(): + match = find_match("for ___ in _list_:\n pass") + if match: + _list_ = match["_list_"].astNode + if _list_.id == "___": + explain("The slot to hold a list in the iteration is empty.

(no_iter_init-blank)
") + return True + elif not data_state(_list_).was_type('list'): + explain("The variable {0!s} is in the list slot of the iteration but is not a list." + "

(no_iter_init)
".format(_list_.id)) + return True + return False + + +# TODO: We need to cover the different cases for these +def wrong_iterator_not_list(): + match = find_match("for ___ in _item_:\n pass") + if match: + _item_ = match["_item_"].astNode + if not data_state(_item_).was_type('list'): + explain("The variable {0!s} has been set to something that is not a list but is placed in the " + "iteration block that must be a list.

(iter_not_list)
".format(_item_.id)) + return True + return False + + +def missing_target_slot_empty(): + match = find_match("for _item_ in ___:\n pass") + if match: + _item_ = match["_item_"].astNode + if _item_.id == "___": + explain("You must fill in the empty slot in the iteration.

(target_empty)
") + return True + return False + + +def list_not_initialized_on_run(): + match = find_match("for ___ in _item_:\n pass") + if match: + _item_ = match["_item_"][0].astNode + if def_use_error(_item_): + explain("The list in your for loop has not been initialized

(no_list_init)
") + return True + return False + + +def list_initialization_misplaced(): + match = find_match("for ___ in _item_:\n pass") + if match: + _item_ = match["_item_"][0].astNode + if data_state(_item_).was_type('list') and def_use_error(_item_): + explain("Initialization of {0!s} is a list but either in the wrong place or redefined" + "

(list_init_misplaced)
".format(_item_.id)) + return True + return False + + +def missing_for_slot_empty(): + match = find_match("for _item_ in _list_:\n pass") + if match: + _item_ = match["_item_"][0].astNode + _list_ = match["_list_"][0].astNode + if _item_.id == "___" or _list_.id == "___": + explain("You must fill in the empty slot in the iteration.

(for_incomplete)
") + return True + return False + + +def wrong_target_reassigned(): + matches = find_matches("for _item_ in ___:\n" + " __expr__") + for match in matches: + __expr__ = match["__expr__"] + _item_ = match["_item_"][0] + submatches = __expr__.find_matches("{} = ___".format(_item_), ) + if submatches: + explain("The variable {0!s} has been reassigned. " + "The iteration variable shouldn't be reassigned" + "

(target_reassign)
".format(_item_)) + return True + return False diff --git a/src/lib/pedal/mistakes/iteration_context.py b/src/lib/pedal/mistakes/iteration_context.py new file mode 100644 index 0000000000..e4b63aba39 --- /dev/null +++ b/src/lib/pedal/mistakes/iteration_context.py @@ -0,0 +1,1204 @@ +from pedal.cait.cait_api import (parse_program, + find_matches, find_match, + find_expr_sub_matches) +from pedal.report.imperative import explain, gently +import pedal.mistakes.instructor_append as append_api +from pedal.toolkit.utilities import * +from pedal.sandbox.compatibility import get_output + + +# ################8.2 Start####################### +def wrong_list_length_8_2(): + matches = find_matches("_list_ = __expr__") + if matches: + for match in matches: + __expr__ = match["__expr__"] + if __expr__.ast_name == "List" and len(__expr__.elts) < 3: + explain('You must have at least three pieces

(list length_8.2)
') + return True + return False + + +def missing_list_initialization_8_2(): + matches = find_matches("shopping_cart = __expr__") + for match in matches: + __expr__ = match["__expr__"] + if __expr__.ast_name == "List": + return False + explain( + 'You must set the variable shopping_cart to a list containing the prices of items in the' + ' shopping cart.

(missing_list_init_8.2)
') + return True + + +def wrong_list_is_constant_8_2(): + matches = find_matches("shopping_cart = __expr__") + for match in matches: + __expr__ = match["__expr__"] + if __expr__.ast_name == "Num": + explain( + 'You must set shoppping_cart to a list of values not to a single number.

' + '(list_is_const_8.2)
') + return True + return False + + +def list_all_zeros_8_2(): + std_ast = parse_program() + lists = std_ast.find_all('List') + is_all_zero = True + for init_list in lists: + for node in init_list.elts: + if node.ast_name == 'Num' and node.n != 0: + is_all_zero = False + break + if is_all_zero: + break + if is_all_zero: + explain('Try seeing what happens when you change the numbers in the list.

(default_list_8.2)
') + return True + return False + + +# ################8.2 End####################### + + +# ################8.3 Start####################### +def wrong_list_initialization_placement_8_3(): + for_matches = find_matches("for ___ in ___:\n" + " pass") + init_matches = find_matches("episode_length_list = ___") + if init_matches and for_matches: + for for_match in for_matches: + for_lineno = for_match.match_lineno + for init_match in init_matches: + if init_match.match_lineno > for_lineno: + explain( + 'The list of episode lengths (episode_length_list) must be initialized before the' + ' iteration which uses this list.

(init_place_8.3)
') + return True + return False + + +def wrong_accumulator_initialization_placement_8_3(): + for_matches = find_matches("for ___ in ___:" + " pass") + init_matches = find_matches("sum_length = 0") + if init_matches and for_matches: + for for_match in for_matches: + for_lineno = for_match.match_lineno + for init_match in init_matches: + if init_match.match_lineno > for_lineno: + explain( + 'The variable to hold the sum of the episode lengths (sum_length) must be ' + 'initialized before the iteration which uses this variable.

' + '(accu_init_place_8.3)
') + return True + return False + + +def wrong_iteration_body_8_3(): + match = find_match("for _item_ in _list_:\n" + " sum_length = ___ + ___\n") + if not match: + explain('The addition of each episode length to the total length is not in the correct place.

' + '(iter_body_8.3)
') + return True + return False + + +def wrong_print_8_3(): + match = find_match("for _item_ in _list_:\n" + " pass\n" + "print(_total_)") + if not match: + explain('The output of the total length of time is not in the correct place. The total length of time should be' + ' output only once after the total length of time has been computed.

(print_8.3)
') + return True + return False + + +# ################8.3 End####################### + + +# ################8.4 Start####################### +def missing_target_slot_empty_8_4(): + matches = find_matches("for _item_ in pages_count_list:\n" + " pass") + if matches: + for match in matches: + _item_ = match["_item_"][0] + if _item_.id == "___": + explain('You must fill in the empty slot in the iteration.

(target_empty_8.4)
') + return True + return False + + +def missing_addition_slot_empty_8_4(): + matches = find_matches("sum_pages + _item_") + if matches: + for match in matches: + _item_ = match["_item_"][0] + if _item_.id == "___": + explain('You must fill in the empty slot in the addition.

(add_empty_8.4)
') + return True + return False + + +def wrong_names_not_agree_8_4(): + matches = find_matches("for _item1_ in pages_count_list:\n" + " sum_pages = sum_pages + _item2_") + if matches: + for match in matches: + # in theory, these will always be different? should test in test_cait + _item1_ = match["_item1_"][0] + _item2_ = match["_item2_"][0] + if _item1_.id != _item2_.id: + explain('Each value of {0!s} must be added to {1!s}.

' + '(name_agree_8.4)
'.format(_item1_.id, _item2_.id)) + return True + return False + + +# ################8.4 End####################### +def wrong_modifying_list_8_5(): + """ + + # old code for record keeping because significantly different semantics + std_ast = parse_program() + list_init = std_ast.find_all('List') + true_sum = 0 + if len(list_init) != 0: + for value in list_init[0].elts: + true_sum = value.n + true_sum + if true_sum != sum([20473, 27630, 17849, 19032, 16378]) or len(list_init) == 0: + explain('Don\'t modify the list

(mod_list_8.5)
') + return True + return False + + Returns: + """ + match = find_match("[20473, 27630, 17849, 19032, 16378]") + if not match: + explain('Don\'t modify the list

(mod_list_8.5)
') + return True + return False + + +def wrong_modifying_list_8_6(): + """ + std_ast = parse_program() + list_init = std_ast.find_all('List') + true_sum = 0 + for value in list_init[0].elts: + true_sum = value.n + true_sum + if true_sum != sum([2.9, 1.5, 2.3, 6.1]): + explain('Don\'t modify the list

(mod_list_8.6)
') + Returns: + """ + match = find_match("_list_ = [2.9, 1.5, 2.3, 6.1]") + if not match: + explain('Don\'t modify the list

(mod_list_8.6)
') + return True + return False + + +def wrong_should_be_counting(): + """ + std_ast = parse_program() + for_loops = std_ast.find_all('For') + for loop in for_loops: + iter_prop = loop.target + assignments = loop.find_all('Assign') + for assignment in assignments: + binops = assignment.find_all('BinOp') + for binop in binops: + if binop.has(iter_prop) and binop.op == 'Add': + explain('This problem asks for the number of items in the list not the total of all the values in ' + 'the list.

(not_count)
') + Returns: + """ + matches = find_matches("for _item_ in ___:\n" + " __expr__") + if matches: + for match in matches: + _item_ = match["_item_"][0] + __expr__ = match["__expr__"] + submatches = __expr__.find_matches("___ = ___ + {}".format(_item_.id), ) + if submatches: + explain( + 'This problem asks for the number of items in the list not the total of all the values in the list.' + '

(not_count)
') + return True + return False + + +def wrong_should_be_summing(): + """ + std_ast = parse_program() + for_loops = std_ast.find_all('For') + for loop in for_loops: + assignments = loop.find_all('Assign') + for assignment in assignments: + binops = assignment.find_all('BinOp') + for binop in binops: + if binop.has(1) and binop.op == 'Add': + explain('This problem asks for the total of all the values in the list not the number of items in ' + 'the list.

(not_sum)
') + """ + matches = find_matches("for _item_ in ___:\n" + " __expr__") + if matches: + for match in matches: + __expr__ = match["__expr__"] + submatches = __expr__.find_matches("___ = 1 + ___", ) + if submatches: + explain('This problem asks for the total of all the values in the list not the number of ' + 'items in the list.

(not_sum)
') + return True + return False + + +def missing_addition_slot_empty(): + """ + std_ast = parse_program() + assignments = std_ast.find_all('Assign') + for assignment in assignments: + # left = assignment.target + right = assignment.value + binOp = right.find_all('BinOp') + if len(binOp) == 1: + binOp = binOp[0] + if binOp.op == 'Add': + if binOp.left.ast_name == 'Name' and binOp.right.ast_name == 'Name': + if binOp.left.id == '___' or binOp.right.id == '___': + explain('You must fill in the empty slot in the addition.

(add_empty)
') + return True + return False + Returns: + """ + matches = find_matches("___ + _item_") + if matches: + for match in matches: + _item_ = match["_item_"][0] + if _item_.id == "___": + explain('You must fill in the empty slot in the addition.

(add_empty)
') + return True + return False + + +def wrong_cannot_sum_list(): + """ + + std_ast = parse_program() + for_loops = std_ast.find_all('For') + for loop in for_loops: + list_prop = loop.iter + assignments = loop.find_all('Assign') + for assignment in assignments: + binops = assignment.find_all('BinOp') + for binop in binops: + if binop.has(list_prop) and binop.op == 'Add': + explain('Addition can only be done with a single value at a time, not with an entire list at one' + ' time.

(sum_list)
') + Returns: + """ + matches = find_matches("for ___ in _list_ :\n" + " __expr__") + if matches: + for match in matches: + _list_ = match["_list_"][0] + __expr__ = match["__expr__"] + submatches = __expr__.find_matches("___ = ___ + {}".format(_list_.id), ) + if submatches: + explain('Addition can only be done with a single value at a time, not with an entire list at one' + ' time.

(sum_list)
') + return True + return False + + +def missing_no_print(): + prints = find_match('print(___)', cut=True) + if not prints: + explain('Program does not output anything.

(no_print)
') + return True + return False + + +def missing_counting_list(): + """ + std_ast = parse_program() + has_count = False + for_loops = std_ast.find_all('For') + if len(for_loops) > 0: + for loop in for_loops: + assignments = loop.find_all('Assign') + if len(assignments) < 1: + continue + for assignment in assignments: + binops = assignment.find_all('BinOp') + if len(binops) < 1: + continue + lhs = assignment.target + for binop in binops: + if binop.has(lhs) and binop.has(1) and binop.op == 'Add': + has_count = True + if not has_count: + explain('Count the total number of items in the list using iteration.

(miss_count_list)
') + Returns: + """ + matches = find_matches("for _item_ in ___:\n" + " __expr__") + if matches: + for match in matches: + __expr__ = match["__expr__"] + submatches = __expr__.find_matches("_sum_ = _sum_ + 1", ) + if submatches: + return False + explain( + 'Count the total number of items in the list using iteration.

(miss_count_list)
') + return True + + +def missing_summing_list(): + """ + std_ast = parse_program() + has_total = False + for_loops = std_ast.find_all('For') + if len(for_loops) > 0: + for loop in for_loops: + assignments = loop.find_all('Assign') + if len(assignments) < 1: + continue + iter_prop = loop.target + for assignment in assignments: + binops = assignment.find_all('BinOp') + if len(binops) < 1: + continue + lhs = assignment.target + for binop in binops: + if binop.has(lhs) and binop.has(iter_prop) and binop.op == 'Add': + has_total = True + if not has_total: + explain('Sum the total of all list elements using iteration.

(miss_sum_list)
') + Returns: + """ + matches = find_matches("for _item_ in ___:\n" + " __expr__") + if matches: + for match in matches: + _item_ = match["_item_"][0] + __expr__ = match["__expr__"] + submatches = find_expr_sub_matches("_sum_ = _sum_ + {}" + .format(_item_.id), __expr__) + if submatches: + return False + explain('Sum the total of all list elements using iteration.

(miss_sum_list)
') + return True + + +def missing_zero_initialization(): + """ + + std_ast = parse_program() + for_loops = std_ast.find_all('For') + accumulator = None + loop_acu = None + for loop in for_loops: + assignments = loop.find_all('Assign') + for assignment in assignments: + binops = assignment.find_all('BinOp') + if len(binops) > 0: + lhs = assignment.target + for binop in binops: + if binop.has(lhs) and binop.op == 'Add': + accumulator = lhs + loop_acu = loop + accu_init = False + if accumulator is not None: + assignments = std_ast.find_all('Assign') + for assignment in assignments: + if loop_acu.lineno > assignment.lineno: + lhs = assignment.target + if lhs.id == accumulator.id and assignment.has(0): + accu_init = True + break + if not accu_init and accumulator is not None: + explain('The addition on the first iteration step is not correct because either the variable ' + '{0!s} has not been initialized to an appropriate initial value or it has not been placed' + ' in an appropriate location

(miss_zero_init)
'.format(accumulator.id)) + return False + return True + Returns: + """ + + matches01 = find_matches("for ___ in ___:\n" + " __expr__") + if matches01: + for match01 in matches01: + __expr__ = match01["__expr__"] + submatches01 = __expr__.find_matches("_sum_ = _sum_ + ___", ) + if submatches01: + for submatch01 in submatches01: + _sum_ = submatch01["_sum_"][0] + matches02 = find_matches(("{} = 0\n" + "for ___ in ___:\n" + " __expr__").format(_sum_.id)) + if not matches02: + explain('The addition on the first iteration step is not correct because either the variable ' + '{0!s} has not been initialized to an appropriate initial value or it has ' + 'not been placed in an appropriate location

' + '(miss_zero_init)
'.format(_sum_.id)) + return True + return False + + +def wrong_printing_list(): + matches = find_matches("for ___ in ___:\n" + " __expr__") + if matches: + for match in matches: + __expr__ = match["__expr__"] + if __expr__.find_matches("print(___)", ): + explain('You should be printing a single value.

(list_print)
') + return True + return False + + +# TODO: This might be reason to rethink letting instructor symbols map to multiple items +def missing_average(): + matches_missing = find_matches("for ___ in ___:\n" + " pass\n" + "__expr__") + matches = [] + if matches_missing: + for match in matches_missing: + __expr__ = match["__expr__"] + sub_matches = __expr__.find_matches("_total_/_count_", ) + if sub_matches: + for sub_match in sub_matches: + _total_ = sub_match["_total_"][0] + _count_ = sub_match["_count_"][0] + if _total_.id != _count_.id: + matches.append(match) + if not len(matches) > 0: + explain('An average value is not computed.

(no_avg)
') + return True + return False + + +def warning_average_in_iteration(): + matches = find_matches("for ___ in ___:\n" + " __expr__\n") + if matches: + for match in matches: + __expr__ = match["__expr__"] + submatches = __expr__.find_matches("_average_ = _total_/_count_", ) + if submatches: + for submatch in submatches: + _total_ = submatch["_total_"][0] + _count_ = submatch["_count_"][0] + _average_ = submatch["_average_"][0] + if _total_.id != _count_.id != _average_.id and _total_.id != _average_.id: + explain('An average value is best computed after the properties name {0!s}(total)' + ' and {1!s} are completely known rather than recomputing the average on' + ' each iteration.

(avg_in_iter)
'.format(_total_.id, _count_.id)) + return True + + return False + + +def wrong_average_denominator(): + matches = find_matches("for ___ in ___:\n" + " __expr__\n" # where expr contains _count_ = _count_ + 1 + "__expr2__") # where expr2 contains ___/_value_ + # where _value_.id != _count_.id + if matches: + for match in matches: + __expr__ = match["__expr__"] + __expr2__ = match["__expr2__"] + # _value_ = match["_value_"][0] + submatches = __expr__.find_matches("_count_ = _count_ + 1", ) + submatches02 = find_expr_sub_matches("___/_value_", __expr2__) + if submatches and submatches02: + for submatch in submatches: + for submatch02 in submatches02: + _count_ = submatch["_count_"][0] + _value_ = submatch02["_value_"][0] + if _count_.id != _value_.id: + explain('The average is not calculated correctly.

(avg_denom)
') + return True + return False + + +def wrong_average_numerator(): + matches = find_matches("for _item_ in ___:\n" + " __expr__\n" # where expr contains _total_ = _total_ + 1 + "__expr2__") # where expr2 contains _value_/___ + if matches: + for match in matches: + __expr__ = match["__expr__"] + __expr2__ = match["__expr2__"] + _item_ = match["_item_"][0] + submatches = __expr__.find_matches("_total_ = _total_ + {}".format(_item_.id), ) + submatches02 = find_expr_sub_matches("_value_/___", __expr2__) + if submatches and submatches02: + for submatch in submatches: + for submatch02 in submatches02: + _value_ = submatch02["_value_"][0] + _total_ = submatch["_total_"][0] + if _total_.id != _value_.id: + explain('The average is not calculated correctly.

(avg_numer)
') + return True + return False + + +# #######################AVERAGE END########################### +def wrong_compare_list(): + matches = find_matches("for ___ in _list_:\n" + " if __expr__:\n" + " pass") + if matches: + for match in matches: + _list_ = match["_list_"][0] + __expr__ = match["__expr__"] + if __expr__.has(_list_.astNode): + explain('Each item in the list {0!s} must be compared one item at a time.

' + '(comp_list)
'.format(_list_.id)) + return True + return False + + +def wrong_for_inside_if(): + match = find_match("if ___:\n" + " for ___ in ___:\n" + " pass") + if match: + explain('The iteration should not be inside the decision block.

(for_in_if)
') + return True + return False + + +def iterator_is_function(): + std_ast = parse_program() + for_loops = std_ast.find_all('For') + # noinspection PyBroadException + try: + for loop in for_loops: + list_prop = loop.iter + if list_prop.ast_name == 'Call': + explain('You should make a variable for the list instead of using a function call for the list' + '

(iter_is_func)
') + return True + except Exception: + return False + return False + + +# ##########################9.1 START############################ +def wrong_list_initialization_9_1(): + match = find_match('rainfall_list = weather.get("Precipitation","Location","Blacksburg, VA")') + if not match: + explain('The list of rainfall amounts (rainfall_list) is not initialized properly.' + '

(list_init_9.1)
') + return True + return False + + +def wrong_accumulator_initialization_9_1(): + match = find_match("rainfall_sum = 0") + if not match: + explain('The variable to hold the total value of the rainfall amounts (rainfall_sum) is not ' + 'initialized properly.

(accu_init_9.1)
') + return True + return False + + +def wrong_accumulation_9_1(): + matches = find_matches("rainfall_sum = _item_ + rainfall") + if matches: + for match in matches: + _item_ = match["_item_"][0] + if _item_.id != "rainfall_sum": + explain('The addition of each rainfall amount to rainfall_sum is not correct.' + '

(accu_9.1)
') + return True + return False + + +def wrong_list_initialization_placement_9_1(): + match = find_match("rainfall_list = ___\n" + "for _item_ in _list_:\n" + " pass") + if not match: + explain('The list of rainfall amount (rainfall_list) must be initialized before the iteration that' + ' uses this list.

(list_init_place_9.1)
') + return True + return False + + +# TODO: Convert this to matching API +def wrong_accumulator_initialization_placement_9_1(): + std_ast = parse_program() + assignments = std_ast.find_all('Assign') + loops = std_ast.find_all('For') + list_init = None + init_after_loop = False + for assignment in assignments: + if assignment.target.id == 'rainfall_sum': + list_init = assignment + break + for loop in loops: + if list_init is not None and loop.lineno > list_init.lineno: + init_after_loop = True + break + if list_init is None or not init_after_loop: + explain('The variable for the sum of all the rainfall amounts (rainfall_sum) must be initialized ' + 'before the iteration which uses this variable.

(accu_init_place_9.1)
') + + +# TODO: Convert this to matching API +def wrong_iteration_body_9_1(): + std_ast = parse_program() + loops = std_ast.find_all('For') + assignment_in_for = False + for loop in loops: + assignments = loop.find_all('Assign') + for assignment in assignments: + if assignment.target.id == 'rainfall_sum': + assignment_in_for = True + break + if assignment_in_for: + break + if not assignment_in_for: + explain('The addition of each rainfall amount to the total rainfall is not in the correct place.

' + '(iter_body_9.1)
') + + +def wrong_print_9_1(): + """ + + std_ast = parse_program() + for_loops = std_ast.find_all('For') + # has_for = len(for_loops) > 0 + for_loc = [] + wrong_print_placement = True + for loop in for_loops: + end_node = loop.next_tree + if end_node is not None: + for_loc.append(end_node.lineno) + calls = std_ast.find_all('Call') + for call in calls: + if call.func.id == 'print': + for loc in for_loc: + if call.func.lineno >= loc: + wrong_print_placement = False + break + if not wrong_print_placement: + break + if wrong_print_placement: + explain('The output of the total rainfall amount is not in the correct place. The total rainfall should be ' + 'output only once after the total rainfall has been computed.

(print_9.1)
') + Returns: + """ + match = find_match("for _item_ in _list_:\n" + " pass\n" + "print(_total_)") + if not match: + explain('The output of the total rainfall amount is not in the correct place. The total rainfall should be ' + 'output only once after the total rainfall has been computed.

(print_9.1)
') + return True + return False + + +# ##########################9.1 END############################ + + +# ##########################9.2 START############################ +# TODO: Convert this to matching API +def wrong_list_initialization_9_2(): + std_ast = parse_program() + assignments = std_ast.find_all('Assign') + has_call = False + for assignment in assignments: + if assignment.target.id == 'rainfall_list': + call = assignment.find_all('Call') + if len(call) == 1: + args = call[0].args + if len(args) == 3: + if args[0].s == 'Precipitation' and args[1].s == 'Location' and args[2].s == 'Blacksburg, VA': + has_call = True + break + if not has_call: + explain('The list of rainfall amounts (rainfall_list) is not initialized properly.' + '

(list_init_9.2)
') + return not has_call + + +# TODO: Convert this to matching API +def wrong_accumulator_initialization_9_2(): + std_ast = parse_program() + assignments = std_ast.find_all('Assign') + has_assignment = False + for assignment in assignments: + if assignment.target.id == 'rainfall_count' and assignment.value.ast_name == 'Num': + if assignment.value.n == 0: + has_assignment = True + break + if not has_assignment: + explain('The variable to hold the total value of the rainfall amounts (rainfall_count) is not ' + 'initialized properly.

(accu_init_9.2)
') + return not has_assignment + + +def wrong_accumulation_9_2(): + matches = find_matches("rainfall_count = _item_ + 1") + if matches: + for match in matches: + _item_ = match["_item_"][0] + if _item_.id != "rainfall_count": + explain( + 'The adding of another day with rainfall to the total count of days with rainfall ' + '(rainfall_count) is not correct.

(accu_9.2)
') + return True + return False + + +# TODO: Convert this to matching API +def wrong_list_initialization_placement_9_2(): + std_ast = parse_program() + assignments = std_ast.find_all('Assign') + loops = std_ast.find_all('For') + list_init = None + init_after_loop = False + for assignment in assignments: + if assignment.target.id == 'rainfall_list': + list_init = assignment + break + for loop in loops: + if list_init is not None and loop.lineno > list_init.lineno: + init_after_loop = True + break + if list_init is None or not init_after_loop: + explain('The list of rainfall amount (rainfall_list) must be initialized before the iteration that' + ' uses this list.

(list_init_place_9.2)
') + return True + return False + + +# TODO: Convert this to matching API +def wrong_accumulator_initialization_placement_9_2(): + std_ast = parse_program() + assignments = std_ast.find_all('Assign') + loops = std_ast.find_all('For') + list_init = None + init_after_loop = False + for assignment in assignments: + if assignment.target.id == 'rainfall_count': + list_init = assignment + break + if list_init is not None: + for loop in loops: + if loop.lineno > list_init.lineno: + init_after_loop = True + break + if list_init is None or not init_after_loop: + explain('The variable for the count of the number of days having rain (rainfall_count) must be ' + 'initialized before the iteration which uses this variable.

(accu_init_place_9.2)
') + return True + return False + + +def wrong_iteration_body_9_2(): + matches = find_matches("for _item_ in _list_:\n" + " if __expr__:\n" + " pass") + if matches: + for match in matches: + __expr__ = match["__expr__"] + if __expr__.numeric_logic_check(1, 'var > 0'): + return False + explain('The test (if) to determine if a given amount of rainfall is greater than (>) zero is not in the ' + 'correct place.

(iter_body_9.2)
') + return True + + +def wrong_decision_body_9_2(): + matches = find_matches("if __expr__:\n" + " rainfall_count = rainfall_count + 1") + if matches: + for match in matches: + __expr__ = match["__expr__"] + if __expr__.numeric_logic_check(1, 'var > 0'): + return False + explain('The increase by 1 in the number of days having rainfall (rainfall_count) is not in the ' + 'correct place.

(dec_body_9.2)
') + return True + + +def wrong_print_9_2(): + match = find_match("for _item_ in _list_:\n" + " pass\n" + "print(_total_)") + if not match: + explain('The output of the total number of days with rainfall is not in the correct place. The total number of ' + 'days should be output only once after the total number of days has been computed.

' + '(print_9.2)
') + return True + return False + + +# ##########################9.2 END############################ + + +# ##########################9.6 START############################ +def wrong_comparison_9_6(): + matches = find_matches("if __comp__:\n" + " pass") + if matches: + for match in matches: + __comp__ = match["__comp__"] + if not __comp__.numeric_logic_check(1, 'var > 80'): + explain( + 'In this problem you should be finding temperatures above 80 degrees.

(comp_9.6)
') + return True + return False + + +# ##########################9.6 END############################ + + +# ##########################10.2 START############################ +def wrong_conversion_10_2(): + """ + ''' + # code version 2 start + binops = __expr__.find_all('BinOp') + for binop in binops: + if binop.has(_target_.astNode) and binop.has(0.04) and binop.op_name == 'Mult': + return False + # code version 2 end + ''' + Returns: + """ + matches = find_matches("for _target_ in ___:\n" + " __expr__") + if matches: + for match in matches: + # code version 1 start + _target_ = match["_target_"][0] + __expr__ = match["__expr__"] + matches02 = __expr__.find_matches("_target_*0.04", ) + if matches02: + for match02 in matches02: + _target_02 = match02["_target_"][0] + if _target_.id == _target_02.id: + return False + # code version 1 end + explain('The conversion of {0!s} to inches is not correct.

' + '(conv_10.2)
'.format(_target_.id)) + return True + return False + + +# ##########################10.2 END############################ + + +# ##########################10.3 START############################ +def wrong_filter_condition_10_3(): + matches = find_matches("if __expr__:\n" + " pass") + if matches: + for match in matches: + __expr__ = match["__expr__"] + if __expr__.numeric_logic_check(1, "var > 0") or __expr__.numeric_logic_check(1, "var != 0"): + return False + explain('The condition used to filter the year when artists died is not correct.

(filt_10.3)
') + return True + return False + + +# ##########################10.3 END############################ + + +# ##########################10.4 START############################ +def wrong_and_filter_condition_10_4(): + matches = find_matches("for _temp_ in _list_:\n" + " if __expr__:\n" + " pass") + if matches: + for match in matches: + _temp_ = match["_temp_"][0] + __expr__ = match["__expr__"] + if (__expr__.has(_temp_.astNode) and + not __expr__.numeric_logic_check(1, "32 <= temp <= 50")): + explain( + 'The condition used to filter the temperatures into the specified range of temperatures is not ' + 'correct.

(filt_and_10.4)
') + return True + return False + + +def wrong_nested_filter_condition_10_4(): + matches = find_matches("for _temp_ in _list_:\n" + " if __cond1__:\n" + " if __cond2__:\n" + " pass") + if matches: + for match in matches: + _temp_ = match["_temp_"][0].astNode + __cond1__ = match["__cond1__"] + __cond2__ = match["__cond2__"] + if not ( + __cond1__.has(_temp_) and __cond2__.has(_temp_) and ( + __cond1__.numeric_logic_check( + 1, + "32 <= temp") and __cond2__.numeric_logic_check( + 1, + "temp <= 50") or __cond2__.numeric_logic_check( + 1, + "32 <= temp") and __cond1__.numeric_logic_check( + 1, + "temp <= 50"))): + explain( + 'The decisions used to filter the temperatures into the specified range of temperatures is not ' + 'correct.

(nest_filt_10.4)
') + return True + return False + + +# ##########################10.4 END############################ + + +# ########################10.5 START############################### +def wrong_conversion_problem_10_5(): + matches = find_matches("for _item_ in ___:\n" + " __expr__") + if matches: + for match in matches: + _item_ = match["_item_"][0] + __expr__ = match["__expr__"] + matches02 = __expr__.find_matches("_item_*0.62", ) + if matches02: + for match02 in matches02: + _item_02 = match02["_item_"][0] + if _item_02.id == _item_.id: + return False + explain('The conversion from kilometers to miles is not correct.

(conv_10.5)
') + return True + return False + + +def wrong_filter_problem_atl1_10_5(): + """ + find pattern where expression is equal to _item_*0.62 and + where the condition is not equivalent to _expr_ > 10 + Returns: + """ + + matches = find_matches("for _item_ in ___:\n" + " if __cond__:\n" + " _list_.append(__expr__)") + if matches: + for match in matches: + _item_ = match["_item_"][0].astNode + __cond__ = match["__cond__"] + __expr__ = match["__expr__"] + matches02 = __expr__.find_matches("_item_*0.62", ) + if matches02: + for match02 in matches02: + _item_02 = match02["_item_"][0].astNode + if (_item_.id == _item_02.id and + __cond__.has(_item_) and + not __cond__.numeric_logic_check(0.1, "item > 16.1290322580645")): + explain('You are not correctly filtering out values from the list.

' + '(filt_alt1_10.5)
') + return True + return False + + +def wrong_filter_problem_atl2_10_5(): + matches = find_matches("for _item_ in ___:\n" + " _miles_ = __expr__\n" + " if __cond__:\n" + " _list_.append(_miles_)") + if matches: + for match in matches: + __expr__ = match["__expr__"] + __cond__ = match["__cond__"] + _item_ = match["_item_"][0].astNode + _miles_ = match["_miles_"][0].astNode + matches02 = __expr__.find_matches("_item_*0.62", ) + if matches02: + for match02 in matches02: + _item_02 = match02["_item_"][0].astNode + if _item_.id == _item_02.id: + if not (__cond__.has(_miles_) and + __cond__.numeric_logic_check(1, "_item_ > 10")): + explain('You are not correctly filtering out values from the list.

' + '(filt_alt2_10.5)
') + return True + return False + + +def wrong_append_problem_atl1_10_5(): + matches = find_matches("for _item_ in ___:\n" + " if __cond__:\n" + " _list_.append(__expr__)") + if matches: + for match in matches: + _item_ = match["_item_"][0].astNode + __cond__ = match["__cond__"] + __expr__ = match["__expr__"] + if (__cond__.numeric_logic_check(0.1, "item > 16.1290322580645") and + __cond__.has(_item_)): + new_code = "{}*0.62".format(_item_.id) + matches02 = __expr__.find_matches(new_code, ) + if not matches02: + explain('You are not appending the correct values.

(app_alt1_10.5)
') + return True + return False + + +def wrong_append_problem_atl2_10_5(): + matches = find_matches("for _item_ in ___:\n" + " _miles_ = _item_ * 0.62\n" + " if __cond__:\n" + " _list_.append(_var_)") + for match in matches: + __cond__ = match["__cond__"] + _miles_ = match["_miles_"][0] + _var_ = match["_var_"][0] + if __cond__.has(_miles_) and __cond__.numeric_logic_check(1, "_miles_ > 10"): + if _var_.id != _miles_.id: + explain('You are not appending the correct values

(app_alt2_10.5)
') + return True + return False + + +# ########################10.5 END############################### +def wrong_debug_10_6(): + """ + + + + + Returns: + """ + matches = find_matches('quakes = earthquakes.get("depth","(None)","")\n' + 'quakes_in_miles = []\n' + 'for quake in _list1_:\n' + ' _list2_.append(quake * 0.62)\n' + 'plt.hist(quakes_in_miles)\n' + 'plt.xlabel("Depth in Miles")\n' + 'plt.ylabel("Number of Earthquakes")\n' + 'plt.title("Distribution of Depth in Miles of Earthquakes")\n' + 'plt.show()') + for match in matches: + name1 = match["_list1_"][0].ast_node.id + name2 = match["_list2_"][0].ast_node.id + master_list = ["quake", "quakes", "quakes_in_miles"] + if (name1 in master_list and name2 in master_list and + name1 != "quakes_in_miles" and name2 != "quakes" and + (name1 != "quake" or name2 != "quake")): + return False + explain('This is not one of the two changes needed. Undo the change and try again.

(debug_10.6)
') + return True + + +def wrong_debug_10_7(): + match = find_match("filtered_sentence_counts = []\n" + "book_sentence_counts = classics.get('sentences','(None)','')\n" + "for book in book_sentence_counts:\n" + " if book >= 5000:\n" + " filtered_sentence_counts.append(book)\n" + "plt.hist(filtered_sentence_counts)\n" + "plt.title('Distribution of Number of Sentences in Long Books')\n" + "plt.xlabel('Number of Sentences')\n" + "plt.ylabel('Number of Long Books')\n" + "plt.show()\n") + + if not match: + explain('This is not the change needed. Undo the change and try again.

(debug_10.7)
') + return True + return False + + +# ########################.....############################### +def wrong_initialization_in_iteration(): + matches = find_matches("for ___ in ___:\n" + " __expr__") + if matches: + for match in matches: + __expr__ = match["__expr__"] + submatches = __expr__.find_matches("_assign_ = __expr__", ) + if submatches: + for submatch in submatches: + __expr__sub = submatch["__expr__"] + _assign_ = submatch["_assign_"][0].astNode + if len(__expr__sub.find_all("Name")) == 0: + explain( + 'You only need to initialize {0!s} once. Remember that statements in an ' + 'iteration block happens multiple times' + '

(wrong_init_in_iter)
'.format(_assign_.id)) + return True + return False + + +def wrong_duplicate_var_in_add(): + match = find_match("_item_ + _item_") + if match: + explain('You are adding the same variable twice; you need two different variables in your addition.' + '

(dup_var)
') + return True + return False + + +# ########################PLOTTING############################### +def plot_group_error(output=None): + if output is None: + output = get_output() + if len(output) > 1: + explain('You should only be printing/plotting one thing!

(print_one)
') + return True + elif len(output) == 0: + explain('The algorithm is plotting an empty list. Check your logic.

(blank_plot)
') + return True + elif not isinstance(output[0], list): + explain('You should be plotting, not printing!

(printing)
') + return True + elif len(output[0]) != 1: + explain('You should only be plotting one thing!

(one_plot)
') + return True + + +def all_labels_present(): # TODO: make sure it's before the show, maybe check for default values + """ + plt.title("Distribution of Number of Sentences in Long Books") + plt.xlabel("Number of Sentences") + plt.ylabel("Number of Long Books") + plt.show() + Returns: + """ + + match = find_match("plt.title(___)\nplt.show()") + match02 = find_match("plt.xlabel(___)\nplt.show()") + match03 = find_match("plt.ylabel(___)\nplt.show()") + + if (not match) or (not match02) or (not match03): + gently('Make sure you supply labels to all your axes and provide a title and then call show' + '

(labels_present)
') + return True + return False + + +# TODO: Convert this to matching API +def hard_code_8_5(): # TODO: This one's weird + match = find_matches("print(__num__)") + if match: + for m in match: + __num__ = m["__num__"] + if len(__num__.find_all("Num")) > 0: + explain("Use iteration to calculate the sum.

(hard_code_8.5)
") + return True + return False diff --git a/src/lib/pedal/plugins/__init__.py b/src/lib/pedal/plugins/__init__.py new file mode 100644 index 0000000000..552e872a5b --- /dev/null +++ b/src/lib/pedal/plugins/__init__.py @@ -0,0 +1,13 @@ + +''' +def default_pipeline(tifa=False, cait=True, sandbox=True): + next_section() + results = [] + if tifa: + results.append(tifa_analysis()) + if cait: + results.append(parse_program()) + if sandbox: + results.append(execute()) + return tuple(results) +''' diff --git a/src/lib/pedal/plugins/blockpy_compatibility.py b/src/lib/pedal/plugins/blockpy_compatibility.py new file mode 100644 index 0000000000..660a3d84c6 --- /dev/null +++ b/src/lib/pedal/plugins/blockpy_compatibility.py @@ -0,0 +1,106 @@ +class GracefulExit(Exception): + pass + + +class StudentData: + def __init__(self): + pass + + def get_names_by_type(self, type, exclude_builtins): + pass + + def get_values_by_type(self, type, exclude_builtins): + pass + + +student = StudentData() + + +def get_output(): + pass + + +def reset_output(): + pass + + +def queue_input(*inputs): + pass + + +def get_program(): + pass + + +def parse_program(): + pass + + +def had_execution_time_error(): + pass + + +def limit_execution_time(): + pass + + +def unlimit_execution_time(): + pass + + +def analyze_program(): + pass + + +def def_use_error(AstNode): + pass + + +class CorruptedAstNode: + def __init__(self): + pass + + +def find_match(instructor_code): + pass + + +def find_matches(instructor_code): + pass + + +class ASTMap: + def __init__(self, JSAstMap): + pass + + def get_std_name(self, id): + pass + + def get_std_exp(self, id): + pass + + +class AstNode: + def __init__(self, id): + pass + + def __eq__(self, other): + pass + + def numeric_logic_check(self, mag, expr): + pass + + def __str__(self): + pass + + def __repr__(self): + pass + + def __getattr__(self, key): + pass + + def has(self, AstNode): + pass + + def find_all(self, type): + pass diff --git a/src/lib/pedal/plugins/grade_magic.py b/src/lib/pedal/plugins/grade_magic.py new file mode 100644 index 0000000000..56dca6d845 --- /dev/null +++ b/src/lib/pedal/plugins/grade_magic.py @@ -0,0 +1,389 @@ +# Built-in imports +import json +import requests + +# IPython imports +from IPython.core.magic import (Magics, magics_class, line_magic, cell_magic, line_cell_magic) +from IPython.display import Javascript, display +from IPython.utils.io import capture_output, CapturedIO + +# Logging imports +import os +import sys +from warnings import warn +# from traitlets import Bool +import time + +# TODO: Opportunity here to add in requests-cache. This would allow us to avoid +# the repeated trip. However, you'll need to handle expiring the cache in a +# smart way. One option is to write a command line script to just wipe as +# necessary. Simply deleting the cache file would be pretty easy, assuming it +# installs per user. + +# This really should come in as a configuration setting somewhere. +BLOCKPY_URL = 'https://think.cs.vt.edu/blockpy/load_assignment_give_feedback' + + +def get_response_error(response): + """ + Transform a Response object into a friendlier string. + + Args: + response (requests.Response): A Requests reponse object to parse for + some kind of error. + Returns: + str: A string representation of the URL response. + """ + return "{} {}: {}".format(response.status_code, response.reason, + response.text) + + +def download_on_run(assignment_id): + """ + Download the on_run (give_feedback) code to use to test their solution. + + Args: + assignment_id (int OR str): The ID of the assignment to get the + on_run code for. + Returns: + bool: Whether or not the request was successful. + str: If unsuccesful, a message to display to the user. Otherwise, it'll + be the on_run code. + """ + data = {'assignment_id': assignment_id} + try: + response = requests.get(BLOCKPY_URL, data=data) + except Exception as error: + return False, str(error) + try: + result = response.json() + except ValueError: + # Failed to parse the JSON; perhaps it was some text data? + return False, get_response_error(response) + if result['success']: + return True, result['give_feedback'] + else: + return False, result['message'] + + +PEDAL_PIPELINE = ''' +from pedal.report import * +from pedal.report.imperative import * +clear_report() +from pedal.source import set_source +set_source({student_code}) +from pedal.tifa import tifa_analysis +tifa_analysis(True) +from pedal.sandbox.compatibility import * +queue_input({inputs}) +run_student(True) +student = get_sandbox() +from pedal.cait.cait_api import parse_program +{on_run} +from pedal.resolvers import simple +SUCCESS, SCORE, CATEGORY, LABEL, MESSAGE, DATA, HIDE = simple.resolve() +''' + + +def blockpy_grade(assignment_id, student_code, inputs): + """ + Helper function to capture the request from the server. + + Args: + assignment_id (int): The assignment ID to look up and use the on_run + code for. + student_code (str): The code that was written by the student. + + inputs (str): The inputs to queue into the assignment + + Returns: + str: The HTML formatted feedback for the student. + """ + successful_download, on_run = download_on_run(assignment_id) + # If it failed, let's display some information about why. + if not successful_download: + return on_run + return execute_on_run_code(on_run, student_code, inputs) + + +def execute_on_run_code(on_run, student_code, inputs): + """ + Actually execute the on_run code for the given student code. + """ + # Even though the student code is a string, we need to escape it to prevent + # any weirdness from being in the instructor code. + escaped_student_code = json.dumps(student_code) + instructor_code = PEDAL_PIPELINE.format(on_run=on_run, + student_code=escaped_student_code, + # inputs=','.join(inputs)) + inputs=inputs) + # Execute the instructor code in a new environment + global_variables = globals() + compiled_code = compile(instructor_code, 'instructor_code.py', 'exec') + exec(compiled_code, global_variables) + category = global_variables['CATEGORY'] + label = global_variables['LABEL'] + message = global_variables['MESSAGE'] + # In some cases, we might want to override how the text is rendered. + if category.lower() == 'instructor' and label.lower() == 'explain': + category = "Instructor Feedback" + label = '' + # Return the result as HTML + return '''{}: {}
{}'''.format(category, label, message) + + +# The following string literals are used to create the JavaScript code that +# creates the Python code that will execute the instructor's feedback code +# using the student's Python code. + +# Extract out the student code, embed the result +EXTRACT_STUDENT_CODE = r""" +// Convert Notebook cells to a string of Python code +var makePython = function(cell) { + if (cell.cell_type == "code") { + // Code is embedded unchanged, unless it is magic + var source = cell.get_text(); + if (source.startsWith('%')) { + // Skip magic + return ''; + } else { + return source; + } + } else if (cell.cell_type == "markdown" || + cell.cell_type == "raw") { + // Markdown and text is wrapped in a string. + var escaped_text = cell.get_text().replace(/'''/g, "\\'\\'\\'"); + return "'''"+escaped_text+"'''"; + } +} +var isUsable = function(cell) { + return cell.cell_type == "code" || + cell.cell_type == "markdown" || + cell.cell_type == "raw"; +} +var cells = Jupyter.notebook.get_cells(); +var source_code = cells.filter(isUsable).map(makePython).join("\n"); +source_code = JSON.stringify(source_code); +console.log(source_code); +// Start constructing the feedback code (which will be Python). +var on_run_code = []; +on_run_code.push("student_code="+source_code); +""" + +# Retrieve the last cell, and also recolor it a little for style +ANIMATE_LAST_CELL = r""" +// While we are accessing the server, recolor the last cell a little. +var last = null; +if (cells.length > 0) { + last = cells[cells.length-1]; + $(last.element).animate({"background-color": "#E0E6FF"}, 1000); +} +""" + +# If the %grade magic is used, we run the code directly. +LOCAL_GRADE = r''' +on_run_code.push("from pedal.plugins.grade_magic import execute_on_run_code"); +on_run_code.push('print(execute_on_run_code({on_run_code}, student_code, {inputs}))'); +''' + +# If the %grade_blockpy magic is used, we need to get the on_run from blockpy. +BLOCKPY_GRADE = r''' +on_run_code.push("from pedal.plugins.grade_magic import blockpy_grade"); +on_run_code.push('import json') +on_run_code.push('inputs = {inputs}') +console.log('inputs = {inputs}') +on_run_code.push("print(blockpy_grade({assignment}, student_code, inputs))"); +''' + +# This chunk actually performs the on_run code execution using the kernel. +EXECUTE_CODE = r''' +on_run_code = on_run_code.join("\n"); +console.log(on_run_code); +var kernel = IPython.notebook.kernel; +if (kernel !== null) { + var t = kernel.execute(on_run_code, { 'iopub' : {'output' : function(x) { + if (x.msg_type == "error") { + // If this was an error, show the traceback properly. + if (last !== null) { + last.output_area.append_error(x.content); + console.error(x); + } else { + console.error("Could not append to final cell.", x); + } + } else if (!x.content.data && x.content.text) { + // If it was valid data, we show it as HTML. + console.log(x); + element.html(x.content.text.replace(/\n/g, "
")); + } else { + // I'm not sure what it is - better dump it on the console. + console.log(x); + } + // Decolor the last cell if it was there. + if (last !== null) { + last = cells[cells.length-1]; + $(last.element).animate({"background-color": "white"}, 1000); + } + }}}); +}''' + + +@magics_class +class GradeMagic(Magics): + """ + This class holds the magic for the %grade and %grade_blockpy + """ + + @line_magic + def grade_logstart(self, line=""): + # ######Logging + ts = time.time() + logger = self.shell.logger # logging + old_logfile = self.shell.logfile # logging + directory = os.path.expanduser("log_folder{}~/".format(line)) + logfname = os.path.expanduser("log_folder{}~/log_{}.py~".format(line, ts)) + self.shell.logfile = logfname + loghead = u'# IPython log file\n\n' + try: + os.makedirs(directory, exist_ok=True) + logger.logstart(logfname, loghead, 'rotate', True, True, + True) + except BaseException: + self.shell.logfile = old_logfile + warn("Couldn't start log: %s" % sys.exc_info()[1]) + self.shell.run_code("input = __builtins__.input") + self.shell.run_code("print = __builtins__.print") + self.shell.run_code("sum = __builtins__.sum") + self.shell.run_code("len = __builtins__.len") + + @line_magic + def grade_logstop(self, line=""): + self.shell.logger.logstop() + + def logging(self): + # ######Logging + ts = time.time() + logger = self.shell.logger # logging + old_logfile = self.shell.logfile # logging + logfname = os.path.expanduser("log_folder~/log_{}.py~".format(ts)) + self.shell.logfile = logfname + loghead = u'# IPython log file\n\n' + try: + logger.logstart(logfname, loghead, 'rotate', False, True, + True) + except BaseException: + self.shell.logfile = old_logfile + warn("Couldn't start log: %s" % sys.exc_info()[1]) + logger.timestamp = False + input_hist = self.shell.history_manager.input_hist_raw + logger.log_write(u'\n'.join(input_hist[1:])) + logger.log_write(u'\n') + logger.timestamp = True + self.shell.logger.logstop() + # ######Logging + + # noinspection PyMethodMayBeStatic + def grade_parser(self, line, cell=None): + if ',' in line: + if cell is None: + assignment, line = line.split(",", maxsplit=1) + else: + assignment = None + inputs = json.dumps(line.split(",")) + inputs = "\\'" + inputs[1:len(inputs) - 1] + "\\'" + else: + if cell is None: + assignment, inputs = line, "" + else: + inputs = line + assignment = "" + inputs = json.dumps(inputs) + return {"inputs": inputs, "assignment": assignment} + + # noinspection PyMethodMayBeStatic + def unified_helper(self, local_code, **kwargs): + code = EXTRACT_STUDENT_CODE + code += ANIMATE_LAST_CELL + code += local_code.format(**kwargs) + code += EXECUTE_CODE + return code + + @cell_magic + def grade(self, line="", cell=""): + dump = self.grade_parser(line, cell) + code = self.unified_helper(LOCAL_GRADE, on_run_code="INSTRUCTOR_CODE", inputs=dump['inputs']) + cell = cell.replace("\\", "\\\\") + cell = cell.replace("\n", "\\n") + cell = cell.replace("'", "\\'") + cell = cell.replace('"', '\\"') + # Runs this code in the kernel as python code + # Can also run compiled code + self.shell.run_code("INSTRUCTOR_CODE = " + '"' + cell + '"') + # TODO: This was the easier way for me to get this to work + # This might be worth using in more depth to have less translation + # to and from javascript. See usage_examples + return display(Javascript(code)) + + @line_cell_magic + def usage_examples(self, line="", cell="print('running cell')\nprint('running cell2')"): + # Runs code in the kernel's context + self.shell.run_code("print('fun')") + + # Runs code in kernel's context using compiled code + sample = compile(cell, "usage_examples.py", "exec") + self.shell.run_code(sample) + + # runs javascript code + self.shell.run_cell_magic("javascript", "", "console.log('I do JAVASCRIPT');\n") + # Maybe can use javascript execution to pass things around...not sure though...can't get it to work + # You can pass values, but it doesn't seem to work unless you run it again. + # https://michhar.github.io/javascript-and-python-have-a-party/ + + self.shell.run_cell_magic( + "javascript", "", + # js_code = Javascript( + """var callbacks = { iopub : { output: function(out_data){ console.log(out_data) } } };\n""" + """var code = "fun = 12";\n""" + """IPython.notebook.kernel.execute(code);\n""") + # handle = display(js_code, display_id="usage_examples") + # handle.update(handle) + self.shell.run_cell_magic("javascript", "", "console.log('I do JAVASCRIPT TOO!!');\n") + # captures standard output, standard error, etc. and stops or not stops it + # class IPython.utils.capture.capture_output(stdout=True, stderr=True, display=True) + # Note that Tracebacks aren't put in standard error? + with capture_output(True, False, False) as captured: + print(dir(self)) + self.shell.run_code("print(fun)") + sys.stderr.write("spam\n") + print("I captured stdout") + print(captured.stdout) + print("I captured stderr") + print(captured.stderr) + + @line_magic + def grade_blockpy(self, line=""): + dump = self.grade_parser(line) + code = self.unified_helper(BLOCKPY_GRADE, assignment=dump["assignment"], inputs=dump["inputs"]) + return display(Javascript(code)) + + +def load_ipython_extension(ipython): + """ + Register this plugin with Jupyter Notebooks. Although it is allegedly + necessary in order to make this a plugin, we do not actually use it. + """ + ipython.register_magics(GradeMagic) + + +""" +DEPRECATED: The following lines of code do not seem to be necessary to + register this plugin with Jupyter. +def _jupyter_server_extension_paths(): + return [{ + "module": "pedal.plugins.grade_magic" + }] + +# jupyter serverextension enable --py pedal.plugins.grade_magic +def load_jupyter_server_extension(nbapp): + from IPython import get_ipython + get_ipython().register_magics(GradeMagic) +""" diff --git a/src/lib/pedal/plugins/test_reference_solution.py b/src/lib/pedal/plugins/test_reference_solution.py new file mode 100644 index 0000000000..bc093709c6 --- /dev/null +++ b/src/lib/pedal/plugins/test_reference_solution.py @@ -0,0 +1,138 @@ +''' +Tool for running a Grading script through a series of student reference +solutions. + +python -m pedal.plugins.test_reference_solution +''' + +# Runner +from pedal.report.imperative import clear_report, MAIN_REPORT +from pedal.cait import parse_program +import sys +import os +from io import StringIO +from contextlib import redirect_stdout +import unittest +from unittest.mock import patch, mock_open +import argparse + +# Arguments +DEFAULT_REFERENCE_SOLUTIONS_DIR = "reference_solutions/" + + +class TestReferenceSolutions(unittest.TestCase): + maxDiff = None + + +def substitute_args(arg, student_path, seed): + if arg == "$_STUDENT_MAIN": + return student_path + elif arg == "$_STUDENT_NAME": + return seed + return arg + + +def add_test(class_, name, python_file, + expected_output_path, expected_output, + grader_code, grader_path, grader_args, student_path): + seed = find_seed(python_file) + grader_args = [substitute_args(arg, student_path, seed) for arg in grader_args] + def _inner_test(self): + captured_output = StringIO() + with redirect_stdout(captured_output): + # TODO: mock_open will only work if we are not anticipating + # the student or instructor to open files... + with patch('builtins.open', mock_open(read_data=python_file), + create=True): + with patch.object(sys, 'argv', grader_args): + clear_report() + grader_exec = compile(grader_code, grader_path, 'exec') + exec(grader_exec, globals()) + #print(repr(MAIN_REPORT.feedback[0].mistake['error'])) + actual_output = captured_output.getvalue() + if expected_output is None: + print("File not found:", expected_output_path) + with open(expected_output_path, 'w') as out: + out.write(actual_output) + print("\tCreated missing file with current output") + else: + self.assertEqual(actual_output, expected_output) + setattr(class_, 'test_' + name, _inner_test) + +def find_seed(python_code): + try: + ast = parse_program(python_code) + for assign in ast.find_all("Assign"): + if assign.targets[0].ast_name != "Name": + continue + if assign.targets[0].id == "__STUDENT_SEED__": + if assign.value.ast_name == "Str": + return assign.value.s + elif assign.value.ast_name == "Num": + return assign.value.n + elif assign.value.ast_name == "List": + return [e.n for e in assign.value.elts] + except SyntaxError: + return 0 + return 0 + +# Load reference solutions +def add_all_tests(grader_path, reference_solutions_dir, grader_args, limit): + # Load grader file + with open(grader_path, 'r') as grader_file: + grader_code = grader_file.read() + for filename in os.listdir(reference_solutions_dir): + if limit is not None and limit != filename: + continue + path = os.path.join(reference_solutions_dir, filename) + if path.endswith(".py"): + text_path = path[:-2] + "txt" + with open(path, 'r') as python_file: + python = python_file.read() + if os.path.exists(text_path): + with open(text_path, 'r') as output_file: + output = output_file.read() + else: + output = None + add_test(TestReferenceSolutions, filename[:-3], python, + text_path, output, + grader_code, grader_path, grader_args, path) + + +def run_tests(): + unittest.main(argv=['first-arg-is-ignored']) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Run instructor grading script on a collection of reference solutions') + parser.add_argument('grader', help='The path to the instructor grading script.') + parser.add_argument('--path', '-p', + help='The path to the student reference files. If not given, assumed to be in the same folder ' + 'as the instructor grading script.', + default=DEFAULT_REFERENCE_SOLUTIONS_DIR) + parser.add_argument('--args', '-a', + help='Pass in arguments that the grading script will use. ' + 'Variable substitutions include "$_STUDENT_MAIN".', + default='test_reference_solution.py,$_STUDENT_MAIN,$_STUDENT_NAME') + parser.add_argument('--limit', '-l', help='Limit to a specific file.', default=None) + args = parser.parse_args() + + # Turn the reference solutions path into an absolute filename + if os.path.isabs(args.path): + reference_solutions_path = args.path + else: + reference_solutions_path = os.path.join(os.path.dirname(args.grader), args.path) + + # If no reference solutions folder, let's make it + if not os.path.exists(reference_solutions_path): + os.mkdir(reference_solutions_path) + + # Fix up the passed in args + grader_args = args.args.split(",") + + # Check that we actually have some files to try out + if not os.listdir(reference_solutions_path): + print("No reference solutions found") + else: + add_all_tests(args.grader, reference_solutions_path, grader_args, args.limit) + run_tests() diff --git a/src/lib/pedal/plugins/vpl.py b/src/lib/pedal/plugins/vpl.py new file mode 100644 index 0000000000..d1b61a5cd6 --- /dev/null +++ b/src/lib/pedal/plugins/vpl.py @@ -0,0 +1,148 @@ +from pedal.plugins.vpl_unittest import UnitTestedAssignment + +""" +Some kind of function to break up the sections +""" +import re +import sys +from html.parser import HTMLParser + +from pedal.report import MAIN_REPORT +from pedal import source +from pedal.resolvers import sectional +from pedal.cait.cait_api import expire_cait_cache + + +class VPLStyler(HTMLParser): + HEADERS = ("h1", "h2", "h3", "h4", "h5") + + def __init__(self): + super().__init__() + self.reset() + self.fed = [] + self.inside_pre = False + + def convert(self, html): + self.feed(html) + return self.get_data() + + @property + def text(self): + return ''.join(self.fed) + + def get_data(self): + return self.text + + def force_new_line(self): + if self.text and self.text[-1] not in ("\n", "\r"): + self.fed.append("\n") + + def handle_starttag(self, tag, attrs): + if tag in self.HEADERS: + self.force_new_line() + self.fed.append("-") + elif tag in ("pre",): + self.force_new_line() + self.fed.append(">") + self.inside_pre = True + + def handle_data(self, data): + if self.inside_pre: + # Need to prepend ">" to the start of new lines. + self.fed.append(data.replace("\n", "\n>")) + else: + self.fed.append(data) + + def handle_endtag(self, tag): + if tag in self.HEADERS: + self.fed.append("") + elif tag in ("pre",): + self.fed.append("") + self.inside_pre = False + + +def strip_tags(html): + return VPLStyler().convert(html) + + +def set_maximum_score(number, cap=True, report=None): + if report is None: + report = MAIN_REPORT + report['vpl']['score_maximum'] = number + report['vpl']['score_cap'] = cap + + +def resolve(report=None, custom_success_message=None): + if report is None: + report = MAIN_REPORT + print("<|--") + success, score, hc, messages_by_group = sectional.resolve(report) + last_group = 0 + for group, messages in sorted(messages_by_group.items()): + if group != last_group: + for intermediate_section in range(last_group, group, 2): + print("-" + report['source']['sections'][1 + intermediate_section]) + printed_first_bad = False + for message in messages: + if message['priority'] in ('positive', 'instructions'): + print(strip_tags(message['message'])) + elif not printed_first_bad: + print(strip_tags(message['message'])) + printed_first_bad = True + last_group = group + print("-Overall") + if success: + if custom_success_message is None: + print("Complete! Great job!") + else: + print(custom_success_message) + else: + print("Incomplete") + print("--|>") + print("Grade :=>>", round(score)) + + +class SectionalAssignment: + max_points = 1 + sections = None + + def __init__(self, filename=None, max_points=None, report=None): + self.report = MAIN_REPORT if report is None else report + find_file(filename if filename else self.filename, + sections=True, report=report) + set_maximum_score(self.max_points + if max_points is None else max_points) + source.check_section_exists(self.sections) + + def pre_test(self): + source.next_section() + verified = source.verify_section() + expire_cait_cache() + return verified + + def post_test(self): + return True + + def resolve(self): + checks = ((self.pre_test() and + getattr(self, attr)() and + self.post_test()) + for attr in dir(self) + if attr.startswith('test_') and + callable(getattr(self, attr))) + if all(checks): + self.report.set_success() + resolve(report=self.report) + + +from pedal.plugins.vpl_unittest import UnitTestedAssignment + + +def unittest_resolver(phases, report=None, custom_success_message=None): + success = True + for title, phase in phases: + outcome = phase()._run_all_tests() + if not outcome: + break + success = success and outcome + resolve(custom_success_message=custom_success_message) diff --git a/src/lib/pedal/plugins/vpl_safe_runner.py b/src/lib/pedal/plugins/vpl_safe_runner.py new file mode 100644 index 0000000000..7bba8e6e44 --- /dev/null +++ b/src/lib/pedal/plugins/vpl_safe_runner.py @@ -0,0 +1,10 @@ +from pedal import run +from pedal import set_source_file +import sys + +if __name__ == "__main__": + set_source_file(sys.argv[1] if len(sys.argv) > 1 else 'main.py') + student = run(context=False) + print(student.raw_output) + if student.exception: + print(student.exception_formatted, file=sys.stderr) diff --git a/src/lib/pedal/plugins/vpl_unittest.py b/src/lib/pedal/plugins/vpl_unittest.py new file mode 100644 index 0000000000..09e4c08119 --- /dev/null +++ b/src/lib/pedal/plugins/vpl_unittest.py @@ -0,0 +1,112 @@ +from unittest.util import safe_repr +from pedal import gently +from pedal.assertions.assertions import _normalize_string + + +class UnitTestedAssignment: + DELTA = .001 + + class AssertionException(Exception): + def __init__(self, message): + self.message = message + + def __init__(self): + pass + + def setUp(self): + pass + + def tearDown(self): + pass + + def _run_all_tests(self): + methods = [func for func in dir(self) + if callable(getattr(self, func)) and + func.startswith('test_')] + all_passed = True + for method in methods: + self.setUp() + try: + getattr(self, method)() + except UnitTestedAssignment.AssertionException as e: + gently(e.message) + all_passed = False + self.tearDown() + return all_passed + + def assertSimilarStrings(self, first, second, msg): + if _normalize_string(first) != _normalize_string(second): + return self.assertEqual(first, second, msg, exact=True) + + def assertNotSimilarStrings(self, first, second, msg): + if _normalize_string(first) == _normalize_string(second): + return self.assertEqual(first, second, msg, exact=True) + + def assertLessEqual(self, val1, val2, msg=None): + if not (val1 <= val2): + self.fail(msg, "{} is not less than or equal to {}".format(safe_repr(val1), safe_repr(val2))) + + def assertGreaterEqual(self, val1, val2, msg=None): + if not (val1 >= val2): + self.fail(msg, "{} is not greater than or equal to {}".format(safe_repr(val1), safe_repr(val2))) + + def assertNotEqual(self, val1, val2, msg=None, exact=False): + if val1 != val2: + return + if not exact and isinstance(val1, str) and isinstance(val2, str): + self.assertNotSimilarStrings(val1, val2, msg) + elif (not exact and isinstance(val1, (int, float)) and + isinstance(val2, (int, float))): + if abs(val2 - val1) > UnitTestedAssignment.DELTA: + return + standardMsg = "{} == {}".format(safe_repr(val1), safe_repr(val2)) + self.fail(msg, standardMsg) + + def assertEqual(self, val1, val2, msg=None, exact=False): + if val1 == val2: + return + if not exact and isinstance(val1, str) and isinstance(val2, str): + self.assertSimilarStrings(val1, val2, msg) + elif (not exact and isinstance(val1, (int, float)) and + isinstance(val2, (int, float))): + if abs(val2 - val1) <= UnitTestedAssignment.DELTA: + return + standardMsg = "{} != {}".format(safe_repr(val1), safe_repr(val2)) + self.fail(msg, standardMsg) + + def assertIn(self, member, container, msg=None): + if member not in container: + standardMsg = "{} not found in {}".format(safe_repr(member), + safe_repr(container)) + self.fail(msg, standardMsg) + + def assertNotIn(self, member, container, msg=None): + if member in container: + standardMsg = "{} found in {}".format(safe_repr(member), + safe_repr(container)) + self.fail(msg, standardMsg) + + def assertTrue(self, value, msg=None): + if not value: + self.fail(msg, "{} is not true".format(value)) + + def assertFalse(self, value, msg=None): + if value: + self.fail(msg, "{} is not false".format(value)) + + def assertSandbox(self, sandbox, msg=None): + if sandbox.exception is not None: + self.fail(msg, sandbox.format_exception()) + + def assertIsInstance(self, value, parent, msg=None): + if not isinstance(value, parent): + self.fail(msg, "{} is not an instance of {}".format(safe_repr(value), safe_repr(parent))) + + def assertHasAttr(self, object, attr, msg=None): + if not hasattr(object, attr): + self.fail(msg, "{} does not have an attribute named {}".format(safe_repr(object), safe_repr(attr))) + + def fail(self, message, standardMsg): + if message is None: + message = standardMsg + raise UnitTestedAssignment.AssertionException(message) diff --git a/src/lib/pedal/questions/__init__.py b/src/lib/pedal/questions/__init__.py new file mode 100644 index 0000000000..95a7db77c8 --- /dev/null +++ b/src/lib/pedal/questions/__init__.py @@ -0,0 +1,88 @@ +""" +A tool for providing dynamic questions to learners. +""" + +NAME = 'Questions' +SHORT_DESCRIPTION = "Provides dynamic questions to learners" +DESCRIPTION = ''' +''' +REQUIRES = [] +OPTIONALS = [] +CATEGORY = 'Instructions' + +__all__ = ['NAME', 'DESCRIPTION', 'SHORT_DESCRIPTION', 'REQUIRES', 'OPTIONALS', + 'Question', 'Pool', 'set_seed'] + +from pedal.report.imperative import MAIN_REPORT +from pedal.questions.setup import _setup_questions, set_seed, _name_hash + +class QuestionGrader: + def _get_functions_with_filter(self, filter='grade_'): + return [getattr(self, method_name) for method_name in dir(self) + if method_name.startswith(filter) and + callable(getattr(self, method_name))] + def _test(self, question): + methods = self._get_functions_with_filter() + for method in methods: + method(question) + +class Question: + def __init__(self, name, instructions, tests, seed=None, report=None): + self.name = name + self.instructions = instructions + self.tests = tests + self.seed = seed + if report is None: + report = MAIN_REPORT + self.report = report + self.answered = False + + def answer(self): + self.answered = True + + def ask(self): + if isinstance(self.tests, QuestionGrader): + self.tests._test(self) + else: + for test in self.tests: + test(self) + if not self.answered: + self.report.attach('Question', category='Instructions', tool='Questions', + group=self.report.group, + priority='instructions', + hint=self.instructions) + +class Pool: + _POOL_TRACKER = 0 + def __init__(self, name, choices, seed=None, report=None, position=None): + self.name = name + self.choices = choices + self.seed = seed + if report is None: + report = MAIN_REPORT + self.report = report + if position is None: + position = Pool._POOL_TRACKER + Pool._POOL_TRACKER += 1 + self.position = position + + def choose(self, force=None): + _setup_questions(self.report) + if force is None: + if self.seed is None: + force = self.report['questions']['seed'] + if isinstance(force, str): + force = _name_hash(force+self.name) + # Assume iterable; could be check that throws better error + if not isinstance(force, int): + force = force[self.position] + else: + force = self.seed + return self.choices[force % len(self.choices)] + + @property + def answered(self): + for choice in self.choices: + if choice.answered: + return True + return False diff --git a/src/lib/pedal/questions/design.md b/src/lib/pedal/questions/design.md new file mode 100644 index 0000000000..b38978ff92 --- /dev/null +++ b/src/lib/pedal/questions/design.md @@ -0,0 +1,92 @@ +# Questions Tool + +The questions model flips the script of Feedback generation to also generate +instructions. It is assumed that an environment would provide some initial +meta-instruction, and that initial evaluation would generate some new question +specific instructions. + +Taxonomy: +* Question: A bundle of instructions and tests that can be delivered to a + student, not as feedback, but as a narrowing/modification of the + original problem. +* Pool: A collection of Questions that can be drawn from randomly to + individualize the student experiment. +* Instructions: The (HTML) text rendered to the learner to prepare them for + a question. The default is to assume that this would be static, + but more interesting possibilities could occur. +* Tests: The collection of feedback control logic that is bundled for this + question. This speaks to the idea of better encapsulation for + the control logic - perhaps it is time for the Organizers from + Assertions to be promoted to their own Tool? +* Seed: A value (expected to be constant for a given user) that can act as + an "offset" for selecting problems. This allows users to + deterministically receive feedback from a feedback engine. + Numeric seeds allow specifically selecting questions from the pool, + while String seeds are hashed to "random" indexes. An example is to + use student usernames, emails, or internal IDs (probably better to + treat numeric IDs as strings). + +# Delivery + +By default, new question text is delivered by the Resolver as feedback +that appears at the top of all the other feedback without preventing any +subsequent feedback, similar to the way Compliments do not prevent actual +feedback. + +However, Resolvers probably want to override this. For example, BlockPy would +probably want to modify the problem instructions area. VPL would probably +want to isolate the instructions to their own group or to provide a header with +them. + +# Timing + +Here are a few different models of the timing of questions: +1. The student requests initial feedback, and all questions appear. +2. The student indicates in some way which question they want to appear, + and that question's Instructions appear. +3. Students are given a single initial question, and when they complete it, + the text of a new question appears. +4. Instead of a single question appearing, the students are presented with + a choice of questions (see CYOA). + +# Random Pool + +Frequently, instructors need to be able to draw a question from a pool. + +A design principle based on research is that questions should be as equal +in difficulty and learning objectives as possible. Granted - there are +pedagogical design decisions that could justify breaking that guideline. +We should encourage question equivalency but allow the instructor to have +wildly different questions. + +In theory, question selection doesn't have to be random. Subclasses +could be created that draw on data sources about the user - these could +be local data sources ("You struggled a lot on the last question, so let's +try another one that's similar") or more exotic ("My records indicate that you +haven't really mastered IF statements, so let's do some practice with that.") + +# Templated Questions + +Being able to generate questions based on a template or some rules appears +to be a popular request. In the Random Pool model, we had a static set of +Questions. But now we have a series of rules for generating questions. + +TemplateQuestion is a class for creating questions from finite sets of terms. +You provide a set of variables, some templated text (Python Format? Jinja2?), +and the set of values for variables. From this, questions could be automatically +generated. + +DynamicQuestion is a more general-purpose class for true dynamic generation of +problems. The model here would be to subclass and redefine components +by overriding methods. + +# CYOA + +One of the more interesting ideas is to support Choose-Your-Own-Adventure +style chains of questions. In this model, completing a question could unlock +multiple paths to move forward on. + +Open Questions: +* How do students indicate a choice along the path? +* How do we elegantly connect Decision A with Paths B, C, and D; keeping + in mind that game flow is a DAG or possibly even a graph. diff --git a/src/lib/pedal/questions/graders.py b/src/lib/pedal/questions/graders.py new file mode 100644 index 0000000000..20efa494e6 --- /dev/null +++ b/src/lib/pedal/questions/graders.py @@ -0,0 +1,105 @@ +from pedal.questions import QuestionGrader + +from pedal import run, compliment, explain, gently +from pedal.report.imperative import MAIN_REPORT +from pedal.assertions.assertions import * +from pedal.toolkit.functions import * + +class FunctionGrader(QuestionGrader): + MAX_POINTS = 10 + DEFINITION_POINTS = 3 + COMPONENTS_POINTS = 1 + MAX_COMPONENTS_POINTS = 2 + UNIT_TEST_TYPE_POINTS = None + UNIT_TEST_VALUE_POINTS = None + UNIT_TEST_TOTAL_POINTS = 5 + UNIT_TEST_TYPE_RATIO = .5 + UNIT_TEST_COMPLETION_POINTS = 2 + + def __init__(self, function_name, signature, tests): + super().__init__() + self.function_name = function_name + self.signature = signature + self.tests = tests + self.points = 0 + + def _test(self, question): + defined = self.grade_definition(question) + + if not defined: + return self.report_status(question) + + self.grade_components(question) + + passed_tests = self.grade_unit_tests(question) + if not passed_tests: + return self.report_status(question) + + self.report_success(question) + + def report_status(self, question): + pass + + def report_success(self, question): + question.answer() + + def grade_definition(self, question): + self.student = run(report_exceptions=True, context=False) + self.student.report_exceptions_mode=False + + self.definition = match_signature_muted(self.function_name, *self.signature) + if not assertGenerally(self.definition): + gently("Function not defined") + return False + + if self.student.exception: + return False + if not assertHasFunction(self.student, self.function_name): + gently("Function defined incorrectly") + return False + + self.points += self.DEFINITION_POINTS + return True + + def grade_components(self, question): + self.component_points = 0 + components = self._get_functions_with_filter('grade_component_') + for component in components: + component(question) + self.component_points = min(self.component_points, self.MAX_COMPONENTS_POINTS) + self.points += self.component_points + + def assertEqual(self, *parameters): + return assertEqual(*parameters) + + def grade_unit_tests(self, question): + all_good = True + if self.UNIT_TEST_TOTAL_POINTS is None: + TYPE_POINT_ADD = self.UNIT_TEST_TYPE_POINTS + VALUE_POINT_ADD = self.UNIT_TEST_VALUE_POINTS + else: + ratio = self.UNIT_TEST_TYPE_RATIO + TYPE_POINT_ADD = (self.UNIT_TEST_TOTAL_POINTS/len(self.tests) * (ratio)) + VALUE_POINT_ADD = (self.UNIT_TEST_TOTAL_POINTS/len(self.tests) * (1-ratio)) + for arguments, expected in self.tests: + #import sys + #print(repr(arguments), file=sys.stderr) + result = self.student.call(self.function_name, *arguments, context=False) + #print(repr(self.student.exception), file=sys.stderr) + if self.student.exception: + all_good = False + continue + if assertIsInstance(result, type(expected)): + self.points += TYPE_POINT_ADD + else: + all_good = False + continue + if self.assertEqual(result, expected): + self.points += VALUE_POINT_ADD + else: + all_good = False + if all_good: + self.points += self.UNIT_TEST_COMPLETION_POINTS + else: + gently("Failing unit tests") + return all_good diff --git a/src/lib/pedal/questions/setup.py b/src/lib/pedal/questions/setup.py new file mode 100644 index 0000000000..b261b6bc27 --- /dev/null +++ b/src/lib/pedal/questions/setup.py @@ -0,0 +1,39 @@ +from pedal.report.imperative import MAIN_REPORT + +import hashlib + +def _name_hash(name): + return hashlib.md5(name.encode('utf8')).digest()[0] + +def _setup_questions(report): + ''' + Initialize any necessary fields for the report's question tool. + + Args: + report (Report): The report object to store data and feedback in. + ''' + if 'questions' not in report: + report['questions'] = { + 'seed': 0 + } + +def set_seed(seed_value, report=None): + ''' + Sets the seed that will be used in selecting questions. + + Args: + seed_value (int or str or iterable[int]): The value to use when + selecting questions, deterministically. If int, the same index + will be used for all questions. If an iterable of ints, each + one will serve as the index for the corresponding problem (throws + an exception if the iterable isn't long enough). If a string, + it will be hashed to a value (the hash is deterministic across + platforms) that will be modulo'd to be in the right range for the + pool. Presently, hashing generates values from [0, 256) so you + need to limit your questions to 256. + report (Report): The report object to store data and feedback in. If + left None, defaults to the global MAIN_REPORT. + ''' + if report is None: + report = MAIN_REPORT + report['questions']['seed'] = seed_value diff --git a/src/lib/pedal/report/__init__.py b/src/lib/pedal/report/__init__.py new file mode 100644 index 0000000000..250ba0da0c --- /dev/null +++ b/src/lib/pedal/report/__init__.py @@ -0,0 +1,8 @@ +""" +The collection of classes and functions used to store the fundamental Report +and Feedback objects. +""" + +from pedal.report.report import Report +from pedal.report.feedback import Feedback +from pedal.report.imperative import * diff --git a/src/lib/pedal/report/feedback.py b/src/lib/pedal/report/feedback.py new file mode 100644 index 0000000000..9ccb53aaef --- /dev/null +++ b/src/lib/pedal/report/feedback.py @@ -0,0 +1,102 @@ +""" +Simple data classes for storing feedback to present to learners. +""" + +__all__ = ['Feedback'] + + +class Feedback: + """ + A class for storing raw feedback. + + Attributes: + label (str): An internal name for this specific piece of feedback. + tool (str): An internal name for indicating the tool that created + this feedback. + category (str): A human-presentable name showable to the learner. + More than one Feedback will be in a category, most + likely. + priority (str): An indication of how important this feedback is. + Might be "high/medium/low" or the name of a + category (tool?) to supersede. Exactly how this gets + used is up to the resolver. A special kind of priority + is "positive" - which indicates that this feedback is + positive, and the information is good to convey to the + student. + group (int or str): The group that this piece of feedback should be + associated with. Some resolvers want to group feedback using this + identifier. + result (bool): Whether or not this feedback is associated with the + learner completing the task ("Success!"). + performance (float): A relative amount that this feedback contributes + to the students' performance (think in terms of + partial credit, like "Triggering this feedback + is worth 20% (.2)"). + misconceptions (Message): A description of the misconception that + is believed to be in the student's mind, + or perhaps the relevant concept from the + material that should be associated with + this. ("Variables must be initialized + before they are used"). + mistakes (Message): A description of the error or bug that the + student has created ("NameError on line 5: sum + has not been defined"). + hints (Message): A suggestion for what the student can do + ("Initialize the sum variable on line 2"). + constraints (Message): A description of the task requirements or + task type that the student has violated + ("You used a for loop, but this question + expected you to use recursion."). + metacognitives (Message): A suggestion for more regulative + strategies ("You have been working for + 5 hours, perhaps it is time to take + a break?"). + """ + MESSAGE_TYPES = ['hint', 'mistake', 'misconception', + 'constraint', 'metacognitive'] + + def __init__(self, label, tool='instructor', + category='Instructor feedback', priority=None, group=None, + result=None, performance=None, misconception=None, + mistake=None, hint=None, constraint=None, + metacognitive=None): + # Metadata + self.label = label + self.tool = tool + self.category = category + self.priority = priority + self.group = group + # Data + self.result = result + self.performance = performance + self.misconception = misconception + self.mistake = mistake + self.hint = hint + self.constraint = constraint + self.metacognitive = metacognitive + + def __str__(self): + return "".format(self.label) + + def __repr__(self): + metadata = "" + if self.tool is not None: + metadata += ", tool=" + self.tool + if self.category is not None: + metadata += ", category=" + self.category + if self.priority is not None: + metadata += ", priority=" + self.priority + if self.group is not None: + metadata += ", group=" + str(self.group) + data = "" + return "Feedback({}{}{})".format(self.label, metadata, data) + + +""" +A Message is one of: + str + Dict with a `message` field and any other suitable fields, such as: + html_message: An HTML message instead of a plaintext message. + line: The line number to highlight + error: The error message to render +""" diff --git a/src/lib/pedal/report/imperative.py b/src/lib/pedal/report/imperative.py new file mode 100644 index 0000000000..28b88eb225 --- /dev/null +++ b/src/lib/pedal/report/imperative.py @@ -0,0 +1,80 @@ +""" +Imperative style commands for constructing feedback in a convenient way. +Uses a global report object (MAIN_REPORT). +""" + +__all__ = ['set_success', 'compliment', 'give_partial', 'explain', + 'gently', 'hide_correctness', 'suppress', 'log', 'debug', + 'clear_report', 'get_all_feedback', 'MAIN_REPORT'] + +from pedal.report.report import Report + +#: The global Report object. Meant to be used as a default singleton +#: for any tool, so that instructors do not have to create their own Report. +#: Of course, all APIs are expected to work with a given Report, and only +#: default to this Report when no others are given. +MAIN_REPORT = Report() + + +def set_success(): + """ + Creates Successful feedback for the user, indicating that the entire + assignment is done. + """ + MAIN_REPORT.set_success() + + +def compliment(message, line=None): + """ + Create a positive feedback for the user, potentially on a specific line of + code. + + Args: + message (str): The message to display to the user. + line (int): The relevant line of code to reference. + """ + MAIN_REPORT.compliment(message, line) + + +def give_partial(value, message=None): + """ + Increases the user's current score by the `value`. Optionally display + a positive message too. + + Args: + value (number): The number to increase the user's score by. + message (str): The message to display to the user. + """ + MAIN_REPORT.give_partial(value, message) + + +def explain(message, priority='medium', line=None, label='explain'): + MAIN_REPORT.explain(message, priority, line, label=label) + + +def gently(message, line=None, label='explain'): + MAIN_REPORT.gently(message, line, label=label) + + +def hide_correctness(): + MAIN_REPORT.hide_correctness() + + +def suppress(category, label=True): + MAIN_REPORT.suppress(category, label) + + +def log(message): + MAIN_REPORT.log(message) + + +def debug(message): + MAIN_REPORT.debug(message) + + +def clear_report(): + MAIN_REPORT.clear() + + +def get_all_feedback(): + return MAIN_REPORT.feedback diff --git a/src/lib/pedal/report/report.py b/src/lib/pedal/report/report.py new file mode 100644 index 0000000000..dd7690a97c --- /dev/null +++ b/src/lib/pedal/report/report.py @@ -0,0 +1,156 @@ +from pedal.report.feedback import Feedback + +__all__ = ['Report'] + + +class Report: + """ + A class for storing Feedback generated by Tools, along with any auxiliary + data that the Tool might want to provide for other tools. + + Attributes: + feedback (list of Feedback): The raw feedback generated for this Report + so far. + suppressions (list of tuple(str, str)): The categories and labels that + have been suppressed so far. + group (int or str): The label for the current group. Feedback given + by a Tool will automatically receive the current `group`. This + is used by the Source tool, for example, in order to group feedback + by sections. + group_names (dict[group:str]): A printable, student-facing name for the + group. When a group needs to be rendered out to the user, this + will override whatever label was going to be presented instead. + group_order (sequence or callable or None): The mechanism to use to + order groups. If a sequence, the order will be inferred based on + the order of elements in the sequence. If a callable, the callable + will be used as a key function for `sort`. If `None`, then defaults + to the natural ordering of the groups. Defaults to `None`. + hooks (dict[str: list[callable]): A dictionary mapping events to + a list of callable functions. Tools can register functions on + hooks to have them executed when the event is triggered by another + tool. For example, the Assertions tool has hooks on the Source tool + to trigger assertion resolutions before advancing to next sections. + _results (dict of str => any): Maps tool names to their data. The + namespace for a tool can be used to + store whatever they want, but will + probably be in a dictionary itself. + """ + group_order = None + + def __init__(self): + """ + Creates a new Report instance. + """ + self.clear() + + def clear(self): + self.feedback = [] + self.suppressions = {} + self._results = {} + self.group = None + self.group_names = {} + self.hooks = {} + + def set_success(self, group=None): + """ + Creates Successful feedback for the user, indicating that the entire + assignment is done. + """ + if group is None: + group = self.group + self.feedback.append(Feedback('set_success', priority='positive', + result=True, group=group)) + + def give_partial(self, value, message=None, group=None): + if value is None: + return False + if group is None: + group = self.group + self.feedback.append(Feedback('give_partial', performance=value, + priority='positive', + group=group, + mistake=message)) + return True + + def hide_correctness(self): + self.suppressions['success'] = [] + + def explain(self, message, priority='medium', line=None, group=None, + label='explain'): + misconception = {'message': message} + if line is not None: + misconception['line'] = line + if group is None: + group = self.group + self.attach(label, priority=priority, category='instructor', + group=group, misconception=misconception) + + def gently(self, message, line=None, group=None, label='explain'): + self.explain(message, priority='student', line=line, group=group, + label=label) + + def compliment(self, message, line=None, group=None, label='explain'): + self.explain(message, priority='positive', line=line, group=group, + label=label) + + def attach(self, label, **kwargs): + self.feedback.append(Feedback(label, **kwargs)) + + def log(self, message): + pass + + def debug(self, message): + pass + + def suppress(self, category, label=True, where=True): + """ + Args: + category (str): The category of feedback to suppress. + label (str): A specific label to match against and suppress. + where (bool or group): Which group of report to localize the + suppression to. If instead `True` is passed, the suppression + occurs in every group globally. + TODO: Currently, only global suppression is supported. + """ + category = category.lower() + if isinstance(label, str): + label = label.lower() + if category not in self.suppressions: + self.suppressions[category] = [] + self.suppressions[category].append(label) + + def add_hook(self, event, function): + """ + Register the `function` to be executed when the given `event` is + triggered. + + Args: + event (str): An event name. Multiple functions can be triggered for + the same `event`. The format is as follows: + "pedal.module.function.extra" + + The `".extra"` component is optional to add further nuance, but + the general idea is that you are referring to functions that, + when called, should trigger other functions to be called first. + function (callable): A callable function. This function should + accept a keyword parameter named `report`, which will + """ + if event not in self.hooks: + self.hooks[event] = [] + self.hooks[event].append(function) + + def execute_hooks(self, event): + if event in self.hooks: + for function in self.hooks[event]: + function(report=self) + + def __getitem__(self, key): + if key not in self._results: + self._results[key] = {} + return self._results[key] + + def __setitem__(self, key, value): + self._results[key] = value + + def __contains__(self, key): + return key in self._results diff --git a/src/lib/pedal/resolvers/__init__.py b/src/lib/pedal/resolvers/__init__.py new file mode 100644 index 0000000000..0bbf9c8536 --- /dev/null +++ b/src/lib/pedal/resolvers/__init__.py @@ -0,0 +1,19 @@ +""" + +Resolver Types + +Does there need to be some kind of hook for Tools to wrap up their business? + +Simple + Find the highest priority feedback and show that, along with any positive feedback. + +Sectional + Find the highest priority feedback for each section, and show that along with any positive feedback. + +Full + Report all feedback, grouped by tool/category/priority/time. + +Full Summary + Report all feedback but divided into frequencies of labels grouped by tool/category/priority/time. + +""" diff --git a/src/lib/pedal/resolvers/core.py b/src/lib/pedal/resolvers/core.py new file mode 100644 index 0000000000..07a0b696e2 --- /dev/null +++ b/src/lib/pedal/resolvers/core.py @@ -0,0 +1,22 @@ +from pedal.report.imperative import MAIN_REPORT + + +def make_resolver(func, report=None): + ''' + Decorates the given function as a Resolver. This means that when the + function is executed, the `"pedal.resolver.resolve"` event will be + triggered. + + Args: + func (callable): The function to decorate. + report (Report): The Report to trigger the event on. If None, then use + the `MAIN_REPORT`. + ''' + if report is None: + report = MAIN_REPORT + + def resolver_wrapper(): + report.execute_hooks("pedal.resolvers.resolve") + return func() + + return resolver_wrapper diff --git a/src/lib/pedal/resolvers/readme.md b/src/lib/pedal/resolvers/readme.md new file mode 100644 index 0000000000..8b244293ae --- /dev/null +++ b/src/lib/pedal/resolvers/readme.md @@ -0,0 +1,3 @@ +# Resolvers + +A tool for selecting and managing reported data from other tools, in order to select a relevant piece of feedback. \ No newline at end of file diff --git a/src/lib/pedal/resolvers/sectional.py b/src/lib/pedal/resolvers/sectional.py new file mode 100644 index 0000000000..4150be96be --- /dev/null +++ b/src/lib/pedal/resolvers/sectional.py @@ -0,0 +1,77 @@ +import sys + +from pedal.resolvers import simple +from pedal.report import MAIN_REPORT + + +def resolve(report=None, priority_key=None): + """ + Args: + report (Report): The report object to resolve down. Defaults to the + global MAIN_REPORT + + Returns + str: A string of HTML feedback to be delivered + """ + if report is None: + report = MAIN_REPORT + if priority_key is None: + priority_key = simple.by_priority + # Prepare feedbacks + feedbacks = report.feedback + feedbacks.sort(key=lambda f: (f.group or 0, priority_key(f))) + suppressions = report.suppressions + # Process + final_success = False + final_score = 0 + finals = {} + found_failure = False + for feedback in feedbacks: + group = feedback.group or 0 + category = feedback.category.lower() + if category in suppressions: + if True in suppressions[category]: + continue + elif feedback.label.lower() in suppressions[category]: + continue + success, partial, message, data = simple.parse_feedback(feedback) + final_success = success or final_success + final_score += partial + if message is not None: + #print("RESETING GROUP", group, message[:20], found_failure, feedback.priority) + if group not in finals: + finals[group] = [] + found_failure = False + if feedback.priority not in ('positive', 'instructions'): + if found_failure: + continue + found_failure = True + entry = {'label': feedback.label, + 'message': message, + 'category': feedback.category, + 'priority': feedback.priority, + 'data': data} + if feedback.priority == 'instructions': + # Find end of instructions + index = 0 + for feedback in finals[group]: + if feedback['priority'] != 'instructions': + break + index += 1 + finals[group].insert(index, entry) + elif feedback.priority != 'positive': + finals[group].insert(0, entry) + else: + finals[group].append(entry) + #from pprint import pprint + #pprint(finals) + final_hide_correctness = suppressions.get('success', False) + if not finals: + finals[0] = [{ + 'label': 'No errors', + 'category': 'Instructor', + 'data': [], + 'priority': 'medium', + 'message': "No errors reported." + }] + return (final_success, final_score, final_hide_correctness, finals) diff --git a/src/lib/pedal/resolvers/simple.py b/src/lib/pedal/resolvers/simple.py new file mode 100644 index 0000000000..59222f2cea --- /dev/null +++ b/src/lib/pedal/resolvers/simple.py @@ -0,0 +1,156 @@ +from pedal.report import MAIN_REPORT, Feedback +from pedal.resolvers.core import make_resolver + +DEFAULT_CATEGORY_PRIORITY = [ + 'syntax', + 'mistakes', + 'instructor', + 'analyzer', + 'runtime', + 'student', + 'positive', + 'instructions', + 'uncategorized', +] + +# For compatibility with the old feedback API +LEGACY_CATEGORIZATIONS = { + # 'student': 'runtime', + 'parser': 'syntax', + 'verifier': 'syntax', + 'instructor': 'instructor' +} + + +def by_priority(feedback): + """ + Converts a feedback into a numeric representation for sorting. + + Args: + feedback (Feedback): The feedback object to convert + Returns: + float: A decimal number representing the feedback's relative priority. + """ + category = 'uncategorized' + if feedback.category is not None: + category = feedback.category.lower() + priority = 'medium' + if feedback.priority is not None: + priority = feedback.priority.lower() + priority = LEGACY_CATEGORIZATIONS.get(priority, priority) + if category in DEFAULT_CATEGORY_PRIORITY: + value = DEFAULT_CATEGORY_PRIORITY.index(category) + else: + value = len(DEFAULT_CATEGORY_PRIORITY) + offset = .5 + if priority == 'low': + offset = .7 + elif priority == 'high': + offset = .3 + elif priority not in ('low', 'medium', 'high'): + if priority in DEFAULT_CATEGORY_PRIORITY: + value = DEFAULT_CATEGORY_PRIORITY.index(priority) + offset = .1 + return value + offset + + +def parse_message(component): + if isinstance(component, str): + return component + elif isinstance(component, list): + return '
\n'.join(parse_message(c) for c in component) + elif isinstance(component, dict): + if "html" in component: + return component["html"] + elif "message" in component: + return component["message"] + else: + raise ValueError("Component has no message field: " + str(component)) + else: + raise ValueError("Invalid component type: " + str(type(component))) + + +def parse_data(component): + if isinstance(component, str): + return [{'message': component}] + elif isinstance(component, list): + return component + elif isinstance(component, dict): + return [component] + + +def parse_feedback(feedback): + # Default returns + success = False + performance = 0 + message = None + data = [] + # Actual processing + for feedback_type in Feedback.MESSAGE_TYPES: + feedback_value = getattr(feedback, feedback_type) + if feedback_value is not None: + data.extend(parse_data(feedback_value)) + parsed_message = parse_message(feedback_value) + if parsed_message is not None: + message = parsed_message + if feedback.result is not None: + success = feedback.result + if feedback.performance is not None: + performance = feedback.performance + return success, performance, message, data + + +@make_resolver +def resolve(report=None, priority_key=None): + """ + Args: + report (Report): The report object to resolve down. Defaults to the + global MAIN_REPORT + + Returns + str: A string of HTML feedback to be delivered + """ + if report is None: + report = MAIN_REPORT + if priority_key is None: + priority_key = by_priority + # Prepare feedbacks + feedbacks = report.feedback + feedbacks.sort(key=priority_key) + suppressions = report.suppressions + # Process + final_success = False + final_score = 0 + final_message = None + final_category = 'Instructor' + final_label = 'No errors' + final_data = [] + for feedback in feedbacks: + category = feedback.category.lower() + if category in suppressions: + if True in suppressions[category]: + continue + elif feedback.label.lower() in suppressions[category]: + continue + success, partial, message, data = parse_feedback(feedback) + final_success = success or final_success + final_score += partial + if (message is not None and + final_message is None and + feedback.priority != 'positive'): + final_message = message + final_category = feedback.category + final_label = feedback.label + final_data = data + if final_message is None: + final_message = "No errors reported." + final_hide_correctness = suppressions.get('success', False) + if (not final_hide_correctness and final_success and + final_label == 'No errors' and + final_category == 'Instructor'): + final_category = 'Complete' + final_label = 'Complete' + final_message = "Great work!" + return (final_success, final_score, final_category, + final_label, final_message, final_data, + final_hide_correctness) diff --git a/src/lib/pedal/sandbox/__init__.py b/src/lib/pedal/sandbox/__init__.py new file mode 100644 index 0000000000..11e4843641 --- /dev/null +++ b/src/lib/pedal/sandbox/__init__.py @@ -0,0 +1,34 @@ +from pedal.report import MAIN_REPORT +from pedal.sandbox.sandbox import Sandbox + +# Compatibility API +''' +run_student +queue_input +reset_output +get_output +''' + + +def reset(report=None): + if report is None: + report = MAIN_REPORT + report['sandbox']['run'] = Sandbox(filename=report['source']['filename']) + + +def run(raise_exceptions=True, report=None, coverage=False, threaded=False, inputs=None): + if report is None: + report = MAIN_REPORT + if 'run' not in report['sandbox']: + report['sandbox']['run'] = Sandbox(filename=report['source']['filename'], threaded=threaded) + sandbox = report['sandbox']['run'] + source_code = report['source']['code'] + sandbox.record_coverage = coverage + sandbox.run(source_code, _as_filename=report['source']['filename'], _inputs=inputs) + if raise_exceptions and sandbox.exception is not None: + name = str(sandbox.exception.__class__)[8:-2] + report.attach(name, category='Runtime', tool='Sandbox', + section=report['source']['section'], + mistakes={'message': sandbox.format_exception(), + 'error': sandbox.exception}) + return sandbox diff --git a/src/lib/pedal/sandbox/compatibility.py b/src/lib/pedal/sandbox/compatibility.py new file mode 100644 index 0000000000..3fb94f9807 --- /dev/null +++ b/src/lib/pedal/sandbox/compatibility.py @@ -0,0 +1,121 @@ +import sys + +from pedal.sandbox.sandbox import Sandbox +from pedal.sandbox.messages import EXTENDED_ERROR_EXPLANATION + +from pedal.report import MAIN_REPORT, Feedback + + +def _check_sandbox(report): + if 'run' not in report['sandbox']: + report['sandbox']['run'] = Sandbox() + return report['sandbox']['run'] + + +def run_student(raise_exceptions=False, report=None, old_style_messages=False): + if report is None: + report = MAIN_REPORT + sandbox = _check_sandbox(report) + source_code = report['source']['code'] + sandbox.run(source_code, report_exceptions=not raise_exceptions) + if raise_exceptions: + raise_exception(sandbox.exception, sandbox.exception_position, + report=report, message=None if old_style_messages else + sandbox.exception_formatted) + return sandbox.exception + + +def queue_input(*inputs, **kwargs): + if 'report' not in kwargs: + report = MAIN_REPORT + else: + report = kwargs['report'] + sandbox = _check_sandbox(report) + sandbox.set_input(inputs) + + +def reset_output(report=None): + if report is None: + report = MAIN_REPORT + sandbox = _check_sandbox(report) + sandbox.set_output(None) + + +def get_output(report=None): + if report is None: + report = MAIN_REPORT + sandbox = _check_sandbox(report) + return sandbox.output + + +def get_plots(report=None): + if report is None: + report = MAIN_REPORT + sandbox = _check_sandbox(report) + mock_plt = sandbox.modules['matplotlib.pyplot'] + return mock_plt.plots + + +def capture_output(function, *args, **kwargs): + if 'report' in kwargs: + report = kwargs['report'] + else: + report = MAIN_REPORT + sandbox = _check_sandbox(report) + sandbox.set_output(None) + sandbox.call(function.__name__, *args) + return sandbox.output + + +def get_sandbox(report=None): + if report is None: + report = MAIN_REPORT + sandbox = _check_sandbox(report) + return sandbox + + +def raise_exception(exception, position=None, report=None, message=None): + if report is None: + report = MAIN_REPORT + sandbox = _check_sandbox(report) + if exception is None: + return + extended = EXTENDED_ERROR_EXPLANATION.get(exception.__class__, "") + if message is None: + message = "
{}
\n{}".format(str(exception), extended) + # Skulpt compatible name lookup + name = str(exception.__class__)[8:-2] + report.attach(name, category='Runtime', tool='Sandbox', + mistake={'message': message, + 'error': exception, + 'position': position, + 'traceback': None}) + sandbox.exception = exception + + +def get_student_data(report=None): + if report is None: + report = MAIN_REPORT + sandbox = _check_sandbox(report) + return sandbox + + +def set_sandbox(sandbox, report=None): + """ + Update the sandbox to hold the new sandbox instance. Particularly useful + for Skulpt, which needs to set the sandbox in an unusual way. + """ + if report is None: + report = MAIN_REPORT + report['sandbox']['run'] = sandbox + return sandbox + + +def trace_lines(report=None): + if report is None: + report = MAIN_REPORT + sandbox = _check_sandbox(report) + if sandbox.tracer_style == 'coverage': + return sandbox.trace.lines - sandbox.trace.missing + else: + return [] diff --git a/src/lib/pedal/sandbox/exceptions.py b/src/lib/pedal/sandbox/exceptions.py new file mode 100644 index 0000000000..2961915195 --- /dev/null +++ b/src/lib/pedal/sandbox/exceptions.py @@ -0,0 +1,169 @@ +import traceback +import os +import sys + +class SandboxException(Exception): + """ + Generic base exception for sandbox errors. + """ + +class SandboxStudentCodeException(SandboxException): + """ + Caused by an error in student code + """ + def __init__(self, actual): + self.actual = actual + +class SandboxPreventModule(Exception): + """ + Caused by student attempting to load a module that they shouldn't. + """ + + +class SandboxHasNoFunction(SandboxException): + """ + Caused by attempting to access a function that the student hasn't created. + """ + + +class SandboxHasNoVariable(SandboxException): + """ + Caused by attempting to access a variable that the student hasn't created. + """ + + +class SandboxNoMoreInputsException(Exception): + """ + Caused by the student calling `input` when the instructor hasn't provided + enough inputs. Typically, the student has an infinite loop around their + `input` function. + """ + + +BuiltinKeyError = KeyError + + +class KeyError(BuiltinKeyError): + """ + A version of KeyError that replaces the built-in with one small + modification: when printing an explanatory message, the message is not + rendered as a tuple. Because that's stupid and the fact that it made it + into CPython is just rude. + + See Also: + https://github.com/python/cpython/blob/master/Objects/exceptions.c#L1556 + """ + __module__ = "builtins" + + def __init__(self, original, message): + self.__cause__ = original.__cause__ + self.__traceback__ = original.__traceback__ + self.__context__ = original.__context__ + self.message = message + + def __str__(self): + return self.message + + +def _add_context_to_error(e, message): + if isinstance(e, BuiltinKeyError): + new_args = repr(e.args[0]) + message + e = KeyError(e, new_args) + e.args = tuple([new_args]) + elif isinstance(e, OSError): + # TODO: Investigate OSError, since they have so many args. + # Might be weird. + e.args = tuple([e.args[0] + message]) + return e + elif e.args: + e.args = tuple([e.args[0] + message]) + return e +x=sys.stdout +class SandboxTraceback: + """ + Class for reformatting tracebacks to have more pertinent information. + """ + + def __init__(self, exception, exc_info, full_traceback, + instructor_filename, line_offset, student_filename, + original_code_lines): + """ + Args: + exception (Exception): The exception that was raised. + exc_info (ExcInfo): The result of sys.exc_info() when the exception + was raised. + full_traceback (bool): Whether or not to provide the full traceback + or just the parts relevant to students. + instructor_filename (str): The name of the instructor file, which + can be used to avoid reporting instructor code in the + traceback. + """ + self.line_offset = line_offset + self.exception = exception + self.exc_info = exc_info + self.full_traceback = full_traceback + self.instructor_filename = instructor_filename + self.student_filename = student_filename + self.line_number = traceback.extract_tb(exc_info[2])[-1][1] + self.original_code_lines = original_code_lines + + def _clean_traceback_line(self, line): + return line.replace(', in ', '', 1) + + def format_exception(self, preamble=""): + if not self.exception: + return "" + if isinstance(self.exception, TimeoutError): + return str(self.exception) + cl, exc, tb = self.exc_info + while tb and self._is_relevant_tb_level(tb): + tb = tb.tb_next + length = self._count_relevant_tb_levels(tb) + tb_e = traceback.TracebackException(cl, self.exception, tb, limit=length, + capture_locals=False) + # print(list(), file=x) + for frame in tb_e.stack: + if frame.filename == os.path.basename(self.student_filename): + frame.lineno += self.line_offset + frame._line = self.original_code_lines[frame.lineno-1] + lines = [self._clean_traceback_line(line) + for line in tb_e.format()] + lines[0] = "Traceback:\n" + return preamble + ''.join(lines) + + def _count_relevant_tb_levels(self, tb): + length = 0 + while tb and not self._is_relevant_tb_level(tb): + length += 1 + tb = tb.tb_next + return length + + def _is_relevant_tb_level(self, tb): + """ + Determines if the give part of the traceback is relevant to the user. + + Returns: + boolean: True means it is NOT relevant + """ + # Are in verbose mode? + if self.full_traceback: + return False + filename, a_, b_, _ = traceback.extract_tb(tb, limit=1)[0] + # Is the error in the instructor file? + if filename == self.instructor_filename: + return True + # Is the error in this test directory? + current_directory = os.path.dirname(os.path.realpath(__file__)) + if filename.startswith(current_directory): + return True + # Is the error related to a file in the parent directory? + parent_directory = os.path.dirname(current_directory) + # Currently we don't refer to this? + # Is the error in a local file? + if filename.startswith('.'): + return False + # Is the error in an absolute path? + if not os.path.isabs(filename): + return False + # Okay, it's not a student related file + return True diff --git a/src/lib/pedal/sandbox/messages.py b/src/lib/pedal/sandbox/messages.py new file mode 100644 index 0000000000..7cb4df5294 --- /dev/null +++ b/src/lib/pedal/sandbox/messages.py @@ -0,0 +1,61 @@ +# Skulpt has weird errors, and is missing some errors. Compatibility. +try: + ParseError +except NameError: + class ParseError(Exception): + pass +try: + SyntaxError +except NameError: + class SyntaxError(Exception): + pass +try: + ReferenceError +except NameError: + class ReferenceError(Exception): + pass +try: + EOFError +except NameError: + class EOFError(Exception): + pass +try: + MemoryError +except NameError: + class MemoryError(Exception): + pass +try: + OSError +except NameError: + class OSError(Exception): + pass +try: + TokenError +except NameError: + class TokenError(Exception): + pass +try: + TimeLimitError +except NameError: + class TimeLimitError(Exception): + pass + +EXTENDED_ERROR_EXPLANATION = { + ParseError: "A parse error means that Python does not understand the syntax on the line the error message points out. Common examples are forgetting commas beteween arguments or forgetting a : (colon) on a for statement.
Suggestion: To fix a parse error you just need to look carefully at the line with the error and possibly the line before it. Make sure it conforms to all of Python's rules.", + TypeError: "Type errors most often occur when an expression tries to combine two objects with types that should not be combined. Like using + to add a number to a list instead of .append, or dividing a string by a number.
Suggestion: To fix a type error you will most likely need to trace through your code and make sure the variables have the types you expect them to have.", + SyntaxError: "This message indicates that Python can't figure out the syntax of a particular statement. Some examples are assigning to a literal, or a function call.
Suggestion: Check your assignment statements and make sure that the left hand side of the assignment is a variable, not a literal (e.g., 7 or \"hello\") or a function.", + NameError: "A name error almost always means that you have used a variable before it has a value. Often this may be a simple typo, so check the spelling carefully.
Suggestion: Check the right hand side of assignment statements and your function calls, this is the most likely place for a NameError to be found. It really helps to step through your code, one line at a time, mentally keeping track of your variables.", + ValueError: "A ValueError most often occurs when you pass a parameter to a built-in function, and the function is expecting one type and you pass something different. For instance, if you try to convert a non-numeric string to an int, you will get a ValueError:
  int(\"Corgi\") # ValueError: invalid literal for int() with base 10

Suggestion: The error message gives you a pretty good hint about the name of the function as well as the value that is incorrect. Look at the error message closely and then trace back to the variable containing the problematic value. }", + AttributeError: "This happens when you try to do SOMETHING.WHATEVER and either SOMETHING wasn't declared or WHATEVER isn't an attribute of SOMETHING. This error message is telling you that the object on the left hand side of the dot, does not have the attribute or method on the right hand side.
Suggestion: You were probably trying to either get access to some data (weather.get) or append (a_list.append). If it's the first one, you should make sure the module is imported and that you are called its function correctly. If it's the second one, you should make sure you spelled \"append\" right and that you are using a variable with a list for a value.", + TokenError: "Most of the time this error indicates that you have forgotten a right parenthesis or have forgotten to close a pair of quotes.
Suggestion: Check each line of your program and make sure that your parenthesis are balanced.", + IndexError: "This message means that you are trying to index past the end of a string or a list. For example, if your list has 3 things in it and you try to access the item at position 5.
Suggestion: Remember that the first item in a list or string is at index position 0, quite often this message comes about because you are off by one. Remember in a list of length 3 the last legal index is 2.
favorite_colors = [\"red\", \"blue\", \"green\"]\nfavorite_colors[2] # prints green favorite_color[3] # raises an IndexError
", + ImportError: "This error message indicates that you are trying to import a module that does not exist, or is not in the same directory as your python script.
Suggestion: One problem may simply be that you have a typo - remember, you must not capitalize the module name. Another common problem is that you have placed the module in a different directory. Finally, if you're using a dataset module, then it might not be imported. Use the \"Import Datasets\" button below!", + ReferenceError: "This is a really hard error to get, so I'm not entirely sure what you did.
Suggestion: Bring this code to the instructor. ", + ZeroDivisionError: "This tells you that you are trying to divide by 0. Typically this is because the value of the variable in the denominator of a division expression has the value 0.
Suggestion: Are you sure you are dividing by the right variable? Are you sure that that variable has the value you expect - is it possible that you counted the number of elements in an empty list, for instance?", + IndentationError: "This error occurs when you have not indented your code properly. This is most likely to happen as part of an if, for, while or def statement.
Suggestion: Check your if, def, for, and while statements to be sure the lines are properly indented beneath them (seriously, this happens ALL the time). Another source of this error comes from copying and pasting code where you have accidentally left some bits of code lying around that don't belong there anymore. Finally, a very sinister but unlikely possibility is that you have some tab characters in your code, which look identical to four spaces. Never, ever use tabs, and carefully check code from the internet to make sure it doesn't have tabs.", + EOFError: "If you are using input() or raw_input() commands, then this error happens when they don't get the right ending.
Suggestion: It's hard to protect against users. However, if you're using input(), you might be able to use raw_input() instead to avoid this problem. ", + IOError: "This is a very easy error to get. The most common reason is that you were trying to open a file and it wasn't in the right place.
Suggestion: Make sure that the file is in the right place - print out the file path, and then check that it's definitely on your computer at that location. If you need help doing file processing, you should probably check with an instructor.", + KeyError: "A dictionary has a bunch of keys that you can use to get data. This error is caused by you trying to refer to a key that does not exist.
Suggestion: The most common reason you get this exception is that you have a typo in your dictionary access. Check your spelling. Also double check that the key definitely exists.", + MemoryError: "Somehow, you have run out of memory.
Suggestion: Make sure you are filtering your dataset! Alternatively, bring your code to an instructor.", + OSError: "It's hard to say what an OSError is without deep checking. Many things can cause it.
Suggestion: Bring your code to an instructor. ", + TimeLimitError: "A TimeLimit error means that BlockPy wasn't able to process your program fast enough. Typically, this means that you're iterating through too many elements."} diff --git a/src/lib/pedal/sandbox/mocked.py b/src/lib/pedal/sandbox/mocked.py new file mode 100644 index 0000000000..e421cfbdb1 --- /dev/null +++ b/src/lib/pedal/sandbox/mocked.py @@ -0,0 +1,271 @@ +""" +Mocked functions that can be used to prevent malicious or accidental `eval` +behavior. +""" +import re +import types + +from pedal.sandbox.exceptions import (SandboxNoMoreInputsException, + SandboxPreventModule) + + +def _disabled_compile(source, filename, mode, flags=0, dont_inherit=False): + """ + A version of the built-in `compile` method that fails with a runtime + error. + """ + raise RuntimeError("You are not allowed to call 'compile'.") + + +def _disabled_eval(object, globals=globals(), locals=locals()): + """ + A version of the built-in `eval` method that fails with a runtime + error. + """ + raise RuntimeError("You are not allowed to call 'eval'.") + + +# ------------------------------------------------------------- + + +def _disabled_exec(object, globals=globals(), locals=locals()): + """ + A version of the built-in `exec` method that fails with a runtime + error. + """ + raise RuntimeError("You are not allowed to call 'exec'.") + + +# ------------------------------------------------------------- + + +def _disabled_globals(): + """ + A version of the built-in `globals` method that fails with a runtime + error. + """ + raise RuntimeError("You are not allowed to call 'globals'.") + +class FunctionNotAllowed(Exception): + pass + +def disabled_builtin(name): + def _disabled_version(*args, **kwargs): + raise FunctionNotAllowed("You are not allowed to call '{}'.".format(name)) + return _disabled_version + + +_OPEN_FORBIDDEN_NAMES = re.compile(r"(^[./])|(\.py$)") +_OPEN_FORBIDDEN_MODES = re.compile(r"[wa+]") + + +def _restricted_open(name, mode='r', buffering=-1): + if _OPEN_FORBIDDEN_NAMES.search(name): + raise RuntimeError("The filename you passed to 'open' is restricted.") + elif _OPEN_FORBIDDEN_MODES.search(mode): + raise RuntimeError("You are not allowed to 'open' files for writing.") + else: + return _original_builtins['open'](name, mode, buffering) + + +try: + __builtins__ +except NameError: + _default_builtins = {'globals': globals, + 'locals': locals, + 'open': open, + 'input': input} +else: + if isinstance(__builtins__, types.ModuleType): + _default_builtins = __builtins__.__dict__ + else: + _default_builtins = __builtins__ + +_original_builtins = { + 'globals': _default_builtins['globals'], + 'locals': _default_builtins['locals'], + 'open': _default_builtins['open'], + 'input': _default_builtins['input'], + 'exec': _default_builtins.get('exec', _disabled_exec), + 'eval': _default_builtins.get('eval', _disabled_eval), + 'compile': _default_builtins.get('compile', _disabled_compile), +} + + +def _make_inputs(*input_list, **kwargs): + """ + Helper function for creating mock user input. + + Params: + input_list (list of str): The list of inputs to be returned + Returns: + function (str=>str): The mock input function that is returned, which + will return the next element of input_list each + time it is called. + """ + if 'repeat' in kwargs: + repeat = kwargs['repeat'] + else: + repeat = None + generator = iter(input_list) + + def mock_input(prompt=''): + print(prompt) + try: + return next(generator) + except StopIteration as SI: + if repeat is None: + # TODO: Make this a custom exception + raise SandboxNoMoreInputsException("User had no more input to give.") + else: + return repeat + + return mock_input + + +_sys_modules = {} + + +def _override_builtins(namespace, custom_builtins): + """ + Add the custom builtins to the `namespace` (and the original `__builtins__`) + suitable for `exec`. + """ + # Obtain the dictionary of built-in methods, which might not exist in + # some python versions (e.g., Skulpt) + + # Create a shallow copy of the dictionary of built-in methods. Then, + # we'll take specific ones that are unsafe and replace them. + namespace["__builtins__"] = _default_builtins.copy() + for name, function in custom_builtins.items(): + namespace["__builtins__"][name] = function + + +def create_module(module_name): + submodule_names = module_name.split(".") + modules = {} + root = types.ModuleType(submodule_names[0]) + modules[submodule_names[0]] = root + reconstructed_path = submodule_names[0] + for submodule_name in submodule_names[1:]: + reconstructed_path += "." + submodule_name + new_submodule = types.ModuleType(reconstructed_path) + setattr(root, submodule_name, new_submodule) + modules[reconstructed_path] = new_submodule + return root, modules + + +class MockModule: + def _generate_patches(self): + return {k: v for k, v in vars(self).items() + if not k.startswith('_')} + + def _add_to_module(self, module): + for name, value in self._generate_patches().items(): + setattr(module, name, value) + + +class BlockedModule(MockModule): + MODULE_NAME = "this module" + + def _generate_patches(self): + return {'__getattr__': self.prevent_module} + + def prevent_module(self, **kwargs): + raise SandboxPreventModule("You cannot import {module_name} from student code.".format( + module_name=self.MODULE_NAME + )) + + +class MockPedal(BlockedModule): + MODULE_NAME = "pedal" + + +class MockPlt(MockModule): + """ + Mock MatPlotLib library that can be used to capture plot data. + + Attributes: + plots (list of dict): The internal list of plot dictionaries. + """ + + def __init__(self): + super().__init__() + self._reset_plots() + + def show(self, **kwargs): + self.plots.append(self.active_plot) + self._reset_plot() + + def unshown_plots(self): + return self.active_plot['data'] + + def __repr__(self): + return repr(self.plots) + + def __str__(self): + return str(self.plots) + + def _reset_plots(self): + self.plots = [] + self._reset_plot() + + def _reset_plot(self): + self.active_plot = {'data': [], + 'xlabel': None, 'ylabel': None, + 'title': None, 'legend': False} + + def hist(self, data, **kwargs): + label = kwargs.get('label', None) + self.active_plot['data'].append({'type': 'hist', 'values': data, + 'label': label}) + + def plot(self, xs, ys=None, **kwargs): + label = kwargs.get('label', None) + if ys is None: + self.active_plot['data'].append({'type': 'line', + 'x': list(range(len(xs))), + 'y': xs, 'label': label}) + else: + self.active_plot['data'].append({'type': 'line', 'x': xs, + 'y': ys, 'label': label}) + + def scatter(self, xs, ys, **kwargs): + label = kwargs.get('label', None) + self.active_plot['data'].append({'type': 'scatter', 'x': xs, + 'y': ys, 'label': label}) + + def xlabel(self, label, **kwargs): + self.active_plot['xlabel'] = label + + def title(self, label, **kwargs): + self.active_plot['title'] = label + + def suptitle(self, label, **kwargs): + self.title(label, **kwargs) + + def ylabel(self, label, **kwargs): + self.active_plot['ylabel'] = label + + def legend(self, **kwargs): + self.active_plot['legend'] = True + + def _generate_patches(self): + def dummy(**kwargs): + pass + + return dict(hist=self.hist, plot=self.plot, + scatter=self.scatter, show=self.show, + xlabel=self.xlabel, ylabel=self.ylabel, + title=self.title, legend=self.legend, + xticks=dummy, yticks=dummy, + autoscale=dummy, axhline=dummy, + axhspan=dummy, axvline=dummy, + axvspan=dummy, clf=dummy, + cla=dummy, close=dummy, + figlegend=dummy, figimage=dummy, + suptitle=self.suptitle, text=dummy, + tick_params=dummy, ticklabel_format=dummy, + tight_layout=dummy, xkcd=dummy, + xlim=dummy, ylim=dummy, + xscale=dummy, yscale=dummy) diff --git a/src/lib/pedal/sandbox/result.py b/src/lib/pedal/sandbox/result.py new file mode 100644 index 0000000000..e7d269d9f3 --- /dev/null +++ b/src/lib/pedal/sandbox/result.py @@ -0,0 +1,369 @@ +class SandboxResult: + """ + Proxy class for wrapping results from executing student code. Attempts + to perfectly emulate the underlying data value, so that users will never + realize they have a proxy. The advantage is that special information is + available in the corresponding Sandbox about this result that can give + more context. + + Attributes: + value (any): The actual data stored in this class that we are proxying. + If the underlying proxy object has a field called `value`, then + you can use either `_actual_value` to access the proxied object. + _actual_call_id (int): The call that was used to generate this result. + _actual_sandbox (Sandbox): The sandbox that was used to generate this + result. If None, then the sandbox was lost. + + """ + ASSIGNABLE_ATTRS = ['value', '_actual_call_id', '_actual_sandbox', + '_clone_this_result'] + + def __init__(self, value, call_id=None, sandbox=None): + """ + Args: + value (any): Literally any type of data. + call_id (int): The unique call ID that generated this result. If + None, then the SandboxResult was generated by manipulating an earlier + result. + TODO: We could actually remember the operations applied to this + instance and use them to reconstruct the transformations... + sandbox (Sandbox): The sandbox that was used to generate this + result. If None, then the sandbox was lost. + """ + self.value = value + self._actual_call_id = call_id + self._actual_sandbox = sandbox + + def __getattribute__(self, name): + """ + Get the attribute with the given `name`. This allows us to pass + most attributes along to the underlying `value`, while still + maintaining access to the proxy's attributes. + """ + v = object.__getattribute__(self, "value") + if name == "__class__": + return v.__class__ + elif name == "__actual_class__": + return object.__getattribute__(self, "__class__") + elif name == "_actual_value": + return v + elif name in SandboxResult.ASSIGNABLE_ATTRS: + return object.__getattribute__(self, name) + elif name == "value" and not hasattr(v, "value"): + return v + else: + return SandboxResult(object.__getattribute__(v, name), + object.__getattribute__(self, "_actual_call_id"), + object.__getattribute__(self, "_actual_sandbox")) + + def __setattr__(self, name, value): + if name in SandboxResult.ASSIGNABLE_ATTRS: + object.__setattr__(self, name, value) + else: + setattr(self.value, name, value) + + def __delattr__(self, name): + if name in SandboxResult.ASSIGNABLE_ATTRS: + object.__delattr__(self, name, value) + else: + delattr(self.value, name, value) + + def _clone_this_result(self, new_value): + """ + Create a new SandboxResult based on this current one. Copies over the + `call_id` and `sandbox`. + + Args: + new_value (any): The new value to be proxying. + Returns: + SandboxResult + """ + return SandboxResult(new_value, + call_id=self._actual_call_id, + sandbox=self._actual_sandbox) + + def __repr__(self): + """ + Returns the representation of the proxied object. + + Returns: + str: The `repr` of the proxied object. + """ + return repr(self.value) + + def __str__(self): + """ + Returns the string representation of the proxied object. + + Returns: + str: The `str` of the proxied object. + """ + return str(self.value) + + def __bytes__(self): + return bytes(self.value) + + def __format__(self, format_spec): + return format(self.value, format_spec) + + def __call__(self, *args): + """ + Returns the result of calling the proxied object with the args. + + Returns: + SandboxResult: A proxy of the Sandbox object. + """ + return self._clone_this_result(self.value(*args)) + + def __hash__(self): + return hash(self.value) + + def __bool__(self): + return bool(self.value) + + def __dir__(self): + return dir(self.value) + + def __instancecheck__(self, instance): + return isinstance(self.value, instance) + + def __subclasscheck__(self, subclass): + return issubclass(self.value, subclass) + + def __len__(self): + ''' + Fun fact: cpython DEMANDS that __len__ return an integer. Not something + that looks like an integer, but a true, honest-to-god integer that + can fit into a slot. + https://stackoverflow.com/questions/42521449/how-does-python-ensure-the-return-value-of-len-is-an-integer-when-len-is-cal + ''' + return len(self.value) + + def __getitem__(self, key): + return self._clone_this_result(self.value[key]) + + def __setitem__(self, key, value): + self.value[key] = value + + def __delitem__(self, key): + del self.value[key] + + def __missing__(self, key): + return self.value.__missing__(key) + + def __iter__(self): + return iter(self.value) + + def __reversed__(self): + return reversed(self.value) + + def __contains__(self, item): + return self.value.__contains__(item) + + def __eq__(self, other): + """ + Test if the proxied object is equal to the given `other`. + + Args: + other (any): The other object. + + Returns: + bool or any: Returns whatever the proxy object's __eq__ returns. + """ + if isinstance(other, SandboxResult): + return self.value == other.value + return self.value == other + + def __lt__(self, other): + if isinstance(other, SandboxResult): + return self.value < other.value + return self.value < other + + def __le__(self, other): + if isinstance(other, SandboxResult): + return self.value <= other.value + return self.value <= other + + def __gt__(self, other): + if isinstance(other, SandboxResult): + return self.value > other.value + return self.value > other + + def __ge__(self, other): + if isinstance(other, SandboxResult): + return self.value >= other.value + return self.value >= other + + def __ne__(self, other): + if isinstance(other, SandboxResult): + return self.value != other.value + return self.value != other + + ## Numeric Operations + + def __add__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value + other.value) + return self._clone_this_result(self.value + other) + + def __sub__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value - other.value) + return self._clone_this_result(self.value - other) + + def __mul__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value * other.value) + return self._clone_this_result(self.value * other) + + def __matmul__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value.__matmul__(other.value)) + return self._clone_this_result(self.value.__matmul__(other)) + + def __truediv__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value.__truediv__(other.value)) + return self._clone_this_result(self.value.__truediv__(other)) + + def __floordiv__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value.__floordiv__(other.value)) + return self._clone_this_result(self.value.__floordiv__(other)) + + def __mod__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value.__mod__(other.value)) + return self._clone_this_result(self.value.__mod__(other)) + + def __divmod__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value.__divmod__(other.value)) + return self._clone_this_result(self.value.__divmod__(other)) + + def __pow__(self, other, *modulo): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value.__pow__(other.value, *modulo)) + return self._clone_this_result(self.value.__pow__(other, *modulo)) + + def __lshift__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value.__lshift__(other.value)) + return self._clone_this_result(self.value.__lshift__(other)) + + def __rshift__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value.__rshift__(other.value)) + return self._clone_this_result(self.value.__rshift__(other)) + + def __and__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value.__and__(other.value)) + return self._clone_this_result(self.value.__and__(other)) + + def __xor__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value.__xor__(other.value)) + return self._clone_this_result(self.value.__xor__(other)) + + def __or__(self, other): + if isinstance(other, SandboxResult): + return self._clone_this_result(self.value.__or__(other.value)) + return self._clone_this_result(self.value.__or__(other)) + + def __radd__(self, other): + if isinstance(self.value, str): + return self._clone_this_result(self.value.__add__(other)) + return self._clone_this_result(self.value.__radd__(other)) + + def __rsub__(self, other): + return self._clone_this_result(self.value.__rsub__(other)) + + def __rmul__(self, other): + return self._clone_this_result(self.value.__rmul__(other)) + + def __rmatmul__(self, other): + return self._clone_this_result(self.value.__rmatmul__(other)) + + def __rtruediv__(self, other): + return self._clone_this_result(self.value.__rtruediv__(other)) + + def __rfloordiv__(self, other): + return self._clone_this_result(self.value.__rfloordiv__(other)) + + def __rmod__(self, other): + return self._clone_this_result(self.value.__rmod__(other)) + + def __rdivmod__(self, other): + return self._clone_this_result(self.value.__rdivmod__(other)) + + def __rpow__(self, other): + return self._clone_this_result(self.value.__rpow__(other)) + + def __rlshift__(self, other): + return self._clone_this_result(self.value.__rlshift__(other)) + + def __rand__(self, other): + return self._clone_this_result(self.value.__rand__(other)) + + def __rxor__(self, other): + return self._clone_this_result(self.value.__rxor__(other)) + + def __ror__(self, other): + return self._clone_this_result(self.value.__ror__(other)) + + ## TODO: __iadd__ and other in-place assignment operators? + + def __neg__(self): + return self._clone_this_result(self.value.__neg__()) + + def __pos__(self): + return self._clone_this_result(self.value.__pos__()) + + def __abs__(self): + return self._clone_this_result(self.value.__abs__()) + + def __invert__(self): + return self._clone_this_result(self.value.__invert__()) + + def __complex__(self): + return self._clone_this_result(self.value.__complex__()) + + def __int__(self): + return self._clone_this_result(self.value.__int__()) + + def __float__(self): + return self._clone_this_result(self.value.__float__()) + + def __round__(self, *ndigits): + return self._clone_this_result(self.value.__round__(*ndigits)) + + def __trunc__(self): + return self._clone_this_result(self.value.__trunc__()) + + def __floor__(self): + return self._clone_this_result(self.value.__floor__()) + + def __ceil__(self): + return self._clone_this_result(self.value.__ceil__()) + + def __enter__(self): + return self.value.__enter__() + + def __exit__(self, exc_type, exc_value, traceback): + return self.value.__exit__(exc_type, exc_value, traceback) + + def __await__(self): + return self.value.__await__() + + def __aiter__(self): + return self.value.__aiter__() + + def __anext__(self): + return self.value.__anext__() + + def __aenter__(self): + return self.value.__aenter__() + + def __aexit__(self, exc_type, exc_value, traceback): + return self.value.__aexit__(exc_type, exc_value, traceback) diff --git a/src/lib/pedal/sandbox/sandbox.py b/src/lib/pedal/sandbox/sandbox.py new file mode 100644 index 0000000000..2d30c523e1 --- /dev/null +++ b/src/lib/pedal/sandbox/sandbox.py @@ -0,0 +1,688 @@ +from pprint import pprint +import ast +import re +import sys +import io +import os +import string +from unittest.mock import patch + +from pedal.report import MAIN_REPORT +from pedal.sandbox import mocked +from pedal.sandbox.exceptions import (SandboxTraceback, SandboxHasNoFunction, + SandboxStudentCodeException, + SandboxHasNoVariable, _add_context_to_error) +from pedal.sandbox.timeout import timeout +from pedal.sandbox.messages import EXTENDED_ERROR_EXPLANATION +from pedal.sandbox.result import SandboxResult +from pedal.sandbox.tracer import (SandboxCallTracer, SandboxCoverageTracer, + SandboxBasicTracer) + + +def _dict_extends(d1, d2): + """ + Helper function to create a new dictionary with the contents of the two + given dictionaries. Does not modify either dictionary, and the values are + copied shallowly. If there are repeats, the second dictionary wins ties. + + The function is written to ensure Skulpt compatibility. + + Args: + d1 (dict): The first dictionary + d2 (dict): The second dictionary + Returns: + dict: The new dictionary + """ + d3 = {} + for key, value in d1.items(): + d3[key] = value + for key, value in d2.items(): + d3[key] = value + return d3 + + +class SandboxVariable: + def __init__(self, name, value): + self.name = name + self.value = value + + +class DataSandbox: + """ + Simplistic Mixin class that contains the functions for accessing a + self-contained student data namespace. + """ + + def __init__(self): + super().__init__() + self.data = {} + + def get_names_by_type(self, type, exclude_builtins=True): + result = [] + for name, value in self.data.items(): + if isinstance(value, type): + if exclude_builtins and name.startswith('__'): + continue + result.append(name) + return result + + def get_values_by_type(self, type, exclude_builtins=True): + names = self.get_names_by_type(type, exclude_builtins) + return [self.data[name] for name in names] + + def get_variables_by_type(self, type, exclude_builtins=True): + names = self.get_names_by_type(type, exclude_builtins) + return [(name, self.data[name]) for name in names] + + @property + def functions(self): + """ + Retrieve a list of all the callable names in the students' namespace. + In other words, get a list of all the functions the student defined. + + Returns: + list of callables + """ + return {k: v for k, v in self.data.items() if callable(v)} + + @property + def var(self): + return {k: SandboxVariable(k, v) for k, v in self.data.items()} + + +class Sandbox(DataSandbox): + """ + + The Sandbox is a container that can safely execute student code and store + the result. + + Attributes: + data: The namespace produced by the students' code. This is basically + a dictionary mapping valid python names to their values. + raw_output (str): The exact literal results of all the `print` calls + made so far, including the "\n" characters. + output (list of str): The current lines of output, broken up by + distinct print calls (not "\n" characters). Note that this will + not have any "\n" characters unless you explicitly printed them. + output_contexts (dict[str:list[str]]): The output for each call context. + call_id (int): The current call_id of the most recent call. Is + initially 0, indicating the original sandbox creation. + modules: A dictionary of the mocked modules (accessible by their + imported names). + context: A list of strings representing the code previously run through + this sandbox via .call. + contextualize (bool): Whether or not to contextualize stack frames. + """ + + CONTEXT_MESSAGE = ( + "\n\nThe error above occurred when I ran:
\n
{context}
" + ) + FILE_CONTEXT_MESSAGE = ( + "\n\nThe error above occurred when I ran your file: {filename}" + ) + TRACER_STYLES = { + 'coverage': SandboxCoverageTracer, + 'calls': SandboxCallTracer, + 'none': SandboxBasicTracer, + } + + def __init__(self, initial_data=None, + initial_raw_output=None, + initial_exception=None, + modules=None, full_traceback=False, + tracer_style='none', + threaded=False, report=None, + context=None, result_proxy=SandboxResult, + instructor_filename="instructor_tests.py", + allowed_functions=None): + """ + Args: + initial_data (dict[str:Any]): An initial namespace to provide when + executing the students' code. The keys must be strings and + should be valid Python names. Defaults to None, which will be + an empty namespace. + initial_exception (Exception): An initial exception to load into + the Sandbox. Usually you will let the students' code generate + its own exceptions, but if you're constructing a sandbox you + might need to specify one. Defaults to None. + modules: A dictionary of strings (valid python package names) that + map to either the value True (if we provide a default + implementation) or a user-created MockedModule. By default, + we mock out the following modules: + * matplotlib + * pedal + context (False, None, or list[str]): How to contextualize calls by + default in this Sandbox. False means no contextualization. + None (default) means contextualize automatically. If you give + a list[str], then it assumes you want to contextualize + automatically but starting off with the given strings. + initial_raw_output (str): The initial printed output for the + sandbox. Usually defaults to None to indicate a blank printed + area. + instructor_filename (str): The filename to display in tracebacks, + when executing student code in instructor tests. Although you + can specify something else, defaults to "instructor_tests.py". + """ + super().__init__() + if initial_data is None: + initial_data = {} + self.data = initial_data + + # Context + self.call_id = 0 + self.target_contexts = {self.call_id: []} + self.call_contexts = {self.call_id: []} + self.input_contexts = {self.call_id: []} + self.context = context + self.keep_context = False + # Update outputs + self.set_output(initial_raw_output) + # filename + self.instructor_filename = instructor_filename + # Temporary data + self._temporaries = set() + self._backups = {} + # Exception + self.exception = initial_exception + self.exception_position = None + self.exception_formatted = None + self.report_exceptions_mode = False + self.raise_exceptions_mode = False + # Input + self.inputs = None + # Modules + if modules is None: + modules = {'matplotlib': True, + 'pedal': mocked.MockPedal()} + self.mocked_modules = {} + self.modules = {} + self.add_mocks(modules) + self.mocked_functions = { + 'compile': mocked._disabled_compile, + 'eval': mocked._disabled_eval, + 'exec': mocked._disabled_exec, + 'globals': mocked._disabled_globals, + 'open': mocked._restricted_open + } + if allowed_functions is not None: + for function_name in allowed_functions: + if function_name in self.mocked_functions: + del self.mocked_functions[function_name] + # Patching + self._current_patches = [] + # Settings + self.full_traceback = full_traceback + self.MAXIMUM_VALUE_LENGTH = 120 + # Tracer Styles + self.tracer_style = tracer_style + # Proxying results + self.result_proxy = result_proxy + # report + if report is None: + report = MAIN_REPORT + self.report = report + # Threading + self.threaded = threaded + self.allowed_time = 3 + + def _set_tracer_style(self, tracer_style): + self._tracer_style = tracer_style.lower() + self.trace = self.TRACER_STYLES[tracer_style.lower()]() + + def _get_tracer_style(self): + return self._tracer_style + + tracer_style = property(_get_tracer_style, _set_tracer_style) + + def add_mocks(self, modules): + """ + :param modules: Keyword listing of modules and their contents + (MockedModules) or True (if its one that we have a + default implementation for). + :type modules: dict + """ + for module_name, module_data in modules.items(): + self._add_mock(module_name, module_data) + + def _add_mock(self, module_name, module_data): + # MatPlotLib's PyPlot + if module_name == 'matplotlib': + matplotlib, modules = mocked.create_module('matplotlib.pyplot') + self.mocked_modules.update(modules) + if module_data is True: + mock_plt = mocked.MockPlt() + mock_plt._add_to_module(matplotlib.pyplot) + self.modules['matplotlib.pyplot'] = mock_plt + else: + module_data._add_to_module(matplotlib.pyplot) + else: + root, modules = mocked.create_module(module_name) + self.mocked_modules.update(modules) + self.modules[module_name] = module_data + module_data._add_to_module(root) + + def set_output(self, raw_output): + """ + Change the current printed output for the sandbox to the given value. + If None is given, then clears all the given output (empty list for + `output` and empty string for `raw_output`). + + Args: + raw_output (str): The new raw_output for the sandbox. To compute + the `output` attribute, the system splits and rstrips at + newlines. + """ + if raw_output is None: + self.raw_output = "" + self.output = [] + self.output_contexts = {self.call_id: list(self.output)} + else: + self.raw_output = raw_output + lines = raw_output.rstrip().split("\n") + self.output = [line.rstrip() for line in lines] + self.output_contexts[self.call_id] = list(self.output) + + def append_output(self, raw_output): + """ + Adds the string of `raw_output` to the current `raw_output` attribute. + The added string will be split on newlines and rstripped to append + to the `output` attribute. + + Args: + raw_output (str): The new raw_output for the sandbox. To compute + the `output` attribute, the system splits and rstrips at + newlines. + """ + self.raw_output += raw_output + lines = raw_output.rstrip().split("\n") + lines = [line.rstrip() for line in lines] + if self.raw_output: + self.output.extend(lines) + self.output_contexts[self.call_id].extend(lines) + + def set_input(self, inputs): + """ + Queues the given value as the next arguments to the `input` function. + """ + if isinstance(inputs, tuple): + self.inputs = mocked._make_inputs(*inputs) + else: + self.inputs = inputs + + def _track_inputs(self, inputs): + """ + Wraps an input function with a tracker. + """ + + def _input_tracker(*args, **kwargs): + value_entered = inputs(*args, **kwargs) + self.input_contexts[self.call_id].append(value_entered) + return value_entered + + return _input_tracker + + def _purge_temporaries(self): + """ + Delete any variables in the namespace that have been made as + temporaries. This happens automatically after you execute code. + """ + for key in self._temporaries: + if key in self._backups: + self.data[key] = self.backups[key] + else: + del self.data[key] + self._temporaries = set() + + def _is_long_value(self, value): + return len(repr(value)) > 25 + + def _make_temporary(self, category, name, value, context): + """ + Create a temporary variable in the namespace for the given + category/name. This is used to load arguments into the namespace to + be used in function calls. Temporaries are only created if the value's + repr length is too long, as defined by _is_long_value. + + Args: + category (str): A categorical division for the temporary variable + that can help keep the namespace distinctive - there are a + few different kinds of categories (e.g., for regular positional + args, star args, kwargs). + name (str): A distinctive ID for this variable. The final variable + name will be "_temporary__". + value: The value for this argument. + Returns: + str: The new name for the temporary variable. + """ + if isinstance(value, SandboxVariable): + return value.name + if not self._is_long_value(value): + return repr(value) + key = '_temporary_{}_{}'.format(category, name) + if key in self.data: + self._backups[key] = self.data[key] + self._temporaries.add(key) + self.data[key] = value + if context is None: + self.call_contexts[self.call_id].append("{} = {}".format(key, value)) + return key + + def run_file(filename, as_filename=None, modules=None, inputs=None, + threaded=None, context=None, report_exceptions=None, + raise_exceptions=None): + """ + Load the given filename and execute it within the current namespace. + + Args: + context (False, None, or list[str]): The context to give any + exceptions. If None, then the recorded context will be used. If + a string, tracebacks will be shown with the given context. If + False, no context will be given. + """ + if _as_filename is None: + _as_filename = filename + with open(filename, 'r') as code_file: + code = code_file.read() + '\n' + self.run(code, as_filename, modules, inputs, threaded, + context, report_exceptions, raise_exceptions) + + def list(self, *args): + pass + + def call(self, function, *args, **kwargs): + """ + Args: + function (str): The name of the function to call that was defined + by the user. + as_filename (str): The filename to use when calling this function. + Defaults to the instructor filename, since you are calling + code on the student's behalf. + target (str): The new variable in the namespace to assign to. By + default this will be "_". If you use None, then no variable + will be assigned to. Note that this could overwrite a variable + in the user namespace. + TODO: Add a feature to prevent user namespace overwriting. + input (list of str): The strings to send in to calls to input. + You can also pass in a generator to construct strings + dynamically. + threaded (bool): Whether or not the function execution should be + executed in a separate thread. Defaults to True. This prevents + timeouts from occuring in the students' code (a TimeOutError + will be thrown after 3 seconds). + context (False, None, or list[str]): The context to give any + exceptions. If None, then the recorded context will be used. If + a string, tracebacks will be shown with the given context. If + False, no context will be given. + keep_context (bool): Whether or not to stay in the current context, + or to start a new one. Defaults to False. + Returns: + If the call was successful, returns the result of executing the + code. Otherwise, it will return an Exception relevant to the + failure (might be a SandboxException, might be a user-space + exception). + """ + # Confirm that the function_name exists + if function not in self.functions: + if function not in self.data: + self.exception = SandboxHasNoVariable( + "The function {function} does not exist.".format(function=function) + ) + else: + self.exception = SandboxHasNoFunction( + "The variable {function} is not a function.".format(function=function) + ) + return self.exception + # Parse kwargs for any special arguments. + as_filename = kwargs.pop('as_filename', self.instructor_filename) + target = kwargs.pop('target', '_') + modules = kwargs.pop('modules', {}) + inputs = kwargs.pop('inputs', self.inputs) + threaded = kwargs.pop('threaded', self.threaded) + context = kwargs.pop('context', self.context) + keep_context = kwargs.pop('keep_context', self.keep_context) + report_exceptions = kwargs.pop('report_exceptions', self.report_exceptions_mode) + raise_exceptions = kwargs.pop('raise_exceptions', self.raise_exceptions_mode) + # Create the actual arguments and call + if not keep_context or not self.call_id: + self.call_id += 1 + self.output_contexts[self.call_id] = [] + self.call_contexts[self.call_id] = [] + self.input_contexts[self.call_id] = [] + # Always update the target context to be most recent + self.target_contexts[self.call_id] = target + actual, student = self._construct_call(function, args, kwargs, target, + context) + if context is None: + context = student + # if context is None: + # self.call_contexts[self.call_id].append(student_call) + # if context is not False: + # self.call_contexts[self.call_id] = context + self.run(actual, as_filename, modules, inputs, + threaded=threaded, + context=context, keep_context=keep_context, + report_exceptions=report_exceptions, + raise_exceptions=raise_exceptions) + self._purge_temporaries() + if self.exception is None: + self._ = self.data[target] + if self.result_proxy is not None: + self._ = self.result_proxy(self._, call_id=self.call_id, + sandbox=self) + return self._ + else: + # TODO: Might need to wrap this in case the student was supposed + # to return an exception - weird circumstance though + return self.exception + + def make_safe_variable(self, name): + """ + Tries to construct a safe variable name in the current namespace, based + off the given one. This is accomplished by appending a "_" and a number + of increasing value until no comparable name exists in the namespace. + This is particularly useful when you want to create a variable name to + assign to, but you are concerned that the user might have a variable + with that name already, which their code relies on. + + Args: + name (str): A desired target name. + Returns: + str: A safe target name, based off the given one. + """ + current_addition = "" + attempt_index = 2 + while name + current_addition in self.data: + current_addition = "_{}".format(attempt_index) + attempt_index += 1 + return name + current_addition + + def _construct_call(self, function, args, kwargs, target, context): + str_args = [self._make_temporary('arg', index, value, context) + for index, value in enumerate(args)] + str_kwargs = ["{}={}".format(key, + self._make_temporary('kwarg', key, value, context)) + for key, value in kwargs.items()] + arguments = ", ".join(str_args + str_kwargs) + call = "{}({})".format(function, arguments) + if target is None: + actual = call + else: + actual = "{} = {}".format(target, call) + student_call = call if target is "_" else actual + return actual, student_call + + def _start_patches(self, *patches): + self._current_patches.append(patches) + for patch in patches: + patch.start() + + def _stop_patches(self): + patches = self._current_patches.pop() + for patch in patches: + patch.stop() + + def _capture_exception(self, exception, exc_info, report_exceptions, + raise_exceptions, context, keep_context): + self.exception = exception + if context is not False: + if context is None or keep_context: + contexts = self.call_contexts[self.call_id] + if context is not None: + contexts.append(context) + context = '\n'.join(contexts)#[1:]) + if context.strip(): + context = self.CONTEXT_MESSAGE.format(context=context) + else: + context = self.FILE_CONTEXT_MESSAGE.format(filename=self.report['source']['filename']) + self.exception = _add_context_to_error(self.exception, context) + line_offset = self.report['source'].get('line_offset', 0) + student_filename = self.report['source']['filename'] + traceback = SandboxTraceback(self.exception, exc_info, + self.full_traceback, + self.instructor_filename, + line_offset, student_filename, + self.report['source']['lines']) + self.exception_position = {'line': traceback.line_number} + self.exception_formatted = traceback.format_exception() + self.exception_name = str(self.exception.__class__)[8:-2] + # Do we add the exception to the report? + if report_exceptions is False: + return True + if report_exceptions is None and not self.report_exceptions_mode: + return True + self.report.attach(self.exception_name, + group=self.report.group, + category='Runtime', tool='Sandbox', + mistake={'message': self.exception_formatted, + 'error': self.exception}) + if raise_exceptions is True: + raise SandboxStudentCodeException(self.exception) + return False + + def run(self, code, as_filename=None, modules=None, inputs=None, + threaded=None, report_exceptions=True, raise_exceptions=False, + context=False, keep_context=False): + """ + Execute the given string of code in this sandbox. + + Args: + code (str): The string of code to be executed. + as_filename (str): The filename to use when executing the code - + this is cosmetic, technically speaking, it has no relation + to anything on disk. It will be present in tracebacks. + Defaults to Source's filename. + modules (dict[str:Module]): Modules to mock. + inputs (list[str]): The inputs to give from STDIN, as a list of + strings. You can also give a function that emulates the + input function; e.g., consuming a prompt (str) and producing + strings. This could be used to make a more interactive input + system. + context (str): The context to give any exceptions. + If None, then the recorded context will be used. If a string, + tracebacks will be shown with the given context. If False, + no context will be given (the default). + threaded (bool): whether or not to run this code in a separate + thread. Defaults to :attribute:`Sandbox.threaded`. + report_exceptions (bool): Whether or not to capture exceptions. + """ + # Handle any threading if necessary + if threaded is None: + threaded = self.threaded + if threaded: + try: + return timeout(self.allowed_time, self.run, code, as_filename, + modules, inputs, False, + report_exceptions, raise_exceptions, + context, keep_context) + except TimeoutError as timeout_exception: + self._capture_exception(timeout_exception, sys.exc_info(), + report_exceptions, raise_exceptions, + context, keep_context) + return self + + if as_filename is None: + as_filename = os.path.basename(self.report['source']['filename']) + if inputs is None: + if self.inputs is None: + inputs = mocked._make_inputs('0', repeat='0') + else: + inputs = self.inputs + if isinstance(inputs, (tuple, list)): + inputs = mocked._make_inputs(*inputs) + elif isinstance(inputs, str): + inputs = mocked._make_inputs(inputs) + inputs = self._track_inputs(inputs) + # Override builtins and mock stuff out + mocked_functions = self.mocked_functions.copy() + mocked_functions['input'] = inputs + mocked_functions['raw_input'] = inputs + mocked_functions['sys'] = sys + mocked_functions['os'] = os + mocked._override_builtins(self.data, mocked_functions) + + + self.exception = None + self.exception_position = None + self.exception_formatted = None + + # Patch in dangerous built-ins + capture_stdout = io.StringIO() + self._start_patches( + patch('sys.stdout', capture_stdout), + patch('time.sleep', return_value=None), + patch.dict('sys.modules', self.mocked_modules) + ) + # Compile and run student code + try: + compiled_code = compile(code, as_filename, 'exec') + with self.trace._as_filename(as_filename, code): + exec(compiled_code, self.data) + except Exception as user_exception: + self._capture_exception(user_exception, sys.exc_info(), + report_exceptions, raise_exceptions, + context, keep_context) + finally: + self._stop_patches() + self.append_output(capture_stdout.getvalue()) + if context is None: + self.call_contexts[self.call_id].append(code) + elif isinstance(context, str): + self.call_contexts[self.call_id].append(context) + elif context is not False: + self.call_contexts[self.call_id] = context + return self + + +def run(initial_data=None, initial_raw_output=None, initial_exception=None, + allowed_functions=None, + modules=None, inputs=None, report_exceptions=True, raise_exceptions=False, + context=None, + full_traceback=False, tracer_style='none', threaded=False, + result_proxy=SandboxResult, + instructor_filename="instructor_tests.py", + code=None, as_filename=None, report=None): + if report is None: + report = MAIN_REPORT + if 'run' not in report['sandbox']: + report['sandbox']['settings'] = [ + initial_data, initial_raw_output, initial_exception, modules, + full_traceback, tracer_style, threaded, report, context, + result_proxy, instructor_filename, allowed_functions + ] + report['sandbox']['run'] = Sandbox(*report['sandbox']['settings']) + + sandbox = report['sandbox']['run'] + if code is None: + code = report['source']['code'] + sandbox.run(code, as_filename, modules, inputs, threaded, + report_exceptions, raise_exceptions, context=context, keep_context=False) + return sandbox + + +def reset(report=None): + if report is None: + report = MAIN_REPORT + if 'settings' in report['sandbox']: + report['sandbox']['run'] = Sandbox(*report['sandbox']['settings']) + else: + run(report=report) diff --git a/src/lib/pedal/sandbox/timeout.py b/src/lib/pedal/sandbox/timeout.py new file mode 100644 index 0000000000..be83a578cb --- /dev/null +++ b/src/lib/pedal/sandbox/timeout.py @@ -0,0 +1,155 @@ +""" +A module that exposes a useful method (`timeout`) that can execute a +function asynchronously and terminiate if it exceeds a given `duration`. +""" + +import sys +import time + +try: + import threading +except BaseException: + threading = None +try: + import ctypes +except BaseException: + ctypes = None + + +class InterruptableThread(threading.Thread): + ''' + A thread that can be interrupted. + ''' + + def __init__(self, func, args, kwargs): + threading.Thread.__init__(self) + self.func, self.args, self.kwargs = func, args, kwargs + self.daemon = True + self.result = None + self.exc_info = (None, None, None) + + def run(self): + ''' + Begin thread execution, calling the `func` that was originally + passed in. + ''' + try: + self.result = self.func(*self.args, **self.kwargs) + except Exception: + self.exc_info = sys.exc_info() + + @staticmethod + def _async_raise(thread_id, exception): + ''' + Static method to raise an error asychronously using the ctypes module. + ''' + # Cache the function for convenience + RaiseAsyncException = ctypes.pythonapi.PyThreadState_SetAsyncExc + + states_modified = RaiseAsyncException(ctypes.c_long(thread_id), + ctypes.py_object(exception)) + if states_modified == 0: + raise ValueError("nonexistent thread id") + elif states_modified > 1: + RaiseAsyncException(thread_id, 0) + raise SystemError("PyThreadState_SetAsyncExc failed") + + def raise_exception(self, exception): + ''' + Trigger a thread ending exception! + ''' + assert self.is_alive(), "thread must be started" + for thread_id, thread in threading._active.items(): + if thread is self: + InterruptableThread._async_raise(thread_id, exception) + return + + def terminate(self): + self.exc_info = sys.exc_info() + self.raise_exception(SystemExit) + + +def timeout(duration, func, *args, **kwargs): + """ + Executes a function and kills it (throwing an exception) if it runs for + longer than the specified duration, in seconds. + """ + + # If libraries are not available, then we execute normally + if None in (threading, ctypes): + return func(*args, **kwargs) + + target_thread = InterruptableThread(func, args, kwargs) + target_thread.start() + target_thread.join(duration) + + if target_thread.is_alive(): + target_thread.terminate() + timeout_exception = TimeoutError('Your code took too long to run ' + '(it was given {} seconds); ' + 'maybe you have an infinite loop?'.format(duration)) + raise timeout_exception + else: + if target_thread.exc_info[0] is not None: + ei = target_thread.exc_info + # Python 2 had the three-argument raise statement; thanks to PEP + # 3109 for showing how to convert that to valid Python 3 statements. + e = ei[0](ei[1]) + e.__traceback__ = ei[2] + e.exc_info = target_thread.exc_info + raise e + + +# ========================================================================= + + +class _TimeoutData: + """ + Port of Craig Estep's AdaptiveTimeout JUnit rule from the VTCS student + library. + """ + + # ------------------------------------------------------------- + def __init__(self, ceiling): + self.ceiling = ceiling # sec + self.maximum = ceiling * 2 # sec + self.minimum = 0.25 # sec + self.threshold = 0.6 + self.rampup = 1.4 + self.rampdown = 0.5 + self.start = self.end = 0 + self.non_terminating_methods = 0 + + # ------------------------------------------------------------- + + def before_test(self): + """ + Call this before a test case runs in order to reset the timer. + """ + self.start = time.time() + + # ------------------------------------------------------------- + + def after_test(self): + """ + Call this after a test case runs. This will examine how long it took + the test to execute, and if it required an amount of time greater than + the current ceiling, it will adaptively adjust the allowed time for + the next test. + """ + self.end = time.time() + diff = self.end - self.start + + if diff > self.ceiling: + self.non_terminating_methods += 1 + + if self.non_terminating_methods >= 2: + if self.ceiling * self.rampdown < self.minimum: + self.ceiling = self.minimum + else: + self.ceiling = (self.ceiling * self.rampdown) + elif diff > self.ceiling * self.threshold: + if self.ceiling * self.rampup > self.maximum: + self.ceiling = self.maximum + else: + self.ceiling = (self.ceiling * self.rampup) diff --git a/src/lib/pedal/sandbox/tracer.py b/src/lib/pedal/sandbox/tracer.py new file mode 100644 index 0000000000..9deb134617 --- /dev/null +++ b/src/lib/pedal/sandbox/tracer.py @@ -0,0 +1,108 @@ +import sys +import os + +try: + import coverage +except ImportError: + coverage = None + +try: + from bdb import Bdb, BdbQuit +except ImportError: + class Bdb: + pass + + + class BdbQuit: + pass + + +class SandboxBasicTracer: + def __init__(self): + super().__init__() + self.filename = "student.py" + + def _as_filename(self, filename, code): + if os.path.isabs(filename): + self.filename = filename + else: + self.filename = os.path.abspath(filename) + self.code = code + return self + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_val, traceback): + pass + + +class SandboxCoverageTracer(SandboxBasicTracer): + def __init__(self): + super().__init__() + if coverage is None: + raise ImportError("The coverage package is not available.") + self.n_missing = None + self.n_statements = None + self.pc_covered = None + self.missing = set() + self.lines = set() + # self.s = sys.stdout + + def __enter__(self): + # Force coverage to accept the code + self.original = coverage.python.get_python_source + + def _get_source_correctly(reading_filename): + print(reading_filename, file=self.s) + if reading_filename == self.filename: + return self.code + else: + return self.original(reading_filename) + + coverage.python.get_python_source = _get_source_correctly + self.coverage = coverage.Coverage() + self.coverage.start() + + def __exit__(self, exc_type, exc_val, traceback): + self.coverage.stop() + self.coverage.save() + # Restore the get_python_source reader + coverage.python.get_python_source = self.original + self.original = None + # Actually analyze the data, attach some data + analysis = self.coverage._analyze(self.filename) + # print(vars(self.coverage._analyze(self.filename)), file=self.s) + self.n_missing = analysis.numbers.n_missing + self.n_statements = analysis.numbers.n_statements + self.pc_covered = analysis.numbers.pc_covered + self.missing = analysis.missing + self.lines = analysis.statements - analysis.missing + + @property + def percent_covered(self): + return self.pc_covered + + +class SandboxCallTracer(SandboxBasicTracer, Bdb): + def __init__(self): + super().__init__() + self.calls = {} + + def user_call(self, frame, argument_list): + code = frame.f_code + name = code.co_name + if name not in self.calls: + self.calls[name] = [] + self.calls[name].append(code) + + def __enter__(self): + self.reset() + self._old_trace = sys.gettrace() + sys.settrace(self.trace_dispatch) + + def __exit__(self, exc_type, exc_val, traceback): + sys.settrace(self._old_trace) + self.quitting = True + # Return true to suppress exception (if it is a BdbQuit) + return isinstance(exc_type, BdbQuit) diff --git a/src/lib/pedal/sk_mod_instructor_list.txt b/src/lib/pedal/sk_mod_instructor_list.txt new file mode 100644 index 0000000000..5203e8c5d9 --- /dev/null +++ b/src/lib/pedal/sk_mod_instructor_list.txt @@ -0,0 +1,40 @@ +GracefulExit + +StudentData (instance is `student`) + __init__(self) + get_names_by_type(self, type, exclude_builtins) + get_values_by_type(self, type, exclude_builtins) +get_output() +reset_output() +queue_input(*inputs) +get_program() +run_student() + +parse_program() +had_execution_time_error() +limit_execution_time() +unlimit_execution_time() +analyze_program() + +def_use_error(AstNode) + +CorruptedAstNode + __init__(self) +find_match(instructor_code) +find_matches(instructor_code) + +ASTMap + __init__(self, JSAstMap) + get_std_name(self, id) + get_std_exp(self, id) + +AstNode + __init__(self, id) + __eq__(self, other) + numeric_logic_check(self, mag, expr) + __str__(self) + __repr__(self) + __getattr__(self, key) + has(self, AstNode) + find_all(self, type) + \ No newline at end of file diff --git a/src/lib/pedal/source/__init__.py b/src/lib/pedal/source/__init__.py new file mode 100644 index 0000000000..54a9f902b5 --- /dev/null +++ b/src/lib/pedal/source/__init__.py @@ -0,0 +1,109 @@ +""" +A package for verifying source code. +""" + +from pedal.source.sections import * +from pedal.report import MAIN_REPORT +import re +import ast + +NAME = 'Source' +SHORT_DESCRIPTION = "Verifies source code and attaches it to the report" +DESCRIPTION = ''' +''' +REQUIRES = [] +OPTIONALS = [] +CATEGORY = 'Syntax' + +__all__ = ['NAME', 'DESCRIPTION', 'SHORT_DESCRIPTION', 'REQUIRES', 'OPTIONALS', + 'set_source', 'check_section_exists', 'next_section', 'verify_section', + 'set_source_file'] +DEFAULT_PATTERN = r'^(##### Part .+)$' + + +def set_source(code, filename='__main__.py', sections=False, independent=False, + report=None): + """ + Sets the contents of the Source to be the given code. Can also be + optionally given a filename. + + Args: + code (str): The contents of the source file. + filename (str): The filename of the students' code. Defaults to + __main__.py. + sections (str or bool): Whether or not the file should be divided into + sections. If a str, then it should be a + Python regular expression for how the sections + are separated. If False, there will be no + sections. If True, then the default pattern + will be used: '^##### Part (\\d+)$' + report (Report): The report object to store data and feedback in. If + left None, defaults to the global MAIN_REPORT. + """ + if report is None: + report = MAIN_REPORT + report['source']['code'] = code + report['source']['full'] = code + report['source']['lines'] = code.split("\n") + report['source']['filename'] = filename + report['source']['independent'] = independent + report['source']['success'] = True + if not sections: + report['source']['sections'] = None + report['source']['section'] = None + _check_issues(code, report) + else: + if sections: + pattern = DEFAULT_PATTERN + else: + pattern = sections + report.group = 0 + report['source']['section_pattern'] = pattern + report['source']['section'] = 0 + report['source']['line_offset'] = 0 + report['source']['sections'] = re.split(pattern, code, + flags=re.MULTILINE) + report['source']['code'] = report['source']['sections'][0] + + +def _check_issues(code, report): + if code.strip() == '': + report.attach('Blank source', category='Syntax', tool=NAME, + group=report['source']['section'], + mistake="Source code file is blank.") + report['source']['success'] = False + try: + parsed = ast.parse(code, report['source']['filename']) + report['source']['ast'] = parsed + except SyntaxError as e: + report.attach('Syntax error', category='Syntax', tool='Source', + group=report['source']['section'], + mistake={'message': "Invalid syntax on line " + + str(e.lineno), + 'error': e, + 'position': {"line": e.lineno}}) + report['source']['success'] = False + report['source']['ast'] = ast.parse("") + + +def get_program(report=None): + if report is None: + report = MAIN_REPORT + return report['source']['code'] + +def set_source_file(filename, sections=False, independent=False, report=None): + if report is None: + report = MAIN_REPORT + try: + with open(filename, 'r') as student_file: + set_source(student_file.read(), filename=filename, + sections=sections, independent=independent, + report=report) + except IOError: + message = ("The given filename ('{filename}') was either not found" + " or could not be opened. Please make sure the file is" + " available.").format(filename=filename) + report.attach('Source File Not Found', category='Syntax', tool='Source', + group=0 if sections else None, + mistake={'message': message}) + report['source']['success'] = False diff --git a/src/lib/pedal/source/sections.py b/src/lib/pedal/source/sections.py new file mode 100644 index 0000000000..b81205b539 --- /dev/null +++ b/src/lib/pedal/source/sections.py @@ -0,0 +1,132 @@ +from pedal.report import MAIN_REPORT +import ast + + +#def move_to_section(section_number, name, report=None): +# pass + +def _calculate_section_number(section_index): + return int((section_index+1)/2) + +def next_section(name="", report=None): + if report is None: + report = MAIN_REPORT + report.execute_hooks('source.next_section.before') + source = report['source'] + #if not report['source']['success']: + # return False + source['section'] += 2 + section_index = source['section'] + section_number = _calculate_section_number(section_index) + sections = source['sections'] + found = len(source['sections']) + if section_index < found: + if source['independent']: + source['code'] = ''.join(sections[section_index]) + old_code = ''.join(sections[:section_index]) + source['line_offset'] = len(old_code.split("\n"))-1 + else: + source['code'] = ''.join(sections[:section_index + 1]) + report.group = section_index + else: + report.attach('Syntax error', category='Syntax', tool='Source', + mistake=("Tried to advance to next section but the " + "section was not found. Tried to load section " + "{count}, but there were only {found} sections." + ).format(count=section_number, found=found)) + +def check_section_exists(section_number, report=None): + """ + Checks that the right number of sections exist. The prologue before the + first section is 0, while subsequent ones are 1, 2, 3, etc. + So if you have 3 sections in your code plus the prologue, + you should pass in 3 and not 4 to verify that all of them exist. + """ + if report is None: + report = MAIN_REPORT + if not report['source']['success']: + return False + found = int((len(report['source']['sections']) - 1) / 2) + if section_number > found: + report.attach('Syntax error', category='Syntax', tool='Source', + group=report['source']['section'], + mistake=("Incorrect number of sections in your file. " + "Expected {count}, but only found {found}" + ).format(count=section_number, found=found)) + + +def verify_section(report=None): + if report is None: + report = MAIN_REPORT + source = report['source'] + #if not source['success']: + # return False + code = source['code'] + try: + parsed = ast.parse(code, source['filename']) + source['ast'] = parsed + except SyntaxError as e: + report.attach('Syntax error', category='Syntax', tool='Source', + group=source['section'], + mistake={'message': "Invalid syntax on line " + + str(e.lineno+source['line_offset'])+"\n", + 'error': e, + 'position': {"line": e.lineno}}) + source['success'] = False + if 'ast' in source: + del source['ast'] + return source['success'] + + +class _finish_section: + def __init__(self, number, *functions): + if isinstance(number, int): + self.number = number + else: + self.number = -1 + functions = [number] + list(functions) + self.functions = functions + for function in functions: + self(function, False) + + def __call__(self, f=None, quiet=True): + if f is not None: + f() + if quiet: + print("\tNEXT SECTION") + + def __enter__(self): + pass + + def __exit__(self, x, y, z): + print("\tNEXT SECTION") + # return wrapped_f + + +def finish_section(number, *functions, **kwargs): + if 'next_section' in kwargs: + next_section = kwargs['next_section'] + else: + next_section = False + if len(functions) == 0: + x = _finish_section(number, *functions) + x() + else: + result = _finish_section(number, *functions) + if next_section: + print("\tNEXT SECTION") + return result + + +def section(number): + """ + """ + pass + + +def precondition(function): + pass + + +def postcondition(function): + pass diff --git a/src/lib/pedal/tifa/.gitignore b/src/lib/pedal/tifa/.gitignore new file mode 100644 index 0000000000..401fcfe58a --- /dev/null +++ b/src/lib/pedal/tifa/.gitignore @@ -0,0 +1 @@ +_temp_tifa.py \ No newline at end of file diff --git a/src/lib/pedal/tifa/__init__.py b/src/lib/pedal/tifa/__init__.py new file mode 100644 index 0000000000..8b1bdb934d --- /dev/null +++ b/src/lib/pedal/tifa/__init__.py @@ -0,0 +1,105 @@ +""" +Python Type Inferencer and Flow Analyzer (TIFA) + +TIFA uses a number of simplifications of the Python language. + * Variables cannot change type + * Variables cannot be deleted + * Complex types have to be homogenous + * No introspection or reflective characteristics + * No dunder methods + * No closures (maybe?) + * You cannot write a variable out of scope + * You cannot read a mutable variable out of scope + * No multiple inheritance + +Additionally, it reads the following as issues: + * Cannot read a variable without having first written to it. + * Cannot rewrite a variable unless it has been read. + +Important concepts: + +.. glossary:: + + Issue + A problematic situation in the submitted code that will be reported + but may not stop the execution. However, when an Issue occurs, + any results may be invalid. + + Error + A situation in execution that terminates the program. + + Name + A name of a variable + + Scope + The context of a function, with its own namespaces. Represented + internally using numeric IDs (Scope IDs). + + Scope Chain + A stack of scopes, with the innermost scope on top. + + Fully Qualified Name + A string representation of a variable and its scope + chain, written using "/". For example: 0/1/4/my_variable_name + + Path + A single path of execution through the control flow; every program + has at least one sequential path, but IFs, FORs, WHILEs, etc. can + cause multiple paths. Paths are represented using numeric IDs (Path + IDs). + + State + Information about a Name that indicates things like the variable's + current type and whether that name has been read, set, or + overwritten. + + Identifier + A wrapper around variables, used to hold their potential + non-existence (which is an Issue but not an Error). + + Type + A symbolic representation of the variable's type. + + Literal + Sometimes, we need a specialized representation of a literal value + to be passed around. This is particularly important for accessing + elements in an tuples. + + Name Map + (Path x Fully Qualified Names) => States +""" +import time; stopwatch = time.time(); +from pedal.tifa.tifa import Tifa +print("Phase {}: {} secs".format("Tifa.Tifa loaded", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() +from pedal.report import MAIN_REPORT + +NAME = 'TIFA' +SHORT_DESCRIPTION = "Finds common issues caused by students." +DESCRIPTION = '''Python Type Inferencer and Flow Analyzer (TIFA) + +Tifa traverses an AST to detect common issues made by students. +''' +REQUIRES = ['Source'] +OPTIONALS = [] + + +def tifa_analysis(python_3=True, report=None): + """ + Perform the TIFA analysis and attach the results to the Report. + + Args: + python_3 (bool): Whether to expect a Python3 formated file, or Python + 2. This has slight nuance on certain AST elements. + report (:class:`Report`): The Report object to attach results to. + Defaults to :data:`MAIN_REPORT`. + """ + if report is None: + report = MAIN_REPORT + t = Tifa(python_3=python_3, report=report) + t.process_code(report['source']['code']) + return t + + +__all__ = ['NAME', 'DESCRIPTION', 'SHORT_DESCRIPTION', + 'REQUIRES', 'OPTIONALS', + 'tifa_analysis', 'Tifa'] diff --git a/src/lib/pedal/tifa/builtin_definitions.py b/src/lib/pedal/tifa/builtin_definitions.py new file mode 100644 index 0000000000..a35e56be03 --- /dev/null +++ b/src/lib/pedal/tifa/builtin_definitions.py @@ -0,0 +1,231 @@ +from pedal.tifa.type_definitions import (UnknownType, FunctionType, + NumType, NoneType, BoolType, + TupleType, ListType, StrType, + FileType, DictType, ModuleType, + SetType, DayType, TimeType, ClassType, + LiteralNum) + +def get_builtin_module(name): + if name == 'matplotlib': + return ModuleType('matplotlib', + submodules={ + 'pyplot': ModuleType('pyplot', fields={ + 'plot': FunctionType(name='plot', returns=NoneType()), + 'hist': FunctionType(name='hist', returns=NoneType()), + 'scatter': FunctionType(name='scatter', returns=NoneType()), + 'show': FunctionType(name='show', returns=NoneType()), + 'xlabel': FunctionType(name='xlabel', returns=NoneType()), + 'ylabel': FunctionType(name='ylabel', returns=NoneType()), + 'title': FunctionType(name='title', returns=NoneType()), + }) + }) + elif name == 'pprint': + return ModuleType('pprint', + fields={ + 'pprint': FunctionType(name='pprint', returns=NoneType()) + }) + elif name == 'random': + return ModuleType('random', + fields={ + 'randint': FunctionType(name='randint', returns=NumType()) + }) + elif name == 'string': + return ModuleType('string', + fields={ + 'letters': StrType(empty=False), + 'digits': StrType(empty=False), + 'ascii_letters': StrType(empty=False), + 'punctuation': StrType(empty=False), + 'printable': StrType(empty=False), + 'whitespace': StrType(empty=False), + 'ascii_uppercase': StrType(empty=False), + 'ascii_lowercase': StrType(empty=False), + 'hexdigits': StrType(empty=False), + 'octdigits': StrType(empty=False), + }) + elif name == 'turtle': + return ModuleType('turtle', + fields={ + 'forward': FunctionType(name='forward', returns=NoneType()), + 'backward': FunctionType(name='backward', returns=NoneType()), + 'color': FunctionType(name='color', returns=NoneType()), + 'right': FunctionType(name='right', returns=NoneType()), + 'left': FunctionType(name='left', returns=NoneType()), + }) + elif name == 'parking': + return ModuleType('parking', + fields={ + 'Time': FunctionType(name='Time', returns=TimeType()), + 'now': FunctionType(name='now', returns=TimeType()), + 'Day': FunctionType(name='Day', returns=DayType()), + 'today': FunctionType(name='today', returns=DayType()), + }), + elif name == 'math': + return ModuleType('math', + fields={ + 'ceil': FunctionType(name='ceil', returns=NumType()), + 'copysign': FunctionType(name='copysign', returns=NumType()), + 'fabs': FunctionType(name='fabs', returns=NumType()), + 'factorial': FunctionType(name='factorial', returns=NumType()), + 'floor': FunctionType(name='floor', returns=NumType()), + 'fmod': FunctionType(name='fmod', returns=NumType()), + 'frexp': FunctionType(name='frexp', returns=NumType()), + 'fsum': FunctionType(name='fsum', returns=NumType()), + 'gcd': FunctionType(name='gcd', returns=NumType()), + 'isclose': FunctionType(name='isclose', returns=BoolType()), + 'isfinite': FunctionType(name='isfinite', returns=BoolType()), + 'isinf': FunctionType(name='isinf', returns=BoolType()), + 'isnan': FunctionType(name='isnan', returns=BoolType()), + 'ldexp': FunctionType(name='ldexp', returns=NumType()), + 'modf': FunctionType(name='modf', returns=NumType()), + 'trunc': FunctionType(name='trunc', returns=NumType()), + 'log': FunctionType(name='log', returns=NumType()), + 'log1p': FunctionType(name='log1p', returns=NumType()), + 'log2': FunctionType(name='log2', returns=NumType()), + 'log10': FunctionType(name='log10', returns=NumType()), + 'pow': FunctionType(name='pow', returns=NumType()), + 'sqrt': FunctionType(name='sqrt', returns=NumType()), + 'sin': FunctionType(name='sin', returns=NumType()), + 'cos': FunctionType(name='cos', returns=NumType()), + 'tan': FunctionType(name='tan', returns=NumType()), + 'asin': FunctionType(name='asin', returns=NumType()), + 'acos': FunctionType(name='acos', returns=NumType()), + 'atan': FunctionType(name='atan', returns=NumType()), + 'atan2': FunctionType(name='atan2', returns=NumType()), + 'hypot': FunctionType(name='hypot', returns=NumType()), + 'degrees': FunctionType(name='degrees', returns=NumType()), + 'radians': FunctionType(name='radians', returns=NumType()), + 'sinh': FunctionType(name='sinh', returns=NumType()), + 'cosh': FunctionType(name='cosh', returns=NumType()), + 'tanh': FunctionType(name='tanh', returns=NumType()), + 'asinh': FunctionType(name='asinh', returns=NumType()), + 'acosh': FunctionType(name='acosh', returns=NumType()), + 'atanh': FunctionType(name='atanh', returns=NumType()), + 'erf': FunctionType(name='erf', returns=NumType()), + 'erfc': FunctionType(name='erfc', returns=NumType()), + 'gamma': FunctionType(name='gamma', returns=NumType()), + 'lgamma': FunctionType(name='lgamma', returns=NumType()), + 'pi': NumType(), + 'e': NumType(), + 'tau': NumType(), + 'inf': NumType(), + 'nan': NumType(), + }) + + +def _builtin_sequence_constructor(sequence_type): + """ + Helper function for creating constructors for the Set and List types. + These constructors use the subtype of the arguments. + + Args: + sequence_type (Type): A function for creating new sequence types. + """ + + def sequence_call(tifa, function_type, callee, args, position): + # TODO: Should inherit the emptiness too + return_type = sequence_type(empty=True) + if args: + return_type.subtype = args[0].index(LiteralNum(0)) + return_type.empty = False + return return_type + + return sequence_call + + +def _builtin_zip(tifa, function_type, callee, args, position): + """ + Definition of the built-in zip function, which consumes a series of + sequences and returns a list of tuples, with each tuple composed of the + elements of the sequence paired (or rather, tupled) together. + """ + if args: + tupled_types = TupleType(subtypes=[]) + for arg in args: + tupled_types.append(arg.index(0)) + return ListType(tupled_types, empty=False) + return ListType(empty=True) + + +def get_builtin_function(name): + # Void Functions + if name == "print": + return FunctionType(name="print", returns=NoneType()) + # Math Functions + elif name == "int": + return FunctionType(name="int", returns=NumType()) + elif name == "abs": + return FunctionType(name="abs", returns=NumType()) + elif name == "float": + return FunctionType(name="float", returns=NumType()) + elif name == "len": + return FunctionType(name="len", returns=NumType()) + elif name == "ord": + return FunctionType(name="ord", returns=NumType()) + elif name == "pow": + return FunctionType(name="pow", returns=NumType()) + elif name == "round": + return FunctionType(name="round", returns=NumType()) + elif name == "sum": + return FunctionType(name="sum", returns=NumType()) + # Boolean Functions + elif name == "bool": + return FunctionType(name="bool", returns=BoolType()) + elif name == "all": + return FunctionType(name="all", returns=BoolType()) + elif name == "any": + return FunctionType(name="any", returns=BoolType()) + elif name == "isinstance": + return FunctionType(name="isinstance", returns=BoolType()) + # String Functions + elif name == "input": + return FunctionType(name="input", returns=StrType()) + elif name == "str": + return FunctionType(name="str", returns=StrType()) + elif name == "chr": + return FunctionType(name="chr", returns=StrType()) + elif name == "repr": + return FunctionType(name="repr", returns=StrType()) + # File Functions + elif name == "open": + return FunctionType(name="open", returns=FileType()) + # List Functions + elif name == "map": + return FunctionType(name="map", returns=ListType(empty=False)) + elif name == "list": + return FunctionType(name="list", + definition=_builtin_sequence_constructor(ListType)) + # Set Functions + elif name == "set": + return FunctionType(name="set", + definition=_builtin_sequence_constructor(SetType)) + # Dict Functions + elif name == "dict": + return FunctionType(name="dict", returns=DictType()) + # Pass through + elif name == "sorted": + return FunctionType(name="sorted", returns='identity') + elif name == "reversed": + return FunctionType(name="reversed", returns='identity') + elif name == "filter": + return FunctionType(name="filter", returns='identity') + # Special Functions + elif name == "type": + return FunctionType(name="type", returns=UnknownType()) + elif name == "range": + return FunctionType(name="range", returns=ListType(NumType(), empty=False)) + elif name == "dir": + return FunctionType(name="dir", returns=ListType(StrType(), empty=False)) + elif name == "max": + return FunctionType(name="max", returns='element') + elif name == "min": + return FunctionType(name="min", returns='element') + elif name == "zip": + return FunctionType(name="zip", returns=_builtin_zip) + elif name == "__import__": + return FunctionType(name="__import__", returns=ModuleType()) + elif name == "globals": + return FunctionType(name="globals", + returns=DictType(keys=StrType(), + values=UnknownType(), + empty=False)) diff --git a/src/lib/pedal/tifa/identifier.py b/src/lib/pedal/tifa/identifier.py new file mode 100644 index 0000000000..f250069edb --- /dev/null +++ b/src/lib/pedal/tifa/identifier.py @@ -0,0 +1,24 @@ +class Identifier: + """ + A representation of an Identifier, encapsulating its current level of + existence, scope and State. + + Attributes: + exists (bool): Whether or not the variable actually is defined anywhere. + It is possible that a variable was retrieved that does + not actually exist yet, which indicates it might need to + be created. + in_scope (bool): Whether or not the variable exists in the current + scope. Used to detect the presence of certain kinds + of errors where the user is using a variable from + a different scope. + scoped_name (str): The fully qualified name of the variable, including + its scope chain. + state (State): The current state of the variable. + """ + + def __init__(self, exists, in_scope=False, scoped_name="UNKNOWN", state=""): + self.exists = exists + self.in_scope = in_scope + self.scoped_name = scoped_name + self.state = state diff --git a/src/lib/pedal/tifa/messages.py b/src/lib/pedal/tifa/messages.py new file mode 100644 index 0000000000..655b248e07 --- /dev/null +++ b/src/lib/pedal/tifa/messages.py @@ -0,0 +1,154 @@ +import ast + +OPERATION_DESCRIPTION = { + ast.Pow: "an exponent", + ast.Add: "an addition", + ast.Mult: "a multiplication", + ast.Sub: "a subtraction", + ast.Div: "a division", + ast.FloorDiv: "a division", + ast.Mod: "a modulo", + ast.LShift: "a left shift", + ast.RShift: "a right shift", + ast.BitOr: "a bit or", + ast.BitAnd: "a bit and", + ast.BitXor: "a bit xor", + ast.And: "an and", + ast.Or: "an or", + ast.Eq: "an ==", + ast.NotEq: "a !=", + ast.Lt: "a <", + ast.LtE: "a <=", + ast.Gt: "a >", + ast.GtE: "a >=", + ast.Is: "an is", + ast.IsNot: "an is not", + ast.In: "an in", + ast.NotIn: "an not in", +} + + +def _format_message(issue, data): + if issue == 'Action after return': + # A path had a statement after a return. + return ("You performed an action after already returning from a " + "function, on line {line}. You can only return on a path " + "once.").format(line=data['position']['line']) + elif issue == 'Return outside function': + # Attempted to return outside of a function + return ("You attempted to return outside of a function on line {line}." + " But you can only return from within a function." + ).format(line=data['position']['line']) + elif issue == 'Write out of scope': + # DEPRECATED + # Attempted to modify a variable in a higher scope + return False + return ("You attempted to write a variable from a higher scope " + "(outside the function) on line {line}. You should only " + "use variables inside the function they were declared in." + ).format(line=data['position']['line']) + elif issue == 'Unconnected blocks': + # Any names with ____ + return ("It looks like you have unconnected blocks on line {line}. " + "Before you run your program, you must make sure that all " + "of your blocks are connected that there are no unfilled " + "holes.").format(line=data['position']['line']) + elif issue == 'Iteration Problem': + # Iteration list is the iteration variable + return ("The variable {name} was iterated on line " + "{line} but you used the same variable as the iteration " + "variable. You should choose a different variable name " + "for the iteration variable. Usually, the iteration variable " + "is the singular form of the iteration list (e.g., " + "for a_dog in dogs:).").format( + line=data['position']['line'], + name=data['name']) + elif issue == 'Initialization Problem': + # A variable was read before it was defined + return ("The variable {name} was used on line {line}, " + "but it was not given a value on a previous line. " + "You cannot use a variable until it has been given a value." + ).format(line=data['position']['line'], name=data['name']) + elif issue == 'Possible Initialization Problem': + # A variable was read but was not defined in every branch + if data['name'] == '*return': + return False + return ("The variable {name} was used on line {line}, " + "but it was possibly not given a value on a previous " + "line. You cannot use a variable until it has been given " + "a value. Check to make sure that this variable was " + "declared in all of the branches of your decision." + ).format(line=data['position']['line'], name=data['name']) + elif issue == 'Unused Variable': + # A variable was not read after it was defined + name = data['name'] + if data['type'].is_equal('function'): + kind = 'function' + body = 'definition' + else: + kind = 'variable' + body = 'value' + return ("The {kind} {name} was given a {body}, but " + "was never used after that." + ).format(name=name, kind=kind, body=body) + elif issue == 'Overwritten Variable': + return ("The variable {name} was given a value, but " + "{name} was changed on line {line} before it " + "was used. One of the times that you gave {name} " + "a value was incorrect." + ).format(line=data['position']['line'], name=data['name']) + elif issue == 'Iterating over non-list': + if 'name' not in data or data['name'] is None: + expression = "expression" + else: + expression = "variable {}".format(data['name']) + return ("The {expression} is not a list, but you used " + "it in the iteration on line {line}. You should only iterate " + "over sequences like lists." + ).format(line=data['position']['line'], expression=expression) + elif issue == 'Iterating over empty list': + if 'name' not in data or data['name'] is None: + expression = "expression" + else: + expression = "variable {}".format(data['name']) + return ("The {expression} was set as an empty list, " + "and then you attempted to use it in an iteration on line " + "{line}. You should only iterate over non-empty lists." + ).format(line=data['position']['line'], expression=expression) + elif issue == 'Incompatible types': + op = OPERATION_DESCRIPTION.get(data['operation'].__class__, + str(data['operation'])) + left = data['left'].singular_name + right = data['right'].singular_name + line = data['position']['line'] + return ("You used {op} operation with {left} and {right} on line " + "{line}. But you can't do that with that operator. Make " + "sure both sides of the operator are the right type." + ).format(op=op, left=left, right=right, line=line) + elif issue == 'Read out of scope': + return ("You attempted to read a variable from a different scope on " + "line {line}. You should only use variables inside the " + "function they were declared in." + ).format(line=data['position']['line']) + return False + + +''' +TODO: Finish these checks +"Empty Body": [], # Any use of pass on its own +"Malformed Conditional": [], # An if/else with empty else or if +"Unnecessary Pass": [], # Any use of pass +"Append to non-list": [], # Attempted to use the append method on a non-list +"Used iteration list": [], # +"Unused iteration variable": [], # +"Type changes": [], # +"Unknown functions": [], # +"Not a function": [], # Attempt to call non-function as function +"Recursive Call": [], +"Incorrect Arity": [], +"Aliased built-in": [], # +"Method not in Type": [], # A method was used that didn't exist for that type +"Submodule not found": [], +"Module not found": [], +"Else on loop body": [], # Used an Else on a For or While +''' diff --git a/src/lib/pedal/tifa/readme.md b/src/lib/pedal/tifa/readme.md new file mode 100644 index 0000000000..fd100ad20c --- /dev/null +++ b/src/lib/pedal/tifa/readme.md @@ -0,0 +1,5 @@ +PyTIFA is the Python Type Inferencer and Flow Analyzer, also called just Tifa. + +Tifa is meant to be used on simple programs written in learning situations, in order to provide type information and detect certain common issues. It makes some very strong assumptions and doesn't support all language features. + +Tifa is supported by Skulpt. This means that Tifa is a Python library that can be passed in a string of Python source code in order to traverse its AST using underlying JavaScript libraries. If that isn't confusing, then we invite you to make pull requests. \ No newline at end of file diff --git a/src/lib/pedal/tifa/state.py b/src/lib/pedal/tifa/state.py new file mode 100644 index 0000000000..fe75ccd428 --- /dev/null +++ b/src/lib/pedal/tifa/state.py @@ -0,0 +1,77 @@ +def check_trace(state): + past_types = [state.type] + for past_state in state.trace: + past_types.extend(check_trace(past_state)) + return past_types + + +class State: + """ + A representation of a variable at a particular point in time of the program. + + Attributes: + name (str): The name of the variable, without its scope chain + trace (list of State): A recursive definition of previous States for + this State. + type (Type): The current type of this variable. + method (str): One of 'store', 'read', (TODO). Indicates the change that + occurred to this variable at this State. + position (dict): A Position dictionary indicating where this State + change occurred in the source code. + read (str): One of 'yes', 'no', or 'maybe'. Indicates if this variable + has been read since it was last changed. If merged from a + diverging path, it is possible that it was "maybe" read. + set (str): One of 'yes', 'no', or 'maybe'. Indicates if this variable + has been set since it was last read. If merged from a + diverging path, it is possible that it was "maybe" changed. + over (str): One of 'yes', 'no', or 'maybe'. Indicates if this variable + has been overwritten since it was last set. If merged from a + diverging path, it is possible that it was "maybe" changed. + over_position (dict): A Position indicating where the State was + previously set versus when it was overwritten. + """ + + def __init__(self, name, trace, type, method, position, + read='maybe', set='maybe', over='maybe', over_position=None): + self.name = name + self.trace = trace + self.type = type + self.method = method + self.position = position + self.over_position = over_position + self.read = read + self.set = set + self.over = over + + def copy(self, method, position): + """ + Make a copy of this State, copying this state into the new State's trace + """ + return State(self.name, [self], self.type, method, position, + self.read, self.set, self.over, self.over_position) + + def __str__(self): + """ + Create a string representation of this State. + """ + return "{method}(r:{read},s:{set},o:{over},{type})".format( + method=self.method, + read=self.read[0], + set=self.set[0], + over=self.over[0], + type=self.type.__class__.__name__ + ) + + def __repr__(self): + """ + Create a string representation of this State. + """ + return str(self) + + def was_type(self, a_type): + """ + Retrieve all the types that this variable took on over its entire + trace. + """ + past_types = check_trace(self) + return any(past_type.is_equal(a_type) for past_type in past_types) diff --git a/src/lib/pedal/tifa/tifa.py b/src/lib/pedal/tifa/tifa.py new file mode 100644 index 0000000000..93e86cc626 --- /dev/null +++ b/src/lib/pedal/tifa/tifa.py @@ -0,0 +1,1243 @@ +import time; stopwatch = time.time(); +print("Phase {}: {} secs".format("Tifa.Tifa Before all imports", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() +import ast +from pprint import pprint +print("Phase {}: {} secs".format("Tifa.Tifa Before import", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() +from pedal.report import MAIN_REPORT + +from pedal.tifa.type_definitions import (UnknownType, RecursedType, + FunctionType, ClassType, InstanceType, + NumType, NoneType, BoolType, TupleType, + ListType, StrType, GeneratorType, + DictType, ModuleType, SetType, + # FileType, DayType, TimeType, + type_from_json, type_to_literal, + LiteralNum, LiteralBool, + LiteralNone, LiteralStr, + LiteralTuple) +from pedal.tifa.builtin_definitions import (get_builtin_module, get_builtin_function) +from pedal.tifa.type_operations import (merge_types, are_types_equal, + VALID_UNARYOP_TYPES, VALID_BINOP_TYPES, + ORDERABLE_TYPES, INDEXABLE_TYPES) +from pedal.tifa.identifier import Identifier +from pedal.tifa.state import State +from pedal.tifa.messages import _format_message + +__all__ = ['Tifa'] + + +class Tifa(ast.NodeVisitor): + """ + TIFA Class for traversing an AST and finding common issues. + + Args: + python_3 (bool): Whether to parse the code in regular PYTHON_3 mode or + the modified AST that Skulpt uses. + report (Report): The report object to store data and feedback in. If + left None, defaults to the global MAIN_REPORT. + """ + + def __init__(self, python_3=True, report=None): + if report is None: + report = MAIN_REPORT + self.report = report + self._initialize_report() + self.PYTHON_3 = python_3 + + def _initialize_report(self): + """ + Initialize a successful report with possible set of issues. + """ + self.report['tifa'] = { + 'success': True, + 'variables': {}, + 'top_level_variables': {}, + 'issues': {} + } + + def report_issue(self, issue, data=None): + """ + Report the given issue with associated metadata, including the position + if not explicitly included. + """ + if data is None: + data = {} + if 'position' not in data: + data['position'] = self.locate() + data['message'] = _format_message(issue, data) + if issue not in self.report['tifa']['issues']: + self.report['tifa']['issues'][issue] = [] + self.report['tifa']['issues'][issue].append(data) + if data['message']: + self.report.attach(issue, category='Analyzer', tool='TIFA', + mistake=data) + + def locate(self, node=None): + """ + Return a dictionary representing the current location within the + AST. + + Returns: + Position dict: A dictionary with the fields 'column' and 'line', + indicating the current position in the source code. + """ + if node is None: + if self.node_chain: + node = self.node_chain[-1] + else: + node = self.final_node + return {'column': node.col_offset, 'line': node.lineno} + + def process_code(self, code, filename="__main__"): + """ + Processes the AST of the given source code to generate a report. + + Args: + code (str): The Python source code + filename (str): The filename of the source code (defaults to __main__) + Returns: + Report: The successful or successful report object + """ + # Code + self.source = code.split("\n") if code else [] + filename = filename + + # Attempt parsing - might fail! + try: + ast_tree = ast.parse(code, filename) + except Exception as error: + self.report['tifa']['success'] = False + self.report['tifa']['error'] = error + self.report.attach('tifa_error', category='Analyzer', tool='TIFA', + mistake={ + 'message': "Could not parse code", + 'error': error + }) + return self.report['tifa'] + try: + return self.process_ast(ast_tree) + except Exception as error: + self.report['tifa']['success'] = False + self.report['tifa']['error'] = error + self.report.attach('tifa_error', category='Analyzer', tool='TIFA', + mistake={ + 'message': "Could not process code", + 'error': error + }) + return self.report['tifa'] + + def process_ast(self, ast_tree): + """ + Given an AST, actually performs the type and flow analyses to return a + report. + + Args: + ast (Ast): The AST object + Returns: + Report: The final report object created (also available as a field). + """ + self._reset() + # Traverse every node + self.visit(ast_tree) + + # Check afterwards + self.report['tifa']['variables'] = self.name_map + self._finish_scope() + + # Collect top level variables + self._collect_top_level_variables() + # print(self.report['variables']) + + return self.report['tifa'] + + def _collect_top_level_variables(self): + """ + Walk through the variables and add any at the top level to the + top_level_variables field of the report. + """ + top_level_variables = self.report['tifa']['top_level_variables'] + main_path_vars = self.name_map[self.path_chain[0]] + for full_name in main_path_vars: + split_name = full_name.split("/") + if len(split_name) == 2 and split_name[0] == str(self.scope_chain[0]): + name = split_name[1] + top_level_variables[name] = main_path_vars[full_name] + + def _reset(self): + """ + Reinitialize fields for maintaining the system + """ + # Unique Global IDs + self.path_id = 0 + self.scope_id = 0 + self.ast_id = 0 + + # Human readable names + self.path_names = ['*Module'] + self.scope_names = ['*Module'] + self.node_chain = [] + + # Complete record of all Names + self.scope_chain = [self.scope_id] + self.path_chain = [self.path_id] + self.name_map = {} + self.name_map[self.path_id] = {} + self.definition_chain = [] + self.path_parents = {} + self.final_node = None + self.class_scopes = {} + + def find_variable_scope(self, name): + """ + Walk through this scope and all enclosing scopes, finding the relevant + identifier given by `name`. + + Args: + name (str): The name of the variable + Returns: + Identifier: An Identifier for the variable, which could potentially + not exist. + """ + for scope_level, scope in enumerate(self.scope_chain): + for path_id in self.path_chain: + path = self.name_map[path_id] + full_name = "/".join(map(str, self.scope_chain[scope_level:])) + "/" + name + if full_name in path: + is_root_scope = (scope_level == 0) + return Identifier(True, is_root_scope, + full_name, path[full_name]) + + return Identifier(False) + + def find_variable_out_of_scope(self, name): + """ + Walk through every scope and determine if this variable can be found + elsewhere (which would be an issue). + + Args: + name (str): The name of the variable + Returns: + Identifier: An Identifier for the variable, which could potentially + not exist. + """ + for path in self.name_map.values(): + for full_name in path: + unscoped_name = full_name.split("/")[-1] + if name == unscoped_name: + return Identifier(True, False, unscoped_name, path[full_name]) + return Identifier(False) + + def find_path_parent(self, path_id, name): + if name in self.name_map[path_id]: + return Identifier(True, state=self.name_map[path_id][name]) + else: + path_parent = self.path_parents.get(path_id) + if path_parent is None: + return Identifier(False) + else: + return self.find_path_parent(path_parent, name) + + def _finish_scope(self): + """ + Walk through all the variables present in this scope and ensure that + they have been read and not overwritten. + """ + path_id = self.path_chain[0] + for name in self.name_map[path_id]: + if Tifa.in_scope(name, self.scope_chain): + state = self.name_map[path_id][name] + if state.over == 'yes': + position = state.over_position + self.report_issue('Overwritten Variable', + {'name': state.name, 'position': position}) + if state.read == 'no': + self.report_issue('Unused Variable', + {'name': state.name, 'type': state.type, + 'position': state.position}) + + def visit(self, node): + """ + Process this node by calling its appropriate visit_* + + Args: + node (AST): The node to visit + Returns: + Type: The type calculated during the visit. + """ + # Start processing the node + self.node_chain.append(node) + self.ast_id += 1 + + # Actions after return? + if len(self.scope_chain) > 1: + return_state = self.find_variable_scope("*return") + if return_state.exists and return_state.in_scope: + if return_state.state.set == "yes": + self.report_issue("Action after return") + + # No? All good, let's enter the node + self.final_node = node + result = ast.NodeVisitor.visit(self, node) + + # Pop the node out of the chain + self.ast_id -= 1 + self.node_chain.pop() + + # If a node failed to return something, return the UNKNOWN TYPE + if result is None: + return UnknownType() + else: + return result + + def _visit_nodes(self, nodes): + """ + Visit all the nodes in the given list. + + Args: + nodes (list): A list of values, of which any AST nodes will be + visited. + """ + for node in nodes: + if isinstance(node, ast.AST): + self.visit(node) + + def walk_targets(self, targets, type, walker): + """ + Iterate through the targets and call the given function on each one. + + Args: + targets (list of Ast nodes): A list of potential targets to be + traversed. + type (Type): The given type to be unraveled and applied to the + targets. + walker (Ast Node, Type -> None): A function that will process + each target and unravel the type. + """ + for target in targets: + walker(target, type) + + def _walk_target(self, target, type): + """ + Recursively apply the type to the target + + Args: + target (Ast): The current AST node to process + type (Type): The type to apply to this node + """ + if isinstance(target, ast.Name): + self.store_iter_variable(target.id, type, self.locate(target)) + return target.id + elif isinstance(target, (ast.Tuple, ast.List)): + result = None + for i, elt in enumerate(target.elts): + elt_type = type.index(LiteralNum(i)) + potential_name = self._walk_target(elt, elt_type) + if potential_name is not None and result is None: + result = potential_name + return result + + def visit_Assign(self, node): + """ + Simple assignment statement: + __targets__ = __value__ + + Args: + node (AST): An Assign node + Returns: + None + """ + # Handle value + value_type = self.visit(node.value) + # Handle targets + self._visit_nodes(node.targets) + + # TODO: Properly handle assignments with subscripts + def action(target, type): + if isinstance(target, ast.Name): + self.store_variable(target.id, type) + elif isinstance(target, (ast.Tuple, ast.List)): + for i, elt in enumerate(target.elts): + eltType = type.index(LiteralNum(i)) + action(elt, eltType) + elif isinstance(target, ast.Subscript): + pass + elif isinstance(target, ast.Attribute): + left_hand_type = self.visit(target.value) + if isinstance(left_hand_type, InstanceType): + left_hand_type.add_attr(target.attr, type) + # TODO: Otherwise we attempted to assign to a non-instance + # TODO: Handle minor type changes (e.g., appending to an inner list) + + self.walk_targets(node.targets, value_type, action) + + def visit_AugAssign(self, node): + # Handle value + right = self.visit(node.value) + # Handle target + left = self.visit(node.target) + # Target is always a Name, Subscript, or Attribute + name = self.identify_caller(node.target) + + # Handle operation + self.load_variable(name) + if isinstance(left, UnknownType) or isinstance(right, UnknownType): + return UnknownType() + elif type(node.op) in VALID_BINOP_TYPES: + op_lookup = VALID_BINOP_TYPES[type(node.op)] + if type(left) in op_lookup: + op_lookup = op_lookup[type(left)] + if type(right) in op_lookup: + op_lookup = op_lookup[type(right)] + result_type = op_lookup(left, right) + self.store_variable(name, result_type) + return result_type + + self.report_issue("Incompatible types", + {"left": left, "right": right, + "operation": node.op}) + + def visit_Attribute(self, node): + # Handle value + value_type = self.visit(node.value) + # Handle ctx + # TODO: Handling contexts + # Handle attr + return value_type.load_attr(node.attr, self, node.value, self.locate()) + + def visit_BinOp(self, node): + # Handle left and right + left = self.visit(node.left) + right = self.visit(node.right) + + # Handle operation + if isinstance(left, UnknownType) or isinstance(right, UnknownType): + return UnknownType() + elif type(node.op) in VALID_BINOP_TYPES: + op_lookup = VALID_BINOP_TYPES[type(node.op)] + if type(left) in op_lookup: + op_lookup = op_lookup[type(left)] + if type(right) in op_lookup: + op_lookup = op_lookup[type(right)] + return op_lookup(left, right) + + self.report_issue("Incompatible types", + {"left": left, "right": right, + "operation": node.op}) + return UnknownType() + + def visit_Bool(self, node): + return BoolType() + + def visit_BoolOp(self, node): + # Handle left and right + values = [] + for value in node.values: + values.append(self.visit(value)) + + # TODO: Truthiness is not supported! Probably need a Union type + # TODO: Literals used as truthy value + + # Handle operation + return BoolType() + + def visit_Call(self, node): + # Handle func part (Name or Attribute) + function_type = self.visit(node.func) + # TODO: Need to grab the actual type in some situations + callee = self.identify_caller(node) + + # Handle args + arguments = [self.visit(arg) for arg in node.args] + + # TODO: Handle keywords + # TODO: Handle starargs + # TODO: Handle kwargs + if isinstance(function_type, FunctionType): + # Test if we have called this definition before + if function_type.definition not in self.definition_chain: + self.definition_chain.append(function_type.definition) + # Function invocation + result = function_type.definition(self, function_type, callee, + arguments, self.locate()) + self.definition_chain.pop() + return result + else: + self.report_issue("Recursive Call", {"name": callee}) + elif isinstance(function_type, ClassType): + constructor = function_type.get_constructor().definition + self.definition_chain.append(constructor) + result = constructor(self, constructor, callee, arguments, self.locate()) + self.definition_chain.pop() + if '__init__' in function_type.fields: + initializer = function_type.fields['__init__'] + if isinstance(initializer, FunctionType): + self.definition_chain.append(initializer) + initializer.definition(self, initializer, result, [result] + arguments, self.locate()) + self.definition_chain.pop() + return result + else: + self.report_issue("Not a function", {"name": callee}) + return UnknownType() + + def visit_ClassDef(self, node): + class_name = node.name + new_class_type = ClassType(class_name) + self.store_variable(class_name, new_class_type) + # TODO: Define a new scope definition that executes the body + # TODO: find __init__, execute that + definitions_scope = self.scope_chain[:] + class_scope = Tifa.NewScope(self, definitions_scope, class_type=new_class_type) + with class_scope: + self.generic_visit(node) + + def visit_Compare(self, node): + # Handle left and right + left = self.visit(node.left) + comparators = [self.visit(compare) for compare in node.comparators] + + # Handle ops + for op, right in zip(node.ops, comparators): + if isinstance(op, (ast.Eq, ast.NotEq, ast.Is, ast.IsNot)): + continue + elif isinstance(op, (ast.Lt, ast.LtE, ast.GtE, ast.Gt)): + if are_types_equal(left, right): + if isinstance(left, ORDERABLE_TYPES): + continue + elif isinstance(op, (ast.In, ast.NotIn)): + if isinstance(right, INDEXABLE_TYPES): + continue + self.report_issue("Incompatible types", + {"left": left, "right": right, + "operation": op}) + return BoolType() + + def _visit_collection_loop(self, node): + # Handle the iteration list + iter = node.iter + iter_list_name = None + if isinstance(iter, ast.Name): + iter_list_name = iter.id + if iter_list_name == "___": + self.report_issue("Unconnected blocks", + {"position": self.locate(iter)}) + state = self.iterate_variable(iter_list_name, self.locate(iter)) + iter_type = state.type + else: + iter_type = self.visit(iter) + + if iter_type.is_empty(): + self.report_issue("Iterating over empty list", + {"name": iter_list_name, + "position": self.locate(iter)}) + + if not isinstance(iter_type, INDEXABLE_TYPES): + self.report_issue("Iterating over non-list", + {"name": iter_list_name, + "position": self.locate(iter)}) + + iter_subtype = iter_type.index(LiteralNum(0)) + + # Handle the iteration variable + iter_variable_name = self._walk_target(node.target, iter_subtype) + + if iter_variable_name and iter_list_name: + if iter_variable_name == iter_list_name: + self.report_issue("Iteration Problem", + {"name": iter_variable_name, + "position": self.locate(node.target)}) + + def visit_comprehension(self, node): + self._visit_collection_loop(node) + # Handle the bodies + self.visit_statements(node.ifs) + + def visit_Dict(self, node): + """ + Three types of dictionaries + - empty + - uniform type + - record + """ + type = DictType() + if not node.keys: + type.empty = True + else: + type.empty = False + all_literals = True + keys, values, literals = [], [], [] + for key, value in zip(node.keys, node.values): + literal = self.get_literal(key) + key, value = self.visit(key), self.visit(value) + values.append(value) + keys.append(key) + if literal is not None: + literals.append(literal) + else: + all_literals = False + + if all_literals: + type.literals = literals + type.values = values + else: + type.keys = key + type.values = value + return type + + def visit_DictComp(self, node): + # TODO: Handle comprehension scope + for generator in node.generators: + self.visit(generator) + keys = self.visit(node.key) + values = self.visit(node.value) + return DictType(keys=keys, values=values) + + def visit_For(self, node): + self._visit_collection_loop(node) + # Handle the bodies + self.visit_statements(node.body) + self.visit_statements(node.orelse) + + def visit_FunctionDef(self, node): + # Name + function_name = node.name + position = self.locate() + definitions_scope = self.scope_chain[:] + + def definition(tifa, call_type, call_name, parameters, call_position): + function_scope = Tifa.NewScope(self, definitions_scope) + with function_scope: + # Process arguments + args = node.args.args + if len(args) != len(parameters): + self.report_issue('Incorrect Arity', {"position": position}) + # TODO: Handle special types of parameters + for arg, parameter in zip(args, parameters): + name = arg.arg if self.PYTHON_3 else arg.id + if parameter is not None: + parameter = parameter.clone_mutably() + self.store_variable(name, parameter, position) + if len(args) < len(parameters): + for undefined_parameter in parameters[len(args):]: + self.store_variable(name, UnknownType(), position) + self.visit_statements(node.body) + return_state = self.find_variable_scope("*return") + return_value = NoneType() + # If the pseudo variable exists, we load it and get its type + if return_state.exists and return_state.in_scope: + return_state = self.load_variable("*return", call_position) + return_value = return_state.type + return return_value + + function = FunctionType(definition=definition, name=function_name) + self.store_variable(function_name, function) + return function + + def visit_GeneratorExp(self, node): + # TODO: Handle comprehension scope + for generator in node.generators: + self.visit(generator) + return GeneratorType(self.visit(node.elt)) + + def visit_If(self, node): + # Visit the conditional + self.visit(node.test) + + if len(node.orelse) == 1 and isinstance(node.orelse[0], ast.Pass): + self.report_issue("Malformed Conditional") + elif len(node.body) == 1 and isinstance(node.body[0], ast.Pass): + if node.orelse: + self.report_issue("Malformed Conditional") + + # Visit the bodies + this_path_id = self.path_chain[0] + if_path = Tifa.NewPath(self, this_path_id, "i") + with if_path: + for statement in node.body: + self.visit(statement) + else_path = Tifa.NewPath(self, this_path_id, "e") + with else_path: + for statement in node.orelse: + self.visit(statement) + + # Combine two paths into one + # Check for any names that are on the IF path + self.merge_paths(this_path_id, if_path.id, else_path.id) + + def visit_IfExp(self, node): + # Visit the conditional + self.visit(node.test) + + # Visit the body + body = self.visit(node.body) + + # Visit the orelse + orelse = self.visit(node.orelse) + + if are_types_equal(body, orelse): + return body + + # TODO: Union type? + return UnknownType() + + def visit_Import(self, node): + # Handle names + for alias in node.names: + asname = alias.asname or alias.name + module_type = self.load_module(alias.name) + self.store_variable(asname, module_type) + + def visit_ImportFrom(self, node): + # Handle names + for alias in node.names: + if node.module is None: + asname = alias.asname or alias.name + module_type = self.load_module(alias.name) + else: + module_name = node.module + asname = alias.asname or alias.name + module_type = self.load_module(module_name) + name_type = module_type.load_attr(alias.name, self, + callee_position=self.locate()) + self.store_variable(asname, name_type) + + def visit_Lambda(self, node): + # Name + position = self.locate() + definitions_scope = self.scope_chain[:] + + def definition(tifa, call_type, call_name, parameters, call_position): + function_scope = Tifa.NewScope(self, definitions_scope) + with function_scope: + # Process arguments + args = node.args.args + if len(args) != len(parameters): + self.report_issue('Incorrect Arity', {"position": position}) + # TODO: Handle special types of parameters + for arg, parameter in zip(args, parameters): + name = arg.arg if self.PYTHON_3 else arg.id + if parameter is not None: + parameter = parameter.clone_mutably() + self.store_variable(name, parameter, position) + if len(args) < len(parameters): + for undefined_parameter in parameters[len(args):]: + self.store_variable(name, UnknownType(), position) + return_value = self.visit(node.body) + return return_value + + return FunctionType(definition=definition) + + def visit_List(self, node): + type = ListType() + if node.elts: + type.empty = False + # TODO: confirm homogenous subtype + for elt in node.elts: + type.subtype = self.visit(elt) + else: + type.empty = True + return type + + def visit_ListComp(self, node): + # TODO: Handle comprehension scope + for generator in node.generators: + self.visit(generator) + return ListType(self.visit(node.elt)) + + def visit_Name(self, node): + name = node.id + if name == "___": + self.report_issue("Unconnected blocks") + if isinstance(node.ctx, ast.Load): + if name == "True" or name == "False": + return BoolType() + elif name == "None": + return NoneType() + else: + variable = self.find_variable_scope(name) + builtin = get_builtin_function(name) + if not variable.exists and builtin: + return builtin + else: + state = self.load_variable(name) + return state.type + else: + variable = self.find_variable_scope(name) + if variable.exists: + return variable.state.type + else: + return UnknownType() + + def visit_Num(self, node): + return NumType() + + def visit_Return(self, node): + if len(self.scope_chain) == 1: + self.report_issue("Return outside function") + if node.value is not None: + self.return_variable(self.visit(node.value)) + else: + self.return_variable(NoneType()) + + def visit_SetComp(self, node): + # TODO: Handle comprehension scope + for generator in node.generators: + self.visit(generator) + return SetType(self.visit(node.elt)) + + def visit_statements(self, nodes): + # TODO: Check for pass in the middle of a series of statement + if any(isinstance(node, ast.Pass) for node in nodes): + pass + return [self.visit(statement) for statement in nodes] + + def visit_Str(self, node): + if node.s == "": + return StrType(True) + else: + return StrType(False) + + def visit_Subscript(self, node): + # Handle value + value_type = self.visit(node.value) + # Handle slice + if isinstance(node.slice, ast.Index): + literal = self.get_literal(node.slice.value) + if literal is None: + dynamic_literal = type_to_literal(self.visit(node.slice.value)) + return value_type.index(dynamic_literal) + else: + return value_type.index(literal) + elif isinstance(node.slice, ast.Slice): + if node.slice.lower is not None: + self.visit(node.slice.lower) + if node.slice.upper is not None: + self.visit(node.slice.upper) + if node.slice.step is not None: + self.visit(node.slice.step) + return value_type + + def visit_Tuple(self, node): + type = TupleType() + if not node.elts: + type.empty = True + type.subtypes = [] + else: + type.empty = False + # TODO: confirm homogenous subtype + type.subtypes = [self.visit(elt) for elt in node.elts] + return type + + def visit_UnaryOp(self, node): + # Handle operand + operand = self.visit(node.operand) + + if isinstance(node.op, ast.Not): + return BoolType() + elif isinstance(operand, UnknownType): + return UnknownType() + elif type(node.op) in VALID_UNARYOP_TYPES: + op_lookup = VALID_UNARYOP_TYPES[type(node.op)] + if type(node.op) in op_lookup: + op_lookup = op_lookup[type(node.op)] + if type(operand) in op_lookup: + op_lookup = op_lookup[type(operand)] + return op_lookup(operand) + return UnknownType() + + def visit_While(self, node): + # Visit conditional + self.visit(node.test) + + # Visit the bodies + this_path_id = self.path_id + # One path is that we never enter the body + empty_path = Tifa.NewPath(self, this_path_id, "e") + with empty_path: + pass + # Another path is that we loop through the body and check the test again + body_path = Tifa.NewPath(self, this_path_id, "w") + with body_path: + for statement in node.body: + self.visit(statement) + # Revisit conditional + self.visit(node.test) + # If there's else bodies (WEIRD) then we should check them afterwards + if node.orelse: + self.report_issue("Else on loop body") + for statement in node.orelse: + self.visit(statement) + + # Combine two paths into one + # Check for any names that are on the IF path + self.merge_paths(this_path_id, body_path.id, empty_path.id) + + def visit_With(self, node): + if self.PYTHON_3: + for item in node.items: + type_value = self.visit(item.context_expr) + self.visit(item.optional_vars) + self._walk_target(item.optional_vars, type_value) + else: + type_value = self.visit(node.context_expr) + # self.visit(node.optional_vars) + self._walk_target(node.optional_vars, type_value) + # Handle the bodies + self.visit_statements(node.body) + + def _scope_chain_str(self, name=None): + """ + Convert the current scope chain to a string representation (divided + by "/"). + + Returns: + str: String representation of the scope chain. + """ + if name: + return "/".join(map(str, self.scope_chain)) + "/" + name + else: + return "/".join(map(str, self.scope_chain)) + + def identify_caller(self, node): + """ + Figures out the variable that was used to kick off this call, + which is almost always the relevant Name to track as being updated. + If the origin wasn't a Name, nothing will need to be updated so None + is returned instead. + + TODO: Is this sufficient? + + Args: + node (AST): An AST node + Returns: + str or None: The name of the variable or None if no origin could + be found. + """ + if isinstance(node, ast.Name): + return node.id + elif isinstance(node, ast.Call): + return self.identify_caller(node.func) + elif isinstance(node, (ast.Attribute, ast.Subscript)): + return self.identify_caller(node.value) + return None + + def iterate_variable(self, name, position=None): + """ + Update the variable by iterating through it - this doesn't do anything + fancy yet. + """ + return self.load_variable(name, position) + + def store_iter_variable(self, name, type, position=None): + state = self.store_variable(name, type, position) + state.read = 'yes' + return state + + def return_variable(self, type): + return self.store_variable("*return", type) + + def append_variable(self, name, type, position=None): + return self.store_variable(name, type, position) + + def store_variable(self, name, type, position=None): + """ + Update the variable with the given name to now have the new type. + + Args: + name (str): The unqualified name of the variable. The variable will + be assumed to be in the current scope. + type (Type): The new type of this variable. + Returns: + State: The new state of the variable. + """ + if position is None: + position = self.locate() + full_name = self._scope_chain_str(name) + current_path = self.path_chain[0] + variable = self.find_variable_scope(name) + if not variable.exists: + # Create a new instance of the variable on the current path + new_state = State(name, [], type, 'store', position, + read='no', set='yes', over='no') + self.name_map[current_path][full_name] = new_state + else: + new_state = self.trace_state(variable.state, "store", position) + if not variable.in_scope: + self.report_issue("Write out of scope", {'name': name}) + # Type change? + if not are_types_equal(type, variable.state.type): + self.report_issue("Type changes", + {'name': name, 'old': variable.state.type, + 'new': type, 'position': position}) + new_state.type = type + # Overwritten? + if variable.state.set == 'yes' and variable.state.read == 'no': + new_state.over_position = position + new_state.over = 'yes' + else: + new_state.set = 'yes' + new_state.read = 'no' + self.name_map[current_path][full_name] = new_state + # If this is a class scope... + current_scope = self.scope_chain[0] + if current_scope in self.class_scopes: + self.class_scopes[current_scope].add_attr(name, new_state.type) + return new_state + + def load_variable(self, name, position=None): + """ + Retrieve the variable with the given name. + + Args: + name (str): The unqualified name of the variable. If the variable is + not found in the current scope or an enclosing sope, all + other scopes will be searched to see if it was read out + of scope. + Returns: + State: The current state of the variable. + """ + full_name = self._scope_chain_str(name) + current_path = self.path_chain[0] + variable = self.find_variable_scope(name) + if position is None: + position = self.locate() + if not variable.exists: + out_of_scope_var = self.find_variable_out_of_scope(name) + # Create a new instance of the variable on the current path + if out_of_scope_var.exists: + self.report_issue("Read out of scope", {'name': name}) + else: + self.report_issue("Initialization Problem", {'name': name}) + new_state = State(name, [], UnknownType(), 'load', position, + read='yes', set='no', over='no') + self.name_map[current_path][full_name] = new_state + else: + new_state = self.trace_state(variable.state, "load", position) + if variable.state.set == 'no': + self.report_issue("Initialization Problem", {'name': name}) + if variable.state.set == 'maybe': + self.report_issue("Possible Initialization Problem", {'name': name}) + new_state.read = 'yes' + if not variable.in_scope: + self.name_map[current_path][variable.scoped_name] = new_state + else: + self.name_map[current_path][full_name] = new_state + return new_state + + def load_module(self, chain): + """ + Finds the module in the set of available modules. + + Args: + chain (str): A chain of module imports (e.g., "matplotlib.pyplot") + Returns: + ModuleType: The specific module with its members, or an empty + module type. + """ + module_names = chain.split('.') + potential_module = get_builtin_module(module_names[0]) + if potential_module is not None: + base_module = potential_module + for module in module_names: + if (isinstance(base_module, ModuleType) and + module in base_module.submodules): + base_module = base_module.submodules[module] + else: + self.report_issue("Module not found", {"name": chain}) + return base_module + else: + try: + actual_module = __import__(chain, globals(), {}, + ['_tifa_definitions']) + definitions = actual_module._tifa_definitions() + return type_from_json(definitions) + except Exception as e: + self.report_issue("Module not found", + {"name": chain, "error": str(e)}) + return ModuleType() + + def combine_states(self, left, right): + state = State(left.name, [left], left.type, 'branch', self.locate(), + read=left.read, set=left.set, over=left.over, + over_position=left.over_position) + if right is None: + state.read = 'no' if left.read == 'no' else 'maybe' + state.set = 'no' if left.set == 'no' else 'maybe' + state.over = 'no' if left.over == 'no' else 'maybe' + else: + if not are_types_equal(left.type, right.type): + self.report_issue("Type changes", {'name': left.name, + 'old': left.type, + 'new': right.type}) + state.read = Tifa.match_rso(left.read, right.read) + state.set = Tifa.match_rso(left.set, right.set) + state.over = Tifa.match_rso(left.over, right.over) + if left.over == 'no': + state.over_position = right.over_position + state.trace.append(right) + return state + + def merge_paths(self, parent_path_id, left_path_id, right_path_id): + """ + Combines any variables on the left and right path into the parent + name space. + + Args: + parent_path_id (int): The parent path of the left and right branches + left_path_id (int): One of the two paths + right_path_id (int): The other of the two paths. + """ + # Combine two paths into one + # Check for any names that are on the IF path + for left_name in self.name_map[left_path_id]: + left_state = self.name_map[left_path_id][left_name] + right_identifier = self.find_path_parent(right_path_id, left_name) + if right_identifier.exists: + # Was on both IF and ELSE path + right_state = right_identifier.state + else: + # Was only on IF path, potentially on the parent path + right_state = self.name_map[parent_path_id].get(left_name) + combined = self.combine_states(left_state, right_state) + self.name_map[parent_path_id][left_name] = combined + # Check for names that are on the ELSE path but not the IF path + for right_name in self.name_map[right_path_id]: + if right_name not in self.name_map[left_path_id]: + right_state = self.name_map[right_path_id][right_name] + # Potentially on the parent path + parent_state = self.name_map[parent_path_id].get(right_name) + combined = self.combine_states(right_state, parent_state) + self.name_map[parent_path_id][right_name] = combined + + def trace_state(self, state, method, position): + """ + Makes a copy of the given state with the given method type. + + Args: + state (State): The state to copy (as in, we trace a copy of it!) + method (str): The operation being applied to the state. + Returns: + State: The new State + """ + return state.copy(method, position) + + @staticmethod + def in_scope(full_name, scope_chain): + """ + Determine if the fully qualified variable name is in the given scope + chain. + + Args: + full_name (str): A fully qualified variable name + scope_chain (list): A representation of a scope chain. + Returns: + bool: Whether the variable lives in this scope + """ + # Get this entity's full scope chain + name_scopes = full_name.split("/")[:-1] + # against the reverse scope chain + checking_scopes = [str(s) for s in scope_chain[::-1]] + return name_scopes == checking_scopes + + @staticmethod + def match_rso(left, right): + if left == right: + return left + else: + return "maybe" + + def get_literal(self, node): + if isinstance(node, ast.Num): + return LiteralNum(node.n) + elif isinstance(node, ast.Str): + return LiteralStr(node.s) + elif isinstance(node, ast.Tuple): + values = [] + for elt in node.elts: + subvalue = self.get_literal(elt) + if subvalue is not None: + values.append(subvalue) + else: + return None + return LiteralTuple(values) + elif isinstance(node, ast.Name): + if node.id == "None": + return LiteralNone() + elif node.id == "False": + return LiteralBool(False) + elif node.id == "True": + return LiteralBool(True) + return None + + class NewPath: + """ + Context manager for entering and leaving execution paths (e.g., if + statements).) + + Args: + tifa (Tifa): The tifa instance, so we can modify some of its + properties that track variables and paths. + origin_path (int): The path ID parent to this one. + name (str): The symbolic name of this path, typically 'i' for an IF + body and 'e' for ELSE body. + + Fields: + id (int): The path ID of this path + """ + + def __init__(self, tifa, origin_path, name): + self.tifa = tifa + self.name = name + self.origin_path = origin_path + self.id = None + + def __enter__(self): + self.tifa.path_id += 1 + self.id = self.tifa.path_id + self.tifa.path_names.append(str(self.id) + self.name) + self.tifa.path_chain.insert(0, self.id) + self.tifa.name_map[self.id] = {} + self.tifa.path_parents[self.id] = self.origin_path + + def __exit__(self, type, value, traceback): + self.tifa.path_names.pop() + self.tifa.path_chain.pop(0) + + class NewScope: + """ + Context manager for entering and leaving scopes (e.g., inside of + function calls). + + Args: + tifa (Tifa): The tifa instance, so we can modify some of its + properties that track variables and paths. + definitions_scope_chain (list of int): The scope chain of the + definition + """ + + def __init__(self, tifa, definitions_scope_chain, class_type=None): + self.tifa = tifa + self.definitions_scope_chain = definitions_scope_chain + self.class_type = class_type + + def __enter__(self): + # Manage scope + self.old_scope = self.tifa.scope_chain[:] + # Move to the definition's scope chain + self.tifa.scope_chain = self.definitions_scope_chain[:] + # And then enter its body's new scope + self.tifa.scope_id += 1 + self.tifa.scope_chain.insert(0, self.tifa.scope_id) + # Register as class potentially + if self.class_type is not None: + self.class_type.scope_id = self.tifa.scope_id + self.tifa.class_scopes[self.tifa.scope_id] = self.class_type + + def __exit__(self, type, value, traceback): + # Finish up the scope + self.tifa._finish_scope() + # Leave the body + self.tifa.scope_chain.pop(0) + # Restore the scope + self.tifa.scope_chain = self.old_scope + +print("Phase {}: {} secs".format("Tifa.Tifa Evaled", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() \ No newline at end of file diff --git a/src/lib/pedal/tifa/type_definitions.py b/src/lib/pedal/tifa/type_definitions.py new file mode 100644 index 0000000000..0535adfbe2 --- /dev/null +++ b/src/lib/pedal/tifa/type_definitions.py @@ -0,0 +1,514 @@ +def are_literals_equal(first, second): + if first is None or second is None: + return False + elif not isinstance(first, type(second)): + return False + else: + if isinstance(first, LiteralTuple): + if len(first.value) != len(second.value): + return False + for l, s in zip(first.value, second.value): + if not are_literals_equal(l, s): + return False + return True + elif not isinstance(first, LiteralValue): + return True + else: + return first.value == second.value + + +class LiteralValue: + """ + A special literal representation of a value, used to represent access on + certain container types. + """ + + def __init__(self, value): + self.value = value + + +class LiteralNum(LiteralValue): + """ + Used to capture indexes of containers. + """ + + def type(self): + return NumType() + + +class LiteralBool(LiteralValue): + def type(self): + return BoolType() + + +class LiteralStr(LiteralValue): + def type(self): + return StrType() + + +class LiteralTuple(LiteralValue): + def type(self): + return TupleType(self.value) + + +class LiteralNone(LiteralValue): + def type(self): + return LiteralNone() + + +def literal_from_json(val): + if val['type'] == 'LiteralStr': + return LiteralStr(val['value']) + elif val['type'] == 'LiteralNum': + return LiteralNum(val['value']) + elif val['type'] == 'LiteralBool': + return LiteralBool(val['value']) + + +def _dict_extends(d1, d2): + """ + Helper function to create a new dictionary with the contents of the two + given dictionaries. Does not modify either dictionary, and the values are + copied shallowly. If there are repeates, the second dictionary wins ties. + + The function is written to ensure Skulpt compatibility. + + Args: + d1 (dict): The first dictionary + d2 (dict): The second dictionary + """ + d3 = {} + for key, value in d1.items(): + d3[key] = value + for key, value in d2.items(): + d3[key] = value + return d3 + + +class Type: + """ + Parent class for all other types, used to provide a common interface. + + TODO: Handle more complicated object-oriented types and custom types + (classes). + """ + fields = {} + immutable = False + singular_name = 'a type' + + def clone(self): + return self.__class__() + + def __str__(self): + return str(self.__class__.__name__) + + def clone_mutably(self): + if self.immutable: + return self.clone() + else: + return self + + def index(self, i): + return self.clone() + + def load_attr(self, attr, tifa, callee=None, callee_position=None): + if attr in self.fields: + return self.fields[attr] + # TODO: Handle more kinds of common mistakes + if attr == "append": + tifa.report_issue('Append to non-list', + {'name': tifa.identify_caller(callee), + 'position': callee_position, 'type': self}) + return UnknownType() + + def is_empty(self): + return True + + def is_equal(self, other): + # TODO: Make this more sophisticated! + if type(self) not in TYPE_LOOKUPS: + return False + return other in TYPE_LOOKUPS[type(self)] + + def is_instance(self, other): + # TODO: Implement this correctly + return self.is_equal(other) + + +class UnknownType(Type): + """ + A special type used to indicate an unknowable type. + """ + + +class RecursedType(Type): + """ + A special type used as a placeholder for the result of a + recursive call that we have already process. This type will + be dominated by any actual types, but will not cause an issue. + """ + + +class FunctionType(Type): + """ + + Special values for `returns`: + identity: Returns the first argument's type + element: Returns the first argument's first element's type + void: Returns the NoneType + """ + singular_name = 'a function' + + def __init__(self, definition=None, name="*Anonymous", returns=None): + if returns is not None and definition is None: + if returns == 'identity': + def definition(ti, ty, na, args, ca): + if args: + return args[0].clone() + return UnknownType() + elif returns == 'element': + def definition(ti, ty, na, args, ca): + if args: + return args[0].index(0) + return UnknownType() + elif returns == 'void': + def definition(ti, ty, na, args, ca): + return NoneType() + else: + def definition(ti, ty, na, args, ca): + return returns.clone() + self.definition = definition + self.name = name + + +class ClassType(Type): + singular_name = 'a class' + + def __init__(self, name): + self.name = name + self.fields = {} + self.scope_id = None + + def add_attr(self, name, type): + self.fields[name] = type + + def get_constructor(self): + i = InstanceType(self) + return FunctionType(name='__init__', returns=i) + + def clone(self): + return ClassType(self.name) + + +class InstanceType(Type): + def __init__(self, parent): + self.parent = parent + self.fields = parent.fields + + def __str__(self): + return "InstanceTypeOf" + str(self.parent.name) + + def clone(self): + return InstanceType(self.parent) + + def add_attr(self, name, type): + # TODO: What if this is a type change? + self.parent.add_attr(name, type) + + +class NumType(Type): + singular_name = 'a number' + immutable = True + + def index(self, i): + return UnknownType() + + +class NoneType(Type): + singular_name = 'a None' + immutable = True + + +class BoolType(Type): + singular_name = 'a boolean' + immutable = True + + +class TupleType(Type): + """ + """ + singular_name = 'a tuple' + + def __init__(self, subtypes=None): + if subtypes is None: + subtypes = [] + self.subtypes = subtypes + + def index(self, i): + if isinstance(i, LiteralNum): + return self.subtypes[i.value].clone() + else: + return self.subtypes[i].clone() + + def clone(self): + return TupleType([t.clone() for t in self.subtypes]) + + immutable = True + + +class ListType(Type): + singular_name = 'a list' + + def __init__(self, subtype=None, empty=True): + if subtype is None: + subtype = UnknownType() + self.subtype = subtype + self.empty = empty + + def index(self, i): + return self.subtype.clone() + + def clone(self): + return ListType(self.subtype.clone(), self.empty) + + def load_attr(self, attr, tifa, callee=None, callee_position=None): + if attr == 'append': + def _append(tifa, function_type, callee, args, position): + if args: + cloned_type = ListType(subtype=args[0].clone(), + empty=False) + if callee: + tifa.append_variable(callee, cloned_type, position) + self.empty = False + self.subtype = args[0] + + return FunctionType(_append, 'append') + return Type.load_attr(self, attr, tifa, callee, callee_position) + + def is_empty(self): + return self.empty + + +class StrType(Type): + singular_name = 'a string' + + def __init__(self, empty=False): + self.empty = empty + + def index(self, i): + return StrType() + + def is_empty(self): + return self.empty + + fields = _dict_extends(Type.fields, {}) + immutable = True + + +StrType.fields.update({ + # Methods that return strings + "capitalize": FunctionType(name='capitalize', returns=StrType()), + "center": FunctionType(name='center', returns=StrType()), + "expandtabs": FunctionType(name='expandtabs', returns=StrType()), + "join": FunctionType(name='join', returns=StrType()), + "ljust": FunctionType(name='ljust', returns=StrType()), + "lower": FunctionType(name='lower', returns=StrType()), + "lstrip": FunctionType(name='lstrip', returns=StrType()), + "replace": FunctionType(name='replace', returns=StrType()), + "rjust": FunctionType(name='rjust', returns=StrType()), + "rstrip": FunctionType(name='rstrip', returns=StrType()), + "strip": FunctionType(name='strip', returns=StrType()), + "swapcase": FunctionType(name='swapcase', returns=StrType()), + "title": FunctionType(name='title', returns=StrType()), + "translate": FunctionType(name='translate', returns=StrType()), + "upper": FunctionType(name='upper', returns=StrType()), + "zfill": FunctionType(name='zfill', returns=StrType()), + # Methods that return numbers + "count": FunctionType(name='count', returns=NumType()), + "find": FunctionType(name='find', returns=NumType()), + "rfind": FunctionType(name='rfind', returns=NumType()), + "index": FunctionType(name='index', returns=NumType()), + "rindex": FunctionType(name='rindex', returns=NumType()), + # Methods that return booleans + "startswith": FunctionType(name='startswith', returns=BoolType()), + "endswith": FunctionType(name='endswith', returns=BoolType()), + "isalnum": FunctionType(name='isalnum', returns=BoolType()), + "isalpha": FunctionType(name='isalpha', returns=BoolType()), + "isdigit": FunctionType(name='isdigit', returns=BoolType()), + "islower": FunctionType(name='islower', returns=BoolType()), + "isspace": FunctionType(name='isspace', returns=BoolType()), + "istitle": FunctionType(name='istitle', returns=BoolType()), + "isupper": FunctionType(name='isupper', returns=BoolType()), + # Methods that return List of Strings + "rsplit": FunctionType(name='rsplit', returns=ListType(StrType(), empty=False)), + "split": FunctionType(name='split', returns=ListType(StrType(), empty=False)), + "splitlines": FunctionType(name='splitlines', returns=ListType(StrType(), empty=False)) +}) + + +class FileType(Type): + singular_name = 'a file' + + def index(self, i): + return StrType() + + fields = _dict_extends(Type.fields, { + 'close': FunctionType(name='close', returns='void'), + 'read': FunctionType(name='read', returns=StrType()), + 'readlines': FunctionType(name='readlines', returns=ListType(StrType(), False)) + }) + + def is_empty(self): + return False + + +class DictType(Type): + singular_name = 'a dictionary' + + def __init__(self, empty=False, literals=None, keys=None, values=None): + self.empty = empty + self.literals = literals + self.values = values + self.keys = keys + + def clone(self): + return DictType(self.empty, self.literals, self.keys, self.values) + + def is_empty(self): + return self.empty + + def index(self, i): + if self.empty: + return UnknownType() + elif self.literals is not None: + for literal, value in zip(self.literals, self.values): + if are_literals_equal(literal, i): + return value.clone() + return UnknownType() + else: + return self.keys.clone() + + def load_attr(self, attr, tifa, callee=None, callee_position=None): + if attr == 'items': + def _items(tifa, function_type, callee, args, position): + if self.literals is None: + return ListType(TupleType([self.keys, self.values]), + empty=False) + else: + return ListType(TupleType([self.literals[0].type(), + self.values[0]]), + empty=False) + + return FunctionType(_items, 'items') + elif attr == 'keys': + def _keys(tifa, function_type, callee, args, position): + if self.literals is None: + return ListType(self.keys, empty=False) + else: + return ListType(self.literals[0].type(), empty=False) + + return FunctionType(_keys, 'keys') + elif attr == 'values': + def _values(tifa, function_type, callee, args, position): + if self.literals is None: + return ListType(self.values, empty=False) + else: + return ListType(self.values[0], empty=False) + + return FunctionType(_values, 'values') + return Type.load_attr(self, attr, tifa, callee, callee_position) + + +class ModuleType(Type): + singular_name = 'a module' + + def __init__(self, name="*UnknownModule", submodules=None, fields=None): + self.name = name + if submodules is None: + submodules = {} + self.submodules = submodules + if fields is None: + fields = {} + self.fields = fields + + +class SetType(ListType): + singular_name = 'a set' + + +class GeneratorType(ListType): + singular_name = 'a generator' + + +# Custom parking class in blockpy + +class TimeType(Type): + singular_name = 'a time of day' + + +class DayType(Type): + singular_name = 'a day of the week' + + +try: + from numbers import Number +except Exception: + Number = int + +TYPE_LOOKUPS = { + FunctionType: ('function', FunctionType, 'FunctionType'), + ClassType: ('class', ClassType, 'ClassType'), + InstanceType: ('instance', InstanceType, 'InstanceType'), + NumType: ('num', int, float, complex, NumType, Number, 'NumType'), + BoolType: ('bool', bool, BoolType, 'BoolType'), + NoneType: ('None', None, NoneType, 'NoneType'), + TupleType: ('tuple', tuple, TupleType, 'TupleType'), + ListType: ('list', list, ListType, 'ListType'), + StrType: ('str', str, StrType, 'StrType'), + FileType: ('file', FileType, 'FileType'), + DictType: ('dict', dict, DictType, 'DictType'), + SetType: ('set', set, SetType, 'SetType'), +} + + +def type_from_json(val): + if val['type'] == 'DictType': + values = [type_from_json(v) for v in val['values']] + empty = val.get('empty', None) + if 'literals' in val: + literals = [literal_from_json(l) for l in val['literals']] + return DictType(empty, literals=literals, values=values) + else: + keys = [type_from_json(k) for k in val['keys']] + return DictType(empty, keys=keys, values=values) + elif val['type'] == 'ListType': + return ListType(type_from_json(val.get('subtype', None)), + val.get('empty', None)) + elif val['type'] == 'StrType': + return StrType(val.get('empty', None)) + elif val['type'] == 'BoolType': + return BoolType() + elif val['type'] == 'NoneType': + return NoneType() + elif val['type'] == 'NumType': + return NumType() + elif val['type'] == 'ModuleType': + submodules = {name: type_from_json(m) + for name, m in val.get('submodules', {}).items()} + fields = {name: type_from_json(m) + for name, m in val.get('fields', {}).items()} + return ModuleType(name=val.get('name'), submodules=submodules, + fields=fields) + elif val['type'] == 'FunctionType': + returns = type_from_json(val.get('returns', {'type': 'NoneType'})) + return FunctionType(name=val.get('name'), returns=returns) + + +def type_to_literal(type): + if isinstance(type, NumType): + return LiteralNum(0) + elif isinstance(type, StrType): + return LiteralStr("") + else: + # TODO: Finish the mapping + return LiteralStr("") diff --git a/src/lib/pedal/tifa/type_operations.py b/src/lib/pedal/tifa/type_operations.py new file mode 100644 index 0000000000..21021e13c2 --- /dev/null +++ b/src/lib/pedal/tifa/type_operations.py @@ -0,0 +1,136 @@ +import ast + +from pedal.tifa.type_definitions import (UnknownType, NumType, BoolType, + TupleType, ListType, StrType, + DictType, SetType, GeneratorType, + DayType, TimeType) + + +def merge_types(left, right): + # TODO: Check that lists/sets have the same subtypes + if isinstance(left, (ListType, SetType, GeneratorType)): + if left.empty: + return right.subtype + else: + return left.subtype.clone() + elif isinstance(left, TupleType): + return left.subtypes + right.subtypes + + +def NumType_any(*x): + return NumType() + + +def StrType_any(*x): + return StrType() + + +def BoolType_any(*x): + return BoolType() + + +def keep_left(left, right): + return left + + +def keep_right(left, right): + return right + + +VALID_BINOP_TYPES = { + ast.Add: {NumType: {NumType: NumType_any}, + StrType: {StrType: StrType_any}, + ListType: {ListType: merge_types}, + TupleType: {TupleType: merge_types}}, + ast.Sub: {NumType: {NumType: NumType_any}, + SetType: {SetType: merge_types}}, + ast.Div: {NumType: {NumType: NumType_any}}, + ast.FloorDiv: {NumType: {NumType: NumType_any}}, + ast.Mult: {NumType: {NumType: NumType_any, + StrType: StrType_any, + ListType: keep_right, + TupleType: keep_right}, + StrType: {NumType: StrType_any}, + ListType: {NumType: keep_left}, + TupleType: {NumType: keep_left}}, + ast.Pow: {NumType: {NumType: NumType_any}}, + # TODO: Should we allow old-fashioned string interpolation? + # Currently, I vote no because it makes the code harder and is bad form. + ast.Mod: {NumType: {NumType: NumType_any}}, + ast.LShift: {NumType: {NumType: NumType_any}}, + ast.RShift: {NumType: {NumType: NumType_any}}, + ast.BitOr: {NumType: {NumType: NumType_any}, + BoolType: {NumType: NumType_any, + BoolType: BoolType_any}, + SetType: {SetType: merge_types}}, + ast.BitXor: {NumType: {NumType: NumType_any}, + BoolType: {NumType: NumType_any, + BoolType: BoolType_any}, + SetType: {SetType: merge_types}}, + ast.BitAnd: {NumType: {NumType: NumType_any}, + BoolType: {NumType: NumType_any, + BoolType: BoolType_any}, + SetType: {SetType: merge_types}} +} +VALID_UNARYOP_TYPES = { + ast.UAdd: {NumType: NumType}, + ast.USub: {NumType: NumType}, + ast.Invert: {NumType: NumType} +} + + +def are_types_equal(left, right): + """ + Determine if two types are equal. + + This could be more Polymorphic - move the code for each type into + its respective class instead. + """ + if left is None or right is None: + return False + elif isinstance(left, UnknownType) or isinstance(right, UnknownType): + return False + elif not isinstance(left, type(right)): + return False + elif isinstance(left, (GeneratorType, ListType)): + if left.empty or right.empty: + return True + else: + return are_types_equal(left.subtype, right.subtype) + elif isinstance(left, TupleType): + if left.empty or right.empty: + return True + elif len(left.subtypes) != len(right.subtypes): + return False + else: + for l, r in zip(left.subtypes, right.subtypes): + if not are_types_equal(l, r): + return False + return True + elif isinstance(left, DictType): + if left.empty or right.empty: + return True + elif left.literals is not None and right.literals is not None: + if len(left.literals) != len(right.literals): + return False + else: + for l, r in zip(left.literals, right.literals): + if not are_types_equal(l, r): + return False + for l, r in zip(left.values, right.values): + if not are_types_equal(l, r): + return False + return True + elif left.literals is not None or right.literals is not None: + return False + else: + keys_equal = are_types_equal(left.keys, right.keys) + values_equal = are_types_equal(left.values, right.values) + return keys_equal and values_equal + else: + return True + + +ORDERABLE_TYPES = (NumType, BoolType, StrType, ListType, DayType, TimeType, + SetType, TupleType) +INDEXABLE_TYPES = (StrType, ListType, SetType, TupleType, DictType) diff --git a/src/lib/pedal/toolkit/__init__.py b/src/lib/pedal/toolkit/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/lib/pedal/toolkit/files.py b/src/lib/pedal/toolkit/files.py new file mode 100644 index 0000000000..e8edf6c73f --- /dev/null +++ b/src/lib/pedal/toolkit/files.py @@ -0,0 +1,56 @@ +from pedal.cait.cait_api import parse_program +from pedal.report.imperative import explain +from pedal.toolkit.utilities import ensure_literal + + +def files_not_handled_correctly(*filenames): + """ + Statically detect if files have been opened and closed correctly. + This is only useful in the case of very simplistic file handling. + """ + if filenames and isinstance(filenames[0], int): + num_filenames = filenames[0] + actual_filenames = False + else: + num_filenames = len(filenames) + actual_filenames = True + ast = parse_program() + calls = ast.find_all("Call") + called_open = [] + closed = [] + for a_call in calls: + if a_call.func.ast_name == 'Name': + if a_call.func.id == 'open': + if not a_call.args: + explain("You have called the open function " + "without any arguments. It needs a filename.") + return True + called_open.append(a_call) + elif a_call.func.id == 'close': + explain("You have attempted to call close as a " + "function, but it is actually a method of the " + "file object.", 'verifier') + return True + elif a_call.func.ast_name == 'Attribute': + if a_call.func.attr == 'open': + explain("You have attempted to call open as a " + "method, but it is actually a built-in function.") + return True + elif a_call.func.attr == 'close': + closed.append(a_call) + if len(called_open) < num_filenames: + explain("You have not opened all the files you were supposed to.") + return True + elif len(called_open) > num_filenames: + explain("You have opened more files than you were supposed to.") + return True + withs = ast.find_all("With") + if len(withs) + len(closed) < num_filenames: + explain("You have not closed all the files you were supposed to.") + return True + elif len(withs) + len(closed) > num_filenames: + explain("You have closed more files than you were supposed to.") + return True + if actual_filenames: + return ensure_literal(*filenames) + return False diff --git a/src/lib/pedal/toolkit/functions.py b/src/lib/pedal/toolkit/functions.py new file mode 100644 index 0000000000..a4a92da1e6 --- /dev/null +++ b/src/lib/pedal/toolkit/functions.py @@ -0,0 +1,343 @@ +from pedal.cait.cait_api import parse_program +from pedal.report.imperative import gently, explain, MAIN_REPORT +from pedal.sandbox import compatibility +import ast + +DELTA = 0.001 + + +def all_documented(): + ast = parse_program() + defs = ast.find_all('FunctionDef') + ast.find_all("ClassDef") + for a_def in defs: + if a_def.name == "__init__": + continue + if (a_def.body and + (a_def.body[0].ast_name != "Expr" or + a_def.body[0].value.ast_name != "Str")): + if a_def.ast_name == 'FunctionDef': + gently("You have an undocumented function: " + a_def.name) + else: + gently("You have an undocumented class: " + a_def.name) + return False + return True + + +def get_arg_name(node): + name = node.id + if name is None: + return node.arg + else: + return name + + +def match_function(name, root=None): + if root is None: + ast = parse_program() + else: + ast = root + defs = ast.find_all('FunctionDef') + for a_def in defs: + if a_def._name == name: + return a_def + return None + +def match_signature_muted(name, length, *parameters): + ast = parse_program() + defs = ast.find_all('FunctionDef') + for a_def in defs: + if a_def._name == name: + found_length = len(a_def.args.args) + if found_length != length: + return None + elif parameters: + for parameter, arg in zip(parameters, a_def.args.args): + arg_name = get_arg_name(arg) + if arg_name != parameter: + return None + else: + return a_def + else: + return a_def + return None + + +def match_signature(name, length, *parameters): + ast = parse_program() + defs = ast.find_all('FunctionDef') + for a_def in defs: + if a_def._name == name: + found_length = len(a_def.args.args) + if found_length < length: + gently("The function named {} has fewer parameters ({}) than expected ({}). " + "

(insuff_args)".format(name, found_length, length)) + elif found_length > length: + gently("The function named {} has more parameters ({}) than expected ({}). " + "

(excess_args)".format(name, found_length, length)) + elif parameters: + for parameter, arg in zip(parameters, a_def.args.args): + arg_name = get_arg_name(arg) + if arg_name != parameter: + gently("Error in definition of {}. Expected a parameter named {}, instead " + "found {}.

(name_missing)".format(name, parameter, arg_name)) + return None + else: + return a_def + else: + return a_def + else: + gently("No function named {name} was found." + "

(missing_func_{name})".format(name=name)) + return None + + +GREEN_CHECK = "✔" +RED_X = "❌" + + +def output_test(name, *tests): + student = compatibility.get_student_data() + if name in student.data: + the_function = student.data[name] + if callable(the_function): + result = ("" + "" + ) + success = True + success_count = 0 + for test in tests: + inp = test[:-1] + inputs = ', '.join(["{}".format(repr(i)) for i in inp]) + out = test[-1] + tip = "" + if isinstance(out, tuple): + tip = out[1] + out = out[0] + message = "" + ("" * 2) + test_out = compatibility.capture_output(the_function, *inp) + if isinstance(out, str): + if len(test_out) < 1: + message = message.format(inputs, repr(out), "No output", tip) + message = "" + RED_X + message + "" + if tip: + message += "" + success = False + elif len(test_out) > 1: + message = message.format(inputs, repr(out), "Too many outputs", tip) + message = "" + RED_X + message + "" + if tip: + message += "" + success = False + elif out not in test_out: + message = message.format(inputs, repr(out), repr(test_out[0]), tip) + message = "" + RED_X + message + "" + if tip: + message += "" + success = False + else: + message = message.format(inputs, repr(out), repr(test_out[0]), tip) + message = "" + GREEN_CHECK + message + "" + success_count += 1 + elif out != test_out: + if len(test_out) < 1: + message = message.format(inputs, repr(out), "No output", tip) + else: + message = message.format(inputs, repr(out), repr(test_out[0]), tip) + message = "" + RED_X + message + "" + if tip: + message += "" + success = False + else: + message = message.format(inputs, repr(out), repr(test_out[0]), tip) + message = "" + GREEN_CHECK + message + "" + success_count += 1 + result += message + if success: + return the_function + else: + result = ("I ran your function {} on some new arguments, and it gave the wrong output " + "{}/{} times.".format(name, len(tests) - success_count, len(tests)) + result) + gently(result + "
ArgumentsExpectedActual
{}
{}
" + tip + "
" + tip + "
" + tip + "
" + tip + "
") + return None + else: + gently("You defined {}, but did not define it as a function." + "

(not_func_def)".format(name)) + return None + else: + gently("The function {} was not defined.

(no_func_def)".format(name)) + return None + + +def unit_test(name, *tests): + """ + Show a table + :param name: + :param tests: + :return: + """ + student = compatibility.get_student_data() + if name in student.data: + the_function = student.data[name] + if callable(the_function): + result = ("" + "" + ) + success = True + success_count = 0 + for test in tests: + inp = test[:-1] + inputs = ', '.join(["{}".format(repr(i)) for i in inp]) + out = test[-1] + tip = "" + if isinstance(out, tuple): + tip = out[1] + out = out[0] + message = ("" * 3) + test_out = the_function(*inp) + message = message.format(inputs, repr(test_out), repr(out)) + if (isinstance(out, float) and + isinstance(test_out, (float, int)) and + abs(out - test_out) < DELTA): + message = "" + GREEN_CHECK + message + "" + success_count += 1 + elif out != test_out: + # gently(message) + message = "" + RED_X + message + "" + if tip: + message += "" + success = False + else: + message = "" + GREEN_CHECK + message + "" + success_count += 1 + result += message + if success: + return the_function + else: + result = "I ran your function {} on some new arguments, " \ + "and it failed {}/{} tests.".format(name, len(tests) - success_count, len(tests)) + result + gently(result + "
ArgumentsReturnedExpected
{}
" + tip + "
") + return None + else: + gently("You defined {}, but did not define it as a function.".format(name)) + return None + else: + gently("The function {} was not defined.".format(name)) + return None + + +class _LineVisitor(ast.NodeVisitor): + """ + NodeVisitor subclass that visits every statement of a program and tracks + their line numbers in a list. + + Attributes: + lines (list[int]): The list of lines that were visited. + """ + + def __init__(self): + self.lines = [] + + def _track_lines(self, node): + self.lines.append(node.lineno) + self.generic_visit(node) + + visit_FunctionDef = _track_lines + visit_AsyncFunctionDef = _track_lines + visit_ClassDef = _track_lines + visit_Return = _track_lines + visit_Delete = _track_lines + visit_Assign = _track_lines + visit_AugAssign = _track_lines + visit_AnnAssign = _track_lines + visit_For = _track_lines + visit_AsyncFor = _track_lines + visit_While = _track_lines + visit_If = _track_lines + visit_With = _track_lines + visit_AsyncWith = _track_lines + visit_Raise = _track_lines + visit_Try = _track_lines + visit_Assert = _track_lines + visit_Import = _track_lines + visit_ImportFrom = _track_lines + visit_Global = _track_lines + visit_Nonlocal = _track_lines + visit_Expr = _track_lines + visit_Pass = _track_lines + visit_Continue = _track_lines + visit_Break = _track_lines + + +def check_coverage(report=None): + """ + Checks that all the statements in the program have been executed. + This function only works when a tracer_style has been set in the sandbox, + or you are using an environment that automatically traces calls (e.g., + BlockPy). + + TODO: Make compatible with tracer_style='coverage' + + Args: + report (Report): The Report to draw source code from; if not given, + defaults to MAIN_REPORT. + Returns: + bool or set[int]: If the source file was not parsed, None is returned. + If there were fewer lines traced in execution than are found in + the AST, then the set of unexecuted lines are returned. Otherwise, + False is returned. + """ + if report is None: + report = MAIN_REPORT + if not report['source']['success']: + return None, 0 + lines_executed = set(compatibility.trace_lines()) + if -1 in lines_executed: + lines_executed.remove(-1) + student_ast = report['source']['ast'] + visitor = _LineVisitor() + visitor.visit(student_ast) + lines_in_code = set(visitor.lines) + if lines_executed < lines_in_code: + return lines_in_code - lines_executed, len(lines_executed)/len(lines_in_code) + else: + return False, 1 + +def ensure_coverage(percentage=.5, destructive=False, report=None): + ''' + Note that this avoids destroying the current sandbox instance stored on the + report, if there is one present. + + Args: + destructive (bool): Whether or not to remove the sandbox. + ''' + if report is None: + report = MAIN_REPORT + student_code = report['source']['code'] + unexecuted_lines, percent_covered = check_coverage(report) + if unexecuted_lines: + if percent_covered <= percentage: + gently("Your code coverage is not adequate. You must cover at least half your code to receive feedback.") + return False + return True + +def ensure_cisc108_tests(test_count, report=None): + student = compatibility.get_student_data() + if 'assert_equal' not in student.data: + gently("You have not imported assert_equal from the cisc108 module.") + return False + assert_equal = student.data['assert_equal'] + if not hasattr(assert_equal, 'student_tests'): + gently("The assert_equal function has been modified. Do not let it be overwritten!", + label="Assertion Function Corrupted") + return False + student_tests = assert_equal.student_tests + if student_tests.tests == 0: + gently("You are not unit testing the result.", label="No Student Unit Tests") + return False + elif student_tests.tests < test_count: + gently("You have not written enough unit tests.", label="Not Enough Student Unit Tests") + return False + elif student_tests.failures > 0: + gently("Your unit tests are not passing.", label="Student Unit Tests Failing") + return False + return True diff --git a/src/lib/pedal/toolkit/imports.py b/src/lib/pedal/toolkit/imports.py new file mode 100644 index 0000000000..a5e352ea97 --- /dev/null +++ b/src/lib/pedal/toolkit/imports.py @@ -0,0 +1,25 @@ +from pedal.cait.cait_api import parse_program +from pedal.report.imperative import explain + + +def ensure_imports(*modules): + ast = parse_program() + for module in modules: + imports = ast.find_all("Import") + import_froms = ast.find_all("ImportFrom") + if not imports and not import_froms: + explain("You need to import the {} module.".format(module)) + return True + success = False + if imports: + if any(alias._name == module + for i in imports + for alias in i.names): + success = True + if import_froms: + if any(i.module == module for i in import_froms): + success = True + if not success: + explain("You need to import the {} module.".format(module)) + return True + return False diff --git a/src/lib/pedal/toolkit/plotting.py b/src/lib/pedal/toolkit/plotting.py new file mode 100644 index 0000000000..a1a932db57 --- /dev/null +++ b/src/lib/pedal/toolkit/plotting.py @@ -0,0 +1,129 @@ +from pedal.toolkit.utilities import function_is_called +from pedal.cait.cait_api import parse_program, def_use_error +from pedal.report.imperative import explain, gently +from pedal.sandbox import compatibility + +PLOT_LABEL = {'plot': 'line plot', + 'hist': 'histogram', + 'scatter': 'scatter plot'} + + +def prevent_incorrect_plt(): + ast = parse_program() + plts = [n for n in ast.find_all("Name") if n.id == 'plt'] + if plts and def_use_error(plts[0]): + explain("You have imported the matplotlib.pyplot module, " + "but you did not rename it to plt using " + "import matplotlib.pyplot as plt.

(plt_rename_err)", 'verifier') + return True + matplotlib_names = ['plot', 'hist', 'scatter', + 'title', 'xlabel', 'ylabel', 'show'] + for name in matplotlib_names: + for n in ast.find_all("Name"): + if n.id == name: + if def_use_error(n): + explain(("You have attempted to use the MatPlotLib " + "function named {0}. However, you " + "imported MatPlotLib in a way that does not " + "allow you to use the function directly. I " + "recommend you use plt.{0} instead, " + "after you use import matplotlib.pyplot as " + "plt.

(plt_wrong_import)").format(name), 'verifier') + return True + return False + + +def ensure_correct_plot(function_name): + for a_plot, label in PLOT_LABEL.items(): + if function_name == a_plot: + if not function_is_called(function_name): + gently("You are not calling the {func_name} function." + "

(no_{func_name}_call)".format(func_name=function_name)) + return True + elif function_is_called(a_plot): + gently("You have called the {} function, which makes a {}." + "

(wrong_plt)".format(a_plot, label)) + return True + return False + + +def ensure_show(): + if not function_is_called("show"): + gently("You have not called show function, which " + "actually creates the graph.

(no_show)") + return True + return False + + +def compare_data(plt_type, correct, given): + """ + Determines whether the given data matches any of the data found in the + correct data. This handles plots of different types: if a histogram + was plotted with the expected data for a line plot, it will return True. + + Args: + plt_type (str): The expected type of this plot + correct (List of Int or List of List of Int): The expected data. + given (Dict): The actual plotted data and information + Returns: + bool: Whether the correct data was found in the given plot. + """ + # Infer arguments + if plt_type == 'hist': + correct_xs = None + correct_ys = correct + elif not correct: + correct_xs = [] + correct_ys = [] + elif isinstance(correct[0], (tuple, list)): + # We were given a list of lists of ints + correct_xs, correct_ys = correct + else: + # Assume it is a singular list + correct_xs = list(range(len(correct))) + correct_ys = correct + + if given['type'] == 'hist': + return correct_ys == given['values'] + elif plt_type == 'hist': + return correct_ys == given['y'] + else: + return correct_xs == given['x'] and correct_ys == given['y'] + + +GRAPH_TYPES = {'line': 'line plot', + 'hist': 'histogram', + 'scatter': 'scatter plot'} + + +def check_for_plot(plt_type, data): + """ + Returns any errors found for this plot type and data. + In other words, if it returns False, the plot was found correctly. + """ + if plt_type == 'plot': + plt_type = 'line' + type_found = False + data_found = False + for graph in compatibility.get_plots(): + for a_plot in graph['data']: + data_found_here = compare_data(plt_type, data, a_plot) + if a_plot['type'] == plt_type and data_found_here: + return False + if a_plot['type'] == plt_type: + type_found = True + if data_found_here: + data_found = True + plt_type = GRAPH_TYPES.get(plt_type, plt_type) + if type_found and data_found: + return ("You have created a {}, but it does not have the right data. That data appears to have been plotted " + "in another graph.

(other_plt)".format(plt_type)) + elif type_found: + return ("You have created a {}, but it does not have the right data." + "

(wrong_plt_data)".format(plt_type)) + elif data_found: + return ("You have plotted the right data, but you appear to have not plotted it as a {}." + "

(wrong_plt_type)".format(plt_type)) + else: + return ("You have not created a {} with the proper data." + "

(no_plt)".format(plt_type)) diff --git a/src/lib/pedal/toolkit/printing.py b/src/lib/pedal/toolkit/printing.py new file mode 100644 index 0000000000..f045f4c506 --- /dev/null +++ b/src/lib/pedal/toolkit/printing.py @@ -0,0 +1,22 @@ +from pedal.report.imperative import gently +from pedal.toolkit.utilities import find_function_calls, is_top_level + + +def ensure_prints(count): + prints = find_function_calls('print') + if not prints: + gently("You are not using the print function!

(no_print)") + return False + elif len(prints) > count: + gently("You are printing too many times!

(multiple_print)") + return False + elif len(prints) < count: + gently("You are not printing enough things!

(too_few_print)") + return False + else: + for a_print in prints: + if not is_top_level(a_print): + gently("You have a print function that is not at the top level. That is incorrect for this problem!" + "

(not_top_level_print)") + return False + return prints diff --git a/src/lib/pedal/toolkit/signatures.py b/src/lib/pedal/toolkit/signatures.py new file mode 100644 index 0000000000..e08c4d9838 --- /dev/null +++ b/src/lib/pedal/toolkit/signatures.py @@ -0,0 +1,344 @@ +import re + +from pedal.cait.cait_api import parse_program +from pedal.report.imperative import gently, explain + +""" +Verify indentation + +Format: + + +Any number of text. One final newline separates the next section. + +If line is "Args:" or "Returns:" + Next line will be a "param (type): Description" or "type: Description" + If the next line is indented more than current level, then it is part of the previous part's description. + Otherwise, new entry + +"Note:" + Any level of indentation indicates +""" + +PRIMITIVES = { + 'text': ['text'], + 'str': ['string', 'str', 'unicode'], + 'bytes': ['bytes'], + 'io': ['io'], + 'file': ['file'], + 'num': ['number', 'num', 'numeric'], + 'int': ['int', 'integer'], + 'float': ['float', 'floating'], + 'bool': ['bool', 'boolean'], + 'none': ['none'], + 'any': ['any'] +} +NORMALIZE_PRIMITIVES = {synonym: formal + for formal, synonyms in PRIMITIVES.items() + for synonym in synonyms} +CONTAINERS = { + 'list': (1, ['list']), + 'set': (1, ['set']), + 'optional': (1, ['optional', 'maybe']), + 'dict': (2, ['dict', 'dictionary']), + 'callable': (2, ['callable', 'function', 'func']), + 'union': ('*', ['union', 'itemization']), + 'tuple': ('*', ['tuple', 'pair']), +} +NORMALIZE_CONTAINERS = {synonym: formal + for formal, (length, synonyms) in CONTAINERS.items() + for synonym in synonyms} + +INHERITANCE = { + 'int': 'num', + 'float': 'num', + 'bool': 'num', + 'str': 'text', + 'bytes': 'text', + 'list': 'iterable', + 'tuple': 'iterable', + 'set': 'iterable', + 'dict': 'iterable', + 'file': 'iterable', + 'text': 'iterable' +} + +SPECIAL_PARAMETERS = ["_returns", "yields", "prints", "_raises", + "_report", "_root"] + +''' +Type validation: + Caps does not matter + Primitives: + Containers + Unions + X or Y + X, Y, or Z + X, Y, Z + Function + (X -> Y) + + list[int, str, or bool], dict[int: str], or bool or int +''' + + +class SignatureException(Exception): + pass + + +class Stack: + def __init__(self, identifier="union"): + self.body = [] + self.identifier = identifier + + def append(self, value): + self.body.append(value) + + def __repr__(self): + return "{}[{}]".format(self.identifier, ", ".join(map(repr, self.body))) + + def __hash__(self): + return hash(tuple(self.identifier, self.body)) + + def __lt__(self, other): + if isinstance(other, Stack): + return self.identifier < other.identifier and self.body < other.body + return self.identifier < other + + def __gt__(self, other): + if isinstance(other, Stack): + return self.identifier > other.identifier and self.body > other.body + return self.identifier > other + + def __eq__(self, other): + if isinstance(other, Stack): + return self.identifier == other.identifier and self.body == other.body + return False + + +def _normalize_identifier(identifier): + if identifier in NORMALIZE_PRIMITIVES: + return NORMALIZE_PRIMITIVES[identifier] + elif identifier in NORMALIZE_CONTAINERS: + return NORMALIZE_CONTAINERS[identifier] + else: + return identifier + + +SPECIAL_SYMBOLS = r"\s*(->|\s*[\[\],\(\)\:]|or)\s*" + + +def _parse_tokens(tokens): + result_stack = [Stack()] + tokens = list(reversed(list(tokens))) + while tokens: + current = tokens.pop() + # Ending a parenthetical, better stop here. + if current == ")": + subexpression = result_stack.pop() + result_stack[-1].append(subexpression) + # Ending a square bracket, better stop here. + elif current == "]": + subexpression = result_stack.pop() + result_stack[-1].append(subexpression) + # We've reached the last token! + elif not tokens: + # And had no tokens before this one + # Return the set of tokens + result_stack[-1].append(_normalize_identifier(current)) + # Starting a parentheized expression + elif current == "(": + result_stack.append(Stack()) + # Nullary function + elif current == "->": + result_stack[-1].append(Stack("callable")) + elif current in ("or", ",", ":"): + pass + else: + next = tokens.pop() + # X or ... + if current == "," and next == "or": + tokens.append(next) + if next in ("or", ",", "->", ":"): + result_stack[-1].append(_normalize_identifier(current)) + # X [ ... + elif next == "[": + result_stack.append(Stack(_normalize_identifier(current))) + else: + tokens.append(next) + result_stack[-1].append(_normalize_identifier(current)) + return result_stack.pop() + + +def sort_stacks(s): + if isinstance(s, Stack): + return (True, (s.identifier, s.body)) + return (False, s) + + +def normalize_type(t): + t = t.strip() + tokens = re.split(SPECIAL_SYMBOLS, t) + tokens = [token for token in tokens if token] + parsed = _parse_tokens(tokens) + return parsed + + +def check_piece(left, right, indent=1): + if type(left) != type(right): + return False + elif isinstance(left, Stack): + if left.identifier != right.identifier: + return False + elif len(left.body) != len(right.body): + return False + elif left.identifier == "union": + # Handle them in any order + left.body.sort(key=sort_stacks) + right.body.sort(key=sort_stacks) + # Match them in exact order + for l, r in zip(left.body, right.body): + if not check_piece(l, r, indent=indent + 1): + return False + return True + else: + return left == right + + +def type_check(left, right): + left = normalize_type(left) + right = normalize_type(right) + return check_piece(left, right) + +def find_colon(str): + parens_stack = [] + for i, character in enumerate(str): + if character in '[(': + parens_stack.append(character) + elif character in '])': + parens_stack.pop() + elif character == ':' and not parens_stack: + return i + return 0 + +ARGS = ('args:', 'arg:', 'argument:', 'arguments:', + 'parameters:', 'params:', 'parameter:', 'param:') +ARG_PATTERN = r'(.+)\s*\((.+)\)\s*:(.+)' +RETURNS = ('returns:', 'return:') +def parse_docstring(doc): + # First line's indentation may be different from rest - trust first + # non empty line after the first one. + # Remove taht number of spaces from subsequent lines + # If Line is "Args:" or other special... + # + lines = doc.split("\n") + body = [lines[0]] + args = {} + current_arg = None + returns = [] + current_component = 'body' + indentation = None + inner_indentation = None + for line in lines[1:]: + # Blank line, not interesting! + if not line.strip(): + continue + # Get the actual text + if indentation is None: + indentation = len(line) - len(line.lstrip()) + line = line[indentation:] + potential_command = line.lower().strip() + # New command region? + if potential_command in ARGS: + current_component = 'args' + inner_indentation = None + continue + elif potential_command in RETURNS: + current_component = 'returns' + inner_indentation = None + continue + # Okay, it's content - let's process it + if current_component == 'body': + body.append(line) + else: + if inner_indentation is None: + inner_indentation = len(line) - len(line.lstrip()) + line = line[inner_indentation:] + # Skip indented lines + if not re.match(r'\s', line): + if current_component == 'args': + match = re.search(ARG_PATTERN, line) + current_arg = match.group(1) + type_str = match.group(2) + args[current_arg.strip()] = type_str.strip() + elif current_component == 'returns': + position = find_colon(line) + return_type, comment = line[:position], line[position:] + returns.append(return_type.strip()) + return body, args, ' or '.join(returns) + +def function_signature(function_name, returns=None, yields=None, + prints=None, raises=None, report=None, root=None, + **kwargs): + """ + Determines whether the function with this signature is in the AST. + + TODO: Implement raises, prints, yields + """ + if root is None: + root = parse_program() + # If you encounter any special parameters with a "_", then fix their + # name. This allows for students to have parameters with the given name. + for special_parameter in SPECIAL_PARAMETERS: + if special_parameter in kwargs: + kwargs[special_parameter[1:]] = kwargs.pop(special_parameter) + # Go get the actual docstring, parse it + docstring = None + for function_def in root.find_all("FunctionDef"): + if function_def._name == function_name: + if function_def.body: + if (function_def.body[0].ast_name == "Expr" and + function_def.body[0].value.ast_name == "Str"): + docstring = function_def.body[0].value.s + # Try to match each element in turn. + if docstring is None: + return False + + try: + body, args, parsed_returns = parse_docstring(docstring) + except Exception as e: + return [e], False + failing_parameters = [] + for name, type in kwargs.items(): + if name in args: + if not type_check(type, args[name]): + failing_parameters.append(name) + else: + failing_parameters.append(name) + if returns is None and not returns: + return failing_parameters, True + elif returns is not None and returns: + return failing_parameters, type_check(parsed_returns, returns) + else: + return failing_parameters, False + + +def class_signature(class_name, report=None, root=None, **attributes): + """ + + Args: + class_name: + **attributes: + report: + root: + + Returns: + + """ + if root is None: + root = parse_program() + + +""" + +""" diff --git a/src/lib/pedal/toolkit/upload.py b/src/lib/pedal/toolkit/upload.py new file mode 100644 index 0000000000..9cefd148ba --- /dev/null +++ b/src/lib/pedal/toolkit/upload.py @@ -0,0 +1,54 @@ +import re +from pedal.source import get_program +from pedal.sandbox.compatibility import get_output +from pedal.report.imperative import gently, explain + + +# Feedback for author's name +def check_author_name_on_header(): + code = get_program() + m_author = re.search('Author: \\w+', code) + if not m_author: + gently("You need to add your name to the author field at the top of the file." + "

(name_missing)

") + + +def get_plots(output): + # The p[0] is the first plot in a graph/show + return [p[0] for p in output if isinstance(p[0], dict)] + + +def find_plot_of_type(plot_list, plot_type): + return [p['data'] for p in plot_list if p['type'] == plot_type] + + +# Feedback for copying output of the program in the documentation +def check_output_on_header(expected_output): + code = get_program() + expected_output = str(expected_output) + between_stars = code.split("*****")[2].strip() + between_stars = "\\n".join([x.strip() for x in between_stars.split("\\n")]) + if 'REPLACE THIS TEXT WITH THE OUTPUT OF THIS PROGRAM' in between_stars: + gently("In your code, you need to 'REPLACE THIS TEXT WITH THE OUTPUT OF THIS PROGRAM'" + "

(wrong_output_blank)

") + elif expected_output not in between_stars: + gently("The output you copied between the *****, seems to be incorrect. You may have copied it into the wrong " + "location, or it is incomplete.

(wrong_output_fill)

") + + +def check_problem_submission(prob_id): + if prob_id not in get_program(): + explain("Make sure that you are turning in {}

(wrong_problem)

".format(prob_id)) + return True + + +def check_print_output(multiple_lines): + for line in multiple_lines: + if line not in get_output(): + gently("You are not doing the correct calculation

(catch_all)

") + return True + + +def find_in_code(regex): + code = get_program() + return re.search(regex, code) diff --git a/src/lib/pedal/toolkit/utilities.py b/src/lib/pedal/toolkit/utilities.py new file mode 100644 index 0000000000..d0d0443846 --- /dev/null +++ b/src/lib/pedal/toolkit/utilities.py @@ -0,0 +1,337 @@ +from pedal.cait.cait_api import parse_program +from pedal.report.imperative import gently, explain + + +def is_top_level(ast_node): + ast = parse_program() + for element in ast.body: + if element.ast_name == 'Expr': + if element.value == ast_node: + return True + elif element == ast_node: + return True + return False + + +def no_nested_function_definitions(): + ast = parse_program() + defs = ast.find_all('FunctionDef') + for a_def in defs: + if not is_top_level(a_def): + gently("You have defined a function inside of another block. For instance, you may have placed it inside " + "another function definition, or inside of a loop. Do not nest your function definition!" + "

(nest_func)") + return False + return True + + +def function_prints(): + ast = parse_program() + defs = ast.find_all('FunctionDef') + for a_def in defs: + all_calls = a_def.find_all('Call') + for a_call in all_calls: + if a_call.func.ast_name == 'Name': + if a_call.func.id == 'print': + return True + return False + + +def find_function_calls(name, root=None): + if root is None: + root = parse_program() + all_calls = root.find_all('Call') + calls = [] + for a_call in all_calls: + if a_call.func.ast_name == 'Attribute': + if a_call.func.attr == name: + calls.append(a_call) + elif a_call.func.ast_name == 'Name': + if a_call.func.id == name: + calls.append(a_call) + return calls + + +def function_is_called(name): + return len(find_function_calls(name)) + + +def no_nonlist_nums(): + pass + + +def only_printing_variables(): + ast = parse_program() + all_calls = ast.find_all('Call') + for a_call in all_calls: + if a_call.func.ast_name == 'Name' and a_call.func.id == "print": + for arg in a_call.args: + if arg.ast_name != "Name": + return False + elif arg.id in ('True', 'False', 'None'): + return False + return True + + +def find_prior_initializations(node): + if node.ast_name != "Name": + return None + ast = parse_program() + assignments = ast.find_all("Assign") + cur_line_no = node.lineno + all_assignments = [] + for assignment in assignments: + if assignment.has(node): + if assignment.lineno < cur_line_no: + all_assignments.append(assignment) + return all_assignments + + +def prevent_unused_result(): + ast = parse_program() + exprs = ast.find_all('Expr') + for expr in exprs: + if expr.value.ast_name == "Call": + a_call = expr.value + if a_call.func.ast_name == 'Attribute': + if a_call.func.attr == 'append': + pass + elif a_call.func.attr in ('replace', 'strip', 'lstrip', 'rstrip'): + gently("Remember! You cannot modify a string directly. Instead, you should assign the result back " + "to the string variable.

(str_mutate)") + + +def prevent_builtin_usage(function_names): + # Prevent direction calls + ast = parse_program() + all_calls = ast.find_all('Call') + for a_call in all_calls: + if a_call.func.ast_name == 'Name': + if a_call.func.id in function_names: + explain("You cannot use the builtin function {}.

(builtin_use)".format( + a_call.func.id)) + return a_call.func.id + return None + +# TODO: UGLY HACK. This is to avoid muted=False kwargs in the following +# functions. Apparently skulpt doesn't support this syntax. +muted = False + +def prevent_literal(*literals): + ''' + Confirms that the literal is not in the code, returning False if it is not. + + Args: + *literals (Any...): A series of literal values to look for. + Returns: + AstNode or False: If the literal is found in the code, then it is returned. + ''' + ast = parse_program() + str_values = [s.s for s in ast.find_all("Str")] + num_values = [n.n for n in ast.find_all("Num")] + name_values = ([name.id for name in ast.find_all("Name")]+ + [name.value for name in ast.find_all("NameConstant")]) + for literal in literals: + if isinstance(literal, (int, float)): + if literal in num_values: + if not muted: + explain("Do not use the literal value {} in your code." + "

(hard_code)".format(repr(literal))) + return literal + elif isinstance(literal, str): + if literal in str_values: + if not muted: + explain("Do not use the literal value {} in your code." + "

(hard_code)".format(repr(literal))) + return literal + elif literal in (True, False, None): + if str(literal) in name_values: + if not muted: + explain("Do not use the literal value {} in your code." + "

(hard_code)".format(repr(literal))) + return literal + return False + +def ensure_literal(*literals): + ''' + Confirms that the literal IS in the code, returning False if it is not. + + Args: + *literals (Any...): A series of literal values to look for. + Returns: + AstNode or False: If the literal is found in the code, then it is returned. + ''' + ast = parse_program() + str_values = [s.s for s in ast.find_all("Str")] + num_values = [n.n for n in ast.find_all("Num")] + name_values = ([str(name.id) for name in ast.find_all("Name")]+ + [str(name.value) for name in ast.find_all("NameConstant")]) + for literal in literals: + if literal in (True, False, None): + if str(literal) not in name_values: + if not muted: + explain("You need the literal value {} in your code." + "

(missing_literal)".format(repr(literal))) + return True + elif isinstance(literal, (int, float)): + if literal not in num_values: + if not muted: + explain("You need the literal value {} in your code." + "

(missing_literal)".format(repr(literal))) + return literal + elif isinstance(literal, str): + if literal not in str_values: + if not muted: + explain("You need the literal value {} in your code." + "

(missing_literal)".format(repr(literal))) + return literal + return False + + +def prevent_advanced_iteration(): + ast = parse_program() + if ast.find_all('While'): + explain("You should not use a while loop to solve this problem." + "

(while_usage)") + prevent_builtin_usage(['sum', 'map', 'filter', 'reduce', 'len', 'max', 'min', + 'max', 'sorted', 'all', 'any', 'getattr', 'setattr', + 'eval', 'exec', 'iter']) + + +COMPARE_OP_NAMES = { + "==": "Eq", + "<": "Lt", + "<=": "Lte", + ">=": "Gte", + ">": "Gt", + "!=": "NotEq", + "is": "Is", + "is not": "IsNot", + "in": "In", + "not in": "NotIn"} +BOOL_OP_NAMES = { + "and": "And", + "or": "Or"} +BIN_OP_NAMES = { + "+": "Add", + "-": "Sub", + "*": "Mult", + "/": "Div", + "//": "FloorDiv", + "%": "Mod", + "**": "Pow", + ">>": "LShift", + "<<": "RShift", + "|": "BitOr", + "^": "BitXor", + "&": "BitAnd", + "@": "MatMult"} +UNARY_OP_NAMES = { + # "+=": "UAdd", + # "-=": "USub", + "not": "Not", + "~": "Invert" +} + + +def ensure_operation(op_name, root=None): + if root is None: + root = parse_program() + result = find_operation(op_name, root) + if not result: + gently("You are not using the {} operator.

(missing_op)".format(op_name)) + return result + + +def prevent_operation(op_name, root=None): + if root is None: + root = parse_program() + result = find_operation(op_name, root) + if result: + gently("You may not use the {} operator.

(bad_op)".format(op_name)) + return result + + +def find_operation(op_name, root): + if op_name in COMPARE_OP_NAMES: + compares = root.find_all("Compare") + for compare in compares: + for op in compare.ops: + if op.ast_name == COMPARE_OP_NAMES[op_name]: + return compare + elif op_name in BOOL_OP_NAMES: + boolops = root.find_all("BoolOp") + for boolop in boolops: + if boolop.op_name == BOOL_OP_NAMES[op_name]: + return boolop + elif op_name in BIN_OP_NAMES: + binops = root.find_all("BinOp") + for binop in binops: + if binop.op_name == BIN_OP_NAMES[op_name]: + return binop + elif op_name in UNARY_OP_NAMES: + unaryops = root.find_all("UnaryOp") + for unaryop in unaryops: + if unaryop.op_name == UNARY_OP_NAMES[op_name]: + return unaryop + return False + + +def ensure_recursion(function_name, root=None): + if root is None: + root = parse_program() + all_calls = root.find_all('Call') + calls = [] + for a_call in all_calls: + if a_call.func.ast_name == 'Attribute': + if a_call.func.attr == function_name: + calls.append(a_call) + elif a_call.func.ast_name == 'Name': + if a_call.func.id == function_name: + calls.append(a_call) + return calls + + +def ensure_assignment(variable_name, type=None, value=None, root=None): + """ + Consumes a variable name + TODO: Implement the value parameter + + :param variable_name: The variable name the student is expected to define. + :type variable_name: str + :param type: The string type of the node on the right side of the + assignment. Check GreenTreeSnakes (e.g., "Num", or "Str"). + :type type: str + :return: False or str + """ + if root is None: + root = parse_program() + assignments = root.find_all("Assign") + potentials = [] + for assign in assignments: + if assign.targets[0].ast_name != "Name": + continue + if assign.targets[0].id == variable_name: + potentials.append(assign) + if type is None: + return assign + elif (type == 'Bool' and + assign.value.ast_name == 'Name' and + assign.value.id in ('True', 'False')): + return assign + elif (type == 'Bool' and + assign.value.ast_name == 'NameConstant' and + assign.value.value in (True, False)): + return assign + elif assign.value.ast_name == type: + return assign + if potentials and potentials[0].value.ast_name not in ("Str", "Bool", "Num", "List", "Tuple"): + explain(("You needed to assign a literal value to {variable}, but you " + "created an expression instead.").format(variable=variable_name)) + elif type is None: + explain(("You have not properly assigned anything to the variable " + "{variable}.").format(variable=variable_name)) + else: + explain(("You have not assigned a {type} to the variable {variable}." + "").format(type=type, variable=variable_name)) + return False diff --git a/src/lib/posixpath.py b/src/lib/posixpath.py index e56934f5c4..cb650cb46f 100644 --- a/src/lib/posixpath.py +++ b/src/lib/posixpath.py @@ -1 +1,527 @@ -raise NotImplementedError("posixpath is not yet implemented in Skulpt") +"""Common operations on Posix pathnames. + +Instead of importing this module directly, import os and refer to +this module as os.path. The "os.path" name is an alias for this +module on Posix systems; on other systems (e.g. Windows), +os.path provides the same operations in a manner specific to that +platform, and is an alias to another module (e.g. ntpath). + +Some of this can actually be useful on non-Posix systems too, e.g. +for manipulation of the pathname component of URLs. + +# acbart 7/8/2019: Changed all b'' strings to regular strings. +""" + +# Strings representing various path-related bits and pieces. +# These are primarily for export; internally, they are hardcoded. +# Should be set before imports for resolving cyclic dependency. +curdir = '.' +pardir = '..' +extsep = '.' +sep = '/' +pathsep = ':' +defpath = '/bin:/usr/bin' +altsep = None +devnull = '/dev/null' + +import os +import sys +import stat +import genericpath +from genericpath import * + +__all__ = ["normcase","isabs","join","splitdrive","split","splitext", + "basename","dirname","commonprefix","getsize","getmtime", + "getatime","getctime","islink","exists","lexists","isdir","isfile", + "ismount", "expanduser","expandvars","normpath","abspath", + "samefile","sameopenfile","samestat", + "curdir","pardir","sep","pathsep","defpath","altsep","extsep", + "devnull","realpath","supports_unicode_filenames","relpath", + "commonpath"] + + +def _get_sep(path): + if isinstance(path, bytes): + return '/' #'/' + else: + return '/' + +# Normalize the case of a pathname. Trivial in Posix, string.lower on Mac. +# On MS-DOS this may also turn slashes into backslashes; however, other +# normalizations (such as optimizing '../' away) are not allowed +# (another function should be defined to do that). + +def normcase(s): + """Normalize case of pathname. Has no effect under Posix""" + return os.fspath(s) + + +# Return whether a path is absolute. +# Trivial in Posix, harder on the Mac or MS-DOS. + +def isabs(s): + """Test whether a path is absolute""" + s = os.fspath(s) + sep = _get_sep(s) + return s.startswith(sep) + + +# Join pathnames. +# Ignore the previous parts if a part is absolute. +# Insert a '/' unless the first part is empty or already ends in '/'. + +def join(a, *p): + """Join two or more pathname components, inserting '/' as needed. + If any component is an absolute path, all previous path components + will be discarded. An empty last part will result in a path that + ends with a separator.""" + a = os.fspath(a) + sep = _get_sep(a) + path = a + try: + if not p: + path[:0] + sep #23780: Ensure compatible data type even if p is null. + for b in map(os.fspath, p): + if b.startswith(sep): + path = b + elif not path or path.endswith(sep): + path += b + else: + path += sep + b + except (TypeError, AttributeError, BytesWarning): + genericpath._check_arg_types('join', a, *p) + raise + return path + + +# Split a path in head (everything up to the last '/') and tail (the +# rest). If the path ends in '/', tail will be empty. If there is no +# '/' in the path, head will be empty. +# Trailing '/'es are stripped from head unless it is the root. + +def split(p): + """Split a pathname. Returns tuple "(head, tail)" where "tail" is + everything after the final slash. Either part may be empty.""" + p = os.fspath(p) + sep = _get_sep(p) + i = p.rfind(sep) + 1 + head, tail = p[:i], p[i:] + if head and head != sep*len(head): + head = head.rstrip(sep) + return head, tail + + +# Split a path in root and extension. +# The extension is everything starting at the last dot in the last +# pathname component; the root is everything before that. +# It is always true that root + ext == p. + +def splitext(p): + p = os.fspath(p) + if isinstance(p, bytes): + sep = '/' + extsep = '.' + else: + sep = '/' + extsep = '.' + return genericpath._splitext(p, sep, None, extsep) +#splitext.__doc__ = genericpath._splitext.__doc__ + +# Split a pathname into a drive specification and the rest of the +# path. Useful on DOS/Windows/NT; on Unix, the drive is always empty. + +def splitdrive(p): + """Split a pathname into drive and path. On Posix, drive is always + empty.""" + p = os.fspath(p) + return p[:0], p + + +# Return the tail (basename) part of a path, same as split(path)[1]. + +def basename(p): + """Returns the final component of a pathname""" + p = os.fspath(p) + sep = _get_sep(p) + i = p.rfind(sep) + 1 + return p[i:] + + +# Return the head (dirname) part of a path, same as split(path)[0]. + +def dirname(p): + """Returns the directory component of a pathname""" + p = os.fspath(p) + sep = _get_sep(p) + i = p.rfind(sep) + 1 + head = p[:i] + if head and head != sep*len(head): + head = head.rstrip(sep) + return head + + +# Is a path a symbolic link? +# This will always return false on systems where os.lstat doesn't exist. + +def islink(path): + """Test whether a path is a symbolic link""" + try: + st = os.lstat(path) + except (OSError, ValueError, AttributeError): + return False + return stat.S_ISLNK(st.st_mode) + +# Being true for dangling symbolic links is also useful. + +def lexists(path): + """Test whether a path exists. Returns True for broken symbolic links""" + try: + os.lstat(path) + except (OSError, ValueError): + return False + return True + + +# Is a path a mount point? +# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?) + +def ismount(path): + """Test whether a path is a mount point""" + try: + s1 = os.lstat(path) + except (OSError, ValueError): + # It doesn't exist -- so not a mount point. :-) + return False + else: + # A symlink can never be a mount point + if stat.S_ISLNK(s1.st_mode): + return False + + if isinstance(path, bytes): + parent = join(path, '..') + else: + parent = join(path, '..') + parent = realpath(parent) + try: + s2 = os.lstat(parent) + except (OSError, ValueError): + return False + + dev1 = s1.st_dev + dev2 = s2.st_dev + if dev1 != dev2: + return True # path/.. on a different device as path + ino1 = s1.st_ino + ino2 = s2.st_ino + if ino1 == ino2: + return True # path/.. is the same i-node as path + return False + + +# Expand paths beginning with '~' or '~user'. +# '~' means $HOME; '~user' means that user's home directory. +# If the path doesn't begin with '~', or if the user or $HOME is unknown, +# the path is returned unchanged (leaving error reporting to whatever +# function is called with the expanded path as argument). +# See also module 'glob' for expansion of *, ? and [...] in pathnames. +# (A function should also be defined to do full *sh-style environment +# variable expansion.) + +def expanduser(path): + """Expand ~ and ~user constructions. If user or $HOME is unknown, + do nothing.""" + path = os.fspath(path) + if isinstance(path, bytes): + tilde = '~' + else: + tilde = '~' + if not path.startswith(tilde): + return path + sep = _get_sep(path) + i = path.find(sep, 1) + if i < 0: + i = len(path) + if i == 1: + if 'HOME' not in os.environ: + import pwd + try: + userhome = pwd.getpwuid(os.getuid()).pw_dir + except KeyError: + # bpo-10496: if the current user identifier doesn't exist in the + # password database, return the path unchanged + return path + else: + userhome = os.environ['HOME'] + else: + import pwd + name = path[1:i] + if isinstance(name, bytes): + name = str(name, 'ASCII') + try: + pwent = pwd.getpwnam(name) + except KeyError: + # bpo-10496: if the user name from the path doesn't exist in the + # password database, return the path unchanged + return path + userhome = pwent.pw_dir + if isinstance(path, bytes): + userhome = os.fsencode(userhome) + root = '/' + else: + root = '/' + userhome = userhome.rstrip(root) + return (userhome + path[i:]) or root + + +# Expand paths containing shell variable substitutions. +# This expands the forms $variable and ${variable} only. +# Non-existent variables are left unchanged. + +_varprog = None +_varprogb = None + +def expandvars(path): + """Expand shell variables of form $var and ${var}. Unknown variables + are left unchanged.""" + path = os.fspath(path) + global _varprog, _varprogb + if isinstance(path, bytes): + if '$' not in path: + return path + if not _varprogb: + import re + _varprogb = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII) + search = _varprogb.search + start = '{' + end = '}' + environ = getattr(os, 'environ', None) + else: + if '$' not in path: + return path + if not _varprog: + import re + _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII) + search = _varprog.search + start = '{' + end = '}' + environ = os.environ + i = 0 + while True: + m = search(path, i) + if not m: + break + i, j = m.span(0) + name = m.group(1) + if name.startswith(start) and name.endswith(end): + name = name[1:-1] + try: + if environ is None: + value = os.fsencode(os.environ[os.fsdecode(name)]) + else: + value = environ[name] + except KeyError: + i = j + else: + tail = path[j:] + path = path[:i] + value + i = len(path) + path += tail + return path + + +# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B. +# It should be understood that this may change the meaning of the path +# if it contains symbolic links! + +def normpath(path): + """Normalize path, eliminating double slashes, etc.""" + path = os.fspath(path) + if isinstance(path, bytes): + sep = '/' + empty = '' + dot = '.' + dotdot = '..' + else: + sep = '/' + empty = '' + dot = '.' + dotdot = '..' + if path == empty: + return dot + initial_slashes = path.startswith(sep) + # POSIX allows one or two initial slashes, but treats three or more + # as single slash. + if (initial_slashes and + path.startswith(sep*2) and not path.startswith(sep*3)): + initial_slashes = 2 + comps = path.split(sep) + new_comps = [] + for comp in comps: + if comp in (empty, dot): + continue + if (comp != dotdot or (not initial_slashes and not new_comps) or + (new_comps and new_comps[-1] == dotdot)): + new_comps.append(comp) + elif new_comps: + new_comps.pop() + comps = new_comps + path = sep.join(comps) + if initial_slashes: + path = sep*initial_slashes + path + return path or dot + + +def abspath(path): + """Return an absolute path.""" + path = os.fspath(path) + if not isabs(path): + if isinstance(path, bytes): + cwd = os.getcwdb() + else: + cwd = os.getcwd() + path = join(cwd, path) + return normpath(path) + + +# Return a canonical path (i.e. the absolute location of a file on the +# filesystem). + +def realpath(filename): + """Return the canonical path of the specified filename, eliminating any +symbolic links encountered in the path.""" + filename = os.fspath(filename) + path, ok = _joinrealpath(filename[:0], filename, {}) + return abspath(path) + +# Join two paths, normalizing and eliminating any symbolic links +# encountered in the second path. +def _joinrealpath(path, rest, seen): + if isinstance(path, bytes): + sep = '/' + curdir = '.' + pardir = '..' + else: + sep = '/' + curdir = '.' + pardir = '..' + + if isabs(rest): + rest = rest[1:] + path = sep + + while rest: + name, _, rest = rest.partition(sep) + if not name or name == curdir: + # current dir + continue + if name == pardir: + # parent dir + if path: + path, name = split(path) + if name == pardir: + path = join(path, pardir, pardir) + else: + path = pardir + continue + newpath = join(path, name) + if not islink(newpath): + path = newpath + continue + # Resolve the symbolic link + if newpath in seen: + # Already seen this path + path = seen[newpath] + if path is not None: + # use cached value + continue + # The symlink is not resolved, so we must have a symlink loop. + # Return already resolved part + rest of the path unchanged. + return join(newpath, rest), False + seen[newpath] = None # not resolved symlink + path, ok = _joinrealpath(path, os.readlink(newpath), seen) + if not ok: + return join(path, rest), False + seen[newpath] = path # resolved symlink + + return path, True + + +supports_unicode_filenames = (sys.platform == 'darwin') + +def relpath(path, start=None): + """Return a relative version of a path""" + + if not path: + raise ValueError("no path specified") + + path = os.fspath(path) + if isinstance(path, bytes): + curdir = '.' + sep = '/' + pardir = '..' + else: + curdir = '.' + sep = '/' + pardir = '..' + + if start is None: + start = curdir + else: + start = os.fspath(start) + + try: + start_list = [x for x in abspath(start).split(sep) if x] + path_list = [x for x in abspath(path).split(sep) if x] + # Work out how much of the filepath is shared by start and path. + i = len(commonprefix([start_list, path_list])) + + rel_list = [pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return curdir + return join(*rel_list) + except (TypeError, AttributeError, BytesWarning, DeprecationWarning): + genericpath._check_arg_types('relpath', path, start) + raise + + +# Return the longest common sub-path of the sequence of paths given as input. +# The paths are not normalized before comparing them (this is the +# responsibility of the caller). Any trailing separator is stripped from the +# returned path. + +def commonpath(paths): + """Given a sequence of path names, returns the longest common sub-path.""" + + if not paths: + raise ValueError('commonpath() arg is an empty sequence') + + paths = tuple(map(os.fspath, paths)) + if isinstance(paths[0], bytes): + sep = '/' + curdir = '.' + else: + sep = '/' + curdir = '.' + + try: + split_paths = [path.split(sep) for path in paths] + + try: + isabs, = set(p[:1] == sep for p in paths) + except ValueError: + raise ValueError("Can't mix absolute and relative paths") from None + + split_paths = [[c for c in s if c and c != curdir] for s in split_paths] + s1 = min(split_paths) + s2 = max(split_paths) + common = s1 + for i, c in enumerate(s1): + if c != s2[i]: + common = s1[:i] + break + + prefix = sep if isabs else sep[:0] + return prefix + sep.join(common) + except (TypeError, AttributeError): + genericpath._check_arg_types('commonpath', *paths) + raise \ No newline at end of file diff --git a/src/lib/reprlib.py b/src/lib/reprlib.py new file mode 100644 index 0000000000..827e256129 --- /dev/null +++ b/src/lib/reprlib.py @@ -0,0 +1,161 @@ +"""Redo the builtin repr() (representation) but with limits on most sizes.""" + +__all__ = ["Repr", "repr", "recursive_repr"] + +from itertools import islice +from _thread import get_ident + +def recursive_repr(fillvalue='...'): + 'Decorator to make a repr function return fillvalue for a recursive call' + + def decorating_function(user_function): + repr_running = set() + + def wrapper(self): + key = id(self), get_ident() + if key in repr_running: + return fillvalue + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + + # Can't use functools.wraps() here because of bootstrap issues + wrapper.__module__ = getattr(user_function, '__module__') + wrapper.__doc__ = getattr(user_function, '__doc__') + wrapper.__name__ = getattr(user_function, '__name__') + wrapper.__qualname__ = getattr(user_function, '__qualname__') + wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) + return wrapper + + return decorating_function + +class Repr: + + def __init__(self): + self.maxlevel = 6 + self.maxtuple = 6 + self.maxlist = 6 + self.maxarray = 5 + self.maxdict = 4 + self.maxset = 6 + self.maxfrozenset = 6 + self.maxdeque = 6 + self.maxstring = 30 + self.maxlong = 40 + self.maxother = 30 + + def repr(self, x): + return self.repr1(x, self.maxlevel) + + def repr1(self, x, level): + typename = type(x).__name__ + if ' ' in typename: + parts = typename.split() + typename = '_'.join(parts) + if hasattr(self, 'repr_' + typename): + return getattr(self, 'repr_' + typename)(x, level) + else: + return self.repr_instance(x, level) + + def _repr_iterable(self, x, level, left, right, maxiter, trail=''): + n = len(x) + if level <= 0 and n: + s = '...' + else: + newlevel = level - 1 + repr1 = self.repr1 + pieces = [repr1(elem, newlevel) for elem in islice(x, maxiter)] + if n > maxiter: pieces.append('...') + s = ', '.join(pieces) + if n == 1 and trail: right = trail + right + return '%s%s%s' % (left, s, right) + + def repr_tuple(self, x, level): + return self._repr_iterable(x, level, '(', ')', self.maxtuple, ',') + + def repr_list(self, x, level): + return self._repr_iterable(x, level, '[', ']', self.maxlist) + + def repr_array(self, x, level): + if not x: + return "array('%s')" % x.typecode + header = "array('%s', [" % x.typecode + return self._repr_iterable(x, level, header, '])', self.maxarray) + + def repr_set(self, x, level): + if not x: + return 'set()' + x = _possibly_sorted(x) + return self._repr_iterable(x, level, '{', '}', self.maxset) + + def repr_frozenset(self, x, level): + if not x: + return 'frozenset()' + x = _possibly_sorted(x) + return self._repr_iterable(x, level, 'frozenset({', '})', + self.maxfrozenset) + + def repr_deque(self, x, level): + return self._repr_iterable(x, level, 'deque([', '])', self.maxdeque) + + def repr_dict(self, x, level): + n = len(x) + if n == 0: return '{}' + if level <= 0: return '{...}' + newlevel = level - 1 + repr1 = self.repr1 + pieces = [] + for key in islice(_possibly_sorted(x), self.maxdict): + keyrepr = repr1(key, newlevel) + valrepr = repr1(x[key], newlevel) + pieces.append('%s: %s' % (keyrepr, valrepr)) + if n > self.maxdict: pieces.append('...') + s = ', '.join(pieces) + return '{%s}' % (s,) + + def repr_str(self, x, level): + s = original_repr(x[:self.maxstring]) + if len(s) > self.maxstring: + i = max(0, (self.maxstring-3)//2) + j = max(0, self.maxstring-3-i) + s = original_repr(x[:i] + x[len(x)-j:]) + s = s[:i] + '...' + s[len(s)-j:] + return s + + def repr_int(self, x, level): + s = original_repr(x) # XXX Hope this isn't too slow... + if len(s) > self.maxlong: + i = max(0, (self.maxlong-3)//2) + j = max(0, self.maxlong-3-i) + s = s[:i] + '...' + s[len(s)-j:] + return s + + def repr_instance(self, x, level): + try: + s = original_repr(x) + # Bugs in x.__repr__() can cause arbitrary + # exceptions -- then make up something + except Exception: + return '<%s instance at %#x>' % (x.__class__.__name__, id(x)) + if len(s) > self.maxother: + i = max(0, (self.maxother-3)//2) + j = max(0, self.maxother-3-i) + s = s[:i] + '...' + s[len(s)-j:] + return s + + +def _possibly_sorted(x): + # Since not all sequences of items can be sorted and comparison + # functions may raise arbitrary exceptions, return an unsorted + # sequence in that case. + try: + return sorted(x) + except Exception: + return list(x) + +original_repr = repr +aRepr = Repr() +repr = aRepr.repr diff --git a/src/lib/stat.py b/src/lib/stat.py index 5c7d0647ee..5ea14c1362 100644 --- a/src/lib/stat.py +++ b/src/lib/stat.py @@ -1 +1,179 @@ -raise NotImplementedError("stat is not yet implemented in Skulpt") +"""Constants/functions for interpreting results of os.stat() and os.lstat(). + +Suggested usage: from stat import * +""" + +# Indices for stat struct members in the tuple returned by os.stat() + +ST_MODE = 0 +ST_INO = 1 +ST_DEV = 2 +ST_NLINK = 3 +ST_UID = 4 +ST_GID = 5 +ST_SIZE = 6 +ST_ATIME = 7 +ST_MTIME = 8 +ST_CTIME = 9 + +# Extract bits from the mode + +def S_IMODE(mode): + """Return the portion of the file's mode that can be set by + os.chmod(). + """ + return mode & 0o7777 + +def S_IFMT(mode): + """Return the portion of the file's mode that describes the + file type. + """ + return mode & 0o170000 + +# Constants used as S_IFMT() for various file types +# (not all are implemented on all systems) + +S_IFDIR = 0o040000 # directory +S_IFCHR = 0o020000 # character device +S_IFBLK = 0o060000 # block device +S_IFREG = 0o100000 # regular file +S_IFIFO = 0o010000 # fifo (named pipe) +S_IFLNK = 0o120000 # symbolic link +S_IFSOCK = 0o140000 # socket file + +# Functions to test for each file type + +def S_ISDIR(mode): + """Return True if mode is from a directory.""" + return S_IFMT(mode) == S_IFDIR + +def S_ISCHR(mode): + """Return True if mode is from a character special device file.""" + return S_IFMT(mode) == S_IFCHR + +def S_ISBLK(mode): + """Return True if mode is from a block special device file.""" + return S_IFMT(mode) == S_IFBLK + +def S_ISREG(mode): + """Return True if mode is from a regular file.""" + return S_IFMT(mode) == S_IFREG + +def S_ISFIFO(mode): + """Return True if mode is from a FIFO (named pipe).""" + return S_IFMT(mode) == S_IFIFO + +def S_ISLNK(mode): + """Return True if mode is from a symbolic link.""" + return S_IFMT(mode) == S_IFLNK + +def S_ISSOCK(mode): + """Return True if mode is from a socket.""" + return S_IFMT(mode) == S_IFSOCK + +# Names for permission bits + +S_ISUID = 0o4000 # set UID bit +S_ISGID = 0o2000 # set GID bit +S_ENFMT = S_ISGID # file locking enforcement +S_ISVTX = 0o1000 # sticky bit +S_IREAD = 0o0400 # Unix V7 synonym for S_IRUSR +S_IWRITE = 0o0200 # Unix V7 synonym for S_IWUSR +S_IEXEC = 0o0100 # Unix V7 synonym for S_IXUSR +S_IRWXU = 0o0700 # mask for owner permissions +S_IRUSR = 0o0400 # read by owner +S_IWUSR = 0o0200 # write by owner +S_IXUSR = 0o0100 # execute by owner +S_IRWXG = 0o0070 # mask for group permissions +S_IRGRP = 0o0040 # read by group +S_IWGRP = 0o0020 # write by group +S_IXGRP = 0o0010 # execute by group +S_IRWXO = 0o0007 # mask for others (not in group) permissions +S_IROTH = 0o0004 # read by others +S_IWOTH = 0o0002 # write by others +S_IXOTH = 0o0001 # execute by others + +# Names for file flags + +UF_NODUMP = 0x00000001 # do not dump file +UF_IMMUTABLE = 0x00000002 # file may not be changed +UF_APPEND = 0x00000004 # file may only be appended to +UF_OPAQUE = 0x00000008 # directory is opaque when viewed through a union stack +UF_NOUNLINK = 0x00000010 # file may not be renamed or deleted +UF_COMPRESSED = 0x00000020 # OS X: file is hfs-compressed +UF_HIDDEN = 0x00008000 # OS X: file should not be displayed +SF_ARCHIVED = 0x00010000 # file may be archived +SF_IMMUTABLE = 0x00020000 # file may not be changed +SF_APPEND = 0x00040000 # file may only be appended to +SF_NOUNLINK = 0x00100000 # file may not be renamed or deleted +SF_SNAPSHOT = 0x00200000 # file is a snapshot file + + +_filemode_table = ( + ((S_IFLNK, "l"), + (S_IFSOCK, "s"), # Must appear before IFREG and IFDIR as IFSOCK == IFREG | IFDIR + (S_IFREG, "-"), + (S_IFBLK, "b"), + (S_IFDIR, "d"), + (S_IFCHR, "c"), + (S_IFIFO, "p")), + + ((S_IRUSR, "r"),), + ((S_IWUSR, "w"),), + ((S_IXUSR|S_ISUID, "s"), + (S_ISUID, "S"), + (S_IXUSR, "x")), + + ((S_IRGRP, "r"),), + ((S_IWGRP, "w"),), + ((S_IXGRP|S_ISGID, "s"), + (S_ISGID, "S"), + (S_IXGRP, "x")), + + ((S_IROTH, "r"),), + ((S_IWOTH, "w"),), + ((S_IXOTH|S_ISVTX, "t"), + (S_ISVTX, "T"), + (S_IXOTH, "x")) +) + +def filemode(mode): + """Convert a file's mode to a string of the form '-rwxrwxrwx'.""" + perm = [] + for table in _filemode_table: + for bit, char in table: + if mode & bit == bit: + perm.append(char) + break + else: + perm.append("-") + return "".join(perm) + + +# Windows FILE_ATTRIBUTE constants for interpreting os.stat()'s +# "st_file_attributes" member + +FILE_ATTRIBUTE_ARCHIVE = 32 +FILE_ATTRIBUTE_COMPRESSED = 2048 +FILE_ATTRIBUTE_DEVICE = 64 +FILE_ATTRIBUTE_DIRECTORY = 16 +FILE_ATTRIBUTE_ENCRYPTED = 16384 +FILE_ATTRIBUTE_HIDDEN = 2 +FILE_ATTRIBUTE_INTEGRITY_STREAM = 32768 +FILE_ATTRIBUTE_NORMAL = 128 +FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192 +FILE_ATTRIBUTE_NO_SCRUB_DATA = 131072 +FILE_ATTRIBUTE_OFFLINE = 4096 +FILE_ATTRIBUTE_READONLY = 1 +FILE_ATTRIBUTE_REPARSE_POINT = 1024 +FILE_ATTRIBUTE_SPARSE_FILE = 512 +FILE_ATTRIBUTE_SYSTEM = 4 +FILE_ATTRIBUTE_TEMPORARY = 256 +FILE_ATTRIBUTE_VIRTUAL = 65536 + + +# If available, use C implementation +try: + from _stat import * +except ImportError: + pass \ No newline at end of file diff --git a/src/lib/traceback.py b/src/lib/traceback.py index 6eb1ded926..a71d1abb37 100644 --- a/src/lib/traceback.py +++ b/src/lib/traceback.py @@ -1 +1,622 @@ -raise NotImplementedError("traceback is not yet implemented in Skulpt") +"""Extract, format and print information about Python stack traces.""" + +import collections +import linecache +import sys +from itertools import islice + +__all__ = ['extract_stack', 'extract_tb', 'format_exception', + 'format_exception_only', 'format_list', 'format_stack', + 'format_tb', 'print_exc', 'format_exc', 'print_exception', + 'print_last', 'print_stack', 'print_tb', 'clear_frames', + 'FrameSummary', 'StackSummary', 'TracebackException', + 'walk_stack', 'walk_tb'] + +# +# Formatting and printing lists of traceback lines. +# + +def print_list(extracted_list, file=None): + """Print the list of tuples as returned by extract_tb() or + extract_stack() as a formatted stack trace to the given file.""" + if file is None: + file = sys.stderr + for item in StackSummary.from_list(extracted_list).format(): + print(item, file=file, end="") + +def format_list(extracted_list): + """Format a list of tuples or FrameSummary objects for printing. + + Given a list of tuples or FrameSummary objects as returned by + extract_tb() or extract_stack(), return a list of strings ready + for printing. + + Each string in the resulting list corresponds to the item with the + same index in the argument list. Each string ends in a newline; + the strings may contain internal newlines as well, for those items + whose source text line is not None. + """ + return StackSummary.from_list(extracted_list).format() + +# +# Printing and Extracting Tracebacks. +# + +def print_tb(tb, limit=None, file=None): + """Print up to 'limit' stack trace entries from the traceback 'tb'. + + If 'limit' is omitted or None, all entries are printed. If 'file' + is omitted or None, the output goes to sys.stderr; otherwise + 'file' should be an open file or file-like object with a write() + method. + """ + print_list(extract_tb(tb, limit=limit), file=file) + +def format_tb(tb, limit=None): + """A shorthand for 'format_list(extract_tb(tb, limit))'.""" + return extract_tb(tb, limit=limit).format() + +def extract_tb(tb, limit=None): + """ + Return a StackSummary object representing a list of + pre-processed entries from traceback. + + This is useful for alternate formatting of stack traces. If + 'limit' is omitted or None, all entries are extracted. A + pre-processed stack trace entry is a FrameSummary object + containing attributes filename, lineno, name, and line + representing the information that is usually printed for a stack + trace. The line is a string with leading and trailing + whitespace stripped; if the source is not available it is None. + """ + return StackSummary.extract(walk_tb(tb), limit=limit) + +# +# Exception formatting and output. +# + +_cause_message = ( + "\nThe above exception was the direct cause " + "of the following exception:\n\n") + +_context_message = ( + "\nDuring handling of the above exception, " + "another exception occurred:\n\n") + + +def print_exception(etype, value, tb, limit=None, file=None, chain=True): + """Print exception up to 'limit' stack trace entries from 'tb' to 'file'. + + This differs from print_tb() in the following ways: (1) if + traceback is not None, it prints a header "Traceback (most recent + call last):"; (2) it prints the exception type and value after the + stack trace; (3) if type is SyntaxError and value has the + appropriate format, it prints the line where the syntax error + occurred with a caret on the next line indicating the approximate + position of the error. + """ + # format_exception has ignored etype for some time, and code such as cgitb + # passes in bogus values as a result. For compatibility with such code we + # ignore it here (rather than in the new TracebackException API). + if file is None: + file = sys.stderr + for line in TracebackException( + type(value), value, tb, limit=limit).format(chain=chain): + print(line, file=file, end="") + + +def format_exception(etype, value, tb, limit=None, chain=True): + """Format a stack trace and the exception information. + + The arguments have the same meaning as the corresponding arguments + to print_exception(). The return value is a list of strings, each + ending in a newline and some containing internal newlines. When + these lines are concatenated and printed, exactly the same text is + printed as does print_exception(). + """ + # format_exception has ignored etype for some time, and code such as cgitb + # passes in bogus values as a result. For compatibility with such code we + # ignore it here (rather than in the new TracebackException API). + return list(TracebackException( + type(value), value, tb, limit=limit).format(chain=chain)) + + +def format_exception_only(etype, value): + """Format the exception part of a traceback. + + The arguments are the exception type and value such as given by + sys.last_type and sys.last_value. The return value is a list of + strings, each ending in a newline. + + Normally, the list contains a single string; however, for + SyntaxError exceptions, it contains several lines that (when + printed) display detailed information about where the syntax + error occurred. + + The message indicating which exception occurred is always the last + string in the list. + + """ + return list(TracebackException(etype, value, None).format_exception_only()) + + +# -- not official API but folk probably use these two functions. + +def _format_final_exc_line(etype, value): + valuestr = _some_str(value) + if value is None or not valuestr: + line = "%s\n" % etype + else: + line = "%s: %s\n" % (etype, valuestr) + return line + +def _some_str(value): + try: + return str(value) + except: + return '' % type(value).__name__ + +# -- + +def print_exc(limit=None, file=None, chain=True): + """Shorthand for 'print_exception(*sys.exc_info(), limit, file)'.""" + print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain) + +def format_exc(limit=None, chain=True): + """Like print_exc() but return a string.""" + return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain)) + +def print_last(limit=None, file=None, chain=True): + """This is a shorthand for 'print_exception(sys.last_type, + sys.last_value, sys.last_traceback, limit, file)'.""" + if not hasattr(sys, "last_type"): + raise ValueError("no last exception") + print_exception(sys.last_type, sys.last_value, sys.last_traceback, + limit, file, chain) + +# +# Printing and Extracting Stacks. +# + +def print_stack(f=None, limit=None, file=None): + """Print a stack trace from its invocation point. + + The optional 'f' argument can be used to specify an alternate + stack frame at which to start. The optional 'limit' and 'file' + arguments have the same meaning as for print_exception(). + """ + if f is None: + f = sys._getframe().f_back + print_list(extract_stack(f, limit=limit), file=file) + + +def format_stack(f=None, limit=None): + """Shorthand for 'format_list(extract_stack(f, limit))'.""" + if f is None: + f = sys._getframe().f_back + return format_list(extract_stack(f, limit=limit)) + + +def extract_stack(f=None, limit=None): + """Extract the raw traceback from the current stack frame. + + The return value has the same format as for extract_tb(). The + optional 'f' and 'limit' arguments have the same meaning as for + print_stack(). Each item in the list is a quadruple (filename, + line number, function name, text), and the entries are in order + from oldest to newest stack frame. + """ + if f is None: + f = sys._getframe().f_back + stack = StackSummary.extract(walk_stack(f), limit=limit) + stack.reverse() + return stack + + +def clear_frames(tb): + "Clear all references to local variables in the frames of a traceback." + while tb is not None: + try: + tb.tb_frame.clear() + except RuntimeError: + # Ignore the exception raised if the frame is still executing. + pass + tb = tb.tb_next + + +class FrameSummary: + """A single frame from a traceback. + + - :attr:`filename` The filename for the frame. + - :attr:`lineno` The line within filename for the frame that was + active when the frame was captured. + - :attr:`name` The name of the function or method that was executing + when the frame was captured. + - :attr:`line` The text from the linecache module for the + of code that was running when the frame was captured. + - :attr:`locals` Either None if locals were not supplied, or a dict + mapping the name to the repr() of the variable. + """ + + __slots__ = ('filename', 'lineno', 'name', '_line', 'locals') + + def __init__(self, filename, lineno, name, *, lookup_line=True, + locals=None, line=None): + """Construct a FrameSummary. + + :param lookup_line: If True, `linecache` is consulted for the source + code line. Otherwise, the line will be looked up when first needed. + :param locals: If supplied the frame locals, which will be captured as + object representations. + :param line: If provided, use this instead of looking up the line in + the linecache. + """ + self.filename = filename + self.lineno = lineno + self.name = name + self._line = line + if lookup_line: + self.line + self.locals = {k: repr(v) for k, v in locals.items()} if locals else None + + def __eq__(self, other): + if isinstance(other, FrameSummary): + return (self.filename == other.filename and + self.lineno == other.lineno and + self.name == other.name and + self.locals == other.locals) + if isinstance(other, tuple): + return (self.filename, self.lineno, self.name, self.line) == other + return NotImplemented + + def __getitem__(self, pos): + return (self.filename, self.lineno, self.name, self.line)[pos] + + def __iter__(self): + return iter([self.filename, self.lineno, self.name, self.line]) + + def __repr__(self): + return "".format( + filename=self.filename, lineno=self.lineno, name=self.name) + + def __len__(self): + return 4 + + @property + def line(self): + if self._line is None: + self._line = linecache.getline(self.filename, self.lineno).strip() + return self._line + + +def walk_stack(f): + """Walk a stack yielding the frame and line number for each frame. + + This will follow f.f_back from the given frame. If no frame is given, the + current stack is used. Usually used with StackSummary.extract. + """ + if f is None: + f = sys._getframe().f_back.f_back + while f is not None: + yield f, f.f_lineno + f = f.f_back + + +def walk_tb(tb): + """Walk a traceback yielding the frame and line number for each frame. + + This will follow tb.tb_next (and thus is in the opposite order to + walk_stack). Usually used with StackSummary.extract. + """ + while tb is not None: + yield tb.tb_frame, tb.tb_lineno + tb = tb.tb_next + + +_RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. + +class StackSummary(list): + """A stack of frames.""" + + @classmethod + def extract(klass, frame_gen, *, limit=None, lookup_lines=True, + capture_locals=False): + """Create a StackSummary from a traceback or stack object. + + :param frame_gen: A generator that yields (frame, lineno) tuples to + include in the stack. + :param limit: None to include all frames or the number of frames to + include. + :param lookup_lines: If True, lookup lines for each frame immediately, + otherwise lookup is deferred until the frame is rendered. + :param capture_locals: If True, the local variables from each frame will + be captured as object representations into the FrameSummary. + """ + if limit is None: + limit = getattr(sys, 'tracebacklimit', None) + if limit is not None and limit < 0: + limit = 0 + if limit is not None: + if limit >= 0: + frame_gen = islice(frame_gen, limit) + else: + frame_gen = collections.deque(frame_gen, maxlen=-limit) + + result = klass() + fnames = set() + for f, lineno in frame_gen: + co = f.f_code + filename = co.co_filename + name = co.co_name + + fnames.add(filename) + linecache.lazycache(filename, f.f_globals) + # Must defer line lookups until we have called checkcache. + if capture_locals: + f_locals = f.f_locals + else: + f_locals = None + result.append(FrameSummary( + filename, lineno, name, lookup_line=False, locals=f_locals)) + for filename in fnames: + linecache.checkcache(filename) + # If immediate lookup was desired, trigger lookups now. + if lookup_lines: + for f in result: + f.line + return result + + @classmethod + def from_list(klass, a_list): + """ + Create a StackSummary object from a supplied list of + FrameSummary objects or old-style list of tuples. + """ + # While doing a fast-path check for isinstance(a_list, StackSummary) is + # appealing, idlelib.run.cleanup_traceback and other similar code may + # break this by making arbitrary frames plain tuples, so we need to + # check on a frame by frame basis. + result = StackSummary() + for frame in a_list: + if isinstance(frame, FrameSummary): + result.append(frame) + else: + filename, lineno, name, line = frame + result.append(FrameSummary(filename, lineno, name, line=line)) + return result + + def format(self): + """Format the stack ready for printing. + + Returns a list of strings ready for printing. Each string in the + resulting list corresponds to a single frame from the stack. + Each string ends in a newline; the strings may contain internal + newlines as well, for those items with source text lines. + + For long sequences of the same frame and line, the first few + repetitions are shown, followed by a summary line stating the exact + number of further repetitions. + """ + result = [] + last_file = None + last_line = None + last_name = None + count = 0 + for frame in self: + if (last_file is None or last_file != frame.filename or + last_line is None or last_line != frame.lineno or + last_name is None or last_name != frame.name): + if count > _RECURSIVE_CUTOFF: + count -= _RECURSIVE_CUTOFF + result.append(( + ' [Previous line repeated {count} more ' + 'time{s_count}]\n' + ).format(count=count, s_count="s" if count > 1 else "")) + last_file = frame.filename + last_line = frame.lineno + last_name = frame.name + count = 0 + count += 1 + if count > _RECURSIVE_CUTOFF: + continue + row = [] + row.append(' File "{}", line {}, in {}\n'.format( + frame.filename, frame.lineno, frame.name)) + if frame.line: + row.append(' {}\n'.format(frame.line.strip())) + if frame.locals: + for name, value in sorted(frame.locals.items()): + row.append(' {name} = {value}\n'.format(name=name, value=value)) + result.append(''.join(row)) + if count > _RECURSIVE_CUTOFF: + count -= _RECURSIVE_CUTOFF + result.append(( + ' [Previous line repeated {count} more ' + 'time{s_count}]\n' + ).format(count=count, s_count='s' if count > 1 else '')) + return result + + +class TracebackException: + """An exception ready for rendering. + + The traceback module captures enough attributes from the original exception + to this intermediary form to ensure that no references are held, while + still being able to fully print or format it. + + Use `from_exception` to create TracebackException instances from exception + objects, or the constructor to create TracebackException instances from + individual components. + + - :attr:`__cause__` A TracebackException of the original *__cause__*. + - :attr:`__context__` A TracebackException of the original *__context__*. + - :attr:`__suppress_context__` The *__suppress_context__* value from the + original exception. + - :attr:`stack` A `StackSummary` representing the traceback. + - :attr:`exc_type` The class of the original traceback. + - :attr:`filename` For syntax errors - the filename where the error + occurred. + - :attr:`lineno` For syntax errors - the linenumber where the error + occurred. + - :attr:`text` For syntax errors - the text where the error + occurred. + - :attr:`offset` For syntax errors - the offset into the text where the + error occurred. + - :attr:`msg` For syntax errors - the compiler error message. + """ + + def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, + lookup_lines=True, capture_locals=False, _seen=None): + # NB: we need to accept exc_traceback, exc_value, exc_traceback to + # permit backwards compat with the existing API, otherwise we + # need stub thunk objects just to glue it together. + # Handle loops in __cause__ or __context__. + if _seen is None: + _seen = set() + _seen.add(id(exc_value)) + # Gracefully handle (the way Python 2.4 and earlier did) the case of + # being called with no type or value (None, None, None). + if (exc_value and exc_value.__cause__ is not None + and id(exc_value.__cause__) not in _seen): + cause = TracebackException( + type(exc_value.__cause__), + exc_value.__cause__, + exc_value.__cause__.__traceback__, + limit=limit, + lookup_lines=False, + capture_locals=capture_locals, + _seen=_seen) + else: + cause = None + if (exc_value and exc_value.__context__ is not None + and id(exc_value.__context__) not in _seen): + context = TracebackException( + type(exc_value.__context__), + exc_value.__context__, + exc_value.__context__.__traceback__, + limit=limit, + lookup_lines=False, + capture_locals=capture_locals, + _seen=_seen) + else: + context = None + self.exc_traceback = exc_traceback + self.__cause__ = cause + self.__context__ = context + self.__suppress_context__ = \ + exc_value.__suppress_context__ if exc_value else False + # TODO: locals. + self.stack = StackSummary.extract( + walk_tb(exc_traceback), limit=limit, lookup_lines=lookup_lines, + capture_locals=capture_locals) + self.exc_type = exc_type + # Capture now to permit freeing resources: only complication is in the + # unofficial API _format_final_exc_line + self._str = _some_str(exc_value) + if exc_type and issubclass(exc_type, SyntaxError): + # Handle SyntaxError's specially + self.filename = exc_value.filename + self.lineno = str(exc_value.lineno) + self.text = exc_value.text + self.offset = exc_value.offset + self.msg = exc_value.msg + if lookup_lines: + self._load_lines() + + @classmethod + def from_exception(cls, exc, *args, **kwargs): + """Create a TracebackException from an exception.""" + return cls(type(exc), exc, exc.__traceback__, *args, **kwargs) + + def _load_lines(self): + """Private API. force all lines in the stack to be loaded.""" + for frame in self.stack: + frame.line + if self.__context__: + self.__context__._load_lines() + if self.__cause__: + self.__cause__._load_lines() + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + def __str__(self): + return self._str + + def format_exception_only(self): + """Format the exception part of the traceback. + + The return value is a generator of strings, each ending in a newline. + + Normally, the generator emits a single string; however, for + SyntaxError exceptions, it emites several lines that (when + printed) display detailed information about where the syntax + error occurred. + + The message indicating which exception occurred is always the last + string in the output. + """ + if self.exc_type is None: + yield _format_final_exc_line(None, self._str) + return + + stype = self.exc_type.__qualname__ + smod = self.exc_type.__module__ + if smod not in ("__main__", "builtins"): + stype = smod + '.' + stype + + if not issubclass(self.exc_type, SyntaxError): + yield _format_final_exc_line(stype, self._str) + return + + # It was a syntax error; show exactly where the problem was found. + filename = self.filename or "" + lineno = str(self.lineno) or '?' + yield ' File "{}", line {}\n'.format(filename, lineno) + + badline = self.text + offset = self.offset + if badline is not None: + yield ' {}\n'.format(badline.strip()) + if offset is not None: + caretspace = badline.rstrip('\n') + offset = min(len(caretspace), offset) - 1 + caretspace = caretspace[:offset].lstrip() + # non-space whitespace (likes tabs) must be kept for alignment + caretspace = ((c.isspace() and c or ' ') for c in caretspace) + yield ' {}^\n'.format(''.join(caretspace)) + msg = self.msg or "" + yield "{}: {}\n".format(stype, msg) + + def format(self, *, chain=True): + """Format the exception. + + If chain is not *True*, *__cause__* and *__context__* will not be formatted. + + The return value is a generator of strings, each ending in a newline and + some containing internal newlines. `print_exception` is a wrapper around + this method which just prints the lines to a file. + + The message indicating which exception occurred is always the last + string in the output. + """ + if chain: + if self.__cause__ is not None: + #yield from self.__cause__.format(chain=chain) + for g in self.__cause__.format(chain=chain): + yield g + yield _cause_message + elif (self.__context__ is not None and + not self.__suppress_context__): + #yield from self.__context__.format(chain=chain) + for g in self.__cause__.format(chain=chain): + yield g + yield _context_message + if self.exc_traceback is not None: + yield 'Traceback (most recent call last):\n' + #yield from self.stack.format() + for g in self.stack.format(): + yield g + #yield from self.format_exception_only() + for g in self.format_exception_only(): + yield g diff --git a/src/lib/unittest/mock.py b/src/lib/unittest/mock.py new file mode 100644 index 0000000000..379ba4e30d --- /dev/null +++ b/src/lib/unittest/mock.py @@ -0,0 +1,5 @@ +def pass_through(*args, **kwargs): + return pass_through(*args, **kwargs) + +patch = pass_through +patch.dict = pass_through \ No newline at end of file diff --git a/src/symtable.js b/src/symtable.js index d19b9fdb46..e9e82029ab 100644 --- a/src/symtable.js +++ b/src/symtable.js @@ -729,6 +729,10 @@ SymbolTable.prototype.visitExpr = function (e) { throw new Sk.builtin.SyntaxError("'return' with argument inside generator", this.filename); } break; + case Sk.astnodes.YieldFrom: + this.visitExpr(e.value); + this.cur.generator = true; + break; case Sk.astnodes.Compare: this.visitExpr(e.left); this.SEQExpr(e.comparators); diff --git a/support/build/wrapmodules.js b/support/build/wrapmodules.js index b3e8b99ba4..6fd73bcbd6 100644 --- a/support/build/wrapmodules.js +++ b/support/build/wrapmodules.js @@ -2,6 +2,7 @@ const fs = require('fs'); const path = require('path'); const minify = require('babel-minify'); +/* const reqskulpt = require('../run/require-skulpt').requireSkulpt; var skulpt = reqskulpt(); Sk.configure({__future__: Sk.python3}); @@ -11,7 +12,7 @@ function endsWithAny(string, suffixes) { return suffixes.some(function (suffix) { return string.endsWith(suffix); }); -} +}*/ /** * If this optional file exists in the top level directory, it will be diff --git a/test/test_pedal.py b/test/test_pedal.py index af6862cd7a..0940ec51dc 100644 --- a/test/test_pedal.py +++ b/test/test_pedal.py @@ -5,6 +5,8 @@ def click(phase): diff = time.time() - stopwatch print("Phase {}: {} secs".format(phase, round(diff, 2))) stopwatch = time.time() +# import time; stopwatch = time.time(); +# print("Phase {}: {} secs".format(phase, round(time.time() - stopwatch, 2))) ; stopwatch = time.time() import pedal click("Imported pedal") From c26b8aee0d21ff4d2d680a23e9504cf7c69ef37d Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 10 Jul 2019 12:49:59 -0400 Subject: [PATCH 14/68] Test IO --- test/test_io.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 test/test_io.py diff --git a/test/test_io.py b/test/test_io.py new file mode 100644 index 0000000000..f6a9fe829b --- /dev/null +++ b/test/test_io.py @@ -0,0 +1 @@ +import io \ No newline at end of file From 0534d92702336f6007ca688d02571b6a09ba0d1b Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 14 Jul 2019 16:57:09 -0400 Subject: [PATCH 15/68] Pedal tifa stuff --- src/lib/pedal/__init__.py | 7 ------- src/lib/pedal/tifa/__init__.py | 3 +-- src/lib/pedal/tifa/tifa.py | 6 +----- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/lib/pedal/__init__.py b/src/lib/pedal/__init__.py index 26439398c7..27602eb32e 100644 --- a/src/lib/pedal/__init__.py +++ b/src/lib/pedal/__init__.py @@ -11,21 +11,14 @@ # resolver # etc. -import time; stopwatch = time.time(); - from pedal.cait import (find_match, find_matches, parse_program, find_submatches, find_expr_sub_matches, def_use_error, data_state, data_type, expire_cait_cache) -print("Phase {}: {} secs".format("Cait loaded", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() from pedal.report.imperative import (suppress, explain, compliment, give_partial, gently, set_success) -print("Phase {}: {} secs".format("Imperative loaded", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() from pedal.sandbox.sandbox import run, reset -print("Phase {}: {} secs".format("Sandbox loaded", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() from pedal.tifa import tifa_analysis -print("Phase {}: {} secs".format("Tifa loaded", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() from pedal.source import (set_source, check_section_exists, next_section, set_source_file) -print("Phase {}: {} secs".format("Source loaded", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() diff --git a/src/lib/pedal/tifa/__init__.py b/src/lib/pedal/tifa/__init__.py index 8b1bdb934d..f0fe320220 100644 --- a/src/lib/pedal/tifa/__init__.py +++ b/src/lib/pedal/tifa/__init__.py @@ -68,9 +68,8 @@ Name Map (Path x Fully Qualified Names) => States """ -import time; stopwatch = time.time(); + from pedal.tifa.tifa import Tifa -print("Phase {}: {} secs".format("Tifa.Tifa loaded", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() from pedal.report import MAIN_REPORT NAME = 'TIFA' diff --git a/src/lib/pedal/tifa/tifa.py b/src/lib/pedal/tifa/tifa.py index 93e86cc626..aa44677a1b 100644 --- a/src/lib/pedal/tifa/tifa.py +++ b/src/lib/pedal/tifa/tifa.py @@ -1,8 +1,6 @@ -import time; stopwatch = time.time(); -print("Phase {}: {} secs".format("Tifa.Tifa Before all imports", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() import ast from pprint import pprint -print("Phase {}: {} secs".format("Tifa.Tifa Before import", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() + from pedal.report import MAIN_REPORT from pedal.tifa.type_definitions import (UnknownType, RecursedType, @@ -1239,5 +1237,3 @@ def __exit__(self, type, value, traceback): self.tifa.scope_chain.pop(0) # Restore the scope self.tifa.scope_chain = self.old_scope - -print("Phase {}: {} secs".format("Tifa.Tifa Evaled", round(time.time() - stopwatch, 2))) ; stopwatch = time.time() \ No newline at end of file From 5647447dab21b0ef3dd0d8b325634b7946687ada Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 1 Aug 2019 14:08:52 -0400 Subject: [PATCH 16/68] Fix bunch of things --- .gitignore | 1 + loose_compile.py | 130 + src/builtindict.js | 1 + src/compile.js | 15 +- src/env.js | 7 - src/errors.js | 31 + src/import.js | 4 +- src/lib/ast.js | 81 +- src/lib/dataclasses.js | 5 + src/lib/matplotlib/pyplot/__init__.js | 841 +++--- src/lib/pedal/sandbox/mocked.py | 12 +- src/lib/pedal/sandbox/sandbox.py | 37 +- src/lib/pedal/tifa/tifa.py | 19 +- src/lib/turtle.js | 3881 ++++++++++++------------- src/object.js | 10 + src/parser.js | 4 +- src/print.js | 2 +- src/tokenize.js | 95 +- test/decorators.py | 41 + test/fix_dunder_class.py | 1 + test/test_ast.py | 3 +- test/test_ast2.py | 5 + 22 files changed, 2733 insertions(+), 2493 deletions(-) create mode 100644 loose_compile.py create mode 100644 src/lib/dataclasses.js create mode 100644 test/decorators.py create mode 100644 test/fix_dunder_class.py create mode 100644 test/test_ast2.py diff --git a/.gitignore b/.gitignore index 70e3c4d5fa..b631a3f8f5 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ src/internalpython.js Pipfile package-lock.json *~ +/deleteme.txt diff --git a/loose_compile.py b/loose_compile.py new file mode 100644 index 0000000000..7f50224a5c --- /dev/null +++ b/loose_compile.py @@ -0,0 +1,130 @@ +import sys +import os + +from ast import NodeVisitor, parse + +class SkulptCompiler(NodeVisitor): + def __init__(self): + self.indent = 0 + self.unhandled = set() + self.result = [] + self.stack = [] + + def generic_visit(self, node): + self.unhandled.add(type(node).__name__) + return NodeVisitor.generic_visit(self, node) + + def add_statement(self, line): + self.result.append(" "*self.indent + line) + + def enter(self, name): + self.indent += 1 + self.stack.append(name) + + def exit(self): + self.indent -= 1 + self.stack.pop() + + def visit_Module(self, node): + self.add_statement("var $builtinmodule = function (name) {") + self.enter("mod") + self.add_statement("let mod = {};") + for statement in node.body: + self.visit(statement) + self.add_statement("return mod;") + self.exit() + self.add_statement("};") + + @property + def context(self): + return self.stack[-1] + + def visit_ClassDef(self, node): + owner = self.context + self.add_statement( + "{owner}.{name} = Sk.misceval.buildClass({owner}, function($gbl, $loc) {{".format( + owner=owner, name=node.name)) + self.enter("$loc") + for statement in node.body: + self.visit(statement) + self.exit() + bases = ", ".join(self.visit(base) for base in node.bases) + self.add_statement("}}, '{name}', [{bases}], {{}});".format( + name=node.name, bases=bases + )) + + def visit_Name(self, node): + return "Sk.misceval.loadname('{}', $gbl)".format(node.id) + + def visit_FunctionDef(self, node): + owner = self.context + args = ", ".join(arg.arg for arg in node.args.args) + str_args = ", ".join("'{}'".format(arg.arg) for arg in node.args.args) + defaults = ", ".join(self.visit(default) for default in node.args.defaults) + self.add_statement( + "var _{name} = new Sk.builtin.func(function({args}) {{".format( + name=node.name, args=args)) + self.enter(node.name) + for statement in node.body: + self.visit(statement) + self.exit() + self.add_statement("}});") + self.add_statement("_{name}.co_varnames = [{args}]".format( + name=node.name, args=str_args + )) + self.add_statement("_{name}.$defaults = [{defaults}]".format( + name=node.name, defaults=defaults + )) + self.add_statement("{owner}.{name} = _{name}".format( + owner=owner, name=node.name + )) + + def visit_Assign(self, node): + # handle multiple targets + target = node.targets[0] + value = self.visit(node.value) + if type(target).__name__ == 'Attribute': + self.add_statement(self.visit_Attribute(target).format(value=value)) + + def visit_Str(self, node): + return "new Sk.builtins.str('{}')".format(node.s) + + def visit_Num(self, node): + return "new Sk.builtin.int_({})".format(node.n) + + def visit_NameConstant(self, node): + if node.value == True: + return "Sk.builtin.bool.true$" + elif node.value == False: + return "Sk.builtin.bool.false$" + elif node.value == None: + return "Sk.builtin.none.none$" + + def visit_Expr(self, node): + self.add_statement(self.visit(node.value)) + + def visit_Attribute(self, node): + owner = self.visit(node.value) + attr = "Sk.builtins.str('{}')".format(node.attr) + if type(node.ctx).__name__ == "Store": + return "Sk.abstr.sattr({owner}, {attr}, {{value}}, true);".format( + owner=owner, attr=attr + ) + elif type(node.ctx).__name__ == "Load": + return "Sk.abstr.gattr({owner}, {attr}, true)".format(owner=owner, attr=attr) + else: # Del + pass + + + def visit_Return(self, node): + self.add_statement("return {};".format(self.visit(node.value))); + +if __name__ == '__main__': + target = sys.argv[1] + with open(target, 'r') as target_file: + code = target_file.read() + parsed = parse(code) + compiler = SkulptCompiler() + compiled = compiler.visit(parsed) + print("\n".join(compiler.result)) + print(compiler.unhandled) \ No newline at end of file diff --git a/src/builtindict.js b/src/builtindict.js index 9fa053dd40..75252edbcb 100644 --- a/src/builtindict.js +++ b/src/builtindict.js @@ -36,6 +36,7 @@ Sk.builtins = { "ValueError" : Sk.builtin.ValueError, "Exception" : Sk.builtin.Exception, "ZeroDivisionError" : Sk.builtin.ZeroDivisionError, + "SyntaxError" : Sk.builtin.SyntaxError, "AssertionError" : Sk.builtin.AssertionError, "ImportError" : Sk.builtin.ImportError, "IndentationError" : Sk.builtin.IndentationError, diff --git a/src/compile.js b/src/compile.js index 082fbaba4b..3863cc4dd4 100644 --- a/src/compile.js +++ b/src/compile.js @@ -116,7 +116,7 @@ Compiler.prototype.annotateSource = function (ast, shouldStep) { // Do not trace the standard library if (shouldStep && (!this.filename || !this.filename.startsWith('src/lib/'))) { - out("Sk.afterSingleExecution($gbl,"+lineno+","+col_offset+","+this.filename+");\n"); + out("Sk.afterSingleExecution($gbl,"+lineno+","+col_offset+","+JSON.stringify(this.filename)+");\n"); } } }; @@ -2371,8 +2371,10 @@ Compiler.prototype.vstmt = function (s, class_for_super) { } break; case Sk.astnodes.AnnAssign: - val = this.vexpr(s.value); - this.vexpr(s.target, val); + if (s.value !== null) { + val = this.vexpr(s.value); + this.vexpr(s.target, val); + } this.vexpr(s.annotation); break; case Sk.astnodes.AugAssign: @@ -2697,7 +2699,7 @@ Compiler.prototype.cmod = function (mod) { this.u.varDeclsCode += "if ("+modf+".$wakingSuspension!==undefined) { $wakeFromSuspension(); }" + "if (Sk.retainGlobals) {" + - " if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; }" + + //" if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; }" + " if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; $loc.__file__=new Sk.builtins.str('" + this.filename + "');}" + " else { Sk.globals = $gbl; }" + "} else { Sk.globals = $gbl; }"; @@ -2749,8 +2751,9 @@ Compiler.prototype.cmod = function (mod) { * @param {string} filename where it came from * @param {string} mode one of 'exec', 'eval', or 'single' * @param {boolean=} canSuspend if the generated code supports suspension + * @param {boolean=} annotate Whether or not to annotate the source code */ -Sk.compile = function (source, filename, mode, canSuspend) { +Sk.compile = function (source, filename, mode, canSuspend, annotate) { //print("FILE:", filename); // __future__ flags can be set from code // (with "from __future__ import ..." statements), @@ -2767,7 +2770,7 @@ Sk.compile = function (source, filename, mode, canSuspend) { flags.cf_flags = parse.flags; var st = Sk.symboltable(ast, filename); - var c = new Compiler(filename, st, flags.cf_flags, canSuspend, source); // todo; CO_xxx + var c = new Compiler(filename, st, flags.cf_flags, canSuspend, annotate ? source : false); // todo; CO_xxx var funcname = c.cmod(ast); // Restore the global __future__ flags diff --git a/src/env.js b/src/env.js index 1d77e406a4..9365445d4c 100644 --- a/src/env.js +++ b/src/env.js @@ -214,13 +214,6 @@ Sk.configure = function (options) { Sk.exportSymbol("Sk.configure", Sk.configure); -/* -* Replaceable handler for uncaught exceptions -*/ -Sk.uncaughtException = function(err) { - throw err; -}; - /* * Replaceable handler for uncaught exceptions */ diff --git a/src/errors.js b/src/errors.js index 051ad4f662..ecb3f11059 100644 --- a/src/errors.js +++ b/src/errors.js @@ -91,6 +91,14 @@ Sk.builtin.BaseException.prototype.args = { } }; +/* +Sk.builtin.BaseException.prototype.tp$getattr = function(pyName, canSuspend) { + switch (Sk.ffi.remapToJs(pyName)) { + case "__class__": return this.__class__; + default: return undefined; + } +};*/ + Sk.exportSymbol("Sk.builtin.BaseException", Sk.builtin.BaseException); /** @@ -286,8 +294,30 @@ Sk.builtin.SyntaxError = function (args) { return o; } Sk.builtin.StandardError.apply(this, arguments); + if (arguments.length >= 3) { + this.lineno = Sk.ffi.remapToPy(arguments[2]); + } else { + this.lineno = Sk.ffi.remapToPy(null); + } }; Sk.abstr.setUpInheritance("SyntaxError", Sk.builtin.SyntaxError, Sk.builtin.StandardError); +Sk.builtin.SyntaxError.prototype.tp$getattr = function (name) { + if (name != null && (Sk.builtin.checkString(name) || typeof name === "string")) { + var _name = name; + + // get javascript string + if (Sk.builtin.checkString(name)) { + _name = Sk.ffi.remapToJs(name); + } + + if (_name === "lineno") { + return this[_name]; + } + } + + // if we have not returned yet, try the genericgetattr + return Sk.builtin.object.prototype.GenericGetAttr(name); +}; /** * @constructor @@ -356,6 +386,7 @@ Sk.builtin.TypeError = function (args) { return o; } Sk.builtin.StandardError.apply(this, arguments); + this.__class__ = Sk.builtin.TypeError; }; Sk.abstr.setUpInheritance("TypeError", Sk.builtin.TypeError, Sk.builtin.StandardError); Sk.exportSymbol("Sk.builtin.TypeError", Sk.builtin.TypeError); diff --git a/src/import.js b/src/import.js index 7532b4a552..1ec63e2478 100644 --- a/src/import.js +++ b/src/import.js @@ -241,7 +241,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela if (suppliedPyBody) { filename = name + ".py"; - co = Sk.compile(suppliedPyBody, filename, "exec", canSuspend); + co = Sk.compile(suppliedPyBody, filename, "exec", canSuspend, true); } else { co = Sk.misceval.chain(undefined, function() { // If an onBeforeImport method is supplied, call it and if @@ -272,7 +272,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela return Sk.misceval.chain(Sk.importSearchPathForName(searchFileName, ".py", searchPath), function(codeAndPath_) { codeAndPath = codeAndPath_; // We'll want it in a moment if (codeAndPath) { - return Sk.compile(codeAndPath.code, codeAndPath.filename, "exec", canSuspend); + return Sk.compile(codeAndPath.code, codeAndPath.filename, "exec", canSuspend, true); } }, function(co) { if (co) { diff --git a/src/lib/ast.js b/src/lib/ast.js index 2947ca8960..da59b28840 100644 --- a/src/lib/ast.js +++ b/src/lib/ast.js @@ -1,5 +1,12 @@ var $builtinmodule = function (name) { var mod = {}; + + function mangleAppropriately(name) { + switch (name) { + case "name": return "name_$rn$"; + default: return name; + } + } /** * Consumes an AST Node (JS version). Return a list of tuples of @@ -15,7 +22,7 @@ var $builtinmodule = function (name) { } } return fieldList; - } + }; mod.iter_fields = function(node) { return node._fields; @@ -27,7 +34,7 @@ var $builtinmodule = function (name) { } else if (isSpecialPyAst(value)) { var constructorName = functionName(value); return Sk.misceval.callsim(mod[constructorName], constructorName, true); - } else if (typeof value == 'number') { + } else if (typeof value == "number") { return Sk.builtin.assk$(value); } else if (Array === value.constructor) { var subvalues = []; @@ -47,16 +54,16 @@ var $builtinmodule = function (name) { } return Sk.builtin.list(subvalues); } else if (isJsAst(value)) { - var constructorName = functionName(value.constructor) + var constructorName = functionName(value.constructor); return Sk.misceval.callsim(mod[constructorName], value); } else {// Else already a Python value return value; } - } + }; var isJsAst = function(jsNode) { return jsNode instanceof Object && "_astname" in jsNode; - } + }; var isSpecialPyAst = function(val) { if (typeof val == "function") { switch (functionName(val)) { @@ -75,7 +82,7 @@ var $builtinmodule = function (name) { } else { return false; } - } + }; var isPyAst = function(pyValue) { return Sk.misceval.isTrue(Sk.builtin.isinstance(pyValue, mod.AST)); }; @@ -103,7 +110,7 @@ var $builtinmodule = function (name) { } } return resultList; - } + }; // Python node mod.iter_child_nodes = function(node) { @@ -175,7 +182,7 @@ var $builtinmodule = function (name) { if (include_attributes) { for (var i = 0; i < attributeList.length; i += 1) { var field = Sk.ffi.remapToJs(attributeList[i]); - var value = Sk.ffi.remapToJs(node.jsNode[field]) + var value = Sk.ffi.remapToJs(node.jsNode[field]); fieldArgs.push(field+"="+value); } } @@ -183,14 +190,14 @@ var $builtinmodule = function (name) { return rv + fieldArgs + ")"; } else if (isPyList(node)) { var nodes = node.v.map(_format); - nodes = nodes.join(', '); + nodes = nodes.join(", "); return "["+nodes+"]"; } else { return Sk.ffi.remapToJs(node.$r()); } - } + }; return Sk.ffi.remapToPy(_format(node, 0)); - } + }; var depth = 0; var NodeVisitor = function($gbl, $loc) { @@ -199,10 +206,10 @@ var $builtinmodule = function (name) { depth += 1; /** Visit a node. **/ //print(" ".repeat(depth), "VISIT", node.jsNode._astname) - var method_name = 'visit_' + node.jsNode._astname; + var method_name = "visit_" + node.jsNode._astname; //print(" ".repeat(depth), "I'm looking for", method_name) - method_name = Sk.ffi.remapToPy(method_name) - method = Sk.builtin.getattr(self, method_name, $loc.generic_visit) + method_name = Sk.ffi.remapToPy(method_name); + method = Sk.builtin.getattr(self, method_name, $loc.generic_visit); if (method.im_self) { //print(method.im_func.func_code) result = Sk.misceval.callsim(method, node); @@ -239,7 +246,7 @@ var $builtinmodule = function (name) { } return Sk.builtin.none.none$; }); - } + }; mod.NodeVisitor = Sk.misceval.buildClass(mod, NodeVisitor, "NodeVisitor", []); // Python node @@ -255,7 +262,7 @@ var $builtinmodule = function (name) { resultList = resultList.concat(children.v); } return Sk.builtin.list(resultList); - } + }; /*NodeVisitor.prototype.visitList = function(nodes) { for (var j = 0; j < nodes.length; j += 1) { @@ -290,12 +297,12 @@ var $builtinmodule = function (name) { if (partial === true) { // Alternative constructor for Skulpt's weird nodes //console.log(" ".repeat(depth)+"S:", jsNode); - self.jsNode = {'_astname': jsNode}; + self.jsNode = {"_astname": jsNode}; self.astname = jsNode; self._fields = Sk.builtin.list([]); self._attributes = Sk.builtin.list([]); - Sk.abstr.sattr(self, Sk.builtin.str('_fields'), self._fields, true); - Sk.abstr.sattr(self, Sk.builtin.str('_attributes'), self._attributes, true); + Sk.abstr.sattr(self, Sk.builtin.str("_fields"), self._fields, true); + Sk.abstr.sattr(self, Sk.builtin.str("_attributes"), self._attributes, true); //console.log(" ".repeat(depth)+"--", jsNode); } else { //console.log(" ".repeat(depth)+"P:", jsNode._astname); @@ -308,23 +315,25 @@ var $builtinmodule = function (name) { for (var i = 0; i < fieldListJs.length; i += 1) { var field = fieldListJs[i][0], value = fieldListJs[i][1]; //console.log(" ".repeat(depth+1)+field, value) - if (field === 'docstring' && value === undefined) { - value = Sk.builtin.str(''); + if (field === "docstring" && value === undefined) { + value = Sk.builtin.str(""); } else { value = convertValue(value); } + field = mangleAppropriately(field); Sk.abstr.sattr(self, Sk.builtin.str(field), value, true); + // TODO: Figure out why name is getting manged, and make it stop! self._fields.push(Sk.builtin.tuple([Sk.builtin.str(field), value])); } //console.log(" ".repeat(depth)+"FIELDS") - self._fields = Sk.builtin.list(self._fields) - Sk.abstr.sattr(self, Sk.builtin.str('_fields'), self._fields, true); - copyFromJsNode(self, 'lineno', self.jsNode); - copyFromJsNode(self, 'col_offset', self.jsNode); - copyFromJsNode(self, 'end_lineno', self.jsNode); - copyFromJsNode(self, 'end_col_offset', self.jsNode); + self._fields = Sk.builtin.list(self._fields); + Sk.abstr.sattr(self, Sk.builtin.str("_fields"), self._fields, true); + copyFromJsNode(self, "lineno", self.jsNode); + copyFromJsNode(self, "col_offset", self.jsNode); + copyFromJsNode(self, "end_lineno", self.jsNode); + copyFromJsNode(self, "end_col_offset", self.jsNode); self._attributes = Sk.builtin.list(self._attributes); - Sk.abstr.sattr(self, Sk.builtin.str('_attributes'), self._attributes, true); + Sk.abstr.sattr(self, Sk.builtin.str("_attributes"), self._attributes, true); //console.log(" ".repeat(depth)+"--", jsNode._astname); } depth -= 1; @@ -333,7 +342,7 @@ var $builtinmodule = function (name) { return Sk.builtin.str("<_ast."+self.astname+" object>"); }); $loc.__repr__ = $loc.__str__; - } + }; mod.AST = Sk.misceval.buildClass(mod, AST, "AST", []); //mod.literal_eval @@ -344,13 +353,13 @@ var $builtinmodule = function (name) { return Sk.misceval.callsim(mod.Module, new Sk.INHERITANCE_MAP.mod[0]([])); } if (filename === undefined) { - filename = Sk.builtin.str(''); + filename = Sk.builtin.str(""); } var parse = Sk.parse(filename, Sk.ffi.remapToJs(source)); ast = Sk.astFromParse(parse.cst, filename, parse.flags); return Sk.misceval.callsim(mod.Module, ast); // Walk tree and create nodes (lazily?) - } + }; /* mod.Module = function ($gbl, $loc) { @@ -359,12 +368,12 @@ var $builtinmodule = function (name) { function functionName(fun) { var ret = fun.toString(); - ret = ret.substr('function '.length); - ret = ret.substr(0, ret.indexOf('(')); + ret = ret.substr("function ".length); + ret = ret.substr(0, ret.indexOf("(")); if (ret == "In_") { ret = "In"; - } else if (ret == 'Import_') { - ret = 'Import'; + } else if (ret == "Import_") { + ret = "Import"; } return ret; } @@ -376,7 +385,7 @@ var $builtinmodule = function (name) { var nodeType = Sk.INHERITANCE_MAP[base][i]; var nodeName = nodeType.prototype._astname; var nodeClass = function($gbl, $loc) { return this;}; - mod[nodeName] = Sk.misceval.buildClass(mod, nodeClass, nodeName, [mod[base]]) + mod[nodeName] = Sk.misceval.buildClass(mod, nodeClass, nodeName, [mod[base]]); } } diff --git a/src/lib/dataclasses.js b/src/lib/dataclasses.js new file mode 100644 index 0000000000..08d8aeff64 --- /dev/null +++ b/src/lib/dataclasses.js @@ -0,0 +1,5 @@ +let $builtinmodule = function (name) { + let mod = {}; + + return mod; +}; \ No newline at end of file diff --git a/src/lib/matplotlib/pyplot/__init__.js b/src/lib/matplotlib/pyplot/__init__.js index 0cccd416bb..5573d7119e 100644 --- a/src/lib/matplotlib/pyplot/__init__.js +++ b/src/lib/matplotlib/pyplot/__init__.js @@ -17,16 +17,16 @@ var $builtinmodule = function(name) { chart = null; colorCycle = 0; labels = { - 'title': '', - 'x-axis': '', - 'y-axis': '' + "title": "", + "x-axis": "", + "y-axis": "" }; plots = []; extents = { - 'xMin': null, - 'yMin': null, - 'xMax': null, - 'yMax': null + "xMin": null, + "yMin": null, + "xMax": null, + "yMax": null }; } resetChart(); @@ -46,10 +46,10 @@ var $builtinmodule = function(name) { chartCounter += 1; chart = {}; - chart.margin = {'top': 20, 'right': 30, 'bottom': 50, 'left': 40}; + chart.margin = {"top": 20, "right": 30, "bottom": 50, "left": 40}; chart.width = Sk.console.width - chart.margin.left - chart.margin.right; chart.height = Sk.console.height - chart.margin.top - chart.margin.bottom; - chart.id = 'chart' + chartCounter; + chart.id = "chart" + chartCounter; chart.type = type; if (Sk.console.skipDrawing) { @@ -64,12 +64,12 @@ var $builtinmodule = function(name) { if (extents[attr+"Min"] === null) { extents[attr+"Min"] = d3.min(array); } else { - extents[attr+"Min"] = Math.min(d3.min(array), extents[attr+"Min"]) + extents[attr+"Min"] = Math.min(d3.min(array), extents[attr+"Min"]); } if (extents[attr+"Max"] === null) { extents[attr+"Max"] = d3.max(array); } else { - extents[attr+"Max"] = Math.max(d3.max(array), extents[attr+"Max"]) + extents[attr+"Max"] = Math.max(d3.max(array), extents[attr+"Max"]); } } @@ -121,20 +121,20 @@ var $builtinmodule = function(name) { if (xdata === null) { xdata = []; for (var i = 0; i < ydata.length; i++) { - xdata.push(i); + xdata.push(i); } } // empty canvas from previous plots - createChart('line'); + createChart("line"); // Zip up the data var actualData = d3.zip(xdata, ydata).map(function(e) { - return {'x': e[0], 'y': e[1]} + return {"x": e[0], "y": e[1]}; }); // Parse formatting, also keep ColorCycler updated var cycle = jsplotlib.rc["axes.color_cycle"]; - var linestyle = '-', marker= '', + var linestyle = "-", marker= "", color = cycle[colorCycle % cycle.length]; if (stylestring !== null) { var ftm_tuple = jsplotlib._process_plot_format(stylestring); @@ -147,16 +147,16 @@ var $builtinmodule = function(name) { // Save plots.push({ "data": actualData, - "type": 'line', - 'style': { - 'linestyle': linestyle, - 'marker': marker, - 'color': jsplotlib.color_to_hex(color) + "type": "line", + "style": { + "linestyle": linestyle, + "marker": marker, + "color": jsplotlib.color_to_hex(color) } }); // Update min/max - updateMinMax("x", xdata) - updateMinMax("y", ydata) + updateMinMax("x", xdata); + updateMinMax("y", ydata); if (Sk.console.skipDrawing) { return; @@ -172,213 +172,213 @@ var $builtinmodule = function(name) { }*/ if (!chart) { - createChart('line'); + createChart("line"); } - if (chart.type == 'hist' && plots.length < 1) { + if (chart.type == "hist" && plots.length < 1) { resetChart(); return; } if (plots.length == 0) { return; } - if (extents['xMin'] === undefined || extents['yMin'] === undefined) { + if (extents["xMin"] === undefined || extents["yMin"] === undefined) { return; } var yAxisBuffer; // Establish x/y scalers and axes - if (chart.type == 'scatter' || chart.type == 'line') { - yAxisBuffer = 5*Math.max(extents['yMin'].toLocaleString().length, - extents['yMax'].toLocaleString().length); + if (chart.type == "scatter" || chart.type == "line") { + yAxisBuffer = 5*Math.max(extents["yMin"].toLocaleString().length, + extents["yMax"].toLocaleString().length); chart.xScale = d3.scale.linear() - .domain([extents['xMin'], extents['xMax']]) - .range([0, chart.width-yAxisBuffer]); + .domain([extents["xMin"], extents["xMax"]]) + .range([0, chart.width-yAxisBuffer]); chart.xAxis = d3.svg.axis() - .scale(chart.xScale) - .orient("bottom"); - } else if (chart.type == 'hist') { - yAxisBuffer = 5*Math.max(extents['xMin'].toLocaleString().length, - extents['xMax'].toLocaleString().length); + .scale(chart.xScale) + .orient("bottom"); + } else if (chart.type == "hist") { + yAxisBuffer = 5*Math.max(extents["xMin"].toLocaleString().length, + extents["xMax"].toLocaleString().length); chart.xScale = d3.scale.linear() - .domain([extents['xMin'], extents['xMax']]) - .range([0, chart.width-yAxisBuffer]); + .domain([extents["xMin"], extents["xMax"]]) + .range([0, chart.width-yAxisBuffer]); chart.xAxis = d3.svg.axis() - .scale(chart.xScale) - .orient("bottom"); - var bins = plots[0]['bins']; + .scale(chart.xScale) + .orient("bottom"); + var bins = plots[0]["bins"]; var tempScale = d3.scale.linear() - .domain([ - 0, bins - ]) - .range([extents['xMin'], extents['xMax']]); + .domain([ + 0, bins + ]) + .range([extents["xMin"], extents["xMax"]]); var tickArray = d3.range(bins+1) - .map(tempScale).map(function(e) { - return e; - }); + .map(tempScale).map(function(e) { + return e; + }); // TODO: support multiple histograms var histMapper = d3.layout.histogram().bins( - tickArray + tickArray //chart.xScale.ticks(bins) - )(plots[0]['data']); - } else if (chart.type == 'bar') { - yAxisBuffer = 5*Math.max(extents['yMin'].toLocaleString().length, - extents['yMax'].toLocaleString().length); + )(plots[0]["data"]); + } else if (chart.type == "bar") { + yAxisBuffer = 5*Math.max(extents["yMin"].toLocaleString().length, + extents["yMax"].toLocaleString().length); chart.xScale = d3.scale.ordinal() - .domain([extents['xMin'], extents['xMax']]) - .rangeBands([0, chart.width-yAxisBuffer]); + .domain([extents["xMin"], extents["xMax"]]) + .rangeBands([0, chart.width-yAxisBuffer]); chart.xAxis = d3.svg.axis() - .scale(chart.xScale) - .tickFormat(function(d) { return d.index }) - .orient("bottom"); + .scale(chart.xScale) + .tickFormat(function(d) { return d.index; }) + .orient("bottom"); } - if (chart.type !== 'hist') { + if (chart.type !== "hist") { chart.yScale = d3.scale.linear() - .domain([extents['yMin'], extents['yMax']]) - .range([chart.height, 0]); + .domain([extents["yMin"], extents["yMax"]]) + .range([chart.height, 0]); } else { chart.yScale = d3.scale.linear() - .domain([0, d3.max(histMapper, function(d) { return d.y; })]) - .range([chart.height, 0]); + .domain([0, d3.max(histMapper, function(d) { return d.y; })]) + .range([chart.height, 0]); } chart.yAxis = d3.svg.axis() - .scale(chart.yScale) - .orient("left"); + .scale(chart.yScale) + .orient("left"); - chart.mapX = function(d) {return chart.xScale(d.x)}; - chart.mapY = function(d) {return chart.yScale(d.y)}; + chart.mapX = function(d) {return chart.xScale(d.x);}; + chart.mapY = function(d) {return chart.yScale(d.y);}; chart.mapLine = d3.svg.line() - .x(function(d) { return chart.xScale(d.x); }) - .y(function(d) { return chart.yScale(d.y); }) - .interpolate("linear"); + .x(function(d) { return chart.xScale(d.x); }) + .y(function(d) { return chart.yScale(d.y); }) + .interpolate("linear"); // set css classes - chart.svg = d3.select(Sk.console.container).append('div').append('svg'); + chart.svg = d3.select(Sk.console.container).append("div").append("svg"); //$(chart.svg.node()).parent().hide(); - chart.svg.attr('class', 'chart'); - chart.svg.attr('width', Sk.console.width); - chart.svg.attr('height', Sk.console.height); - chart.svg.attr('chartCount', chartCounter); + chart.svg.attr("class", "chart"); + chart.svg.attr("width", Sk.console.width); + chart.svg.attr("height", Sk.console.height); + chart.svg.attr("chartCount", chartCounter); var translation = "translate(" + (chart.margin.left + yAxisBuffer) + "," + chart.margin.top + ")"; chart.canvas = chart.svg.append("g") - .attr("transform", translation); + .attr("transform", translation); chart.canvas.append("g") - .attr("class", "x axis") - .attr("transform", "translate(0," + chart.height + ")") - .call(chart.xAxis); + .attr("class", "x axis") + .attr("transform", "translate(0," + chart.height + ")") + .call(chart.xAxis); chart.canvas.append("g") - .attr("class", "y axis") - .call(chart.yAxis); + .attr("class", "y axis") + .call(chart.yAxis); chart.canvas.select(".x.axis") - .selectAll("text") - .style("font-size","12px"); + .selectAll("text") + .style("font-size","12px"); chart.canvas.select(".y.axis") - .selectAll("text") - .style("font-size","12px"); + .selectAll("text") + .style("font-size","12px"); translation = "translate(" + ( (chart.width-yAxisBuffer) / 2) + " ," + (chart.height + chart.margin.bottom-14) + ")"; chart.canvas.append("text") // text label for the x axis - .attr("transform", translation) - .attr("class", "x-axis-label") - .style("font-size", "14px") - .text(labels['x-axis']) - .style("text-anchor", "middle"); + .attr("transform", translation) + .attr("class", "x-axis-label") + .style("font-size", "14px") + .text(labels["x-axis"]) + .style("text-anchor", "middle"); chart.canvas.append("text") - .attr("transform", "rotate(-90)") - .attr("class", "y-axis-label") - .attr("y", 0 - chart.margin.left-yAxisBuffer) - .attr("x", 0 - (chart.height / 2)) - .attr("dy", "1em") - .text(labels['y-axis']) - .style("font-size", "14px") - .style("text-anchor", "middle"); + .attr("transform", "rotate(-90)") + .attr("class", "y-axis-label") + .attr("y", 0 - chart.margin.left-yAxisBuffer) + .attr("x", 0 - (chart.height / 2)) + .attr("dy", "1em") + .text(labels["y-axis"]) + .style("font-size", "14px") + .style("text-anchor", "middle"); chart.canvas.append("text") - .attr("x", ( (chart.width-yAxisBuffer) / 2)) - .attr("y", 0 - (chart.margin.top / 2)) - .attr("class", "title-text") - .text(labels['title']) - .attr("text-anchor", "middle") - .style("font-size", "14px") - .style("text-decoration", "underline"); + .attr("x", ( (chart.width-yAxisBuffer) / 2)) + .attr("y", 0 - (chart.margin.top / 2)) + .attr("class", "title-text") + .text(labels["title"]) + .attr("text-anchor", "middle") + .style("font-size", "14px") + .style("text-decoration", "underline"); chart.canvas.append("text") - .attr("x", 0) - .attr("y", 0) - .text("BlockPy") - .style("stroke", "#FDFDFD") - .style("font-size", "8px"); - chart.svg.insert('defs', ":first-child") - .append('style') - .attr("type", "text/css") - .text("svg { background-color: white; }\n"+ + .attr("x", 0) + .attr("y", 0) + .text("BlockPy") + .style("stroke", "#FDFDFD") + .style("font-size", "8px"); + chart.svg.insert("defs", ":first-child") + .append("style") + .attr("type", "text/css") + .text("svg { background-color: white; }\n"+ ".axis path,.axis line { fill: none; stroke: black; shape-rendering: crispEdges;}\n"+ ".line { fill: none; stroke-width: 1px;}\n"+ ".circle { r: 3; shape-rendering: crispEdges; }\n"+ - ".bar { shape-rendering: crispEdges;}\n") + ".bar { shape-rendering: crispEdges;}\n"); // Actually draw the chart objects for (var i = 0; i < plots.length; i += 1) { var plot = plots[i]; - if (plot['type'] == 'line') { + if (plot["type"] == "line") { chart.canvas.append("path") - .style('stroke', plot['style']['color']) - .attr('class', "line") - .data(plot['data']) - .attr("d", chart.mapLine(plot['data'])); - } else if (plot['type'] == 'scatter') { + .style("stroke", plot["style"]["color"]) + .attr("class", "line") + .data(plot["data"]) + .attr("d", chart.mapLine(plot["data"])); + } else if (plot["type"] == "scatter") { chart.canvas.append("g") - .attr("class", "series") - .selectAll(".point") - .data(plot['data']) - .enter() - .append('circle') - .style('fill', plot['style']['color']) - .attr("class", "circle") - .attr("cx", chart.mapX) - .attr("cy", chart.mapY) - .attr('r', 2); - } else if (plot['type'] == 'hist') { - chart.canvas.selectAll('.bar') - .data(histMapper) - .enter().append("rect") - .attr("class", "bar") - .style('fill', plot['style']['color']) - .style('stroke', 'black') - .attr("x", function(d) { return chart.xScale(d.x); }) - .attr("width", (chart.width-yAxisBuffer)/(1+histMapper.length)) - .attr("y", function(d) { return chart.yScale(d.y); }) - .attr("height", function(d) { return chart.height - chart.yScale(d.y); }); + .attr("class", "series") + .selectAll(".point") + .data(plot["data"]) + .enter() + .append("circle") + .style("fill", plot["style"]["color"]) + .attr("class", "circle") + .attr("cx", chart.mapX) + .attr("cy", chart.mapY) + .attr("r", 2); + } else if (plot["type"] == "hist") { + chart.canvas.selectAll(".bar") + .data(histMapper) + .enter().append("rect") + .attr("class", "bar") + .style("fill", plot["style"]["color"]) + .style("stroke", "black") + .attr("x", function(d) { return chart.xScale(d.x); }) + .attr("width", (chart.width-yAxisBuffer)/(1+histMapper.length)) + .attr("y", function(d) { return chart.yScale(d.y); }) + .attr("height", function(d) { return chart.height - chart.yScale(d.y); }); } } if (Sk.console.pngMode) { - var doctype = '' + '<' + '!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'; + var doctype = '' + "<" + '!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'; var xml = new XMLSerializer().serializeToString(chart.svg[0][0]); - var blob = new Blob([ doctype + xml], { type: 'image/svg+xml' }); + var blob = new Blob([ doctype + xml], { type: "image/svg+xml" }); var url = window.URL.createObjectURL(blob); //var data = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(xml))); var img = document.createElement("img"); - img.style.display = 'block'; + img.style.display = "block"; var oldChart = chart; var oldPlots = plots; Sk.console.printHtml(img, oldPlots); resetChart(); - oldChart.svg[0][0].parentNode.replaceChild(img, oldChart.svg[0][0]) + oldChart.svg[0][0].parentNode.replaceChild(img, oldChart.svg[0][0]); img.onload = function() { img.onload = null; //TODO: Make this capture a class descendant. Cross the D3/Jquery barrier! - var canvas = document.createElement('canvas'); + var canvas = document.createElement("canvas"); canvas.width = Sk.console.width; canvas.height = Sk.console.height; - var ctx = canvas.getContext('2d'); + var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); var canvasUrl = canvas.toDataURL("image/png"); - img.setAttribute('src', canvasUrl); + img.setAttribute("src", canvasUrl); // Snip off this chart, we can now start a new one. - } + }; img.onerror = function() { - } - img.setAttribute('src', url); + }; + img.setAttribute("src", url); } else { Sk.console.printHtml(chart.svg, plots); // Snip off this chart, we can now start a new one. @@ -395,7 +395,7 @@ var $builtinmodule = function(name) { "' is not supported for title; should be a string."); } - labels['title']= Sk.ffi.remapToJs(s); + labels["title"]= Sk.ffi.remapToJs(s); }; mod.title = new Sk.builtin.func(title_f); @@ -407,7 +407,7 @@ var $builtinmodule = function(name) { "' is not supported for xlabel; should be a string."); } - labels['x-axis']= Sk.ffi.remapToJs(s); + labels["x-axis"]= Sk.ffi.remapToJs(s); }; mod.xlabel = new Sk.builtin.func(xlabel_f); @@ -419,7 +419,7 @@ var $builtinmodule = function(name) { "' is not supported for ylabel; should be a string."); } - labels['y-axis']= Sk.ffi.remapToJs(s); + labels["y-axis"]= Sk.ffi.remapToJs(s); }; mod.ylabel = new Sk.builtin.func(ylabel_f); @@ -430,7 +430,7 @@ var $builtinmodule = function(name) { }; mod.clf = new Sk.builtin.func(clf_f); - UNSUPPORTED = ["semilogx", "semilogy", "specgram", "stackplot", "stem", "step", "streamplot", "tricontour", "tricontourf", "tripcolor", "triplot", "vlines", "xcorr", "barbs", "cla", "grid", "table", "text", "annotate", "ticklabel_format", "locator_params", "tick_params", "margins", "autoscale", "autumn", "cool", "copper", "flag", "gray", "hot", "hsv", "jet", "pink", "prism", "spring", "summer", "winter", "spectral", "hlines", "loglog", "magnitude_spectrum", "pcolor", "pcolormesh", "phase_spectrum", "pie", "plot_date", "psd", "quiver", "quiverkey", "findobj", "switch_backend", "isinteractive", "ioff", "ion", "pause", "rc", "rc_context", "rcdefaults", "gci", "sci", "xkcd", "figure", "gcf", "get_fignums", "get_figlabels", "get_current_fig_manager", "connect", "disconnect", "close", "savefig", "ginput", "waitforbuttonpress", "figtext", "suptitle", "figimage", "figlegend", "hold", "ishold", "over", "delaxes", "sca", "gca", "subplot", "subplots", "subplot2grid", "twinx", "twiny", "subplots_adjust", "subplot_tool", "tight_layout", "box", "xlim", "ylim", "xscale", "yscale", "xticks", "yticks", "minorticks_on", "minorticks_off", "rgrids", "thetagrids", "plotting", "get_plot_commands", "colors", "colormaps", "_setup_pyplot_info_docstrings", "colorbar", "clim", "set_cmap", "imread", "imsave", "matshow", "polar", "plotfile", "_autogen_docstring", "acorr", "arrow", "axhline", "axhspan", "axvline", "axvspan", "bar", "barh", "broken_barh", "boxplot", "cohere", "clabel", "contour", "contourf", "csd", "errorbar", "eventplot", "fill", "fill_between", "fill_betweenx", "hexbin", "hist2d", 'axis'] + UNSUPPORTED = ["semilogx", "semilogy", "specgram", "stackplot", "stem", "step", "streamplot", "tricontour", "tricontourf", "tripcolor", "triplot", "vlines", "xcorr", "barbs", "cla", "grid", "table", "text", "annotate", "ticklabel_format", "locator_params", "tick_params", "margins", "autoscale", "autumn", "cool", "copper", "flag", "gray", "hot", "hsv", "jet", "pink", "prism", "spring", "summer", "winter", "spectral", "hlines", "loglog", "magnitude_spectrum", "pcolor", "pcolormesh", "phase_spectrum", "pie", "plot_date", "psd", "quiver", "quiverkey", "findobj", "switch_backend", "isinteractive", "ioff", "ion", "pause", "rc", "rc_context", "rcdefaults", "gci", "sci", "xkcd", "figure", "gcf", "get_fignums", "get_figlabels", "get_current_fig_manager", "connect", "disconnect", "close", "savefig", "ginput", "waitforbuttonpress", "figtext", "suptitle", "figimage", "figlegend", "hold", "ishold", "over", "delaxes", "sca", "gca", "subplot", "subplots", "subplot2grid", "twinx", "twiny", "subplots_adjust", "subplot_tool", "tight_layout", "box", "xlim", "ylim", "xscale", "yscale", "xticks", "yticks", "minorticks_on", "minorticks_off", "rgrids", "thetagrids", "plotting", "get_plot_commands", "colors", "colormaps", "_setup_pyplot_info_docstrings", "colorbar", "clim", "set_cmap", "imread", "imsave", "matshow", "polar", "plotfile", "_autogen_docstring", "acorr", "arrow", "axhline", "axhspan", "axvline", "axvspan", "bar", "barh", "broken_barh", "boxplot", "cohere", "clabel", "contour", "contourf", "csd", "errorbar", "eventplot", "fill", "fill_between", "fill_betweenx", "hexbin", "hist2d", "axis"]; for (var i = 0; i < UNSUPPORTED.length; i+= 1) { mod[UNSUPPORTED[i]] = new Sk.builtin.func(function() { throw new Sk.builtin.NotImplementedError(UNSUPPORTED[i]+" is not yet implemented"); @@ -439,7 +439,7 @@ var $builtinmodule = function(name) { var legend_f = function() { return Sk.builtin.none.none$; - } + }; mod.legend = new Sk.builtin.func(legend_f); var hist_f = function(kwa) { @@ -470,11 +470,11 @@ var $builtinmodule = function(name) { } // empty canvas from previous plots - createChart('hist'); + createChart("hist"); // Parse formatting, also keep ColorCycler updated var cycle = jsplotlib.rc["axes.color_cycle"]; - var linestyle = ' ', marker= 'o', + var linestyle = " ", marker= "o", color = cycle[colorCycle % cycle.length]; if (stylestring !== null) { var ftm_tuple = jsplotlib._process_plot_format(stylestring); @@ -487,12 +487,12 @@ var $builtinmodule = function(name) { // Save plots.push({ "data": data, - "type": 'hist', + "type": "hist", "bins": bins, - 'style': { - 'linestyle': linestyle, - 'marker': marker, - 'color': jsplotlib.color_to_hex(color) + "style": { + "linestyle": linestyle, + "marker": marker, + "color": jsplotlib.color_to_hex(color) } }); updateMinMax("x", data); @@ -500,7 +500,7 @@ var $builtinmodule = function(name) { if (Sk.console.skipDrawing) { return; } - } + }; hist_f.co_kwargs = true; mod.hist = new Sk.builtin.func(hist_f); @@ -539,7 +539,7 @@ var $builtinmodule = function(name) { var LENGTH = xdata.length, RATE = LENGTH / dot_limit; for (var i = 0; i < dot_limit; i+= 1) { var index = Math.floor((i+Math.random())*RATE); - xdataSampled.push(xdata[index]) + xdataSampled.push(xdata[index]); ydataSampled.push(ydata[index]); } xdata = xdataSampled; @@ -547,15 +547,15 @@ var $builtinmodule = function(name) { } // empty canvas from previous plots - createChart('scatter'); + createChart("scatter"); // Zip up the data var actualData = d3.zip(xdata, ydata).map(function(e) { - return {'x': e[0], 'y': e[1]} + return {"x": e[0], "y": e[1]}; }); // Parse formatting, also keep ColorCycler updated var cycle = jsplotlib.rc["axes.color_cycle"]; - var linestyle = ' ', marker= 'o', + var linestyle = " ", marker= "o", color = cycle[colorCycle % cycle.length]; if (stylestring !== null) { var ftm_tuple = jsplotlib._process_plot_format(stylestring); @@ -568,16 +568,16 @@ var $builtinmodule = function(name) { // Save plots.push({ "data": actualData, - "type": 'scatter', - 'style': { - 'linestyle': linestyle, - 'marker': marker, - 'color': jsplotlib.color_to_hex(color) + "type": "scatter", + "style": { + "linestyle": linestyle, + "marker": marker, + "color": jsplotlib.color_to_hex(color) } }); // Update min/max - updateMinMax("x", xdata) - updateMinMax("y", ydata) + updateMinMax("x", xdata); + updateMinMax("y", ydata); if (Sk.console.skipDrawing) { return; } @@ -624,39 +624,39 @@ jsplotlib._line_counter = 0; /** List of all supported line styles **/ jsplotlib.lineStyles = { - '-': '_draw_solid', - '--': '_draw_dashed', - '-.': '_draw_dash_dot', - ':': '_draw_dotted', - 'None': '_draw_nothing', - ' ': '_draw_nothing', - '': '_draw_nothing', + "-": "_draw_solid", + "--": "_draw_dashed", + "-.": "_draw_dash_dot", + ":": "_draw_dotted", + "None": "_draw_nothing", + " ": "_draw_nothing", + "": "_draw_nothing", }; jsplotlib.lineMarkers = { - '.': 'point', - ',': 'pixel', - 'o': 'circle', - 'v': 'triangle_down', - '^': 'triangle_up', - '<': 'triangle_left', - '>': 'triangle_right', - '1': 'tri_down', - '2': 'tri_up', - '3': 'tri_left', - '4': 'tri_right', - '8': 'octagon', - 's': 'square', - 'p': 'pentagon', - '*': 'star', - 'h': 'hexagon1', - 'H': 'hexagon2', - '+': 'plus', - 'x': 'x', - 'D': 'diamond', - 'd': 'thin_diamond', - '|': 'vline', - '_': 'hline', + ".": "point", + ",": "pixel", + "o": "circle", + "v": "triangle_down", + "^": "triangle_up", + "<": "triangle_left", + ">": "triangle_right", + "1": "tri_down", + "2": "tri_up", + "3": "tri_left", + "4": "tri_right", + "8": "octagon", + "s": "square", + "p": "pentagon", + "*": "star", + "h": "hexagon1", + "H": "hexagon2", + "+": "plus", + "x": "x", + "D": "diamond", + "d": "thin_diamond", + "|": "vline", + "_": "hline", //TICKLEFT: 'tickleft', //TICKRIGHT: 'tickright', //TICKUP: 'tickup', @@ -665,189 +665,186 @@ jsplotlib.lineMarkers = { //CARETRIGHT: 'caretright', //CARETUP: 'caretup', //CARETDOWN: 'caretdown', - "None": 'nothing', + "None": "nothing", //Sk.builtin.none.none$: 'nothing', - ' ': 'nothing', - '': 'nothing' + " ": "nothing", + "": "nothing" }; /** Color short keys **/ jsplotlib.colors = { - 'b': 'blue', - 'g': 'green', - 'r': 'red', - 'c': 'cyan', - 'm': 'magenta', - 'y': 'yellow', - 'k': 'black', - 'w': 'white' + "b": "blue", + "g": "green", + "r": "red", + "c": "cyan", + "m": "magenta", + "y": "yellow", + "k": "black", + "w": "white" }; /** Mapping of all possible CSS colors, that are supported by matplotlib **/ jsplotlib.cnames = { - 'aliceblue': '#F0F8FF', - 'antiquewhite': '#FAEBD7', - 'aqua': '#00FFFF', - 'aquamarine': '#7FFFD4', - 'azure': '#F0FFFF', - 'beige': '#F5F5DC', - 'bisque': '#FFE4C4', - 'black': '#000000', - 'blanchedalmond': '#FFEBCD', - 'blue': '#0000FF', - 'blueviolet': '#8A2BE2', - 'brown': '#A52A2A', - 'burlywood': '#DEB887', - 'cadetblue': '#5F9EA0', - 'chartreuse': '#7FFF00', - 'chocolate': '#D2691E', - 'coral': '#FF7F50', - 'cornflowerblue': '#6495ED', - 'cornsilk': '#FFF8DC', - 'crimson': '#DC143C', - 'cyan': '#00FFFF', - 'darkblue': '#00008B', - 'darkcyan': '#008B8B', - 'darkgoldenrod': '#B8860B', - 'darkgray': '#A9A9A9', - 'darkgreen': '#006400', - 'darkkhaki': '#BDB76B', - 'darkmagenta': '#8B008B', - 'darkolivegreen': '#556B2F', - 'darkorange': '#FF8C00', - 'darkorchid': '#9932CC', - 'darkred': '#8B0000', - 'darksage': '#598556', - 'darksalmon': '#E9967A', - 'darkseagreen': '#8FBC8F', - 'darkslateblue': '#483D8B', - 'darkslategray': '#2F4F4F', - 'darkturquoise': '#00CED1', - 'darkviolet': '#9400D3', - 'deeppink': '#FF1493', - 'deepskyblue': '#00BFFF', - 'dimgray': '#696969', - 'dodgerblue': '#1E90FF', - 'firebrick': '#B22222', - 'floralwhite': '#FFFAF0', - 'forestgreen': '#228B22', - 'fuchsia': '#FF00FF', - 'gainsboro': '#DCDCDC', - 'ghostwhite': '#F8F8FF', - 'gold': '#FFD700', - 'goldenrod': '#DAA520', - 'gray': '#808080', - 'green': '#008000', - 'greenyellow': '#ADFF2F', - 'honeydew': '#F0FFF0', - 'hotpink': '#FF69B4', - 'indianred': '#CD5C5C', - 'indigo': '#4B0082', - 'ivory': '#FFFFF0', - 'khaki': '#F0E68C', - 'lavender': '#E6E6FA', - 'lavenderblush': '#FFF0F5', - 'lawngreen': '#7CFC00', - 'lemonchiffon': '#FFFACD', - 'lightblue': '#ADD8E6', - 'lightcoral': '#F08080', - 'lightcyan': '#E0FFFF', - 'lightgoldenrodyellow': '#FAFAD2', - 'lightgreen': '#90EE90', - 'lightgray': '#D3D3D3', - 'lightpink': '#FFB6C1', - 'lightsage': '#BCECAC', - 'lightsalmon': '#FFA07A', - 'lightseagreen': '#20B2AA', - 'lightskyblue': '#87CEFA', - 'lightslategray': '#778899', - 'lightsteelblue': '#B0C4DE', - 'lightyellow': '#FFFFE0', - 'lime': '#00FF00', - 'limegreen': '#32CD32', - 'linen': '#FAF0E6', - 'magenta': '#FF00FF', - 'maroon': '#800000', - 'mediumaquamarine': '#66CDAA', - 'mediumblue': '#0000CD', - 'mediumorchid': '#BA55D3', - 'mediumpurple': '#9370DB', - 'mediumseagreen': '#3CB371', - 'mediumslateblue': '#7B68EE', - 'mediumspringgreen': '#00FA9A', - 'mediumturquoise': '#48D1CC', - 'mediumvioletred': '#C71585', - 'midnightblue': '#191970', - 'mintcream': '#F5FFFA', - 'mistyrose': '#FFE4E1', - 'moccasin': '#FFE4B5', - 'navajowhite': '#FFDEAD', - 'navy': '#000080', - 'oldlace': '#FDF5E6', - 'olive': '#808000', - 'olivedrab': '#6B8E23', - 'orange': '#FFA500', - 'orangered': '#FF4500', - 'orchid': '#DA70D6', - 'palegoldenrod': '#EEE8AA', - 'palegreen': '#98FB98', - 'paleturquoise': '#AFEEEE', - 'palevioletred': '#DB7093', - 'papayawhip': '#FFEFD5', - 'peachpuff': '#FFDAB9', - 'peru': '#CD853F', - 'pink': '#FFC0CB', - 'plum': '#DDA0DD', - 'powderblue': '#B0E0E6', - 'purple': '#800080', - 'red': '#FF0000', - 'rosybrown': '#BC8F8F', - 'royalblue': '#4169E1', - 'saddlebrown': '#8B4513', - 'salmon': '#FA8072', - 'sage': '#87AE73', - 'sandybrown': '#FAA460', - 'seagreen': '#2E8B57', - 'seashell': '#FFF5EE', - 'sienna': '#A0522D', - 'silver': '#C0C0C0', - 'skyblue': '#87CEEB', - 'slateblue': '#6A5ACD', - 'slategray': '#708090', - 'snow': '#FFFAFA', - 'springgreen': '#00FF7F', - 'steelblue': '#4682B4', - 'tan': '#D2B48C', - 'teal': '#008080', - 'thistle': '#D8BFD8', - 'tomato': '#FF6347', - 'turquoise': '#40E0D0', - 'violet': '#EE82EE', - 'wheat': '#F5DEB3', - 'white': '#FFFFFF', - 'whitesmoke': '#F5F5F5', - 'yellow': '#FFFF00', - 'yellowgreen': '#9ACD32' + "aliceblue": "#F0F8FF", + "antiquewhite": "#FAEBD7", + "aqua": "#00FFFF", + "aquamarine": "#7FFFD4", + "azure": "#F0FFFF", + "beige": "#F5F5DC", + "bisque": "#FFE4C4", + "black": "#000000", + "blanchedalmond": "#FFEBCD", + "blue": "#0000FF", + "blueviolet": "#8A2BE2", + "brown": "#A52A2A", + "burlywood": "#DEB887", + "cadetblue": "#5F9EA0", + "chartreuse": "#7FFF00", + "chocolate": "#D2691E", + "coral": "#FF7F50", + "cornflowerblue": "#6495ED", + "cornsilk": "#FFF8DC", + "crimson": "#DC143C", + "cyan": "#00FFFF", + "darkblue": "#00008B", + "darkcyan": "#008B8B", + "darkgoldenrod": "#B8860B", + "darkgray": "#A9A9A9", + "darkgreen": "#006400", + "darkkhaki": "#BDB76B", + "darkmagenta": "#8B008B", + "darkolivegreen": "#556B2F", + "darkorange": "#FF8C00", + "darkorchid": "#9932CC", + "darkred": "#8B0000", + "darksage": "#598556", + "darksalmon": "#E9967A", + "darkseagreen": "#8FBC8F", + "darkslateblue": "#483D8B", + "darkslategray": "#2F4F4F", + "darkturquoise": "#00CED1", + "darkviolet": "#9400D3", + "deeppink": "#FF1493", + "deepskyblue": "#00BFFF", + "dimgray": "#696969", + "dodgerblue": "#1E90FF", + "firebrick": "#B22222", + "floralwhite": "#FFFAF0", + "forestgreen": "#228B22", + "fuchsia": "#FF00FF", + "gainsboro": "#DCDCDC", + "ghostwhite": "#F8F8FF", + "gold": "#FFD700", + "goldenrod": "#DAA520", + "gray": "#808080", + "green": "#008000", + "greenyellow": "#ADFF2F", + "honeydew": "#F0FFF0", + "hotpink": "#FF69B4", + "indianred": "#CD5C5C", + "indigo": "#4B0082", + "ivory": "#FFFFF0", + "khaki": "#F0E68C", + "lavender": "#E6E6FA", + "lavenderblush": "#FFF0F5", + "lawngreen": "#7CFC00", + "lemonchiffon": "#FFFACD", + "lightblue": "#ADD8E6", + "lightcoral": "#F08080", + "lightcyan": "#E0FFFF", + "lightgoldenrodyellow": "#FAFAD2", + "lightgreen": "#90EE90", + "lightgray": "#D3D3D3", + "lightpink": "#FFB6C1", + "lightsage": "#BCECAC", + "lightsalmon": "#FFA07A", + "lightseagreen": "#20B2AA", + "lightskyblue": "#87CEFA", + "lightslategray": "#778899", + "lightsteelblue": "#B0C4DE", + "lightyellow": "#FFFFE0", + "lime": "#00FF00", + "limegreen": "#32CD32", + "linen": "#FAF0E6", + "magenta": "#FF00FF", + "maroon": "#800000", + "mediumaquamarine": "#66CDAA", + "mediumblue": "#0000CD", + "mediumorchid": "#BA55D3", + "mediumpurple": "#9370DB", + "mediumseagreen": "#3CB371", + "mediumslateblue": "#7B68EE", + "mediumspringgreen": "#00FA9A", + "mediumturquoise": "#48D1CC", + "mediumvioletred": "#C71585", + "midnightblue": "#191970", + "mintcream": "#F5FFFA", + "mistyrose": "#FFE4E1", + "moccasin": "#FFE4B5", + "navajowhite": "#FFDEAD", + "navy": "#000080", + "oldlace": "#FDF5E6", + "olive": "#808000", + "olivedrab": "#6B8E23", + "orange": "#FFA500", + "orangered": "#FF4500", + "orchid": "#DA70D6", + "palegoldenrod": "#EEE8AA", + "palegreen": "#98FB98", + "paleturquoise": "#AFEEEE", + "palevioletred": "#DB7093", + "papayawhip": "#FFEFD5", + "peachpuff": "#FFDAB9", + "peru": "#CD853F", + "pink": "#FFC0CB", + "plum": "#DDA0DD", + "powderblue": "#B0E0E6", + "purple": "#800080", + "red": "#FF0000", + "rosybrown": "#BC8F8F", + "royalblue": "#4169E1", + "saddlebrown": "#8B4513", + "salmon": "#FA8072", + "sage": "#87AE73", + "sandybrown": "#FAA460", + "seagreen": "#2E8B57", + "seashell": "#FFF5EE", + "sienna": "#A0522D", + "silver": "#C0C0C0", + "skyblue": "#87CEEB", + "slateblue": "#6A5ACD", + "slategray": "#708090", + "snow": "#FFFAFA", + "springgreen": "#00FF7F", + "steelblue": "#4682B4", + "tan": "#D2B48C", + "teal": "#008080", + "thistle": "#D8BFD8", + "tomato": "#FF6347", + "turquoise": "#40E0D0", + "violet": "#EE82EE", + "wheat": "#F5DEB3", + "white": "#FFFFFF", + "whitesmoke": "#F5F5F5", + "yellow": "#FFFF00", + "yellowgreen": "#9ACD32" }; jsplotlib.color_to_hex = function(color) { // is color a shortcut? - if (jsplotlib.colors[color]) - color = jsplotlib.colors[color]; + if (jsplotlib.colors[color]) {color = jsplotlib.colors[color];} // is inside cnames array? - if (jsplotlib.cnames[color]) - return jsplotlib.cnames[color]; + if (jsplotlib.cnames[color]) {return jsplotlib.cnames[color];} // check if it is already a hex value if (typeof color == "string") { var match = color.match(/^#(?:[0-9a-fA-F]{3}){1,2}$/); - if (match && match.length === 1) - return match[0]; + if (match && match.length === 1) {return match[0];} } // add rgb colors here @@ -856,7 +853,7 @@ jsplotlib.color_to_hex = function(color) { } // back to default - return jsplotlib.cnames[jsplotlib.rc['lines.color']]; + return jsplotlib.cnames[jsplotlib.rc["lines.color"]]; }; jsplotlib.get_color = function(cs) { @@ -864,51 +861,51 @@ jsplotlib.get_color = function(cs) { }; jsplotlib.parse_marker = function(style) { - if (!style) return "x"; + if (!style) {return "x";} switch (style) { - case '.': + case ".": return "."; - case ',': + case ",": return "x"; - case 'o': + case "o": return "o"; - case 'v': + case "v": return "x"; - case '^': + case "^": return "x"; - case '<': + case "<": return "x"; - case '>': + case ">": return "x"; - case '1': + case "1": return "x"; - case '2': + case "2": return "x"; - case '3': + case "3": return "x"; - case '4': + case "4": return "x"; - case 's': + case "s": return "s"; - case 'p': + case "p": return "x"; - case '*': + case "*": return "x"; - case 'h': + case "h": return "x"; - case 'H': + case "H": return "x"; - case '+': + case "+": return "x"; - case 'x': + case "x": return "x"; - case 'D': + case "D": return "x"; - case 'd': + case "d": return "x"; - case '|': + case "|": return "x"; - case '_': + case "_": return "x"; default: return ""; @@ -940,25 +937,25 @@ jsplotlib._process_plot_format = function(fmt) { color = jsplotlib.to_rgb(fmt); if (color) { return { - 'linestyle': linestyle, - 'marker': marker, - 'color': color + "linestyle": linestyle, + "marker": marker, + "color": color }; } } catch (e) {} // handle the multi char special cases and strip them for the string if (fmt.search(/--/) >= 0) { - linestyle = '--'; - fmt = fmt.replace(/--/, ''); + linestyle = "--"; + fmt = fmt.replace(/--/, ""); } if (fmt.search(/-\./) >= 0) { - linestyle = '-.'; - fmt = fmt.replace(/-\./, ''); + linestyle = "-."; + fmt = fmt.replace(/-\./, ""); } if (fmt.search(/ /) >= 0) { - linestyle = ''; - fmt = fmt.replace(/ /, ''); + linestyle = ""; + fmt = fmt.replace(/ /, ""); } var i; @@ -983,26 +980,26 @@ jsplotlib._process_plot_format = function(fmt) { } color = c; } else { - throw new Sk.builtin.ValueError('Unrecognized character ' + c + - ' in format string'); + throw new Sk.builtin.ValueError("Unrecognized character " + c + + " in format string"); } } if (!linestyle && !marker) { // use defaults --> rcParams['lines.linestyle'] - linestyle = '-'; + linestyle = "-"; } if (!linestyle) { - linestyle = ' '; + linestyle = " "; } if (!marker) { - marker = ''; + marker = ""; } return { - 'linestyle': linestyle, - 'marker': marker, - 'color': color + "linestyle": linestyle, + "marker": marker, + "color": color }; }; @@ -1024,32 +1021,30 @@ jsplotlib._process_plot_format = function(fmt) { if *arg* is *RGBA*, the *A* will simply be discarded. **/ jsplotlib.to_rgb = function(fmt) { - if (!fmt) return null; + if (!fmt) {return null;} var color = null; if (typeof fmt == "string") { fmt_lower = fmt.toLowerCase(); - if (jsplotlib.colors[fmt_lower]) - return jsplotlib.hex2color(jsplotlib.cnames[jsplotlib.colors[fmt_lower]]); + if (jsplotlib.colors[fmt_lower]) {return jsplotlib.hex2color(jsplotlib.cnames[jsplotlib.colors[fmt_lower]]);} // is inside cnames array? - if (jsplotlib.cnames[fmt_lower]) - return jsplotlib.hex2color(jsplotlib.cnames[fmt_lower]); + if (jsplotlib.cnames[fmt_lower]) {return jsplotlib.hex2color(jsplotlib.cnames[fmt_lower]);} - if (fmt_lower.indexOf('#') === 0) { + if (fmt_lower.indexOf("#") === 0) { return jsplotlib.hex2color(fmt_lower); } // is it simple grey shade? var fl = parseFloat(fmt_lower); if (isNaN(fl)) { - throw new Sk.builtin.ValueError('cannot convert argument to rgb sequence'); + throw new Sk.builtin.ValueError("cannot convert argument to rgb sequence"); } if (fl < 0 || fl > 1) { - throw new Sk.builtin.ValueError('gray (string) must be in range 0-1'); + throw new Sk.builtin.ValueError("gray (string) must be in range 0-1"); } return [fl, fl, fl]; @@ -1057,9 +1052,10 @@ jsplotlib.to_rgb = function(fmt) { // check if its a color tuple [r,g,b, [a]] with values from [0-1] if (Array.isArray(fmt)) { - if (fmt.length > 4 || fmt.length < 3) - throw new Sk.builtin.ValueError('sequence length is ' + fmt.length + - '; must be 3 or 4'); + if (fmt.length > 4 || fmt.length < 3) { + throw new Sk.builtin.ValueError("sequence length is " + fmt.length + + "; must be 3 or 4"); + } color = fmt.slice(0, 3); var i; @@ -1067,9 +1063,10 @@ jsplotlib.to_rgb = function(fmt) { for (i = 0; i < 3; i++) { var fl_rgb = parseFloat(fmt); - if (fl_rgb < 0 || fl_rgb > 1) + if (fl_rgb < 0 || fl_rgb > 1) { throw new Sk.builtin.ValueError( - 'number in rbg sequence outside 0-1 range'); + "number in rbg sequence outside 0-1 range"); + } } } @@ -1113,15 +1110,15 @@ jsplotlib.hex2color = function(s) { Expects and rgb tuple with values [0,1] **/ jsplotlib.rgb2hex = function(rgb) { - if (!rgb) return null; + if (!rgb) {return null;} if (rgb.length && rgb.length >= 3) { var i; // some hacky code to rebuild string format :( - var hex_str = '#'; + var hex_str = "#"; for (i = 0; i < 3; i++) { var val = Math.round(rgb[i] * 255).toString(16); - hex_str += val.length == 2 ? val : '0' + val; + hex_str += val.length == 2 ? val : "0" + val; } return hex_str; diff --git a/src/lib/pedal/sandbox/mocked.py b/src/lib/pedal/sandbox/mocked.py index e421cfbdb1..5ab898f899 100644 --- a/src/lib/pedal/sandbox/mocked.py +++ b/src/lib/pedal/sandbox/mocked.py @@ -58,7 +58,8 @@ def _disabled_version(*args, **kwargs): _OPEN_FORBIDDEN_NAMES = re.compile(r"(^[./])|(\.py$)") _OPEN_FORBIDDEN_MODES = re.compile(r"[wa+]") - +# TODO: Turn this into a function that lets us more elegantly specify valid and +# invalid filenames/paths def _restricted_open(name, mode='r', buffering=-1): if _OPEN_FORBIDDEN_NAMES.search(name): raise RuntimeError("The filename you passed to 'open' is restricted.") @@ -67,6 +68,11 @@ def _restricted_open(name, mode='r', buffering=-1): else: return _original_builtins['open'](name, mode, buffering) +# TODO: Allow this to be flexible +def _restricted_import(name, globals=None, locals=None, fromlist=(), level=0): + if name == 'pedal' or name.startswith('pedal.'): + raise RuntimeError("You cannot import pedal!") + return _original_builtins['__import__'](name, globals, locals, fromlist, level) try: __builtins__ @@ -74,7 +80,8 @@ def _restricted_open(name, mode='r', buffering=-1): _default_builtins = {'globals': globals, 'locals': locals, 'open': open, - 'input': input} + 'input': input, + '__import__': __import__} else: if isinstance(__builtins__, types.ModuleType): _default_builtins = __builtins__.__dict__ @@ -89,6 +96,7 @@ def _restricted_open(name, mode='r', buffering=-1): 'exec': _default_builtins.get('exec', _disabled_exec), 'eval': _default_builtins.get('eval', _disabled_eval), 'compile': _default_builtins.get('compile', _disabled_compile), + '__import__': _default_builtins['__import__'] } diff --git a/src/lib/pedal/sandbox/sandbox.py b/src/lib/pedal/sandbox/sandbox.py index 2d30c523e1..7efd0ae469 100644 --- a/src/lib/pedal/sandbox/sandbox.py +++ b/src/lib/pedal/sandbox/sandbox.py @@ -193,7 +193,8 @@ def __init__(self, initial_data=None, # Modules if modules is None: modules = {'matplotlib': True, - 'pedal': mocked.MockPedal()} + 'pedal': mocked.MockPedal() + } self.mocked_modules = {} self.modules = {} self.add_mocks(modules) @@ -202,7 +203,8 @@ def __init__(self, initial_data=None, 'eval': mocked._disabled_eval, 'exec': mocked._disabled_exec, 'globals': mocked._disabled_globals, - 'open': mocked._restricted_open + 'open': mocked._restricted_open, + '__import__': mocked._restricted_import, } if allowed_functions is not None: for function_name in allowed_functions: @@ -522,7 +524,8 @@ def _stop_patches(self): patch.stop() def _capture_exception(self, exception, exc_info, report_exceptions, - raise_exceptions, context, keep_context): + raise_exceptions, context, keep_context, + as_filename="", code=""): self.exception = exception if context is not False: if context is None or keep_context: @@ -536,12 +539,16 @@ def _capture_exception(self, exception, exc_info, report_exceptions, context = self.FILE_CONTEXT_MESSAGE.format(filename=self.report['source']['filename']) self.exception = _add_context_to_error(self.exception, context) line_offset = self.report['source'].get('line_offset', 0) - student_filename = self.report['source']['filename'] + student_filename = self.report['source'].get('filename', as_filename) + if 'lines' in self.report['source']: + lines = self.report['source']['lines'] + else: + lines = code.split("\n") traceback = SandboxTraceback(self.exception, exc_info, self.full_traceback, self.instructor_filename, line_offset, student_filename, - self.report['source']['lines']) + lines) self.exception_position = {'line': traceback.line_number} self.exception_formatted = traceback.format_exception() self.exception_name = str(self.exception.__class__)[8:-2] @@ -597,7 +604,8 @@ def run(self, code, as_filename=None, modules=None, inputs=None, except TimeoutError as timeout_exception: self._capture_exception(timeout_exception, sys.exc_info(), report_exceptions, raise_exceptions, - context, keep_context) + context, keep_context, as_filename, + code) return self if as_filename is None: @@ -619,30 +627,37 @@ def run(self, code, as_filename=None, modules=None, inputs=None, mocked_functions['sys'] = sys mocked_functions['os'] = os mocked._override_builtins(self.data, mocked_functions) - self.exception = None self.exception_position = None self.exception_formatted = None # Patch in dangerous built-ins + x = sys.stdout capture_stdout = io.StringIO() self._start_patches( patch('sys.stdout', capture_stdout), patch('time.sleep', return_value=None), patch.dict('sys.modules', self.mocked_modules) ) - # Compile and run student code + # TODO: Hack, add more flexibile way to specify unusable modules + for module in list(sys.modules.keys()): + if module.startswith('pedal.'): + del sys.modules[module] try: compiled_code = compile(code, as_filename, 'exec') with self.trace._as_filename(as_filename, code): exec(compiled_code, self.data) except Exception as user_exception: - self._capture_exception(user_exception, sys.exc_info(), + self._stop_patches() + info = sys.exc_info() + self._capture_exception(user_exception, info, report_exceptions, raise_exceptions, - context, keep_context) - finally: + context, keep_context, as_filename, + code) + else: self._stop_patches() + finally: self.append_output(capture_stdout.getvalue()) if context is None: self.call_contexts[self.call_id].append(code) diff --git a/src/lib/pedal/tifa/tifa.py b/src/lib/pedal/tifa/tifa.py index aa44677a1b..baccb032db 100644 --- a/src/lib/pedal/tifa/tifa.py +++ b/src/lib/pedal/tifa/tifa.py @@ -445,7 +445,7 @@ def visit_Call(self, node): callee = self.identify_caller(node) # Handle args - arguments = [self.visit(arg) for arg in node.args] + arguments = [self.visit(arg) for arg in node.args] if node.args else [] # TODO: Handle keywords # TODO: Handle starargs @@ -610,7 +610,7 @@ def definition(tifa, call_type, call_name, parameters, call_position): self.report_issue('Incorrect Arity', {"position": position}) # TODO: Handle special types of parameters for arg, parameter in zip(args, parameters): - name = arg.arg if self.PYTHON_3 else arg.id + name = arg.arg if parameter is not None: parameter = parameter.clone_mutably() self.store_variable(name, parameter, position) @@ -712,7 +712,7 @@ def definition(tifa, call_type, call_name, parameters, call_position): self.report_issue('Incorrect Arity', {"position": position}) # TODO: Handle special types of parameters for arg, parameter in zip(args, parameters): - name = arg.arg if self.PYTHON_3 else arg.id + name = arg.arg if parameter is not None: parameter = parameter.clone_mutably() self.store_variable(name, parameter, position) @@ -870,15 +870,10 @@ def visit_While(self, node): self.merge_paths(this_path_id, body_path.id, empty_path.id) def visit_With(self, node): - if self.PYTHON_3: - for item in node.items: - type_value = self.visit(item.context_expr) - self.visit(item.optional_vars) - self._walk_target(item.optional_vars, type_value) - else: - type_value = self.visit(node.context_expr) - # self.visit(node.optional_vars) - self._walk_target(node.optional_vars, type_value) + for item in node.items: + type_value = self.visit(item.context_expr) + self.visit(item.optional_vars) + self._walk_target(item.optional_vars, type_value) # Handle the bodies self.visit_statements(node.body) diff --git a/src/lib/turtle.js b/src/lib/turtle.js index 8d9982df5c..1b001344f0 100644 --- a/src/lib/turtle.js +++ b/src/lib/turtle.js @@ -1,2395 +1,2376 @@ var $builtinmodule = function (name) { -"use strict"; - -function getConfiguredTarget() { - var selector, target; - - selector = (Sk.TurtleGraphics && Sk.TurtleGraphics.target) || "turtle", - target = typeof selector === "string" ? - document.getElementById(selector) : - selector; - // ensure that the canvas container is empty - while (target.firstChild) { - target.removeChild(target.firstChild); - } - return target; -} - -function generateTurtleModule(_target) { - var _module = {}, - _durationSinceRedraw = 0, - _focus = true, - OPTIMAL_FRAME_RATE = 1000/30, - SHAPES = {}, - TURTLE_COUNT = 0, - Types = {}, - _defaultSetup = { - target : "turtle", // DOM element or id of parent container - width : 400, // if set to 0 it will use the target width - height : 400, // if set to 0 it will use the target height - worldWidth : 0, // if set to 0 it will use config.width - worldHeight: 0, // if set to 0 it will use config.height - animate : true, // enabled/disable all animated rendering - bufferSize : 0, // default turtle buffer size - allowUndo : true, // enable ability to use the undo buffer - assets : {} - }, - _frameRequest, - _frameRequestTimeout, - _screenInstance, - _config, - _anonymousTurtle, - _mouseHandler, - _assets; - - // Ensure that the turtle DOM target has a tabindex - // so that it can accept keyboard focus and events - if (!_target.hasAttribute("tabindex")) { - _target.setAttribute("tabindex", 0); + "use strict"; + + function getConfiguredTarget() { + let selector = (Sk.TurtleGraphics && Sk.TurtleGraphics.target) || "turtle"; + let target; + if (typeof selector === "string") { + target = document.getElementById(selector); + } else if (typeof selector === "function") { + target = selector(); + } else { + target = selector; + } + // ensure that the canvas container is empty + while (target.firstChild) { + target.removeChild(target.firstChild); + } + return target; } - Types.FLOAT = function(value) { - return Sk.builtin.float_(value); - }; - Types.COLOR = function(value) { - if (typeof value === "string") { - return new Sk.builtin.str(value); + function generateTurtleModule(_target) { + var _module = {}, + _durationSinceRedraw = 0, + _focus = true, + OPTIMAL_FRAME_RATE = 1000/30, + SHAPES = {}, + TURTLE_COUNT = 0, + Types = {}, + _defaultSetup = { + target : "turtle", // DOM element or id of parent container + width : 400, // if set to 0 it will use the target width + height : 400, // if set to 0 it will use the target height + worldWidth : 0, // if set to 0 it will use config.width + worldHeight: 0, // if set to 0 it will use config.height + animate : true, // enabled/disable all animated rendering + bufferSize : 0, // default turtle buffer size + allowUndo : true, // enable ability to use the undo buffer + assets : {} + }, + _frameRequest, + _frameRequestTimeout, + _screenInstance, + _config, + _anonymousTurtle, + _mouseHandler, + _assets; + + // Ensure that the turtle DOM target has a tabindex + // so that it can accept keyboard focus and events + if (!_target.hasAttribute("tabindex")) { + _target.setAttribute("tabindex", 0); } - else { - for(var i = 0; i < 3; i++) { - value[i] = Sk.builtin.assk$(value[i]); + + Types.FLOAT = function(value) { + return Sk.builtin.float_(value); + }; + Types.COLOR = function(value) { + if (typeof value === "string") { + return new Sk.builtin.str(value); + } else { + for(var i = 0; i < 3; i++) { + value[i] = Sk.builtin.assk$(value[i]); + } + if (value.length === 4) { + value[3] = Sk.builtin.float_(value[3]); + } + return new Sk.builtin.tuple(value); } - if (value.length === 4) { - value[3] = Sk.builtin.float_(value[3]); + }; + Types.TURTLE_LIST = function(value) { + var skValues = []; + for (var i = 0; i < value.length; i++) { + skValues.push(value[i].skInstance); } - return new Sk.builtin.tuple(value); - } - }; - Types.TURTLE_LIST = function(value) { - var skValues = []; - for (var i = 0; i < value.length; i++) { - skValues.push(value[i].skInstance); - } - return new Sk.builtin.tuple(skValues); - }; + return new Sk.builtin.tuple(skValues); + }; - SHAPES.arrow = [[-10,0],[10,0],[0,10]]; - SHAPES.square = [[ 10,-10],[10,10],[-10,10],[-10, -10]]; - SHAPES.triangle = [[10,-5.77],[0,11.55],[-10,-5.77]]; - SHAPES.classic = [[0,0],[-5,-9],[0,-7],[5,-9]]; - SHAPES.turtle = [ - [0,16],[-2,14],[-1,10],[-4,7],[-7,9],[-9,8],[-6,5],[-7,1],[-5,-3],[-8,-6], - [-6,-8],[-4,-5],[0,-7],[4,-5],[6,-8],[8,-6],[5,-3],[7,1],[6,5],[9,8],[7,9], - [4,7],[1,10],[2,14] - ]; - - SHAPES.circle = [ - [10,0],[9.51,3.09],[8.09,5.88],[5.88,8.09],[3.09,9.51],[0,10],[-3.09,9.51], - [-5.88,8.09],[-8.09,5.88],[-9.51,3.09],[-10,0],[-9.51,-3.09],[-8.09,-5.88], - [-5.88,-8.09],[-3.09,-9.51],[-0,-10],[3.09,-9.51],[5.88,-8.09],[8.09,-5.88], - [9.51,-3.09] - ]; - - _config = (function() { - var key; - - if (!Sk.TurtleGraphics) { - Sk.TurtleGraphics = {}; - } + SHAPES.arrow = [[-10,0],[10,0],[0,10]]; + SHAPES.square = [[ 10,-10],[10,10],[-10,10],[-10, -10]]; + SHAPES.triangle = [[10,-5.77],[0,11.55],[-10,-5.77]]; + SHAPES.classic = [[0,0],[-5,-9],[0,-7],[5,-9]]; + SHAPES.turtle = [ + [0,16],[-2,14],[-1,10],[-4,7],[-7,9],[-9,8],[-6,5],[-7,1],[-5,-3],[-8,-6], + [-6,-8],[-4,-5],[0,-7],[4,-5],[6,-8],[8,-6],[5,-3],[7,1],[6,5],[9,8],[7,9], + [4,7],[1,10],[2,14] + ]; - for(key in _defaultSetup) { - if (!Sk.TurtleGraphics.hasOwnProperty(key)) { - Sk.TurtleGraphics[key] = _defaultSetup[key]; - } - } + SHAPES.circle = [ + [10,0],[9.51,3.09],[8.09,5.88],[5.88,8.09],[3.09,9.51],[0,10],[-3.09,9.51], + [-5.88,8.09],[-8.09,5.88],[-9.51,3.09],[-10,0],[-9.51,-3.09],[-8.09,-5.88], + [-5.88,-8.09],[-3.09,-9.51],[-0,-10],[3.09,-9.51],[5.88,-8.09],[8.09,-5.88], + [9.51,-3.09] + ]; - return Sk.TurtleGraphics; - })(); + _config = (function() { + var key; - function getAsset(name) { - var assets = _config.assets, - asset = (typeof assets === "function") ? assets(name) : assets[name]; + if (!Sk.TurtleGraphics) { + Sk.TurtleGraphics = {}; + } - if (typeof asset === "string") { - return new Promise(function(resolve, reject) { - var img = new Image(); - img.onload = function() { - _config.assets[name] = this; - resolve(img); - }; - img.onerror = function() { - reject(new Error("Missing asset: " + asset)); + for(key in _defaultSetup) { + if (!Sk.TurtleGraphics.hasOwnProperty(key)) { + Sk.TurtleGraphics[key] = _defaultSetup[key]; } - img.src = asset; - }); - } - - return new InstantPromise(undefined, asset); - } + } - // InstantPromise is a workaround to allow usage of the clean promise-style - // then/catch syntax but to instantly call resolve the then/catch chain so we - // can avoid creating Suspensions in unnecessary cases. This is desirable - // because Suspensions have a fairly large negative impact on overall - // performance. These 'instant promises' come into play when a tracer() - // call is made with a value other than 1. When tracer is 0 or greater than 1 - // , we can bypass the creation of a Suspension and proceed to the next line of - // code immediately if the current line is not going to involve a screen - // update. We determine if a real promise or InstantPromise is necessary by - // checking FrameManager.willRenderNext() - function InstantPromise(err, result) { - this.lastResult = result; - this.lastError = err; - } + return Sk.TurtleGraphics; + })(); + + function getAsset(name) { + var assets = _config.assets, + asset = (typeof assets === "function") ? assets(name) : assets[name]; + + if (typeof asset === "string") { + return new Promise(function(resolve, reject) { + var img = new Image(); + img.onload = function() { + _config.assets[name] = this; + resolve(img); + }; + img.onerror = function() { + reject(new Error("Missing asset: " + asset)); + }; + img.src = asset; + }); + } - InstantPromise.prototype.then = function(cb) { - if (this.lastError) { - return this; + return new InstantPromise(undefined, asset); } - try { - this.lastResult = cb(this.lastResult); - } catch(e) { - this.lastResult = undefined; - this.lastError = e; + // InstantPromise is a workaround to allow usage of the clean promise-style + // then/catch syntax but to instantly call resolve the then/catch chain so we + // can avoid creating Suspensions in unnecessary cases. This is desirable + // because Suspensions have a fairly large negative impact on overall + // performance. These 'instant promises' come into play when a tracer() + // call is made with a value other than 1. When tracer is 0 or greater than 1 + // , we can bypass the creation of a Suspension and proceed to the next line of + // code immediately if the current line is not going to involve a screen + // update. We determine if a real promise or InstantPromise is necessary by + // checking FrameManager.willRenderNext() + function InstantPromise(err, result) { + this.lastResult = result; + this.lastError = err; } - return this.lastResult instanceof Promise ? this.lastResult : this; - }; + InstantPromise.prototype.then = function(cb) { + if (this.lastError) { + return this; + } - InstantPromise.prototype.catch = function(cb) { - if (this.lastError) { try { - this.lastResult = cb(this.lastError); - this.lastError = undefined; + this.lastResult = cb(this.lastResult); } catch(e) { this.lastResult = undefined; - this.lastError = e; + this.lastError = e; } - } - return this.lastResult instanceof Promise ? this.lastResult : this; - }; + return this.lastResult instanceof Promise ? this.lastResult : this; + }; - function FrameManager() { - this.reset(); - } + InstantPromise.prototype.catch = function(cb) { + if (this.lastError) { + try { + this.lastResult = cb(this.lastError); + this.lastError = undefined; + } catch(e) { + this.lastResult = undefined; + this.lastError = e; + } + } + + return this.lastResult instanceof Promise ? this.lastResult : this; + }; - var _frameManager; - function getFrameManager() { - if (!_frameManager) { - _frameManager = new FrameManager(); + function FrameManager() { + this.reset(); } - return _frameManager; - } - (function(proto) { - var browserFrame; - (function(frame) { - if (frame) { - browserFrame = function(method) { - return (_frameRequest = frame(method)); - }; + var _frameManager; + function getFrameManager() { + if (!_frameManager) { + _frameManager = new FrameManager(); } - })(window.requestAnimationFrame || window.mozRequestAnimationFrame); + return _frameManager; + } + + (function(proto) { + var browserFrame; + (function(frame) { + if (frame) { + browserFrame = function(method) { + return (_frameRequest = frame(method)); + }; + } + })(window.requestAnimationFrame || window.mozRequestAnimationFrame); + + function animationFrame(delay) { + if (!_config.animate) { + return function(method) { + method(); + }; + } + + if (!delay && browserFrame) { + return browserFrame; + } - function animationFrame(delay) { - if (!_config.animate) { return function(method) { - method(); + _frameRequestTimeout = window.setTimeout( + method, + delay || OPTIMAL_FRAME_RATE + ); + return _frameRequestTimeout; }; } - if (!delay && browserFrame) { - return browserFrame; - } - - return function(method) { - _frameRequestTimeout = window.setTimeout( - method, - delay || OPTIMAL_FRAME_RATE - ); - return _frameRequestTimeout; + proto.willRenderNext = function() { + return !!(this._buffer && this._frameCount+1 === this.frameBuffer()); }; - } - proto.willRenderNext = function() { - return !!(this._buffer && this._frameCount+1 === this.frameBuffer()); - }; - - proto.turtles = function() { - return this._turtles; - }; + proto.turtles = function() { + return this._turtles; + }; - proto.addTurtle = function(turtle) { - this._turtles.push(turtle); - }; + proto.addTurtle = function(turtle) { + this._turtles.push(turtle); + }; - proto.reset = function() { - if (this._turtles) { - for(var i = this._turtles.length; --i >= 0;) { - this._turtles[i].reset(); + proto.reset = function() { + if (this._turtles) { + for(var i = this._turtles.length; --i >= 0;) { + this._turtles[i].reset(); + } } - } - this._turtles = []; - this._frames = []; - this._frameCount = 0; - this._buffer = 1; - this._rate = 0; - this._animationFrame = animationFrame(); - }; + this._turtles = []; + this._frames = []; + this._frameCount = 0; + this._buffer = 1; + this._rate = 0; + this._animationFrame = animationFrame(); + }; - proto.addFrame = function(method, countAsFrame) { - var instant = false; + proto.addFrame = function(method, countAsFrame) { + var instant = false; - if (countAsFrame) { - this._frameCount += 1; - } + if (countAsFrame) { + this._frameCount += 1; + } - this.frames().push(method); + this.frames().push(method); - instant = ( - !_config.animate || + instant = ( + !_config.animate || (this._buffer && this._frameCount === this.frameBuffer()) - ); + ); - return instant ? this.update() : new InstantPromise(); - }; + return instant ? this.update() : new InstantPromise(); + }; - proto.frames = function() { - return this._frames; - }; + proto.frames = function() { + return this._frames; + }; - proto.frameBuffer = function(buffer) { - if (typeof buffer === "number") { - this._buffer = buffer | 0; - if (buffer && buffer <= this._frameCount) { - return this.update(); + proto.frameBuffer = function(buffer) { + if (typeof buffer === "number") { + this._buffer = buffer | 0; + if (buffer && buffer <= this._frameCount) { + return this.update(); + } } - } - return this._buffer; - }; + return this._buffer; + }; - proto.refreshInterval = function(rate) { - if (typeof rate === "number") { - this._rate = rate | 0; - this._animationFrame = animationFrame(rate); - } - return this._rate; - }; + proto.refreshInterval = function(rate) { + if (typeof rate === "number") { + this._rate = rate | 0; + this._animationFrame = animationFrame(rate); + } + return this._rate; + }; - proto.update = function() { - return (this._frames && this._frames.length) ? - this.requestAnimationFrame() : - new InstantPromise(); - }; + proto.update = function() { + return (this._frames && this._frames.length) ? + this.requestAnimationFrame() : + new InstantPromise(); + }; - proto.requestAnimationFrame = function() { - var frames = this._frames, - animationFrame = this._animationFrame, - turtles = this._turtles, - sprites = getScreen().spriteLayer(), - turtle, i; - - this._frames = []; - this._frameCount = 0; - - return new Promise(function(resolve) { - animationFrame(function paint() { - for (i = 0; i < frames.length; i++) { - if (frames[i]) { - frames[i](); + proto.requestAnimationFrame = function() { + var frames = this._frames, + animationFrame = this._animationFrame, + turtles = this._turtles, + sprites = getScreen().spriteLayer(), + turtle, i; + + this._frames = []; + this._frameCount = 0; + + return new Promise(function(resolve) { + animationFrame(function paint() { + for (i = 0; i < frames.length; i++) { + if (frames[i]) { + frames[i](); + } } - } - clearLayer(sprites); - for (i = 0; i < turtles.length; i++) { - turtle = turtles[i]; - if (turtle.getState().shown) { - drawTurtle(turtle.getState(), sprites); + clearLayer(sprites); + for (i = 0; i < turtles.length; i++) { + turtle = turtles[i]; + if (turtle.getState().shown) { + drawTurtle(turtle.getState(), sprites); + } } - } - resolve(); + resolve(); + }); }); - }); - }; - })(FrameManager.prototype); + }; + })(FrameManager.prototype); - function MouseHandler() { - var self = this; + function MouseHandler() { + var self = this; - this._target = getTarget(); - this._managers = {}; - this._handlers = { - mousedown : function(e) { - self.onEvent("mousedown", e); - }, - mouseup : function(e) { - self.onEvent("mouseup", e); - }, - mousemove : function(e) { - self.onEvent("mousemove", e); + this._target = getTarget(); + this._managers = {}; + this._handlers = { + mousedown : function(e) { + self.onEvent("mousedown", e); + }, + mouseup : function(e) { + self.onEvent("mouseup", e); + }, + mousemove : function(e) { + self.onEvent("mousemove", e); + } + }; + for (var key in this._handlers) { + this._target.addEventListener(key, this._handlers[key]); } - }; - for (var key in this._handlers) { - this._target.addEventListener(key, this._handlers[key]); } - } - (function(proto) { - proto.onEvent = function(type, e) { - var managers = this._managers[type], - moveManagers = this._managers["mousemove"], - computed = false, - x, y, localX, localY, i; - - function computeCoordinates() { - if (computed) return; - var world = getScreen(); - var rect = world.spriteLayer().canvas.getBoundingClientRect(); - x = e.clientX - rect.left | 0; - y = e.clientY - rect.top | 0; - localX = x * world.xScale + world.llx; - localY = y * world.yScale + world.ury; - computed = true; - } + (function(proto) { + proto.onEvent = function(type, e) { + var managers = this._managers[type], + moveManagers = this._managers["mousemove"], + computed = false, + x, y, localX, localY, i; + + function computeCoordinates() { + if (computed) {return;} + var world = getScreen(); + var rect = world.spriteLayer().canvas.getBoundingClientRect(); + x = e.clientX - rect.left | 0; + y = e.clientY - rect.top | 0; + localX = x * world.xScale + world.llx; + localY = y * world.yScale + world.ury; + computed = true; + } - if ((type === "mousedown" || type === "mouseup") && moveManagers && moveManagers.length) { - computeCoordinates(); - for (i = moveManagers.length; --i >= 0;) { - if (moveManagers[i].test(x, y, localX, localY)) { - moveManagers[i].canMove(type === "mousedown"); + if ((type === "mousedown" || type === "mouseup") && moveManagers && moveManagers.length) { + computeCoordinates(); + for (i = moveManagers.length; --i >= 0;) { + if (moveManagers[i].test(x, y, localX, localY)) { + moveManagers[i].canMove(type === "mousedown"); + } } } - } - if (managers && managers.length) { - computeCoordinates(); - for (i = managers.length; --i >= 0;) { - if (type === "mousemove" && managers[i].canMove() && managers[i].test(x, y, localX, localY)) { - managers[i].trigger([localX, localY]); - }else if(type==="mousedown" && managers[i].test(x, y, localX, localY)){//For onclick event - managers[i].trigger([localX, localY]); + if (managers && managers.length) { + computeCoordinates(); + for (i = managers.length; --i >= 0;) { + if (type === "mousemove" && managers[i].canMove() && managers[i].test(x, y, localX, localY)) { + managers[i].trigger([localX, localY]); + }else if(type==="mousedown" && managers[i].test(x, y, localX, localY)){//For onclick event + managers[i].trigger([localX, localY]); + } } } - } - }; - - proto.reset = function() { - this._managers = {}; - }; + }; - proto.addManager = function(type, manager) { - if (!this._managers[type]) { - this._managers[type] = []; - } + proto.reset = function() { + this._managers = {}; + }; - this._managers[type].push(manager); - }; + proto.addManager = function(type, manager) { + if (!this._managers[type]) { + this._managers[type] = []; + } - })(MouseHandler.prototype); + this._managers[type].push(manager); + }; - function EventManager(type, target) { - this._type = type; - this._target = target; - this._handlers = undefined; - getMouseHandler().addManager(type, this); - } + })(MouseHandler.prototype); - (function(proto) { - proto.reset = function() { + function EventManager(type, target) { + this._type = type; + this._target = target; this._handlers = undefined; - }; + getMouseHandler().addManager(type, this); + } - proto.canMove = function(value) { - if (!this._target || !this._target.hitTest) return false; + (function(proto) { + proto.reset = function() { + this._handlers = undefined; + }; - if (value !== undefined) { - this._target.hitTest.hit = value; - } + proto.canMove = function(value) { + if (!this._target || !this._target.hitTest) {return false;} - return this._target.hitTest.hit; - }; + if (value !== undefined) { + this._target.hitTest.hit = value; + } - proto.test = function(x, y, localX, localY) { - return this._target && this._target.hitTest ? - this._target.hitTest(x, y, localX, localY) : - !!this._target; - }; + return this._target.hitTest.hit; + }; - proto.trigger = function(args) { - var handlers = this._handlers, - i; + proto.test = function(x, y, localX, localY) { + return this._target && this._target.hitTest ? + this._target.hitTest(x, y, localX, localY) : + !!this._target; + }; - if (handlers && handlers.length) { - for (i = 0; i < handlers.length; i++) { - handlers[i].apply({}, args); + proto.trigger = function(args) { + var handlers = this._handlers, + i; + + if (handlers && handlers.length) { + for (i = 0; i < handlers.length; i++) { + handlers[i].apply({}, args); + } } - } - }; + }; - proto.addHandler = function(handler, add) { - var handlers = this._handlers; + proto.addHandler = function(handler, add) { + var handlers = this._handlers; - if (!add && handlers && handlers.length) { + if (!add && handlers && handlers.length) { // remove all existing handlers - while (handlers.shift()) {/* noop */} - } + while (handlers.shift()) {/* noop */} + } - if (typeof handler !== "function") { - if (handlers && !handlers.length) { - this.reset(); + if (typeof handler !== "function") { + if (handlers && !handlers.length) { + this.reset(); + } + return; } - return; - } - if (!handlers) { - handlers = this._handlers = []; - } + if (!handlers) { + handlers = this._handlers = []; + } - handlers.push(handler); - }; - })(EventManager.prototype); + handlers.push(handler); + }; + })(EventManager.prototype); - function Turtle() { - getFrameManager().addTurtle(this); - this._screen = getScreen(); - this._managers = {}; - this.reset(); - } + function Turtle() { + getFrameManager().addTurtle(this); + this._screen = getScreen(); + this._managers = {}; + this.reset(); + } - Turtle.RADIANS = 2 * Math.PI; + Turtle.RADIANS = 2 * Math.PI; - (function(proto) { - proto.hitTest = function(mouseX, mouseY, localX, localY) { - var context = getScreen().hitTestLayer(); - clearLayer(context); - drawTurtle(this.getState(), context); - var pixel = context.getImageData(mouseX,mouseY,1,1).data; - // check alpha first since it is most likely to have a value - return pixel[3] ||pixel[0] || pixel[1] || pixel[2]; - }; + (function(proto) { + proto.hitTest = function(mouseX, mouseY, localX, localY) { + var context = getScreen().hitTestLayer(); + clearLayer(context); + drawTurtle(this.getState(), context); + var pixel = context.getImageData(mouseX,mouseY,1,1).data; + // check alpha first since it is most likely to have a value + return pixel[3] ||pixel[0] || pixel[1] || pixel[2]; + }; - proto.addUpdate = function(method, countAsFrame, stateChanges) { - var self = this, - state = this.getState(), - args = Array.prototype.slice.call(arguments, stateChanges ? 2 : 3); + proto.addUpdate = function(method, countAsFrame, stateChanges) { + var self = this, + state = this.getState(), + args = Array.prototype.slice.call(arguments, stateChanges ? 2 : 3); - return getFrameManager().addFrame(function() { - if (method) { - method.apply(state, args); - } - if (stateChanges) { - for(var key in stateChanges) { - state[key] = stateChanges[key]; + return getFrameManager().addFrame(function() { + if (method) { + method.apply(state, args); } - } - }, countAsFrame); - }; - - proto.getState = function() { - var self = this; - - if (!this._state) { - this._state = { - x : this._x, - y : this._y, - angle : this._angle, - radians : this._radians, - shape : this._shape, - color : this._color, - fill : this._fill, - filling : this._filling, - size : this._size, - speed : this._computed_speed, - down : this._down, - shown : this._shown, - colorMode : this._colorMode, - context : function() { - return self.getPaper(); + if (stateChanges) { + for(var key in stateChanges) { + state[key] = stateChanges[key]; + } } - }; - } - return this._state; - }; + }, countAsFrame); + }; - proto.translate = function(startX, startY, dx, dy, beginPath, isCircle) { - var self = this; - return translate(this, startX, startY, dx, dy, beginPath, isCircle) - .then(function(coords) { - self._x = coords[0]; - self._y = coords[1]; - }); - }; + proto.getState = function() { + var self = this; + + if (!this._state) { + this._state = { + x : this._x, + y : this._y, + angle : this._angle, + radians : this._radians, + shape : this._shape, + color : this._color, + fill : this._fill, + filling : this._filling, + size : this._size, + speed : this._computed_speed, + down : this._down, + shown : this._shown, + colorMode : this._colorMode, + context : function() { + return self.getPaper(); + } + }; + } + return this._state; + }; - proto.rotate = function(startAngle, delta, isCircle) { - var self = this; - return rotate(this, startAngle, delta, isCircle) - .then(function(heading) { - self._angle = heading.angle; - self._radians = heading.radians; - }); - }; + proto.translate = function(startX, startY, dx, dy, beginPath, isCircle) { + var self = this; + return translate(this, startX, startY, dx, dy, beginPath, isCircle) + .then(function(coords) { + self._x = coords[0]; + self._y = coords[1]; + }); + }; - proto.queueMoveBy = function(startX, startY, theta, distance) { - var dx = Math.cos(theta) * distance, - dy = Math.sin(theta) * distance; + proto.rotate = function(startAngle, delta, isCircle) { + var self = this; + return rotate(this, startAngle, delta, isCircle) + .then(function(heading) { + self._angle = heading.angle; + self._radians = heading.radians; + }); + }; - return this.translate(startX, startY, dx, dy, true); - }; + proto.queueMoveBy = function(startX, startY, theta, distance) { + var dx = Math.cos(theta) * distance, + dy = Math.sin(theta) * distance; - proto.queueTurnTo = function(startAngle, endAngle) { - endAngle = endAngle % this._fullCircle; - if (endAngle < 0) { - endAngle += this._fullCircle; - } - return this.rotate(startAngle, endAngle - startAngle); - }; + return this.translate(startX, startY, dx, dy, true); + }; - proto.getManager = function(type) { - if (!this._managers[type]) { - this._managers[type] = new EventManager(type, this); - } - return this._managers[type]; - }; + proto.queueTurnTo = function(startAngle, endAngle) { + endAngle = endAngle % this._fullCircle; + if (endAngle < 0) { + endAngle += this._fullCircle; + } + return this.rotate(startAngle, endAngle - startAngle); + }; - proto.getPaper = function() { - return this._paper || (this._paper = createLayer(2)); - }; + proto.getManager = function(type) { + if (!this._managers[type]) { + this._managers[type] = new EventManager(type, this); + } + return this._managers[type]; + }; - proto.reset = function() { - this._x = 0; - this._y = 0; - this._radians = 0; - this._angle = 0; - this._shown = true; - this._down = true; - this._color = "black"; - this._fill = "black"; - this._shape = "classic"; - this._size = 1; - this._filling = false; - this._undoBuffer = []; - this._speed = 3; - this._computed_speed = 5; - this._colorMode = 1.0; - this._state = undefined; - - for(var key in this._managers) { - this._managers[key].reset(); - } + proto.getPaper = function() { + return this._paper || (this._paper = createLayer(2)); + }; - this._isRadians = false; - this._fullCircle = 360; - this._bufferSize = typeof _config.bufferSize === "number" ? - _config.bufferSize : - 0; + proto.reset = function() { + this._x = 0; + this._y = 0; + this._radians = 0; + this._angle = 0; + this._shown = true; + this._down = true; + this._color = "black"; + this._fill = "black"; + this._shape = "classic"; + this._size = 1; + this._filling = false; + this._undoBuffer = []; + this._speed = 3; + this._computed_speed = 5; + this._colorMode = 1.0; + this._state = undefined; + + for(var key in this._managers) { + this._managers[key].reset(); + } - removeLayer(this._paper); - this._paper = undefined; - }; + this._isRadians = false; + this._fullCircle = 360; + this._bufferSize = typeof _config.bufferSize === "number" ? + _config.bufferSize : + 0; - proto.$degrees = function(fullCircle) { - fullCircle = (typeof fullCircle === "number") ? - Math.abs(fullCircle) : - 360; + removeLayer(this._paper); + this._paper = undefined; + }; - this._isRadians = false; - if (!fullCircle || !this._fullCircle) { - this._angle = this._radians = 0; - } - else { - this._angle = this._angle / this._fullCircle * fullCircle; - } - this._fullCircle = fullCircle; - return this.addUpdate( - undefined, - false, - {angle:this._angle, radians: this._radians} - ); - }; - proto.$degrees.minArgs = 0; - proto.$degrees.co_varnames = ["fullcircle"]; - proto.$degrees.returnType = Types.FLOAT; - - proto.$radians = function() { - if (!this._isRadians) { - this._isRadians = true; - this._angle = this._radians; - this._fullCircle = Turtle.RADIANS; - } + proto.$degrees = function(fullCircle) { + fullCircle = (typeof fullCircle === "number") ? + Math.abs(fullCircle) : + 360; - return this._angle; - }; - proto.$radians.returnType = Types.FLOAT; + this._isRadians = false; + if (!fullCircle || !this._fullCircle) { + this._angle = this._radians = 0; + } else { + this._angle = this._angle / this._fullCircle * fullCircle; + } + this._fullCircle = fullCircle; + return this.addUpdate( + undefined, + false, + {angle:this._angle, radians: this._radians} + ); + }; + proto.$degrees.minArgs = 0; + proto.$degrees.co_varnames = ["fullcircle"]; + proto.$degrees.returnType = Types.FLOAT; + + proto.$radians = function() { + if (!this._isRadians) { + this._isRadians = true; + this._angle = this._radians; + this._fullCircle = Turtle.RADIANS; + } - proto.$position = proto.$pos = function() { - return [this.$xcor(), this.$ycor()]; - }; - proto.$position.returnType = function(value) { - return new Sk.builtin.tuple([ + return this._angle; + }; + proto.$radians.returnType = Types.FLOAT; + + proto.$position = proto.$pos = function() { + return [this.$xcor(), this.$ycor()]; + }; + proto.$position.returnType = function(value) { + return new Sk.builtin.tuple([ Sk.builtin.float_(value[0]), Sk.builtin.float_(value[1]) - ]); - }; + ]); + }; - proto.$towards = function(x,y) { - var coords = getCoordinates(x,y), - radians = Math.PI + Math.atan2(this._y - coords.y, this._x - coords.x), - angle = radians * (this._fullCircle / Turtle.RADIANS); + proto.$towards = function(x,y) { + var coords = getCoordinates(x,y), + radians = Math.PI + Math.atan2(this._y - coords.y, this._x - coords.x), + angle = radians * (this._fullCircle / Turtle.RADIANS); - return angle; - }; - proto.$towards.co_varnames = ["x", "y"]; - proto.$towards.minArgs = 1; - proto.$towards.returnType = Types.FLOAT; + return angle; + }; + proto.$towards.co_varnames = ["x", "y"]; + proto.$towards.minArgs = 1; + proto.$towards.returnType = Types.FLOAT; - proto.$distance = function(x,y) { - var coords = getCoordinates(x,y), - dx = coords.x - this._x, - dy = coords.y - this._y; + proto.$distance = function(x,y) { + var coords = getCoordinates(x,y), + dx = coords.x - this._x, + dy = coords.y - this._y; - return Math.sqrt(dx * dx + dy * dy); - }; - proto.$distance.co_varnames = ["x", "y"]; - proto.$distance.minArgs = 1; - proto.$distance.returnType = Types.FLOAT; + return Math.sqrt(dx * dx + dy * dy); + }; + proto.$distance.co_varnames = ["x", "y"]; + proto.$distance.minArgs = 1; + proto.$distance.returnType = Types.FLOAT; - proto.$heading = function() { - return Math.abs(this._angle) < 1e-13 ? 0 : this._angle; - }; - proto.$heading.returnType = Types.FLOAT; + proto.$heading = function() { + return Math.abs(this._angle) < 1e-13 ? 0 : this._angle; + }; + proto.$heading.returnType = Types.FLOAT; - proto.$xcor = function() { - return Math.abs(this._x) < 1e-13 ? 0 : this._x; - }; - proto.$xcor.returnType = Types.FLOAT; + proto.$xcor = function() { + return Math.abs(this._x) < 1e-13 ? 0 : this._x; + }; + proto.$xcor.returnType = Types.FLOAT; - proto.$ycor = function() { - return Math.abs(this._y) < 1e-13 ? 0 : this._y; - }; - proto.$ycor.returnType = Types.FLOAT; + proto.$ycor = function() { + return Math.abs(this._y) < 1e-13 ? 0 : this._y; + }; + proto.$ycor.returnType = Types.FLOAT; - proto.$forward = proto.$fd = function(distance) { - pushUndo(this); - return this.queueMoveBy(this._x, this._y, this._radians, distance); - }; - proto.$forward.co_varnames = proto.$fd.co_varnames = ["distance"]; + proto.$forward = proto.$fd = function(distance) { + pushUndo(this); + return this.queueMoveBy(this._x, this._y, this._radians, distance); + }; + proto.$forward.co_varnames = proto.$fd.co_varnames = ["distance"]; - proto.$undo = function() { - popUndo(this); - }; + proto.$undo = function() { + popUndo(this); + }; - proto.$undobufferentries = function() { - return this._undoBuffer.length; - }; + proto.$undobufferentries = function() { + return this._undoBuffer.length; + }; - proto.$setundobuffer = function(size) { - this._bufferSize = typeof size === "number" ? - Math.min(Math.abs(size), 1000) : - 0; - }; - proto.$setundobuffer.co_varnames = ["size"]; + proto.$setundobuffer = function(size) { + this._bufferSize = typeof size === "number" ? + Math.min(Math.abs(size), 1000) : + 0; + }; + proto.$setundobuffer.co_varnames = ["size"]; - proto.$backward = proto.$back = proto.$bk = function(distance) { - pushUndo(this); - return this.queueMoveBy(this._x, this._y, this._radians, -distance); - }; - proto.$backward.co_varnames = proto.$back.co_varnames = proto.$bk.co_varnames = ["distance"]; + proto.$backward = proto.$back = proto.$bk = function(distance) { + pushUndo(this); + return this.queueMoveBy(this._x, this._y, this._radians, -distance); + }; + proto.$backward.co_varnames = proto.$back.co_varnames = proto.$bk.co_varnames = ["distance"]; - proto.$goto_$rw$ = proto.$setpos = proto.$setposition = function(x,y) { - var coords = getCoordinates(x,y); + proto.$goto_$rw$ = proto.$setpos = proto.$setposition = function(x,y) { + var coords = getCoordinates(x,y); - pushUndo(this); + pushUndo(this); - return this.translate( - this._x, this._y, - coords.x - this._x, coords.y - this._y, - true - ); - }; - proto.$goto_$rw$.co_varnames = proto.$setpos.co_varnames = proto.$setposition.co_varnames = ["x", "y"]; - proto.$goto_$rw$.minArgs = proto.$setpos.minArgs = proto.$setposition.minArgs = 1; + return this.translate( + this._x, this._y, + coords.x - this._x, coords.y - this._y, + true + ); + }; + proto.$goto_$rw$.co_varnames = proto.$setpos.co_varnames = proto.$setposition.co_varnames = ["x", "y"]; + proto.$goto_$rw$.minArgs = proto.$setpos.minArgs = proto.$setposition.minArgs = 1; - proto.$setx = function(x) { - return this.translate(this._x, this._y, x - this._x, 0, true); - }; - proto.$setx.co_varnames = ["x"]; + proto.$setx = function(x) { + return this.translate(this._x, this._y, x - this._x, 0, true); + }; + proto.$setx.co_varnames = ["x"]; - proto.$sety = function(y) { - return this.translate(this._x, this._y, 0, y - this._y, true); - }; - proto.$sety.co_varnames = ["y"]; - - proto.$home = function() { - var self = this, - angle = this._angle; - - pushUndo(this); - return self.translate(this._x, this._y, -this._x, -this._y, true) - .then(function(position) { - return self.queueTurnTo(angle, 0); - }) - .then(function(heading) { - return undefined; - }); - }; + proto.$sety = function(y) { + return this.translate(this._x, this._y, 0, y - this._y, true); + }; + proto.$sety.co_varnames = ["y"]; - proto.$right = proto.$rt = function(angle) { - pushUndo(this); - return this.rotate(this._angle, -angle); - }; - proto.$right.co_varnames = proto.$rt.co_varnames = ["angle"]; + proto.$home = function() { + var self = this, + angle = this._angle; - proto.$left = proto.$lt = function(angle) { - pushUndo(this); - return this.rotate(this._angle, angle); - }; - proto.$left.co_varnames = proto.$lt.co_varnames = ["angle"]; + pushUndo(this); + return self.translate(this._x, this._y, -this._x, -this._y, true) + .then(function(position) { + return self.queueTurnTo(angle, 0); + }) + .then(function(heading) { + return undefined; + }); + }; - proto.$setheading = proto.$seth = function(angle) { - pushUndo(this); - return this.queueTurnTo(this._angle, angle); - }; - proto.$setheading.co_varnames = proto.$seth.co_varnames = ["angle"]; + proto.$right = proto.$rt = function(angle) { + pushUndo(this); + return this.rotate(this._angle, -angle); + }; + proto.$right.co_varnames = proto.$rt.co_varnames = ["angle"]; - function circleRotate(turtle, angle, radians) { - return function() { - return turtle.addUpdate( - undefined, - false,{angle:angle, radians:radians} - ); + proto.$left = proto.$lt = function(angle) { + pushUndo(this); + return this.rotate(this._angle, angle); }; - } + proto.$left.co_varnames = proto.$lt.co_varnames = ["angle"]; - function circleSegment(turtle, x, y, dx, dy, beginPath) { - return function() { - return turtle.translate(x, y, dx, dy, beginPath, true); + proto.$setheading = proto.$seth = function(angle) { + pushUndo(this); + return this.queueTurnTo(this._angle, angle); }; - } + proto.$setheading.co_varnames = proto.$seth.co_varnames = ["angle"]; - proto.$circle = function(radius, extent, steps) { - var self = this, - x = this._x, - y = this._y, - angle = this._angle, - heading = {}, - states = [], - scale = 1/getScreen().lineScale, - beginPath = true, - endAngle, frac, w, w2, l, i, dx, dy, promise; - - pushUndo(this); - - if (extent === undefined) { - extent = self._fullCircle; + function circleRotate(turtle, angle, radians) { + return function() { + return turtle.addUpdate( + undefined, + false,{angle:angle, radians:radians} + ); + }; } - if (steps === undefined) { - frac = Math.abs(extent)/self._fullCircle; - steps = 1 + ((Math.min(11+Math.abs(radius*scale)/6, 59)*frac) | 0); - } - w = extent / steps; - w2 = 0.5 * w; - l = 2 * radius * Math.sin(w*Math.PI/self._fullCircle); - - if (radius < 0) { - l = -l; - w = -w; - w2 = -w2; - endAngle = angle - extent; - } - else { - endAngle = angle + extent; + function circleSegment(turtle, x, y, dx, dy, beginPath) { + return function() { + return turtle.translate(x, y, dx, dy, beginPath, true); + }; } - promise = getFrameManager().willRenderNext() ? Promise.resolve() : new InstantPromise(); + proto.$circle = function(radius, extent, steps) { + var self = this, + x = this._x, + y = this._y, + angle = this._angle, + heading = {}, + states = [], + scale = 1/getScreen().lineScale, + beginPath = true, + endAngle, frac, w, w2, l, i, dx, dy, promise; - angle += w2; + pushUndo(this); - for(i = 0; i < steps; i++) { - calculateHeading(self, angle + w * i, heading); - dx = Math.cos(heading.radians) * l; - dy = Math.sin(heading.radians) * l; - promise = promise - .then(circleRotate(self, heading.angle, heading.radians)) - .then(circleSegment(self, x, y, dx, dy, beginPath)); - x += dx; - y += dy; - beginPath = false; - } + if (extent === undefined) { + extent = self._fullCircle; + } - promise = promise.then(function() { - calculateHeading(self, endAngle, heading); - self._angle = heading.angle; - self._radians = heading.radians; - return self.addUpdate(undefined, true, heading); - }); + if (steps === undefined) { + frac = Math.abs(extent)/self._fullCircle; + steps = 1 + ((Math.min(11+Math.abs(radius*scale)/6, 59)*frac) | 0); + } + w = extent / steps; + w2 = 0.5 * w; + l = 2 * radius * Math.sin(w*Math.PI/self._fullCircle); + + if (radius < 0) { + l = -l; + w = -w; + w2 = -w2; + endAngle = angle - extent; + } else { + endAngle = angle + extent; + } - return promise; - }; - proto.$circle.co_varnames = ["radius", "extent", "steps"]; - proto.$circle.minArgs = 1; + promise = getFrameManager().willRenderNext() ? Promise.resolve() : new InstantPromise(); - proto.$penup = proto.$up = proto.$pu = function() { - this._down = false; - return this.addUpdate(undefined, false, {down:false}); - }; + angle += w2; - proto.$pendown = proto.$down = proto.$pd = function() { - this._down = true; - return this.addUpdate(undefined, false, {down:true}); - }; + for(i = 0; i < steps; i++) { + calculateHeading(self, angle + w * i, heading); + dx = Math.cos(heading.radians) * l; + dy = Math.sin(heading.radians) * l; + promise = promise + .then(circleRotate(self, heading.angle, heading.radians)) + .then(circleSegment(self, x, y, dx, dy, beginPath)); + x += dx; + y += dy; + beginPath = false; + } - proto.$isdown = function() { - return this._down; - }; + promise = promise.then(function() { + calculateHeading(self, endAngle, heading); + self._angle = heading.angle; + self._radians = heading.radians; + return self.addUpdate(undefined, true, heading); + }); - proto.$speed = function(speed) { - if (speed !== undefined) { - this._speed = Math.max(0, Math.min(1000, speed)); - this._computed_speed = Math.max(0, speed * 2 - 1); - return this.addUpdate(undefined, false, {speed:this._computed_speed}); - } + return promise; + }; + proto.$circle.co_varnames = ["radius", "extent", "steps"]; + proto.$circle.minArgs = 1; - return this._speed; - }; - proto.$speed.minArgs = 0; - proto.$speed.co_varnames = ["speed"]; + proto.$penup = proto.$up = proto.$pu = function() { + this._down = false; + return this.addUpdate(undefined, false, {down:false}); + }; - proto.$pencolor = function(r,g,b,a) { - if (r !== undefined) { - this._color = createColor(this._colorMode,r,g,b,a); - return this.addUpdate(undefined, this._shown, {color : this._color}); - } + proto.$pendown = proto.$down = proto.$pd = function() { + this._down = true; + return this.addUpdate(undefined, false, {down:true}); + }; - return hexToRGB(this._color); - }; - proto.$pencolor.co_varnames = ["r", "g", "b", "a"]; - proto.$pencolor.minArgs = 0; - proto.$pencolor.returnType = Types.COLOR; - - proto.$fillcolor = function(r,g,b,a) { - if (r !== undefined) { - this._fill = createColor(this._colorMode,r,g,b,a); - return this.addUpdate(undefined, this._shown, {fill : this._fill}); - } + proto.$isdown = function() { + return this._down; + }; - return hexToRGB(this._fill); - }; - proto.$fillcolor.co_varnames = ["r", "g", "b", "a"]; - proto.$fillcolor.minArgs = 0; - proto.$fillcolor.returnType = Types.COLOR; - - proto.$color = function(color, fill, b, a) { - if (color !== undefined) { - if (fill === undefined || b !== undefined) { - this._color = createColor(this._colorMode, color, fill, b, a); - this._fill = this._color; - } - else { - this._color = createColor(this._colorMode, color); - this._fill = createColor(this._colorMode, fill); - } - return this.addUpdate(undefined, this._shown, { - color : this._color, - fill : this._fill - }); - } - return [this.$pencolor(), this.$fillcolor()]; - }; - proto.$color.minArgs = 0; - proto.$color.co_varnames = ["color", "fill", "b", "a"]; - proto.$color.returnType = function(value) { - return new Sk.builtin.tuple([ - Types.COLOR(value[0]), - Types.COLOR(value[1]) - ]); - }; + proto.$speed = function(speed) { + if (speed !== undefined) { + this._speed = Math.max(0, Math.min(1000, speed)); + this._computed_speed = Math.max(0, speed * 2 - 1); + return this.addUpdate(undefined, false, {speed:this._computed_speed}); + } - proto.$fill = function(flag) { - var self = this; + return this._speed; + }; + proto.$speed.minArgs = 0; + proto.$speed.co_varnames = ["speed"]; - if (flag !== undefined) { - flag = !!flag; - if (flag === this._filling) return; - this._filling = flag; - if (flag) { - pushUndo(this); - return this.addUpdate(undefined, false, { - filling : true, - fillBuffer : [{x : this._x, y : this._y}] - }); + proto.$pencolor = function(r,g,b,a) { + if (r !== undefined) { + this._color = createColor(this._colorMode,r,g,b,a); + return this.addUpdate(undefined, this._shown, {color : this._color}); } - else { - pushUndo(this); - return this.addUpdate( - function() { - this.fillBuffer.push(this); - drawFill.call(this); - }, - true, - { - filling : false, - fillBuffer : undefined - } - ); + + return hexToRGB(this._color); + }; + proto.$pencolor.co_varnames = ["r", "g", "b", "a"]; + proto.$pencolor.minArgs = 0; + proto.$pencolor.returnType = Types.COLOR; + + proto.$fillcolor = function(r,g,b,a) { + if (r !== undefined) { + this._fill = createColor(this._colorMode,r,g,b,a); + return this.addUpdate(undefined, this._shown, {fill : this._fill}); } - } - return this._filling; - }; - proto.$fill.co_varnames = ["flag"]; - proto.$fill.minArgs = 0; + return hexToRGB(this._fill); + }; + proto.$fillcolor.co_varnames = ["r", "g", "b", "a"]; + proto.$fillcolor.minArgs = 0; + proto.$fillcolor.returnType = Types.COLOR; + + proto.$color = function(color, fill, b, a) { + if (color !== undefined) { + if (fill === undefined || b !== undefined) { + this._color = createColor(this._colorMode, color, fill, b, a); + this._fill = this._color; + } else { + this._color = createColor(this._colorMode, color); + this._fill = createColor(this._colorMode, fill); + } + return this.addUpdate(undefined, this._shown, { + color : this._color, + fill : this._fill + }); + } + return [this.$pencolor(), this.$fillcolor()]; + }; + proto.$color.minArgs = 0; + proto.$color.co_varnames = ["color", "fill", "b", "a"]; + proto.$color.returnType = function(value) { + return new Sk.builtin.tuple([ + Types.COLOR(value[0]), + Types.COLOR(value[1]) + ]); + }; - proto.$begin_fill = function() { - return this.$fill(true); - }; + proto.$fill = function(flag) { + var self = this; + + if (flag !== undefined) { + flag = !!flag; + if (flag === this._filling) {return;} + this._filling = flag; + if (flag) { + pushUndo(this); + return this.addUpdate(undefined, false, { + filling : true, + fillBuffer : [{x : this._x, y : this._y}] + }); + } else { + pushUndo(this); + return this.addUpdate( + function() { + this.fillBuffer.push(this); + drawFill.call(this); + }, + true, + { + filling : false, + fillBuffer : undefined + } + ); + } + } - proto.$end_fill = function() { - return this.$fill(false); - }; + return this._filling; + }; + proto.$fill.co_varnames = ["flag"]; + proto.$fill.minArgs = 0; - proto.$stamp = function() { - pushUndo(this); - return this.addUpdate(function() { - drawTurtle(this, this.context()); - }, true); - }; + proto.$begin_fill = function() { + return this.$fill(true); + }; - proto.$dot = function(size, color, g, b, a) { - pushUndo(this); - size = Sk.builtin.asnum$(size); - size = (typeof size === "number") ? - Math.max(1, Math.abs(size) | 0) : - Math.max(this._size + 4, this._size * 2); + proto.$end_fill = function() { + return this.$fill(false); + }; - color = (color !== undefined) ? - createColor(this._colorMode, color, g, b, a) : - this._color; + proto.$stamp = function() { + pushUndo(this); + return this.addUpdate(function() { + drawTurtle(this, this.context()); + }, true); + }; - return this.addUpdate(drawDot, true, undefined, size, color); - }; - proto.$dot.co_varnames = ["size", "color", "g", "b", "a"]; + proto.$dot = function(size, color, g, b, a) { + pushUndo(this); + size = Sk.builtin.asnum$(size); + size = (typeof size === "number") ? + Math.max(1, Math.abs(size) | 0) : + Math.max(this._size + 4, this._size * 2); - proto.$write = function(message, move, align, font) { - var self = this, - promise, face, size, type, width; + color = (color !== undefined) ? + createColor(this._colorMode, color, g, b, a) : + this._color; - pushUndo(this); + return this.addUpdate(drawDot, true, undefined, size, color); + }; + proto.$dot.co_varnames = ["size", "color", "g", "b", "a"]; - message = String(message); + proto.$write = function(message, move, align, font) { + var self = this, + promise, face, size, type, width; - if (font && font.constructor === Array) { - face = typeof font[0] === "string" ? font[0] : "Arial"; - size = String(font[1] || "12pt"); - type = typeof font[2] === "string" ? font[2] : "normal"; - if (/^\d+$/.test(size)) { - size += "pt"; - } + pushUndo(this); - font = [type, size, face].join(" "); - } + message = String(message); - if (!align) { - align = "left"; - } + if (font && font.constructor === Array) { + face = typeof font[0] === "string" ? font[0] : "Arial"; + size = String(font[1] || "12pt"); + type = typeof font[2] === "string" ? font[2] : "normal"; + if (/^\d+$/.test(size)) { + size += "pt"; + } - promise = this.addUpdate( - drawText, true, undefined, message, align, font - ); + font = [type, size, face].join(" "); + } - if (move && (align === "left" || align === "center")) { - width = measureText(message, font); - if (align === "center") { - width = width/2; + if (!align) { + align = "left"; } - promise = promise.then(function() { - var state = self.getState(); - return self.translate(state.x, state.y, width, 0, true); - }); - } - return promise; - }; - proto.$write.co_varnames = ["message", "move", "align", "font"]; - proto.$write.minArgs = 1; + promise = this.addUpdate( + drawText, true, undefined, message, align, font + ); - proto.$pensize = proto.$width = function(size) { - if (size !== undefined) { - this._size = size; - return this.addUpdate(undefined, this._shown, {size : size}); - } + if (move && (align === "left" || align === "center")) { + width = measureText(message, font); + if (align === "center") { + width = width/2; + } + promise = promise.then(function() { + var state = self.getState(); + return self.translate(state.x, state.y, width, 0, true); + }); + } - return this._size; - }; - proto.$pensize.minArgs = proto.$width.minArgs = 0; - proto.$pensize.co_varnames = proto.$width.co_varnames = ["width"]; + return promise; + }; + proto.$write.co_varnames = ["message", "move", "align", "font"]; + proto.$write.minArgs = 1; - proto.$showturtle = proto.$st = function() { - this._shown = true; - return this.addUpdate(undefined, true, {shown : true}); - }; + proto.$pensize = proto.$width = function(size) { + if (size !== undefined) { + this._size = size; + return this.addUpdate(undefined, this._shown, {size : size}); + } - proto.$hideturtle = proto.$ht = function() { - this._shown = false; - return this.addUpdate(undefined, true, {shown : false}); - }; + return this._size; + }; + proto.$pensize.minArgs = proto.$width.minArgs = 0; + proto.$pensize.co_varnames = proto.$width.co_varnames = ["width"]; - proto.$isvisible = function() { - return this._shown; - }; + proto.$showturtle = proto.$st = function() { + this._shown = true; + return this.addUpdate(undefined, true, {shown : true}); + }; - proto.$shape = function(shape) { - if (shape && SHAPES[shape]) { - this._shape = shape; - return this.addUpdate(undefined, this._shown, {shape : shape}); - } + proto.$hideturtle = proto.$ht = function() { + this._shown = false; + return this.addUpdate(undefined, true, {shown : false}); + }; - return this._shape; - }; - proto.$shape.minArgs = 0; - proto.$shape.co_varnames = ["name"]; - //colormode supported - proto.$colormode = function(cmode){ - if(cmode !== undefined){ - if(cmode === 255) { - this._colorMode = 255; - } else { - this._colorMode = 1.0; - } - return this.addUpdate(undefined, this._shown, {colorMode : this._colorMode}); - } + proto.$isvisible = function() { + return this._shown; + }; - return this._colorMode; - } - proto.$colormode.minArgs = 0; - proto.$colormode.co_varnames = ["cmode"]; - proto.$colormode.returnType = function(value) { - return value === 255 ? Sk.builtin.int_(255) : Sk.builtin.float_(1.0); - }; + proto.$shape = function(shape) { + if (shape && SHAPES[shape]) { + this._shape = shape; + return this.addUpdate(undefined, this._shown, {shape : shape}); + } - proto.$window_width = function() { - return this._screen.$window_width(); - }; + return this._shape; + }; + proto.$shape.minArgs = 0; + proto.$shape.co_varnames = ["name"]; + //colormode supported + proto.$colormode = function(cmode){ + if(cmode !== undefined){ + if(cmode === 255) { + this._colorMode = 255; + } else { + this._colorMode = 1.0; + } + return this.addUpdate(undefined, this._shown, {colorMode : this._colorMode}); + } - proto.$window_height = function() { - return this._screen.$window_height(); - }; + return this._colorMode; + }; + proto.$colormode.minArgs = 0; + proto.$colormode.co_varnames = ["cmode"]; + proto.$colormode.returnType = function(value) { + return value === 255 ? Sk.builtin.int_(255) : Sk.builtin.float_(1.0); + }; - proto.$tracer = function(n, delay) { - return this._screen.$tracer(n, delay); - }; - proto.$tracer.minArgs = 0; - proto.$tracer.co_varnames = ["n", "delay"]; + proto.$window_width = function() { + return this._screen.$window_width(); + }; - proto.$update = function() { - return this._screen.$update(); - }; + proto.$window_height = function() { + return this._screen.$window_height(); + }; - proto.$delay = function(delay) { - return this._screen.$delay(delay); - }; - proto.$delay.minArgs = 0; - proto.$delay.co_varnames = ["delay"]; + proto.$tracer = function(n, delay) { + return this._screen.$tracer(n, delay); + }; + proto.$tracer.minArgs = 0; + proto.$tracer.co_varnames = ["n", "delay"]; - proto.$reset = function() { - this.reset(); - return this.$clear(); - }; + proto.$update = function() { + return this._screen.$update(); + }; - proto.$mainloop = proto.$done = function() { - return this._screen.$mainloop(); - }; + proto.$delay = function(delay) { + return this._screen.$delay(delay); + }; + proto.$delay.minArgs = 0; + proto.$delay.co_varnames = ["delay"]; - proto.$clear = function() { - return this.addUpdate(function() { - clearLayer(this.context()); - }, true); - }; - proto.$dot.minArgs = 0; + proto.$reset = function() { + this.reset(); + return this.$clear(); + }; - proto.$onclick = function(method, btn, add) { - this.getManager("mousedown").addHandler(method, add); - }; - proto.$onclick.minArgs = 1; - proto.$onclick.co_varnames = ["method", "btn", "add"]; + proto.$mainloop = proto.$done = function() { + return this._screen.$mainloop(); + }; - proto.$onrelease = function(method, btn, add) { - this.getManager("mouseup").addHandler(method, add); - }; - proto.$onrelease.minArgs = 1; - proto.$onrelease.co_varnames = ["method", "btn", "add"]; + proto.$clear = function() { + return this.addUpdate(function() { + clearLayer(this.context()); + }, true); + }; + proto.$dot.minArgs = 0; - proto.$ondrag = function(method, btn, add) { - this.getManager("mousemove").addHandler(method, add); - }; - proto.$ondrag.minArgs = 1; - proto.$ondrag.co_varnames = ["method", "btn", "add"]; + proto.$onclick = function(method, btn, add) { + this.getManager("mousedown").addHandler(method, add); + }; + proto.$onclick.minArgs = 1; + proto.$onclick.co_varnames = ["method", "btn", "add"]; - proto.$getscreen = function() { - return Sk.misceval.callsimArray(_module.Screen); - }; - proto.$getscreen.isSk = true; + proto.$onrelease = function(method, btn, add) { + this.getManager("mouseup").addHandler(method, add); + }; + proto.$onrelease.minArgs = 1; + proto.$onrelease.co_varnames = ["method", "btn", "add"]; - proto.$clone = function() { + proto.$ondrag = function(method, btn, add) { + this.getManager("mousemove").addHandler(method, add); + }; + proto.$ondrag.minArgs = 1; + proto.$ondrag.co_varnames = ["method", "btn", "add"]; - var newTurtleInstance = Sk.misceval.callsimOrSuspendArray(_module.Turtle); + proto.$getscreen = function() { + return Sk.misceval.callsimArray(_module.Screen); + }; + proto.$getscreen.isSk = true; - // All the properties that are in getState() - newTurtleInstance.instance._x = this._x; - newTurtleInstance.instance._y = this._y; - newTurtleInstance.instance._angle = this._angle; - newTurtleInstance.instance._radians = this._radians; - newTurtleInstance.instance._shape = this._shape; - newTurtleInstance.instance._color = this._color; - newTurtleInstance.instance._fill = this._fill; - newTurtleInstance.instance._filling = this._filling; - newTurtleInstance.instance._size = this._size; - newTurtleInstance.instance._computed_speed = this._computed_speed; - newTurtleInstance.instance._down = this._down; - newTurtleInstance.instance._shown = this._shown; - newTurtleInstance.instance._colorMode = this._colorMode; + proto.$clone = function() { - // Other properties to copy - newTurtleInstance.instance._isRadians = this._isRadians; - newTurtleInstance.instance._fullCircle = this._fullCircle; - newTurtleInstance.instance._bufferSize = this._bufferSize; - newTurtleInstance.instance._undoBuffer = this._undoBuffer; + var newTurtleInstance = Sk.misceval.callsimOrSuspendArray(_module.Turtle); + // All the properties that are in getState() + newTurtleInstance.instance._x = this._x; + newTurtleInstance.instance._y = this._y; + newTurtleInstance.instance._angle = this._angle; + newTurtleInstance.instance._radians = this._radians; + newTurtleInstance.instance._shape = this._shape; + newTurtleInstance.instance._color = this._color; + newTurtleInstance.instance._fill = this._fill; + newTurtleInstance.instance._filling = this._filling; + newTurtleInstance.instance._size = this._size; + newTurtleInstance.instance._computed_speed = this._computed_speed; + newTurtleInstance.instance._down = this._down; + newTurtleInstance.instance._shown = this._shown; + newTurtleInstance.instance._colorMode = this._colorMode; - newTurtleInstance._clonedFrom = this; + // Other properties to copy + newTurtleInstance.instance._isRadians = this._isRadians; + newTurtleInstance.instance._fullCircle = this._fullCircle; + newTurtleInstance.instance._bufferSize = this._bufferSize; + newTurtleInstance.instance._undoBuffer = this._undoBuffer; - return newTurtleInstance; - }; - proto.$clone.returnType = function(value) { - // When I return the instance here, I'm not sure if it ends up with the right "Turtle" python type. - return value - }; - proto.$getturtle = proto.$getpen = function() { - return this.skInstance; - }; - proto.$getturtle.isSk = true; - })(Turtle.prototype); - - function Screen() { - var w,h; - this._frames = 1; - this._delay = undefined; - this._bgcolor = "none"; - this._mode = "standard"; - this._managers = {}; - this._keyLogger = {}; - - w = (_config.worldWidth || _config.width || getWidth()) / 2; - h = (_config.worldHeight || _config.height || getHeight()) / 2; - - this.setUpWorld(-w,-h,w,h); - } + newTurtleInstance._clonedFrom = this; - (function(proto) { - proto.spriteLayer = function() { - return this._sprites || (this._sprites = createLayer(3)); - }; + return newTurtleInstance; + }; + proto.$clone.returnType = function(value) { + // When I return the instance here, I'm not sure if it ends up with the right "Turtle" python type. + return value; + }; - proto.bgLayer = function() { - return this._background || (this._background = createLayer(1)); - }; + proto.$getturtle = proto.$getpen = function() { + return this.skInstance; + }; + proto.$getturtle.isSk = true; + })(Turtle.prototype); + + function Screen() { + var w,h; + this._frames = 1; + this._delay = undefined; + this._bgcolor = "none"; + this._mode = "standard"; + this._managers = {}; + this._keyLogger = {}; + + w = (_config.worldWidth || _config.width || getWidth()) / 2; + h = (_config.worldHeight || _config.height || getHeight()) / 2; + + this.setUpWorld(-w,-h,w,h); + } - proto.hitTestLayer = function() { - return this._hitTest || (this._hitTest = createLayer(0,true)); - }; + (function(proto) { + proto.spriteLayer = function() { + return this._sprites || (this._sprites = createLayer(3)); + }; - proto.getManager = function(type) { - if (!this._managers[type]) { - this._managers[type] = new EventManager(type, this); - } - return this._managers[type]; - }; + proto.bgLayer = function() { + return this._background || (this._background = createLayer(1)); + }; - proto.reset = function() { - var key; + proto.hitTestLayer = function() { + return this._hitTest || (this._hitTest = createLayer(0,true)); + }; - this._keyListeners = undefined; + proto.getManager = function(type) { + if (!this._managers[type]) { + this._managers[type] = new EventManager(type, this); + } + return this._managers[type]; + }; - for (key in this._keyLogger) { - window.clearInterval(this._keyLogger[key]); - window.clearTimeout(this._keyLogger[key]); - delete this._keyLogger[key]; - } + proto.reset = function() { + var key; - if (this._keyDownListener) { - getTarget().removeEventListener("keydown", this._keyDownListener); - this._keyDownListener = undefined; - } + this._keyListeners = undefined; - if (this._keyUpListener) { - getTarget().removeEventListener("keyup", this._keyUpListener); - this._keyUpListener = undefined; - } + for (key in this._keyLogger) { + window.clearInterval(this._keyLogger[key]); + window.clearTimeout(this._keyLogger[key]); + delete this._keyLogger[key]; + } - if (this._timer) { - window.clearTimeout(this._timer); - this._timer = undefined; - } + if (this._keyDownListener) { + getTarget().removeEventListener("keydown", this._keyDownListener); + this._keyDownListener = undefined; + } - for(key in this._managers) { - this._managers[key].reset(); - } + if (this._keyUpListener) { + getTarget().removeEventListener("keyup", this._keyUpListener); + this._keyUpListener = undefined; + } - this._mode = "standard"; - removeLayer(this._sprites); - this._sprites = undefined; - removeLayer(this._background); - this._background = undefined; - }; + if (this._timer) { + window.clearTimeout(this._timer); + this._timer = undefined; + } - proto.setUpWorld = function(llx, lly, urx, ury) { - var world = this; + for(key in this._managers) { + this._managers[key].reset(); + } - world.llx = llx; - world.lly = lly; - world.urx = urx; - world.ury = ury; - world.xScale = (urx - llx) / getWidth(); - world.yScale = -1 * (ury - lly) / getHeight(); - world.lineScale = Math.min(Math.abs(world.xScale), Math.abs(world.yScale)); - }; + this._mode = "standard"; + removeLayer(this._sprites); + this._sprites = undefined; + removeLayer(this._background); + this._background = undefined; + }; - proto.$setup = function(width, height, startX, startY) { - if (isNaN(parseFloat(width))) { - width = getWidth(); - } - if (isNaN(parseFloat(height))) { - height = getHeight(); - } + proto.setUpWorld = function(llx, lly, urx, ury) { + var world = this; - if (width <= 1) { - width = getWidth() * width; - } - if (height <= 1) { - height = getHeight() * height; - } + world.llx = llx; + world.lly = lly; + world.urx = urx; + world.ury = ury; + world.xScale = (urx - llx) / getWidth(); + world.yScale = -1 * (ury - lly) / getHeight(); + world.lineScale = Math.min(Math.abs(world.xScale), Math.abs(world.yScale)); + }; - this._width = width; - this._height = height; + proto.$setup = function(width, height, startX, startY) { + if (isNaN(parseFloat(width))) { + width = getWidth(); + } + if (isNaN(parseFloat(height))) { + height = getHeight(); + } - this._xOffset = (startX !== undefined && !isNaN(parseInt(startX))) ? - parseInt(startX) : - 0; + if (width <= 1) { + width = getWidth() * width; + } + if (height <= 1) { + height = getHeight() * height; + } - this._yOffset = (startY !== undefined && !isNaN(parseInt(startY))) ? - parseInt(startY) : - 0; + this._width = width; + this._height = height; - if (this._mode === "world") { - return this._setworldcoordinates(this.llx, this.lly, this.urx, this.ury); - } + this._xOffset = (startX !== undefined && !isNaN(parseInt(startX))) ? + parseInt(startX) : + 0; - return this._setworldcoordinates(-width/2, -height/2, width/2, height/2); - }; - proto.$setup.minArgs = 0; - proto.$setup.co_varnames = ["width", "height", "startx", "starty"]; + this._yOffset = (startY !== undefined && !isNaN(parseInt(startY))) ? + parseInt(startY) : + 0; - proto.$register_shape = proto.$addshape = function(name, points) { - if (!points) { - return getAsset(name).then(function(asset) { - SHAPES[name] = asset; - }); - } - else { - SHAPES[name] = points; - } - }; - proto.$register_shape.minArgs = 1; + if (this._mode === "world") { + return this._setworldcoordinates(this.llx, this.lly, this.urx, this.ury); + } - proto.$getshapes = function() { - return Object.keys(SHAPES); - }; + return this._setworldcoordinates(-width/2, -height/2, width/2, height/2); + }; + proto.$setup.minArgs = 0; + proto.$setup.co_varnames = ["width", "height", "startx", "starty"]; - proto.$tracer = function(frames, delay) { - if (frames !== undefined || delay !== undefined) { - if (typeof delay === "number") { - this._delay = delay; - getFrameManager().refreshInterval(delay); - } - if (typeof frames === "number") { - this._frames = frames; - return getFrameManager().frameBuffer(frames); + proto.$register_shape = proto.$addshape = function(name, points) { + if (!points) { + return getAsset(name).then(function(asset) { + SHAPES[name] = asset; + }); + } else { + SHAPES[name] = points; } + }; + proto.$register_shape.minArgs = 1; - return; - } + proto.$getshapes = function() { + return Object.keys(SHAPES); + }; - return this._frames; - }; - proto.$tracer.co_varnames = ["frames", "delay"]; - proto.$tracer.minArgs = 0; + proto.$tracer = function(frames, delay) { + if (frames !== undefined || delay !== undefined) { + if (typeof delay === "number") { + this._delay = delay; + getFrameManager().refreshInterval(delay); + } + if (typeof frames === "number") { + this._frames = frames; + return getFrameManager().frameBuffer(frames); + } - proto.$delay = function(delay) { - if (delay !== undefined) { - return this.$tracer(undefined, delay); - } + return; + } - return this._delay === undefined ? OPTIMAL_FRAME_RATE : this._delay; - }; - proto.$delay.co_varnames = ["delay"]; + return this._frames; + }; + proto.$tracer.co_varnames = ["frames", "delay"]; + proto.$tracer.minArgs = 0; - proto._setworldcoordinates = function(llx, lly, urx, ury) { - var world = this, - turtles = getFrameManager().turtles(); + proto.$delay = function(delay) { + if (delay !== undefined) { + return this.$tracer(undefined, delay); + } - this.setUpWorld(llx, lly, urx, ury); + return this._delay === undefined ? OPTIMAL_FRAME_RATE : this._delay; + }; + proto.$delay.co_varnames = ["delay"]; - if (this._sprites) { - applyWorld(this, this._sprites); - } + proto._setworldcoordinates = function(llx, lly, urx, ury) { + var world = this, + turtles = getFrameManager().turtles(); - if (this._background) { - applyWorld(this, this._background); - } + this.setUpWorld(llx, lly, urx, ury); - return this.$clear(); - }; + if (this._sprites) { + applyWorld(this, this._sprites); + } - proto.$setworldcoordinates = function(llx, lly, urx, ury) { - this._mode = "world"; - return this._setworldcoordinates(llx, lly, urx, ury); - }; - proto.$setworldcoordinates.co_varnames = ["llx", "lly", "urx", "ury"]; - proto.minArgs = 4; + if (this._background) { + applyWorld(this, this._background); + } - proto.$clear = proto.$clearscreen = function() { - this.reset(); - return this.$reset(); - }; + return this.$clear(); + }; - proto.$update = function() { - return getFrameManager().update(); - }; + proto.$setworldcoordinates = function(llx, lly, urx, ury) { + this._mode = "world"; + return this._setworldcoordinates(llx, lly, urx, ury); + }; + proto.$setworldcoordinates.co_varnames = ["llx", "lly", "urx", "ury"]; + proto.minArgs = 4; - proto.$reset = proto.$resetscreen = function() { - var self = this, - turtles = getFrameManager().turtles(); + proto.$clear = proto.$clearscreen = function() { + this.reset(); + return this.$reset(); + }; - return getFrameManager().addFrame(function() { - applyWorld(self, self._sprites); - applyWorld(self, self._background); - for(var i = 0; i < turtles.length; i++) { - turtles[i].reset(); - applyWorld(self, turtles[i]._paper); - } - }, true); - }; + proto.$update = function() { + return getFrameManager().update(); + }; - proto.$window_width = function() { - return getWidth(); - }; + proto.$reset = proto.$resetscreen = function() { + var self = this, + turtles = getFrameManager().turtles(); - proto.$window_height = function() { - return getHeight(); - }; - proto.$delay.minArgs = 0; + return getFrameManager().addFrame(function() { + applyWorld(self, self._sprites); + applyWorld(self, self._background); + for(var i = 0; i < turtles.length; i++) { + turtles[i].reset(); + applyWorld(self, turtles[i]._paper); + } + }, true); + }; - proto.$turtles = function() { - return getFrameManager().turtles(); - }; - proto.$turtles.returnType = Types.TURTLE_LIST; - - proto.$bgpic = function(name) { - var self; - if (name) { - self = this; - return getAsset(name).then(function(asset) { - clearLayer(self.bgLayer(), undefined, asset); - }); - } + proto.$window_width = function() { + return getWidth(); + }; - return this._bgpic; - }; - proto.$bgpic.minArgs = 0; - proto.$bgpic.co_varnames = ["name"]; + proto.$window_height = function() { + return getHeight(); + }; + proto.$delay.minArgs = 0; - proto.$bgcolor = function(color, g, b, a) { - if (color !== undefined) { - this._bgcolor = createColor(this._colorMode, color, g, b, a); - clearLayer(this.bgLayer(), this._bgcolor); - return; - } + proto.$turtles = function() { + return getFrameManager().turtles(); + }; + proto.$turtles.returnType = Types.TURTLE_LIST; + + proto.$bgpic = function(name) { + var self; + if (name) { + self = this; + return getAsset(name).then(function(asset) { + clearLayer(self.bgLayer(), undefined, asset); + }); + } - return hexToRGB(this._bgcolor); - }; - proto.$bgcolor.minArgs = 0; - proto.$bgcolor.co_varnames = ["color", "g", "b", "a"]; - proto.$bgcolor.returnType = Types.COLOR; + return this._bgpic; + }; + proto.$bgpic.minArgs = 0; + proto.$bgpic.co_varnames = ["name"]; + + proto.$bgcolor = function(color, g, b, a) { + if (color !== undefined) { + this._bgcolor = createColor(this._colorMode, color, g, b, a); + clearLayer(this.bgLayer(), this._bgcolor); + return; + } - // no-op - just defined for consistency with python version - proto.$mainloop = proto.$done = function() { - return undefined; - }; + return hexToRGB(this._bgcolor); + }; + proto.$bgcolor.minArgs = 0; + proto.$bgcolor.co_varnames = ["color", "g", "b", "a"]; + proto.$bgcolor.returnType = Types.COLOR; - proto.$bye = function() { - return Sk.TurtleGraphics.reset(); - }; + // no-op - just defined for consistency with python version + proto.$mainloop = proto.$done = function() { + return undefined; + }; - proto.$exitonclick = function() { - this._exitOnClick = true; - return this.getManager("mousedown").addHandler(function() { - resetTurtle(); - }, false); - }; + proto.$bye = function() { + return Sk.TurtleGraphics.reset(); + }; - proto.$onclick = function(method, btn, add) { - if (this._exitOnClick) return; - this.getManager("mousedown").addHandler(method, add); - }; - proto.$onclick.minArgs = 1; - proto.$onclick.co_varnames = ["method", "btn", "add"]; - - var KEY_MAP = { - "8" : /^back(space)?$/i, - "9" : /^tab$/i, - "13" : /^(enter|return)$/i, - "16" : /^shift$/i, - "17" : /^(ctrl|control)$/i, - "18" : /^alt$/i, - "27" : /^esc(ape)?$/i, - "32" : /^space$/i, - "33" : /^page[\s\-]?up$/i, - "34" : /^page[\s\-]?down$/i, - "35" : /^end$/i, - "36" : /^home$/i, - "37" : /^left([\s\-]?arrow)?$/i, - "38" : /^up([\s\-]?arrow)?$/i, - "39" : /^right([\s\-]?arrow)?$/i, - "40" : /^down([\s\-]?arrow)?$/i, - "45" : /^insert$/i, - "46" : /^del(ete)?$/i - }; + proto.$exitonclick = function() { + this._exitOnClick = true; + return this.getManager("mousedown").addHandler(function() { + resetTurtle(); + }, false); + }; - proto._createKeyRepeater = function(key, code) { - var self = this; - // set a timeout for 333ms and if key has not yet been - // released, fire another event and continue firing - // at a rate of ~20 times per second until key is released - self._keyLogger[code] = window.setTimeout(function() { + proto.$onclick = function(method, btn, add) { + if (this._exitOnClick) {return;} + this.getManager("mousedown").addHandler(method, add); + }; + proto.$onclick.minArgs = 1; + proto.$onclick.co_varnames = ["method", "btn", "add"]; + + var KEY_MAP = { + "8" : /^back(space)?$/i, + "9" : /^tab$/i, + "13" : /^(enter|return)$/i, + "16" : /^shift$/i, + "17" : /^(ctrl|control)$/i, + "18" : /^alt$/i, + "27" : /^esc(ape)?$/i, + "32" : /^space$/i, + "33" : /^page[\s\-]?up$/i, + "34" : /^page[\s\-]?down$/i, + "35" : /^end$/i, + "36" : /^home$/i, + "37" : /^left([\s\-]?arrow)?$/i, + "38" : /^up([\s\-]?arrow)?$/i, + "39" : /^right([\s\-]?arrow)?$/i, + "40" : /^down([\s\-]?arrow)?$/i, + "45" : /^insert$/i, + "46" : /^del(ete)?$/i + }; + + proto._createKeyRepeater = function(key, code) { + var self = this; + // set a timeout for 333ms and if key has not yet been + // released, fire another event and continue firing + // at a rate of ~20 times per second until key is released + self._keyLogger[code] = window.setTimeout(function() { // trigger the first repeat after the longer delay - self._keyListeners[key](); - // set up the repeat interval with the quick delay - self._keyLogger[code] = window.setInterval(function() { self._keyListeners[key](); - }, 50); - }, 333); - }; + // set up the repeat interval with the quick delay + self._keyLogger[code] = window.setInterval(function() { + self._keyListeners[key](); + }, 50); + }, 333); + }; - proto._createKeyDownListener = function() { - var self = this; + proto._createKeyDownListener = function() { + var self = this; - if (this._keyDownListener) return; + if (this._keyDownListener) {return;} - this._keyDownListener = function(e) { - if (!focusTurtle()) return; + this._keyDownListener = function(e) { + if (!focusTurtle()) {return;} - var code = e.charCode || e.keyCode, - pressed = String.fromCharCode(code).toLowerCase(), - key, inKeyMap; + var code = e.charCode || e.keyCode, + pressed = String.fromCharCode(code).toLowerCase(), + key, inKeyMap; - if (self._keyLogger[code]) return; + if (self._keyLogger[code]) {return;} - for (key in self._keyListeners) { - inKeyMap = (key.length > 1 && KEY_MAP[code] && KEY_MAP[code].test(key)); - if (key === pressed || inKeyMap) { + for (key in self._keyListeners) { + inKeyMap = (key.length > 1 && KEY_MAP[code] && KEY_MAP[code].test(key)); + if (key === pressed || inKeyMap) { // trigger the intial keydown handler - self._keyListeners[key](); - self._createKeyRepeater(key, code); - e.preventDefault(); - break; + self._keyListeners[key](); + self._createKeyRepeater(key, code); + e.preventDefault(); + break; + } } - } + }; + + getTarget().addEventListener("keydown", this._keyDownListener); }; - getTarget().addEventListener("keydown", this._keyDownListener); - }; + proto._createKeyUpListener = function() { + var self = this; - proto._createKeyUpListener = function() { - var self = this; + if (this._keyUpListener) {return;} - if (this._keyUpListener) return; + this._keyUpListener = function(e) { + var interval = self._keyLogger[e.charCode || e.keyCode]; + if (interval !== undefined) { + e.preventDefault(); + window.clearInterval(interval); + window.clearTimeout(interval); + delete(self._keyLogger[e.charCode || e.keyCode]); + } + }; - this._keyUpListener = function(e) { - var interval = self._keyLogger[e.charCode || e.keyCode]; - if (interval !== undefined) { - e.preventDefault(); - window.clearInterval(interval); - window.clearTimeout(interval); - delete(self._keyLogger[e.charCode || e.keyCode]); - } + getTarget().addEventListener("keyup", this._keyUpListener); }; - getTarget().addEventListener("keyup", this._keyUpListener); - }; + proto.$listen = function() { + this._createKeyUpListener(); + this._createKeyDownListener(); + }; - proto.$listen = function() { - this._createKeyUpListener(); - this._createKeyDownListener(); - }; + proto.$onkey = function(method, keyValue) { + if (typeof keyValue === "function") { + var temp = method; + method = keyValue; + keyValue = temp; + } - proto.$onkey = function(method, keyValue) { - if (typeof keyValue === "function") { - var temp = method; - method = keyValue; - keyValue = temp; - } + keyValue = String(keyValue).toLowerCase(); - keyValue = String(keyValue).toLowerCase(); + if (method && typeof method === "function") { + if (!this._keyListeners) {this._keyListeners = {};} + this._keyListeners[keyValue] = method; + } else { + delete this._keyListeners[keyValue]; + } + }; + proto.$onkey.minArgs = 2; + proto.$onkey.co_varnames = ["method", "keyValue"]; - if (method && typeof method === "function") { - if (!this._keyListeners) this._keyListeners = {}; - this._keyListeners[keyValue] = method; - } - else { - delete this._keyListeners[keyValue]; - } - }; - proto.$onkey.minArgs = 2; - proto.$onkey.co_varnames = ["method", "keyValue"] + proto.$onscreenclick = function(method, btn, add) { + this.getManager("mousedown").addHandler(method, add); + }; + proto.$onscreenclick.minArgs = 1; + proto.$onscreenclick.co_varnames = ["method", "btn", "add"]; - proto.$onscreenclick = function(method, btn, add) { - this.getManager("mousedown").addHandler(method, add); - }; - proto.$onscreenclick.minArgs = 1; - proto.$onscreenclick.co_varnames = ["method", "btn", "add"]; + proto.$ontimer = function(method, interval) { + if (this._timer) { + window.clearTimeout(this._timer); + this._timer = undefined; + } - proto.$ontimer = function(method, interval) { - if (this._timer) { - window.clearTimeout(this._timer); - this._timer = undefined; - } + if (method && typeof interval === "number") { + this._timer = window.setTimeout(method, Math.max(0, interval|0)); + } + }; + proto.$ontimer.minArgs = 0; + proto.$ontimer.co_varnames = ["method", "interval"]; + })(Screen.prototype); - if (method && typeof interval === "number") { - this._timer = window.setTimeout(method, Math.max(0, interval|0)); + function ensureAnonymous() { + if (!_anonymousTurtle) { + _anonymousTurtle = Sk.misceval.callsimArray(_module.Turtle); } - }; - proto.$ontimer.minArgs = 0; - proto.$ontimer.co_varnames = ["method", "interval"]; - })(Screen.prototype); - function ensureAnonymous() { - if (!_anonymousTurtle) { - _anonymousTurtle = Sk.misceval.callsimArray(_module.Turtle); + return _anonymousTurtle.instance; } - return _anonymousTurtle.instance; - } - - function getTarget() { - return _target; - } + function getTarget() { + return _target; + } - function getScreen() { - if (!_screenInstance) { - _screenInstance = new Screen(); + function getScreen() { + if (!_screenInstance) { + _screenInstance = new Screen(); + } + return _screenInstance; } - return _screenInstance; - } - function getMouseHandler() { - if (!_mouseHandler) { - _mouseHandler = new MouseHandler(); + function getMouseHandler() { + if (!_mouseHandler) { + _mouseHandler = new MouseHandler(); + } + return _mouseHandler; } - return _mouseHandler; - } - function getWidth() { - return ( - (_screenInstance && _screenInstance._width) || + function getWidth() { + return ( + (_screenInstance && _screenInstance._width) || _config.width || getTarget().clientWidth || _defaultSetup.width - ) | 0; - } + ) | 0; + } - function getHeight() { - return ( - (_screenInstance && _screenInstance._height) || + function getHeight() { + return ( + (_screenInstance && _screenInstance._height) || _config.height || getTarget().clientHeight || _defaultSetup.height - ) | 0; - } - - function createLayer(zIndex, isHidden) { - var canvas = document.createElement("canvas"), - width = getWidth(), - height = getHeight(), - offset = getTarget().firstChild ? (-height) + "px" : "0", - context; - - canvas.width = width; - canvas.height = height; - canvas.style.position = "relative"; - canvas.style.display = "block"; - canvas.style.setProperty("margin-top",offset); - canvas.style.setProperty("z-index", zIndex); - if (isHidden) { - canvas.style.display = "none"; + ) | 0; } - getTarget().appendChild(canvas); + function createLayer(zIndex, isHidden) { + var canvas = document.createElement("canvas"), + width = getWidth(), + height = getHeight(), + offset = getTarget().firstChild ? (-height) + "px" : "0", + context; - context = canvas.getContext("2d"); - context.lineCap = "round"; - context.lineJoin = "round"; + canvas.width = width; + canvas.height = height; + canvas.style.position = "relative"; + canvas.style.display = "block"; + canvas.style.setProperty("margin-top",offset); + canvas.style.setProperty("z-index", zIndex); + if (isHidden) { + canvas.style.display = "none"; + } - applyWorld(getScreen(), context); + getTarget().appendChild(canvas); - return context; - } + context = canvas.getContext("2d"); + context.lineCap = "round"; + context.lineJoin = "round"; - function cancelAnimationFrame() { - if (_frameRequest) { - (window.cancelAnimationFrame || window.mozCancelAnimationFrame)(_frameRequest); - _frameRequest = undefined; - } - if (_frameRequestTimeout) { - window.clearTimeout(_frameRequestTimeout); - _frameRequestTimeout = undefined; + applyWorld(getScreen(), context); + + return context; } - } - function applyWorld(world, context) { - var llx = world.llx, - lly = world.lly, - urx = world.urx, - ury = world.ury, - xScale = world.xScale, - yScale = world.yScale; - - if (!context) return; - - clearLayer(context); - - context.restore(); - context.save(); - context.scale(1 / xScale, 1 / yScale); - if (lly === 0) { - context.translate(-llx, lly - (ury - lly)); - } else if (lly > 0) { - context.translate(-llx, -lly * 2); - } else { - context.translate(-llx, -ury); + function cancelAnimationFrame() { + if (_frameRequest) { + (window.cancelAnimationFrame || window.mozCancelAnimationFrame)(_frameRequest); + _frameRequest = undefined; + } + if (_frameRequestTimeout) { + window.clearTimeout(_frameRequestTimeout); + _frameRequestTimeout = undefined; + } } - } - function pushUndo(turtle) { - var properties, undoState, i; + function applyWorld(world, context) { + var llx = world.llx, + lly = world.lly, + urx = world.urx, + ury = world.ury, + xScale = world.xScale, + yScale = world.yScale; - if (!_config.allowUndo || !turtle._bufferSize) { - return; - } + if (!context) {return;} - if (!turtle._undoBuffer) { - turtle._undoBuffer = []; - } + clearLayer(context); - while(turtle._undoBuffer.length > turtle._bufferSize) { - turtle._undoBuffer.shift(); + context.restore(); + context.save(); + context.scale(1 / xScale, 1 / yScale); + if (lly === 0) { + context.translate(-llx, lly - (ury - lly)); + } else if (lly > 0) { + context.translate(-llx, -lly * 2); + } else { + context.translate(-llx, -ury); + } } - undoState = {}; - properties = "x y angle radians color fill down filling shown shape size".split(" "); - for(i = 0; i < properties.length; i++) { - undoState[properties[i]] = turtle["_" + properties[i]]; - } + function pushUndo(turtle) { + var properties, undoState, i; - turtle._undoBuffer.push(undoState); + if (!_config.allowUndo || !turtle._bufferSize) { + return; + } - return turtle.addUpdate(function() { - undoState.fillBuffer = this.fillBuffer ? this.fillBuffer.slice() : undefined; - if (turtle._paper && turtle._paper.canvas) { - undoState.image = turtle._paper.canvas.toDataURL(); + if (!turtle._undoBuffer) { + turtle._undoBuffer = []; } - }, false); - } - var undoImage = new Image(); - function popUndo(turtle) { - var undoState; + while(turtle._undoBuffer.length > turtle._bufferSize) { + turtle._undoBuffer.shift(); + } - if (!turtle._bufferSize || !turtle._undoBuffer) { - return; - } + undoState = {}; + properties = "x y angle radians color fill down filling shown shape size".split(" "); + for(i = 0; i < properties.length; i++) { + undoState[properties[i]] = turtle["_" + properties[i]]; + } - undoState = turtle._undoBuffer.pop(); + turtle._undoBuffer.push(undoState); - if (!undoState) { - return; + return turtle.addUpdate(function() { + undoState.fillBuffer = this.fillBuffer ? this.fillBuffer.slice() : undefined; + if (turtle._paper && turtle._paper.canvas) { + undoState.image = turtle._paper.canvas.toDataURL(); + } + }, false); } - for(var key in undoState) { - if (key === "image" || key === "fillBuffer") continue; - turtle["_" + key] = undoState[key]; - } + var undoImage = new Image(); + function popUndo(turtle) { + var undoState; - return turtle.addUpdate(function() { - var img; - if (undoState.image) { - undoImage.src = undoState.image; - img = undoImage; + if (!turtle._bufferSize || !turtle._undoBuffer) { + return; } - clearLayer(this.context(), false, undoImage); - delete undoState.image; - }, true, undoState); - } + undoState = turtle._undoBuffer.pop(); - function removeLayer(layer) { - if (layer && layer.canvas && layer.canvas.parentNode) { - layer.canvas.parentNode.removeChild(layer.canvas); - } - } + if (!undoState) { + return; + } + + for(var key in undoState) { + if (key === "image" || key === "fillBuffer") {continue;} + turtle["_" + key] = undoState[key]; + } - function clearLayer(context, color, image) { - if (!context) return; + return turtle.addUpdate(function() { + var img; + if (undoState.image) { + undoImage.src = undoState.image; + img = undoImage; + } - context.save(); - context.setTransform(1,0,0,1,0,0); - if (color) { - context.fillStyle = color; - context.fillRect(0, 0, context.canvas.width, context.canvas.height); + clearLayer(this.context(), false, undoImage); + delete undoState.image; + }, true, undoState); } - else { - context.clearRect(0, 0, context.canvas.width, context.canvas.height); + + function removeLayer(layer) { + if (layer && layer.canvas && layer.canvas.parentNode) { + layer.canvas.parentNode.removeChild(layer.canvas); + } } - if (image) { - context.drawImage(image, 0, 0); + function clearLayer(context, color, image) { + if (!context) {return;} + + context.save(); + context.setTransform(1,0,0,1,0,0); + if (color) { + context.fillStyle = color; + context.fillRect(0, 0, context.canvas.width, context.canvas.height); + } else { + context.clearRect(0, 0, context.canvas.width, context.canvas.height); + } + + if (image) { + context.drawImage(image, 0, 0); + } + + context.restore(); } - context.restore(); - } + function drawTurtle(state, context) { + var shape = SHAPES[state.shape], + world = getScreen(), + width = getWidth(), + height = getHeight(), + xScale = world.xScale, + yScale = world.yScale, + x, y, bearing; + + if (!context) {return;} + + x = Math.cos(state.radians) / xScale; + y = Math.sin(state.radians) / yScale; + bearing = Math.atan2(y, x) - Math.PI/2; + + context.save(); + context.translate(state.x, state.y); + context.scale(xScale,yScale); + + if (shape.nodeName) { + context.rotate(bearing + Math.PI); + var iw = shape.naturalWidth; + var ih = shape.naturalHeight; + context.drawImage(shape, 0, 0, iw, ih, -iw/2, -ih/2, iw, ih); + } else { + context.rotate(bearing); + context.beginPath(); + context.lineWidth = 1; + context.strokeStyle = state.color; + context.fillStyle = state.fill; + context.moveTo(shape[0][0], shape[0][1]); + for(var i = 1; i < shape.length; i++) { + context.lineTo(shape[i][0], shape[i][1]); + } + context.closePath(); + context.fill(); + context.stroke(); + } - function drawTurtle(state, context) { - var shape = SHAPES[state.shape], - world = getScreen(), - width = getWidth(), - height = getHeight(), - xScale = world.xScale, - yScale = world.yScale, - x, y, bearing; - - if (!context) return; - - x = Math.cos(state.radians) / xScale; - y = Math.sin(state.radians) / yScale; - bearing = Math.atan2(y, x) - Math.PI/2; - - context.save(); - context.translate(state.x, state.y); - context.scale(xScale,yScale); - - if (shape.nodeName) { - context.rotate(bearing + Math.PI); - var iw = shape.naturalWidth; - var ih = shape.naturalHeight; - context.drawImage(shape, 0, 0, iw, ih, -iw/2, -ih/2, iw, ih); + context.restore(); } - else { - context.rotate(bearing); + + function drawDot(size, color) { + var context = this.context(), + screen = getScreen(), + xScale = screen.xScale, + yScale = screen.yScale; + + if (!context) {return;} context.beginPath(); - context.lineWidth = 1; - context.strokeStyle = state.color; - context.fillStyle = state.fill; - context.moveTo(shape[0][0], shape[0][1]); - for(var i = 1; i < shape.length; i++) { - context.lineTo(shape[i][0], shape[i][1]); - } + context.moveTo(this.x, this.y); + size = size * Math.min(Math.abs(xScale),Math.abs(yScale)); + context.arc(this.x, this.y, size/2, 0, Turtle.RADIANS); context.closePath(); + context.fillStyle = color || this.color; context.fill(); - context.stroke(); } - context.restore(); - } - - function drawDot(size, color) { - var context = this.context(), - screen = getScreen(), - xScale = screen.xScale, - yScale = screen.yScale; - - if (!context) return; - context.beginPath(); - context.moveTo(this.x, this.y); - size = size * Math.min(Math.abs(xScale),Math.abs(yScale)); - context.arc(this.x, this.y, size/2, 0, Turtle.RADIANS); - context.closePath(); - context.fillStyle = color || this.color; - context.fill(); - } - - var textMeasuringContext = document.createElement("canvas").getContext("2d"); - function measureText(message, font) { - if (font) { - textMeasuringContext.font = font; + var textMeasuringContext = document.createElement("canvas").getContext("2d"); + function measureText(message, font) { + if (font) { + textMeasuringContext.font = font; + } + return textMeasuringContext.measureText(message).width; } - return textMeasuringContext.measureText(message).width; - } - function drawText(message, align, font) { - var context = this.context(); + function drawText(message, align, font) { + var context = this.context(); - if (!context) return; + if (!context) {return;} - context.save(); - if (font) { - context.font = font; - } - if (align && align.match(/^(left|right|center)$/)) { - context.textAlign = align; - } + context.save(); + if (font) { + context.font = font; + } + if (align && align.match(/^(left|right|center)$/)) { + context.textAlign = align; + } - context.scale(1,-1); - context.fillStyle = this.fill; - context.fillText(message, this.x, -this.y); - context.restore(); - } + context.scale(1,-1); + context.fillStyle = this.fill; + context.fillText(message, this.x, -this.y); + context.restore(); + } - function drawLine(loc, beginPath, endPath) { + function drawLine(loc, beginPath, endPath) { // TODO: make steps in path use square ends of lines // and open and close path at the right times. // See if we can minimize calls to stroke - var context = this.context(); + var context = this.context(); - if (!context) return; + if (!context) {return;} - if (beginPath) { - context.beginPath(); - context.moveTo(this.x, this.y); - } + if (beginPath) { + context.beginPath(); + context.moveTo(this.x, this.y); + } - context.lineWidth = this.size * getScreen().lineScale; - context.strokeStyle = this.color; - context.lineTo(loc.x, loc.y); - context.stroke(); - } + context.lineWidth = this.size * getScreen().lineScale; + context.strokeStyle = this.color; + context.lineTo(loc.x, loc.y); + context.stroke(); + } - function drawFill() { - var context = this.context(), - path = this.fillBuffer, - i; + function drawFill() { + var context = this.context(), + path = this.fillBuffer, + i; - if (!context || !path || !path.length) return; + if (!context || !path || !path.length) {return;} - context.save(); - context.beginPath(); - context.moveTo(path[0].x,path[0].y); - for(i = 1; i < path.length; i++) { - context.lineTo(path[i].x, path[i].y); - } - context.closePath(); - context.fillStyle = this.fill; - context.fill(); - for(i = 1; i < path.length; i++) { - if (!path[i].stroke) { - continue; + context.save(); + context.beginPath(); + context.moveTo(path[0].x,path[0].y); + for(i = 1; i < path.length; i++) { + context.lineTo(path[i].x, path[i].y); } + context.closePath(); + context.fillStyle = this.fill; + context.fill(); + for(i = 1; i < path.length; i++) { + if (!path[i].stroke) { + continue; + } - context.beginPath(); - context.moveTo(path[i-1].x, path[i-1].y); - context.lineWidth = path[i].size * getScreen().lineScale; - context.strokeStyle = path[i].color; - context.lineTo(path[i].x, path[i].y); - context.stroke(); + context.beginPath(); + context.moveTo(path[i-1].x, path[i-1].y); + context.lineWidth = path[i].size * getScreen().lineScale; + context.strokeStyle = path[i].color; + context.lineTo(path[i].x, path[i].y); + context.stroke(); + } + context.restore(); } - context.restore(); - } - function partialTranslate(turtle, x, y, beginPath, countAsFrame) { - return function() { - return turtle.addUpdate( - function(loc) { - if (this.down) { - drawLine.call(this, loc, beginPath); - } - }, - countAsFrame, - {x : x, y : y}, - beginPath - ); - }; - } + function partialTranslate(turtle, x, y, beginPath, countAsFrame) { + return function() { + return turtle.addUpdate( + function(loc) { + if (this.down) { + drawLine.call(this, loc, beginPath); + } + }, + countAsFrame, + {x : x, y : y}, + beginPath + ); + }; + } - function translate(turtle, startX, startY, dx, dy, beginPath, isCircle) { + function translate(turtle, startX, startY, dx, dy, beginPath, isCircle) { // speed is in pixels per ms - var speed = turtle._computed_speed, - screen = getScreen(), - xScale = Math.abs(screen.xScale), - yScale = Math.abs(screen.yScale), - x = startX, - y = startY, - pixels = Math.sqrt(dx * dx * xScale + dy * dy * yScale), - // TODO: allow fractional frame updates? - frames = speed ? Math.round(Math.max(1, pixels / speed)) : 1, - xStep = dx / frames, - yStep = dy / frames, - promise = getFrameManager().willRenderNext() ? - Promise.resolve() : - new InstantPromise(), - countAsFrame = (!speed && isCircle) ? false : true, - i; - - turtle.addUpdate(function() { - if (this.filling) { - this.fillBuffer.push({ - x : this.x, - y : this.y, - stroke : this.down, - color : this.color, - size : this.size - }); + var speed = turtle._computed_speed, + screen = getScreen(), + xScale = Math.abs(screen.xScale), + yScale = Math.abs(screen.yScale), + x = startX, + y = startY, + pixels = Math.sqrt(dx * dx * xScale + dy * dy * yScale), + // TODO: allow fractional frame updates? + frames = speed ? Math.round(Math.max(1, pixels / speed)) : 1, + xStep = dx / frames, + yStep = dy / frames, + promise = getFrameManager().willRenderNext() ? + Promise.resolve() : + new InstantPromise(), + countAsFrame = (!speed && isCircle) ? false : true, + i; + + turtle.addUpdate(function() { + if (this.filling) { + this.fillBuffer.push({ + x : this.x, + y : this.y, + stroke : this.down, + color : this.color, + size : this.size + }); + } + }, false); + + for(i = 0; i < frames; i++) { + x = startX + xStep * (i+1); + y = startY + yStep * (i+1); + promise = promise.then( + partialTranslate(turtle, x, y, beginPath, countAsFrame) + ); + beginPath = false; } - }, false); - - for(i = 0; i < frames; i++) { - x = startX + xStep * (i+1); - y = startY + yStep * (i+1); - promise = promise.then( - partialTranslate(turtle, x, y, beginPath, countAsFrame) - ); - beginPath = false; + + return promise.then(function() { + return [startX + dx, startY + dy]; + }); } - return promise.then(function() { - return [startX + dx, startY + dy]; - }); - } + function partialRotate(turtle, angle, radians, countAsFrame) { + return function() { + return turtle.addUpdate(undefined, countAsFrame, {angle:angle, radians:radians}); + }; + } - function partialRotate(turtle, angle, radians, countAsFrame) { - return function() { - return turtle.addUpdate(undefined, countAsFrame, {angle:angle, radians:radians}); - }; - } + function rotate(turtle, startAngle, delta, isCircle) { + var speed = turtle._computed_speed, + degrees = delta / turtle._fullCircle * 360, + frames = speed ? Math.round(Math.max(1, Math.abs(degrees) / speed)) : 1, + dAngle = delta / frames, + heading = {}, + countAsFrame = (!speed && isCircle) ? false : true, + promise = getFrameManager().willRenderNext() ? + Promise.resolve() : + new InstantPromise(), + i; - function rotate(turtle, startAngle, delta, isCircle) { - var speed = turtle._computed_speed, - degrees = delta / turtle._fullCircle * 360, - frames = speed ? Math.round(Math.max(1, Math.abs(degrees) / speed)) : 1, - dAngle = delta / frames, - heading = {}, - countAsFrame = (!speed && isCircle) ? false : true, - promise = getFrameManager().willRenderNext() ? - Promise.resolve() : - new InstantPromise(), - i; - - // TODO: request how many frames are remaining and only queue up - // a single rotation per screen update - - for(i = 0; i < frames; i++) { - calculateHeading(turtle, startAngle + dAngle * (i+1), heading); - promise = promise.then( - partialRotate(turtle, heading.angle, heading.radians, countAsFrame) - ); - } + // TODO: request how many frames are remaining and only queue up + // a single rotation per screen update - return promise.then(function() { - return calculateHeading(turtle, startAngle + delta); - }); - } + for(i = 0; i < frames; i++) { + calculateHeading(turtle, startAngle + dAngle * (i+1), heading); + promise = promise.then( + partialRotate(turtle, heading.angle, heading.radians, countAsFrame) + ); + } - function getCoordinates(x, y) { - if (y === undefined) { - y = (x && (x.y || x._y || x[1])) || 0; - x = (x && (x.x || x._x || x[0])) || 0; + return promise.then(function() { + return calculateHeading(turtle, startAngle + delta); + }); } - return {x:x, y:y}; - } - // Modified solution of Tim Down's version from stackoverflow - // http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb - function hexToRGB(hex) { - var rgbForm, hexForm, result; - - if (rgbForm = /^rgba?\((\d+),(\d+),(\d+)(?:,([.\d]+))?\)$/.exec(hex)) { - result = [ - parseInt(rgbForm[1]), - parseInt(rgbForm[2]), - parseInt(rgbForm[3]) - ]; - if (rgbForm[4]) { - result.push(parseFloat(rgbForm[4])); + function getCoordinates(x, y) { + if (y === undefined) { + y = (x && (x.y || x._y || x[1])) || 0; + x = (x && (x.x || x._x || x[0])) || 0; } + return {x:x, y:y}; } - else if (/^#?[a-f\d]{3}|[a-f\d]{6}$/i.exec(hex)) { - if (hex.length === 4) { + + // Modified solution of Tim Down's version from stackoverflow + // http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb + function hexToRGB(hex) { + var rgbForm, hexForm, result; + + if (rgbForm = /^rgba?\((\d+),(\d+),(\d+)(?:,([.\d]+))?\)$/.exec(hex)) { + result = [ + parseInt(rgbForm[1]), + parseInt(rgbForm[2]), + parseInt(rgbForm[3]) + ]; + if (rgbForm[4]) { + result.push(parseFloat(rgbForm[4])); + } + } else if (/^#?[a-f\d]{3}|[a-f\d]{6}$/i.exec(hex)) { + if (hex.length === 4) { // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") - hex = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, function(m, r, g, b) { + hex = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, function(m, r, g, b) { return r + r + g + g + b + b; - }); + }); + } + + hexForm = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + result = [ + parseInt(hexForm[1], 16), + parseInt(hexForm[2], 16), + parseInt(hexForm[3], 16) + ]; + } else { + result = hex; } - hexForm = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - result = [ - parseInt(hexForm[1], 16), - parseInt(hexForm[2], 16), - parseInt(hexForm[3], 16) - ]; + return result; } - else { - result = hex; - } - - return result; - } - function createColor(turtleColorMode,color, g, b, a) { - var i; + function createColor(turtleColorMode,color, g, b, a) { + var i; - if (g !== undefined) { - color = [color, g, b, a]; - } + if (g !== undefined) { + color = [color, g, b, a]; + } - if (color.constructor === Array && color.length) { - if(turtleColorMode === 255){//mode is 255 - for(i = 0; i < 3; i++) { - if(typeof color[i] === "number") { - color[i] = Math.max(0, Math.min(255, parseInt(color[i]))); - } else { - throw new Sk.builtin.ValueError("bad color sequence"); + if (color.constructor === Array && color.length) { + if(turtleColorMode === 255){//mode is 255 + for(i = 0; i < 3; i++) { + if(typeof color[i] === "number") { + color[i] = Math.max(0, Math.min(255, parseInt(color[i]))); + } else { + throw new Sk.builtin.ValueError("bad color sequence"); + } } - } - } else {//In python,if the colormode not equals 255,it should be 1.0 - for(i = 0; i < 3; i++) { - if(typeof color[i] === "number") { - if(color[i] <= 1){ - color[i] = Math.max(0, Math.min(255, parseInt(255 * color[i]))); - } else { + } else {//In python,if the colormode not equals 255,it should be 1.0 + for(i = 0; i < 3; i++) { + if(typeof color[i] === "number") { + if(color[i] <= 1){ + color[i] = Math.max(0, Math.min(255, parseInt(255 * color[i]))); + } else { //Raise TurtleGraphicsError,Here use ValueError instead + throw new Sk.builtin.ValueError("bad color sequence"); + } + } else { throw new Sk.builtin.ValueError("bad color sequence"); } - } else { - throw new Sk.builtin.ValueError("bad color sequence"); } } + if (typeof color[i] === "number") { + color[3] = Math.max(0, Math.min(1, color[i])); + color = "rgba(" + color.join(",") + ")"; + } else { + color = "rgb(" + color.slice(0,3).join(",") + ")"; + } + } else if (typeof color === "string" && !color.match(/\s*url\s*\(/i)) { + color = color.replace(/\s+/g, ""); + } else { + return "black"; } - if (typeof color[i] === "number") { - color[3] = Math.max(0, Math.min(1, color[i])); - color = "rgba(" + color.join(",") + ")"; - } - else { - color = "rgb(" + color.slice(0,3).join(",") + ")"; - } - } - else if (typeof color === "string" && !color.match(/\s*url\s*\(/i)) { - color = color.replace(/\s+/g, ""); - } - else { - return "black"; - } - return color; - } + return color; + } - function calculateHeading(turtle, value, heading) { - var angle = turtle._angle || 0, - radians = turtle._radians || 0; + function calculateHeading(turtle, value, heading) { + var angle = turtle._angle || 0, + radians = turtle._radians || 0; - heading || (heading = {}); + heading || (heading = {}); - if (typeof value === "number") { - if (turtle._isRadians) { - angle = radians = value % Turtle.RADIANS; - } - else if (turtle._fullCircle) { - angle = (value % turtle._fullCircle); - radians = angle / turtle._fullCircle * Turtle.RADIANS; - } - else { - angle = radians = 0; - } + if (typeof value === "number") { + if (turtle._isRadians) { + angle = radians = value % Turtle.RADIANS; + } else if (turtle._fullCircle) { + angle = (value % turtle._fullCircle); + radians = angle / turtle._fullCircle * Turtle.RADIANS; + } else { + angle = radians = 0; + } - if (angle < 0) { - angle += turtle._fullCircle; - radians += Turtle.RADIANS; + if (angle < 0) { + angle += turtle._fullCircle; + radians += Turtle.RADIANS; + } } - } - - heading.angle = angle; - heading.radians = radians; - return heading; - } + heading.angle = angle; + heading.radians = radians; - function pythonToJavascriptFunction(pyValue, scope) { - return function() { - var argsJs = Array.prototype.slice.call(arguments), - argsPy = argsJs.map( - function(argJs) {return Sk.ffi.remapToPy(argJs);} - ); + return heading; + } - if (typeof(scope) !== "undefined") { - argsPy.unshift(scope); - } + function pythonToJavascriptFunction(pyValue, scope) { + return function() { + var argsJs = Array.prototype.slice.call(arguments), + argsPy = argsJs.map( + function(argJs) {return Sk.ffi.remapToPy(argJs);} + ); - return Sk.misceval.applyAsync( - undefined, pyValue, undefined, undefined, undefined, argsPy - ).catch(Sk.uncaughtException); - }; - } + if (typeof(scope) !== "undefined") { + argsPy.unshift(scope); + } - function addModuleMethod(klass, module, method, scopeGenerator) { - var publicMethodName = method.replace(/^\$/, ""), - displayName = publicMethodName.replace(/_\$[a-z]+\$$/i, ""), - maxArgs = klass.prototype[method].length, - minArgs = klass.prototype[method].minArgs, - co_varnames = klass.prototype[method].co_varnames || [], - returnType = klass.prototype[method].returnType, - isSk = klass.prototype[method].isSk, - wrapperFn; - - if (minArgs === undefined) { - minArgs = maxArgs; + return Sk.misceval.applyAsync( + undefined, pyValue, undefined, undefined, undefined, argsPy + ).catch(Sk.uncaughtException); + }; } - wrapperFn = function() { - var args = Array.prototype.slice.call(arguments, 0), - instance = scopeGenerator ? scopeGenerator() : args.shift().instance, - i, result, susp, resolution, lengthError; + function addModuleMethod(klass, module, method, scopeGenerator) { + var publicMethodName = method.replace(/^\$/, ""), + displayName = publicMethodName.replace(/_\$[a-z]+\$$/i, ""), + maxArgs = klass.prototype[method].length, + minArgs = klass.prototype[method].minArgs, + co_varnames = klass.prototype[method].co_varnames || [], + returnType = klass.prototype[method].returnType, + isSk = klass.prototype[method].isSk, + wrapperFn; - if (args.length < minArgs || args.length > maxArgs) { - lengthError = minArgs === maxArgs ? - "exactly " + maxArgs : - "between " + minArgs + " and " + maxArgs; - - throw new Sk.builtin.TypeError(displayName + "() takes " + lengthError + " positional argument(s) (" + args.length + " given)"); + if (minArgs === undefined) { + minArgs = maxArgs; } - for (i = args.length; --i >= 0;) { - if (args[i] !== undefined) { - if (args[i] instanceof Sk.builtin.func) { - args[i] = pythonToJavascriptFunction(args[i]); - } - else if (args[i] instanceof Sk.builtin.method) { - args[i] = pythonToJavascriptFunction(args[i].im_func, args[i].im_self); - } - else if (args[i] && args[i].$d instanceof Sk.builtin.dict && args[i].instance) { - args[i] = args[i].instance; - } - else { - args[i] = Sk.ffi.remapToJs(args[i]); - } - } - } + wrapperFn = function() { + var args = Array.prototype.slice.call(arguments, 0), + instance = scopeGenerator ? scopeGenerator() : args.shift().instance, + i, result, susp, resolution, lengthError; - // filter out undefines in a previous implementation of function calls - // non required args were not specified, where as now they are filled with - // default values of None which are translated to null's - var tmp_args = args.slice(); - args = []; - for (i = tmp_args.length; i >= 0; --i) { - if (tmp_args[i] === null) { - continue; + if (args.length < minArgs || args.length > maxArgs) { + lengthError = minArgs === maxArgs ? + "exactly " + maxArgs : + "between " + minArgs + " and " + maxArgs; + + throw new Sk.builtin.TypeError(displayName + "() takes " + lengthError + " positional argument(s) (" + args.length + " given)"); } - args[i] = tmp_args[i]; - } - try { - result = instance[method].apply(instance, args); - } catch(e) { - if (window && window.console) { - window.console.log("wrapped method failed"); - window.console.log(e.stack); + for (i = args.length; --i >= 0;) { + if (args[i] !== undefined) { + if (args[i] instanceof Sk.builtin.func) { + args[i] = pythonToJavascriptFunction(args[i]); + } else if (args[i] instanceof Sk.builtin.method) { + args[i] = pythonToJavascriptFunction(args[i].im_func, args[i].im_self); + } else if (args[i] && args[i].$d instanceof Sk.builtin.dict && args[i].instance) { + args[i] = args[i].instance; + } else { + args[i] = Sk.ffi.remapToJs(args[i]); + } + } } - throw e; - } - if (result instanceof InstantPromise) { - result = result.lastResult; - } + // filter out undefines in a previous implementation of function calls + // non required args were not specified, where as now they are filled with + // default values of None which are translated to null's + var tmp_args = args.slice(); + args = []; + for (i = tmp_args.length; i >= 0; --i) { + if (tmp_args[i] === null) { + continue; + } + args[i] = tmp_args[i]; + } - if (result instanceof Promise) { - result = result.catch(function(e) { + try { + result = instance[method].apply(instance, args); + } catch(e) { if (window && window.console) { - window.console.log("promise failed"); + window.console.log("wrapped method failed"); window.console.log(e.stack); } throw e; - }); + } - susp = new Sk.misceval.Suspension(); + if (result instanceof InstantPromise) { + result = result.lastResult; + } - susp.resume = function() { - return (resolution === undefined) ? - Sk.builtin.none.none$ : - Sk.ffi.remapToPy(resolution); - }; + if (result instanceof Promise) { + result = result.catch(function(e) { + if (window && window.console) { + window.console.log("promise failed"); + window.console.log(e.stack); + } + throw e; + }); - susp.data = { - type: "Sk.promise", - promise: result.then(function(value) { - resolution = value; - return value; - }) - }; + susp = new Sk.misceval.Suspension(); - return susp; - } - else { - if (result === undefined) return Sk.builtin.none.none$; - if (isSk) return result; - if (typeof returnType === "function") { - return returnType(result); + susp.resume = function() { + return (resolution === undefined) ? + Sk.builtin.none.none$ : + Sk.ffi.remapToPy(resolution); + }; + + susp.data = { + type: "Sk.promise", + promise: result.then(function(value) { + resolution = value; + return value; + }) + }; + + return susp; + } else { + if (result === undefined) {return Sk.builtin.none.none$;} + if (isSk) {return result;} + if (typeof returnType === "function") { + return returnType(result); + } + + return Sk.ffi.remapToPy(result); } + }; - return Sk.ffi.remapToPy(result); + wrapperFn.co_varnames = co_varnames.slice(); + wrapperFn.$defaults = []; + + for (var i = minArgs; i < co_varnames.length; i++) { + wrapperFn.$defaults.push(Sk.builtin.none.none$); } - }; - wrapperFn.co_varnames = co_varnames.slice(); - wrapperFn.$defaults = []; + if (!scopeGenerator) { + // make room for the "self" argument + wrapperFn.co_varnames.unshift("self"); + } - for (var i = minArgs; i < co_varnames.length; i++) { - wrapperFn.$defaults.push(Sk.builtin.none.none$); + module[publicMethodName] = new Sk.builtin.func(wrapperFn); } - if (!scopeGenerator) { - // make room for the "self" argument - wrapperFn.co_varnames.unshift("self"); + function TurtleWrapper($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self) { + self.instance = new Turtle(); + self.instance.skInstance = self; + }); + + for(var key in Turtle.prototype) { + if (/^\$[a-z_]+/.test(key)) { + addModuleMethod(Turtle, $loc, key); + } + } } - module[publicMethodName] = new Sk.builtin.func(wrapperFn); - } + function ScreenWrapper($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self) { + self.instance = getScreen(); + }); - function TurtleWrapper($gbl, $loc) { - $loc.__init__ = new Sk.builtin.func(function (self) { - self.instance = new Turtle(); - self.instance.skInstance = self; - }); + for(var key in Screen.prototype) { + if (/^\$[a-z_]+/.test(key)) { + addModuleMethod(Screen, $loc, key); + } + } + } for(var key in Turtle.prototype) { if (/^\$[a-z_]+/.test(key)) { - addModuleMethod(Turtle, $loc, key); + addModuleMethod(Turtle, _module, key, ensureAnonymous); } } - } - - function ScreenWrapper($gbl, $loc) { - $loc.__init__ = new Sk.builtin.func(function (self) { - self.instance = getScreen(); - }); - for(var key in Screen.prototype) { - if (/^\$[a-z_]+/.test(key)) { - addModuleMethod(Screen, $loc, key); + // add Screen method aliases to the main turtle module + // to allow things like: + // import turtle + // turtle.mainloop() + addModuleMethod(Screen, _module, "$mainloop", getScreen); + addModuleMethod(Screen, _module, "$done", getScreen); + addModuleMethod(Screen, _module, "$bye", getScreen); + addModuleMethod(Screen, _module, "$tracer", getScreen); + addModuleMethod(Screen, _module, "$update", getScreen); + addModuleMethod(Screen, _module, "$delay", getScreen); + addModuleMethod(Screen, _module, "$window_width", getScreen); + addModuleMethod(Screen, _module, "$window_height", getScreen); + + _module.Turtle = Sk.misceval.buildClass(_module, TurtleWrapper, "Turtle", []); + _module.Screen = Sk.misceval.buildClass(_module, ScreenWrapper, "Screen", []); + + // Calling focus(false) will block turtle key/mouse events + // until focus(true) is called again or until the turtle DOM target + // is clicked/tabbed into. + function focusTurtle(value) { + if (value !== undefined) { + _focus = !!value; + if (_focus) { + getTarget().focus(); + } else { + getTarget().blur(); + } } - } - } - for(var key in Turtle.prototype) { - if (/^\$[a-z_]+/.test(key)) { - addModuleMethod(Turtle, _module, key, ensureAnonymous); + return _focus; } - } - // add Screen method aliases to the main turtle module - // to allow things like: - // import turtle - // turtle.mainloop() - addModuleMethod(Screen, _module, "$mainloop", getScreen); - addModuleMethod(Screen, _module, "$done", getScreen); - addModuleMethod(Screen, _module, "$bye", getScreen); - addModuleMethod(Screen, _module, "$tracer", getScreen); - addModuleMethod(Screen, _module, "$update", getScreen); - addModuleMethod(Screen, _module, "$delay", getScreen); - addModuleMethod(Screen, _module, "$window_width", getScreen); - addModuleMethod(Screen, _module, "$window_height", getScreen); - - _module.Turtle = Sk.misceval.buildClass(_module, TurtleWrapper, "Turtle", []); - _module.Screen = Sk.misceval.buildClass(_module, ScreenWrapper, "Screen", []); - - // Calling focus(false) will block turtle key/mouse events - // until focus(true) is called again or until the turtle DOM target - // is clicked/tabbed into. - function focusTurtle(value) { - if (value !== undefined) { - _focus = !!value; - if (_focus) { - getTarget().focus(); + function resetTurtle() { + cancelAnimationFrame(); + getScreen().reset(); + getFrameManager().reset(); + + while (_target.firstChild) { + _target.removeChild(_target.firstChild); } - else { - getTarget().blur(); + + if (_mouseHandler) { + _mouseHandler.reset(); } - } - return _focus; - } + _durationSinceRedraw = 0; + _screenInstance = undefined; + _anonymousTurtle = undefined; + _mouseHandler = undefined; + TURTLE_COUNT = 0; + } - function resetTurtle() { - cancelAnimationFrame(); - getScreen().reset(); - getFrameManager().reset(); + function stopTurtle() { + cancelAnimationFrame(); - while (_target.firstChild) { - _target.removeChild(_target.firstChild); - } + if (_mouseHandler) { + _mouseHandler.reset(); + } - if (_mouseHandler) { - _mouseHandler.reset(); + _durationSinceRedraw = 0; + _screenInstance = undefined; + _anonymousTurtle = undefined; + _mouseHandler = undefined; + TURTLE_COUNT = 0; } - _durationSinceRedraw = 0; - _screenInstance = undefined; - _anonymousTurtle = undefined; - _mouseHandler = undefined; - TURTLE_COUNT = 0; + return { + skModule : _module, + reset : resetTurtle, + stop : stopTurtle, + focus : focusTurtle, + Turtle : Turtle, + Screen : Screen + }; } - function stopTurtle() { - cancelAnimationFrame(); - - if (_mouseHandler) { - _mouseHandler.reset(); - } + // See if the TurtleGraphics module has already been loaded + // for the currently configured DOM target element. + var currentTarget = getConfiguredTarget(); - _durationSinceRedraw = 0; - _screenInstance = undefined; - _anonymousTurtle = undefined; - _mouseHandler = undefined; - TURTLE_COUNT = 0; + if (!currentTarget.turtleInstance) { + currentTarget.turtleInstance = generateTurtleModule(currentTarget); + } else { + currentTarget.turtleInstance.reset(); } - return { - skModule : _module, - reset : resetTurtle, - stop : stopTurtle, - focus : focusTurtle, - Turtle : Turtle, - Screen : Screen + Sk.TurtleGraphics.module = currentTarget.turtleInstance.skModule; + Sk.TurtleGraphics.reset = currentTarget.turtleInstance.reset; + Sk.TurtleGraphics.stop = currentTarget.turtleInstance.stop; + Sk.TurtleGraphics.focus = currentTarget.turtleInstance.focus; + Sk.TurtleGraphics.raw = { + Turtle : currentTarget.turtleInstance.Turtle, + Screen : currentTarget.turtleInstance.Screen }; -} - -// See if the TurtleGraphics module has already been loaded -// for the currently configured DOM target element. -var currentTarget = getConfiguredTarget(); - -if (!currentTarget.turtleInstance) { - currentTarget.turtleInstance = generateTurtleModule(currentTarget); -} -else { - currentTarget.turtleInstance.reset(); -} - -Sk.TurtleGraphics.module = currentTarget.turtleInstance.skModule; -Sk.TurtleGraphics.reset = currentTarget.turtleInstance.reset; -Sk.TurtleGraphics.stop = currentTarget.turtleInstance.stop; -Sk.TurtleGraphics.focus = currentTarget.turtleInstance.focus; -Sk.TurtleGraphics.raw = { - Turtle : currentTarget.turtleInstance.Turtle, - Screen : currentTarget.turtleInstance.Screen -}; -return currentTarget.turtleInstance.skModule; + return currentTarget.turtleInstance.skModule; }; diff --git a/src/object.js b/src/object.js index 475fe7c4a4..de4b362aba 100644 --- a/src/object.js +++ b/src/object.js @@ -52,6 +52,16 @@ Sk.builtin.object.prototype.GenericGetAttr = function (pyName, canSuspend) { dict = this["$d"] || this.constructor["$d"]; //print("getattr", tp.tp$name, name); + if (jsName === "__class__") { + if (tp !== null) { + return tp; + } + } else if (jsName === "__name__") { + if (this.tp$name !== null) { + return Sk.ffi.remapToPy(this.tp$name); + } + } + // todo; assert? force? if (dict) { if (dict.mp$lookup) { diff --git a/src/parser.js b/src/parser.js index 0a58d1ad65..77edaa0fd9 100644 --- a/src/parser.js +++ b/src/parser.js @@ -57,6 +57,7 @@ Parser.prototype.setup = function (start) { }; this.stack = [stackentry]; this.used_names = {}; + Sk._setupTokenRegexes(); }; function findInDfa (a, obj) { @@ -344,7 +345,8 @@ Sk.parse = function parse (filename, input) { lineno += 1; column = 0; } - if (type === T_COMMENT) { + + if (tokenInfo.type === T_COMMENT) { parser.addcomment(tokenInfo.string, tokenInfo.start, tokenInfo.end, tokenInfo.line); } } else { diff --git a/src/print.js b/src/print.js index f179bc58a3..e8c76d42ef 100644 --- a/src/print.js +++ b/src/print.js @@ -47,7 +47,7 @@ var print_f = function function_print(kwa) { remap_val = kwargs.mp$lookup(new Sk.builtin.str("file")); if(remap_val !== undefined) { is_none = Sk.builtin.checkNone(remap_val); - if(is_none || remap_val.tp$getattr("write") !== undefined) { + if(is_none || remap_val.tp$getattr(new Sk.builtin.str("write")) !== undefined) { kw_list["file"] = is_none ? kw_list["file"] : remap_val; } else { throw new Sk.builtin.AttributeError("'" + Sk.abstr.typeName(remap_val) + "' object has no attribute 'write'"); diff --git a/src/tokenize.js b/src/tokenize.js index defd09af8b..2c159ff592 100644 --- a/src/tokenize.js +++ b/src/tokenize.js @@ -79,6 +79,31 @@ function rstrip (input, what) { return input.substring(0, i); } +var the_underscore = '_'; +var Lu = '[A-Z]'; +var Ll = '[a-z]'; +var Lt = '[\\u{10B99}-\\u{10B9C}\\u{112A9}\\u{115DC}-\\u{115DD}\\u034F\\u115F-\\u1160\\u17B4-\\u17B5\\u2065\\u3164\\uFFA0\\uFFF0-\\uFFF8\\u{E0000}\\u{E0002}-\\u{E001F}\\u{E0080}-\\u{E00FF}\\u{E01F0}-\\u{E0FFF}\\u{112A9}\\u00D7]'; +var Lm = '[\\u02B0-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0374\\u037A\\u0559\\u06E5-\\u06E6\\u07F4-\\u07F5\\u0971\\u1C78-\\u1C7D\\u1D2C-\\u1D6A\\u1DFD-\\u1DFF\\u2E2F\\u30FC\\uA67F\\uA69C-\\uA69D\\uA717-\\uA71F\\uA788\\uA7F8-\\uA7F9\\uAB5C-\\uAB5F\\uFF70\\uFF9E-\\uFF9F\\u{16F93}-\\u{16F9F}\\u02D0-\\u02D1\\u0640\\u07FA\\u0E46\\u0EC6\\u1843\\u1AA7\\u1C7B\\u3005\\u3031-\\u3035\\u309D-\\u309E\\u30FC-\\u30FE\\uA015\\uA60C\\uA9CF\\uA9E6\\uAA70\\uAADD\\uAAF3-\\uAAF4\\uFF70\\u{16B42}-\\u{16B43}\\u{16FE0}-\\u{16FE1}\\u02B0-\\u02B8\\u02C0-\\u02C1\\u02E0-\\u02E4\\u037A\\u1D2C-\\u1D6A\\u1D78\\u1D9B-\\u1DBF\\u2071\\u207F\\u2090-\\u209C\\u2C7C-\\u2C7D\\uA69C-\\uA69D\\uA770\\uA7F8-\\uA7F9\\uAB5C-\\uAB5F\\uFF9E-\\uFF9F\\u02B2\\u1D62\\u1DA4\\u1DA8\\u2071\\u2C7C\\u2E18-\\u2E19\\u2E2F]'; +var Lo = '[\\u2135-\\u2138\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u3006\\u3400-\\u4DB5\\u4E00-\\u9FEF\\uF900-\\uFA6D\\uFA70-\\uFAD9\\u{17000}-\\u{187F1}\\u{18800}-\\u{18AF2}\\u{1B170}-\\u{1B2FB}\\u{20000}-\\u{2A6D6}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u{2F800}-\\u{2FA1D}\\uAAC0\\uAAC2\\uFE20-\\uFE2F\\u{10D22}-\\u{10D23}\\u{1135D}\\u00AA\\u00BA\\u3400-\\u4DB5\\u4E00-\\u9FEF\\uFA0E-\\uFA0F\\uFA11\\uFA13-\\uFA14\\uFA1F\\uFA21\\uFA23-\\uFA24\\uFA27-\\uFA29\\u{20000}-\\u{2A6D6}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u115F-\\u1160\\u3164\\uFFA0\\u0673\\u17A3-\\u17A4\\u0E40-\\u0E44\\u0EC0-\\u0EC4\\u19B5-\\u19B7\\u19BA\\uAAB5-\\uAAB6\\uAAB9\\uAABB-\\uAABC]'; +var Nl = '[\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u2170-\\u217F\\u2160-\\u216F]'; +var Mn = '[\\u104A-\\u104B\\u102B-\\u102C\\u102D-\\u1030\\u1031\\u1032-\\u1036\\u1038\\u103B-\\u103C\\u103D-\\u103E\\u1056-\\u1057\\u1058-\\u1059\\u105E-\\u1060\\u1062\\u1067-\\u1068\\u1071-\\u1074\\u1082\\u1083-\\u1084\\u1085-\\u1086\\u109C\\u109D\\u1037\\u1039-\\u103A\\u1087-\\u108C\\u108D\\u108F\\u109A-\\u109B\\uA9E5\\uAA7B\\uAA7C\\uAA7D\\uA9E6\\uAA70\\u104A-\\u104B]'; +var Mc = '[\\u0903\\u093B\\u093E-\\u0940\\u0949-\\u094C\\u094E-\\u094F\\u0982-\\u0983\\u09BE-\\u09C0\\u09C7-\\u09C8\\u09CB-\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB-\\u0ACC\\u0B02-\\u0B03\\u0B3E\\u0B40\\u0B47-\\u0B48\\u0B4B-\\u0B4C\\u0B57\\u0BBE-\\u0BBF\\u0BC1-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82-\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7-\\u0CC8\\u0CCA-\\u0CCB\\u0CD5-\\u0CD6\\u0D02-\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82-\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2-\\u0DF3\\u0F7F\\u102B-\\u102C\\u1031\\u1038\\u103B-\\u103C\\u1056-\\u1057\\u1062\\u1067-\\u1068\\u1083-\\u1084\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7-\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930-\\u1931\\u1933-\\u1938\\u1A19-\\u1A1A\\u1A55\\u1A57\\u1A61\\u1A63-\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B82\\u1BA1\\u1BA6-\\u1BA7\\u1BE7\\u1BEA-\\u1BEC\\u1BEE\\u1C24-\\u1C2B\\u1C34-\\u1C35\\u1CF2-\\u1CF3\\uA823-\\uA824\\uA827\\uA880-\\uA881\\uA8B4-\\uA8C3\\uA952\\uA983\\uA9B4-\\uA9B5\\uA9BA-\\uA9BB\\uA9BD-\\uA9BF\\uAA2F-\\uAA30\\uAA33-\\uAA34\\uAA4D\\uAAEB\\uAAEE-\\uAAEF\\uAAF5\\uABE3-\\uABE4\\uABE6-\\uABE7\\uABE9-\\uABEA\\u{11000}\\u{11002}\\u{11082}\\u{110B0}-\\u{110B2}\\u{110B7}-\\u{110B8}\\u{1112C}\\u{11145}-\\u{11146}\\u{11182}\\u{111B3}-\\u{111B5}\\u{111BF}\\u{1122C}-\\u{1122E}\\u{11232}-\\u{11233}\\u{112E0}-\\u{112E2}\\u{11302}-\\u{11303}\\u{1133E}-\\u{1133F}\\u{11341}-\\u{11344}\\u{11347}-\\u{11348}\\u{1134B}-\\u{1134C}\\u{11357}\\u{11362}-\\u{11363}\\u{11435}-\\u{11437}\\u{11440}-\\u{11441}\\u{11445}\\u{114B0}-\\u{114B2}\\u{114B9}\\u{114BB}-\\u{114BE}\\u{114C1}\\u{115AF}-\\u{115B1}\\u{115B8}-\\u{115BB}\\u{115BE}\\u{11630}-\\u{11632}\\u{1163B}-\\u{1163C}\\u{1163E}\\u{116AC}\\u{116AE}-\\u{116AF}\\u{11720}-\\u{11721}\\u{11726}\\u{1182C}-\\u{1182E}\\u{11838}\\u{11A39}\\u{11A57}-\\u{11A58}\\u{11A97}\\u{11C2F}\\u{11C3E}\\u{11CA9}\\u{11CB1}\\u{11CB4}\\u{11D8A}-\\u{11D8E}\\u{11D93}-\\u{11D94}\\u{11D96}\\u{11EF5}-\\u{11EF6}\\u{16F51}-\\u{16F7E}\\u0F3E-\\u0F3F\\u1087-\\u108C\\u108F\\u109A-\\u109B\\u1B44\\u1BAA\\u1CE1\\u1CF7\\u302E-\\u302F\\uA953\\uA9C0\\uAA7B\\uAA7D\\uABEC\\u{111C0}\\u{11235}\\u{1134D}\\u{116B6}\\u{1D16D}-\\u{1D172}\\u09BE\\u09D7\\u0B3E\\u0B57\\u0BBE\\u0BD7\\u0CC2\\u0CD5-\\u0CD6\\u0D3E\\u0D57\\u0DCF\\u0DDF\\u302E-\\u302F\\u{1133E}\\u{11357}\\u{114B0}\\u{114BD}\\u{115AF}\\u{1D165}\\u{1D16E}-\\u{1D172}]'; +var Nd = '[\\u{1D7CE}-\\u{1D7FF}\\uFF10-\\uFF19]'; +var Pc = '\\u2040'; +var Other_ID_Start = '[\\u1885-\\u1886\\u2118\\u212E\\u309B-\\u309C]'; +var Other_ID_Continue = '[\\u00B7\\u0387\\u1369-\\u1371\\u19DA]'; +var id_start = group(Lu, Ll,Lt, Lm, Lo, Nl, the_underscore, Other_ID_Start); +var id_continue = group(id_start, Mn, Mc, Nd, Pc, Other_ID_Continue); +var isidentifier_regex; +// Fall back if we don't support unicode +if (RegExp().unicode === false) { + isidentifier_regex = new RegExp('^' + id_start + '+' + id_continue + '*$', 'u'); +} else { + id_start = group(Lu, Ll, the_underscore); + id_continue = group(id_start, '[0-9]'); + isidentifier_regex = new RegExp('^' + id_start + '+' + id_continue + '*$'); +} + /** * test if string is an identifier * @@ -87,31 +112,7 @@ function rstrip (input, what) { */ function isidentifier(str) { var normalized = str.normalize('NFKC'); - var the_underscore = '_'; - var Lu = '[A-Z]'; - var Ll = '[a-z]'; - var Lt = '[\\u{10B99}-\\u{10B9C}\\u{112A9}\\u{115DC}-\\u{115DD}\\u034F\\u115F-\\u1160\\u17B4-\\u17B5\\u2065\\u3164\\uFFA0\\uFFF0-\\uFFF8\\u{E0000}\\u{E0002}-\\u{E001F}\\u{E0080}-\\u{E00FF}\\u{E01F0}-\\u{E0FFF}\\u{112A9}\\u00D7]'; - var Lm = '[\\u02B0-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0374\\u037A\\u0559\\u06E5-\\u06E6\\u07F4-\\u07F5\\u0971\\u1C78-\\u1C7D\\u1D2C-\\u1D6A\\u1DFD-\\u1DFF\\u2E2F\\u30FC\\uA67F\\uA69C-\\uA69D\\uA717-\\uA71F\\uA788\\uA7F8-\\uA7F9\\uAB5C-\\uAB5F\\uFF70\\uFF9E-\\uFF9F\\u{16F93}-\\u{16F9F}\\u02D0-\\u02D1\\u0640\\u07FA\\u0E46\\u0EC6\\u1843\\u1AA7\\u1C7B\\u3005\\u3031-\\u3035\\u309D-\\u309E\\u30FC-\\u30FE\\uA015\\uA60C\\uA9CF\\uA9E6\\uAA70\\uAADD\\uAAF3-\\uAAF4\\uFF70\\u{16B42}-\\u{16B43}\\u{16FE0}-\\u{16FE1}\\u02B0-\\u02B8\\u02C0-\\u02C1\\u02E0-\\u02E4\\u037A\\u1D2C-\\u1D6A\\u1D78\\u1D9B-\\u1DBF\\u2071\\u207F\\u2090-\\u209C\\u2C7C-\\u2C7D\\uA69C-\\uA69D\\uA770\\uA7F8-\\uA7F9\\uAB5C-\\uAB5F\\uFF9E-\\uFF9F\\u02B2\\u1D62\\u1DA4\\u1DA8\\u2071\\u2C7C\\u2E18-\\u2E19\\u2E2F]'; - var Lo = '[\\u2135-\\u2138\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u3006\\u3400-\\u4DB5\\u4E00-\\u9FEF\\uF900-\\uFA6D\\uFA70-\\uFAD9\\u{17000}-\\u{187F1}\\u{18800}-\\u{18AF2}\\u{1B170}-\\u{1B2FB}\\u{20000}-\\u{2A6D6}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u{2F800}-\\u{2FA1D}\\uAAC0\\uAAC2\\uFE20-\\uFE2F\\u{10D22}-\\u{10D23}\\u{1135D}\\u00AA\\u00BA\\u3400-\\u4DB5\\u4E00-\\u9FEF\\uFA0E-\\uFA0F\\uFA11\\uFA13-\\uFA14\\uFA1F\\uFA21\\uFA23-\\uFA24\\uFA27-\\uFA29\\u{20000}-\\u{2A6D6}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u115F-\\u1160\\u3164\\uFFA0\\u0673\\u17A3-\\u17A4\\u0E40-\\u0E44\\u0EC0-\\u0EC4\\u19B5-\\u19B7\\u19BA\\uAAB5-\\uAAB6\\uAAB9\\uAABB-\\uAABC]'; - var Nl = '[\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u2170-\\u217F\\u2160-\\u216F]'; - var Mn = '[\\u104A-\\u104B\\u102B-\\u102C\\u102D-\\u1030\\u1031\\u1032-\\u1036\\u1038\\u103B-\\u103C\\u103D-\\u103E\\u1056-\\u1057\\u1058-\\u1059\\u105E-\\u1060\\u1062\\u1067-\\u1068\\u1071-\\u1074\\u1082\\u1083-\\u1084\\u1085-\\u1086\\u109C\\u109D\\u1037\\u1039-\\u103A\\u1087-\\u108C\\u108D\\u108F\\u109A-\\u109B\\uA9E5\\uAA7B\\uAA7C\\uAA7D\\uA9E6\\uAA70\\u104A-\\u104B]'; - var Mc = '[\\u0903\\u093B\\u093E-\\u0940\\u0949-\\u094C\\u094E-\\u094F\\u0982-\\u0983\\u09BE-\\u09C0\\u09C7-\\u09C8\\u09CB-\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB-\\u0ACC\\u0B02-\\u0B03\\u0B3E\\u0B40\\u0B47-\\u0B48\\u0B4B-\\u0B4C\\u0B57\\u0BBE-\\u0BBF\\u0BC1-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82-\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7-\\u0CC8\\u0CCA-\\u0CCB\\u0CD5-\\u0CD6\\u0D02-\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82-\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2-\\u0DF3\\u0F7F\\u102B-\\u102C\\u1031\\u1038\\u103B-\\u103C\\u1056-\\u1057\\u1062\\u1067-\\u1068\\u1083-\\u1084\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7-\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930-\\u1931\\u1933-\\u1938\\u1A19-\\u1A1A\\u1A55\\u1A57\\u1A61\\u1A63-\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B82\\u1BA1\\u1BA6-\\u1BA7\\u1BE7\\u1BEA-\\u1BEC\\u1BEE\\u1C24-\\u1C2B\\u1C34-\\u1C35\\u1CF2-\\u1CF3\\uA823-\\uA824\\uA827\\uA880-\\uA881\\uA8B4-\\uA8C3\\uA952\\uA983\\uA9B4-\\uA9B5\\uA9BA-\\uA9BB\\uA9BD-\\uA9BF\\uAA2F-\\uAA30\\uAA33-\\uAA34\\uAA4D\\uAAEB\\uAAEE-\\uAAEF\\uAAF5\\uABE3-\\uABE4\\uABE6-\\uABE7\\uABE9-\\uABEA\\u{11000}\\u{11002}\\u{11082}\\u{110B0}-\\u{110B2}\\u{110B7}-\\u{110B8}\\u{1112C}\\u{11145}-\\u{11146}\\u{11182}\\u{111B3}-\\u{111B5}\\u{111BF}\\u{1122C}-\\u{1122E}\\u{11232}-\\u{11233}\\u{112E0}-\\u{112E2}\\u{11302}-\\u{11303}\\u{1133E}-\\u{1133F}\\u{11341}-\\u{11344}\\u{11347}-\\u{11348}\\u{1134B}-\\u{1134C}\\u{11357}\\u{11362}-\\u{11363}\\u{11435}-\\u{11437}\\u{11440}-\\u{11441}\\u{11445}\\u{114B0}-\\u{114B2}\\u{114B9}\\u{114BB}-\\u{114BE}\\u{114C1}\\u{115AF}-\\u{115B1}\\u{115B8}-\\u{115BB}\\u{115BE}\\u{11630}-\\u{11632}\\u{1163B}-\\u{1163C}\\u{1163E}\\u{116AC}\\u{116AE}-\\u{116AF}\\u{11720}-\\u{11721}\\u{11726}\\u{1182C}-\\u{1182E}\\u{11838}\\u{11A39}\\u{11A57}-\\u{11A58}\\u{11A97}\\u{11C2F}\\u{11C3E}\\u{11CA9}\\u{11CB1}\\u{11CB4}\\u{11D8A}-\\u{11D8E}\\u{11D93}-\\u{11D94}\\u{11D96}\\u{11EF5}-\\u{11EF6}\\u{16F51}-\\u{16F7E}\\u0F3E-\\u0F3F\\u1087-\\u108C\\u108F\\u109A-\\u109B\\u1B44\\u1BAA\\u1CE1\\u1CF7\\u302E-\\u302F\\uA953\\uA9C0\\uAA7B\\uAA7D\\uABEC\\u{111C0}\\u{11235}\\u{1134D}\\u{116B6}\\u{1D16D}-\\u{1D172}\\u09BE\\u09D7\\u0B3E\\u0B57\\u0BBE\\u0BD7\\u0CC2\\u0CD5-\\u0CD6\\u0D3E\\u0D57\\u0DCF\\u0DDF\\u302E-\\u302F\\u{1133E}\\u{11357}\\u{114B0}\\u{114BD}\\u{115AF}\\u{1D165}\\u{1D16E}-\\u{1D172}]'; - var Nd = '[\\u{1D7CE}-\\u{1D7FF}\\uFF10-\\uFF19]'; - var Pc = '\\u2040'; - var Other_ID_Start = '[\\u1885-\\u1886\\u2118\\u212E\\u309B-\\u309C]'; - var Other_ID_Continue = '[\\u00B7\\u0387\\u1369-\\u1371\\u19DA]'; - var id_start = group(Lu, Ll,Lt, Lm, Lo, Nl, the_underscore, Other_ID_Start); - var id_continue = group(id_start, Mn, Mc, Nd, Pc, Other_ID_Continue); - var r; - // Fall back if we don't support unicode - if (RegExp().unicode === false) { - r = new RegExp('^' + id_start + '+' + id_continue + '*$', 'u'); - } else { - id_start = group(Lu, Ll, the_underscore); - id_continue = group(id_start, '[0-9]'); - r = new RegExp('^' + id_start + '+' + id_continue + '*$'); - } - return r.test(normalized); + return isidentifier_regex.test(normalized); } /* we have to use string and ctor to be able to build patterns up. + on /.../ @@ -187,10 +188,10 @@ var PseudoExtras = group('\\\\\\r?\\n|$', Comment_, Triple); var endpats = {} var prefixes = _all_string_prefixes(); for (let _prefix of prefixes) { - endpats[_prefix + "'"] = Single - endpats[_prefix + '"'] = Double - endpats[_prefix + "'''"] = Single3 - endpats[_prefix + '"""'] = Double3 + endpats[_prefix + "'"] = RegExp(Single) + endpats[_prefix + '"'] = RegExp(Double) + endpats[_prefix + "'''"] = RegExp(Single3) + endpats[_prefix + '"""'] = RegExp(Double3) } // A set of all of the single and triple quoted string prefixes, @@ -206,14 +207,8 @@ for (let t of prefixes) { var tabsize = 8 -/** - * internal tokenize function - * - * @param {function(): string} readline - * @param {string} encoding - * @param {function(TokenInfo): void} yield_ - */ -function _tokenize(readline, encoding, yield_) { +var PseudoTokenRegex; +function _setupTokenRegexes() { // we make these regexes here because they can // be changed by the configuration. var LSuffix = Sk.__future__.l_suffix ? '(?:L?)' : ''; @@ -226,6 +221,22 @@ function _tokenize(readline, encoding, yield_) { (Sk.__future__.silent_octal_literal ? SilentOctnumber : Octnumber), Decnumber); var Number_ = group(Imagnumber, Floatnumber, Intnumber); var PseudoToken = Whitespace + group(PseudoExtras, Number_, Funny, ContStr, Name); + PseudoTokenRegex = RegExp(PseudoToken); +} + +Sk._setupTokenRegexes = _setupTokenRegexes; + +Sk.exportSymbol("Sk._setupTokenRegexes", Sk._setupTokenRegexes); + +/** + * internal tokenize function + * + * @param {function(): string} readline + * @param {string} encoding + * @param {function(TokenInfo): void} yield_ + */ +function _tokenize(readline, encoding, yield_) { + var lnum = 0, parenlev = 0, @@ -362,7 +373,7 @@ function _tokenize(readline, encoding, yield_) { capos = line.charAt(pos); } - pseudomatch = RegExp(PseudoToken).exec(line.substring(pos)) + pseudomatch = PseudoTokenRegex.exec(line.substring(pos)) if (pseudomatch) { // scan for tokens var start = pos; var end = start + pseudomatch[1].length; @@ -389,7 +400,7 @@ function _tokenize(readline, encoding, yield_) { //assert not token.endswith("\n") yield_(new TokenInfo(tokens.T_COMMENT, token, spos, epos, line)); } else if (contains(triple_quoted, token)) { - endprog = RegExp(endpats[token]); + endprog = endpats[token]; endmatch = endprog.exec(line.substring(pos)); if (endmatch) { // all on one line pos = endmatch[0].length + pos; @@ -422,9 +433,9 @@ function _tokenize(readline, encoding, yield_) { // character. So it's really looking for // endpats["'"] or endpats['"'], by trying to // skip string prefix characters, if any. - endprog = RegExp(endpats[initial] || - endpats[token[1]] || - endpats[token[2]]); + endprog = endpats[initial] || + endpats[token[1]] || + endpats[token[2]]; contstr = line.substring(start); needcont = 1; contline = line; diff --git a/test/decorators.py b/test/decorators.py new file mode 100644 index 0000000000..725e98707b --- /dev/null +++ b/test/decorators.py @@ -0,0 +1,41 @@ +def WrapFun(banana): + print("During creation") + def innerApple(*args): + print("Before call") + result = banana(*args) + print("After call") + return result + return innerApple + +@WrapFun +@WrapFun +def doPear(a, b): + return a+b + +doPear(1, 2) + +def decorator(cls): + class Wrapper(object): + def __init__(self, *args): + self.wrapped = cls(*args) + + def __getattr__(self, name): + print('Getting the {} of {}'.format(name, self.wrapped)) + return getattr(self.wrapped, name) + + return Wrapper + +@decorator +class C(object): + def __init__(self, x, y): + self.x = x + self.y = y + + +x = C(1,2) +print(x.x) +print(x) +print(type(x)) + +D = decorator(C) +print(D(4, 3).x) \ No newline at end of file diff --git a/test/fix_dunder_class.py b/test/fix_dunder_class.py new file mode 100644 index 0000000000..e44f9e28cd --- /dev/null +++ b/test/fix_dunder_class.py @@ -0,0 +1 @@ +print(TypeError().__class__) \ No newline at end of file diff --git a/test/test_ast.py b/test/test_ast.py index 02e04d06e4..c43d2a6585 100644 --- a/test/test_ast.py +++ b/test/test_ast.py @@ -90,4 +90,5 @@ def visit_Num(self, node): print("All tests complete.") -assert ast.In, "Could not load In ast element" \ No newline at end of file +assert ast.In, "Could not load In ast element" + diff --git a/test/test_ast2.py b/test/test_ast2.py new file mode 100644 index 0000000000..2d18d289e9 --- /dev/null +++ b/test/test_ast2.py @@ -0,0 +1,5 @@ +import ast + +func = ast.parse("def alpha(): pass") + +assert func.body[0].name == "alpha" From 47fc5b07c7972ad9546d522d7dd9f29285360c03 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 7 Aug 2019 08:35:40 -0400 Subject: [PATCH 17/68] Matplotlib support! --- src/compile.js | 4 +- src/lib/image.js | 99 ++++++++++--------- src/lib/matplotlib/pyplot/__init__.js | 131 +++++++++++++------------- 3 files changed, 113 insertions(+), 121 deletions(-) diff --git a/src/compile.js b/src/compile.js index 3863cc4dd4..c0479c33c6 100644 --- a/src/compile.js +++ b/src/compile.js @@ -116,7 +116,7 @@ Compiler.prototype.annotateSource = function (ast, shouldStep) { // Do not trace the standard library if (shouldStep && (!this.filename || !this.filename.startsWith('src/lib/'))) { - out("Sk.afterSingleExecution($gbl,"+lineno+","+col_offset+","+JSON.stringify(this.filename)+");\n"); + out("Sk.afterSingleExecution && Sk.afterSingleExecution($gbl,"+lineno+","+col_offset+","+JSON.stringify(this.filename)+");\n"); } } }; @@ -2339,7 +2339,7 @@ Compiler.prototype.vstmt = function (s, class_for_super) { this.u.doesSuspend = true; } - this.annotateSource(s); + this.annotateSource(s, true); switch (s.constructor) { case Sk.astnodes.FunctionDef: diff --git a/src/lib/image.js b/src/lib/image.js index cb5ba7c165..4413fd9003 100644 --- a/src/lib/image.js +++ b/src/lib/image.js @@ -54,19 +54,19 @@ $builtinmodule = function (name) { susp.data = { type: "Sk.promise", promise: new Promise(function (resolve, reject) { - var newImg = new Image(); - newImg.crossOrigin = ""; - newImg.onerror = function () { - reject(Error("Failed to load URL: " + newImg.src)); - }; - newImg.onload = function () { - self.image = this; - initializeImage(self); - resolve(); - }; - // look for mapping from imagename to url and possible an image proxy server - newImg.src = remapImageIdToURL(imageId); - } + var newImg = new Image(); + newImg.crossOrigin = ""; + newImg.onerror = function () { + reject(Error("Failed to load URL: " + newImg.src)); + }; + newImg.onload = function () { + self.image = this; + initializeImage(self); + resolve(); + }; + // look for mapping from imagename to url and possible an image proxy server + newImg.src = remapImageIdToURL(imageId); + } ) }; return susp; @@ -81,15 +81,15 @@ $builtinmodule = function (name) { // if image proxy server is configured construct url for proxy // return the final URL - var proxy = typeof(Sk.imageProxy) === "function" - ? Sk.imageProxy : function (str) { - url = document.createElement("a"); - url.href = ret; - if (window.location.host !== url.host) { - return Sk.imageProxy + "/" + str; - } - return str; - }; + var proxy = typeof (Sk.imageProxy) === "function" + ? Sk.imageProxy : function (str) { + url = document.createElement("a"); + url.href = ret; + if (window.location.host !== url.host) { + return Sk.imageProxy + "/" + str; + } + return str; + }; var url; var ret; @@ -130,7 +130,7 @@ $builtinmodule = function (name) { for (i = 0; i < self.image.height * self.image.width; i++) { arr[i] = Sk.misceval.callsimArray(self.getPixel, [self, - i % self.image.width, Math.floor(i / self.image.width)]); + i % self.image.width, Math.floor(i / self.image.width)]); } return new Sk.builtin.tuple(arr); }; @@ -197,16 +197,16 @@ $builtinmodule = function (name) { if ((self.updateCount % self.updateInterval) === 0) { if (self.lastx + self.updateInterval >= self.width) { self.lastCtx.putImageData(self.imagedata, self.lastUlx, self.lastUly, - 0, self.lasty, self.width, 2); + 0, self.lasty, self.width, 2); } else if (self.lasty + self.updateInterval >= self.height) { self.lastCtx.putImageData(self.imagedata, self.lastUlx, self.lastUly, - self.lastx, 0, 2, self.height); + self.lastx, 0, 2, self.height); } else { self.lastCtx.putImageData(self.imagedata, self.lastUlx, self.lastUly, - Math.min(x, self.lastx), - Math.min(y, self.lasty), - Math.max(Math.abs(x - self.lastx), 1), - Math.max(Math.abs(y - self.lasty), 1)); + Math.min(x, self.lastx), + Math.min(y, self.lasty), + Math.max(Math.abs(x - self.lastx), 1), + Math.max(Math.abs(y - self.lasty), 1)); } self.lastx = x; self.lasty = y; @@ -309,21 +309,20 @@ $builtinmodule = function (name) { // allow direct access to height/width properties $loc.__getattr__ = new Sk.builtin.func(function (self, key) { key = Sk.ffi.remapToJs(key); - if (key === "height") { - return Sk.builtin.assk$(self.height); - } - else if (key === "width") { - return Sk.builtin.assk$(self.width); - } - }); + if (key === "height") { + return Sk.builtin.assk$(self.height); + } else if (key === "width") { + return Sk.builtin.assk$(self.width); + } + }); // height and width can only be set on creation $loc.__setattr__ = new Sk.builtin.func(function (self, key, value) { key = Sk.ffi.remapToJs(key); - if (key === 'height' || key === 'width') { - throw new Sk.builtin.Exception("Cannot change height or width they can only be set on creation") + if (key === "height" || key === "width") { + throw new Sk.builtin.Exception("Cannot change height or width they can only be set on creation"); } else { - throw new Sk.builtin.Exception("Unknown attribute: " + key) + throw new Sk.builtin.Exception("Unknown attribute: " + key); } }); @@ -474,22 +473,20 @@ $builtinmodule = function (name) { $loc.__getattr__ = new Sk.builtin.func(function (self, key) { key = Sk.ffi.remapToJs(key); - if (key === "red") { - return Sk.builtin.assk$(self.red); - } - else if (key === "green") { - return Sk.builtin.assk$(self.green); - } - else if (key === "blue") { - return Sk.builtin.assk$(self.blue); - } - }); + if (key === "red") { + return Sk.builtin.assk$(self.red); + } else if (key === "green") { + return Sk.builtin.assk$(self.green); + } else if (key === "blue") { + return Sk.builtin.assk$(self.blue); + } + }); $loc.__setattr__ = new Sk.builtin.func(function (self, key, value) { key = Sk.ffi.remapToJs(key); - if (key === 'red' || key === 'green' || key === 'blue') { - self[key] = Sk.builtin.asnum$(value) + if (key === "red" || key === "green" || key === "blue") { + self[key] = Sk.builtin.asnum$(value); } }); diff --git a/src/lib/matplotlib/pyplot/__init__.js b/src/lib/matplotlib/pyplot/__init__.js index 5573d7119e..f60c9f650b 100644 --- a/src/lib/matplotlib/pyplot/__init__.js +++ b/src/lib/matplotlib/pyplot/__init__.js @@ -47,15 +47,11 @@ var $builtinmodule = function(name) { chart = {}; chart.margin = {"top": 20, "right": 30, "bottom": 50, "left": 40}; - chart.width = Sk.console.width - chart.margin.left - chart.margin.right; - chart.height = Sk.console.height - chart.margin.top - chart.margin.bottom; + chart.width = Sk.console.getWidth() - chart.margin.left - chart.margin.right; + chart.height = Sk.console.getHeight() - chart.margin.top - chart.margin.bottom; chart.id = "chart" + chartCounter; chart.type = type; - if (Sk.console.skipDrawing) { - return chart; - } - return chart; } }; @@ -99,10 +95,10 @@ var $builtinmodule = function(name) { var xdata = null; var ydata = null; var stylestring = null; - if (args.length == 1) { + if (args.length === 1) { // ydata ydata = Sk.ffi.remapToJs(args[0]); - } else if (args.length == 2) { + } else if (args.length === 2) { if (Sk.builtin.checkString(args[1])) { // ydata, style ydata = Sk.ffi.remapToJs(args[0]); @@ -112,7 +108,7 @@ var $builtinmodule = function(name) { xdata = Sk.ffi.remapToJs(args[0]); ydata = Sk.ffi.remapToJs(args[1]); } - } else if (args.length == 3) { + } else if (args.length === 3) { // xdata, ydata, style xdata = Sk.ffi.remapToJs(args[0]); ydata = Sk.ffi.remapToJs(args[1]); @@ -158,9 +154,9 @@ var $builtinmodule = function(name) { updateMinMax("x", xdata); updateMinMax("y", ydata); - if (Sk.console.skipDrawing) { + /*if (Sk.console.isMuted()) { return; - } + }*/ }; plot_f.co_kwargs = true; mod.plot = new Sk.builtin.func(plot_f); @@ -174,11 +170,12 @@ var $builtinmodule = function(name) { if (!chart) { createChart("line"); } - if (chart.type == "hist" && plots.length < 1) { + // TODO: Currently, cannot overlay histograms on other charts + if (chart.type === "hist" && plots.length < 1) { resetChart(); return; } - if (plots.length == 0) { + if (plots.length === 0) { return; } if (extents["xMin"] === undefined || extents["yMin"] === undefined) { @@ -188,7 +185,7 @@ var $builtinmodule = function(name) { var yAxisBuffer; // Establish x/y scalers and axes - if (chart.type == "scatter" || chart.type == "line") { + if (chart.type === "scatter" || chart.type === "line") { yAxisBuffer = 5*Math.max(extents["yMin"].toLocaleString().length, extents["yMax"].toLocaleString().length); chart.xScale = d3.scale.linear() @@ -197,7 +194,7 @@ var $builtinmodule = function(name) { chart.xAxis = d3.svg.axis() .scale(chart.xScale) .orient("bottom"); - } else if (chart.type == "hist") { + } else if (chart.type === "hist") { yAxisBuffer = 5*Math.max(extents["xMin"].toLocaleString().length, extents["xMax"].toLocaleString().length); chart.xScale = d3.scale.linear() @@ -221,7 +218,7 @@ var $builtinmodule = function(name) { tickArray //chart.xScale.ticks(bins) )(plots[0]["data"]); - } else if (chart.type == "bar") { + } else if (chart.type === "bar") { yAxisBuffer = 5*Math.max(extents["yMin"].toLocaleString().length, extents["yMax"].toLocaleString().length); chart.xScale = d3.scale.ordinal() @@ -232,6 +229,7 @@ var $builtinmodule = function(name) { .tickFormat(function(d) { return d.index; }) .orient("bottom"); } + // Fix y-axis if (chart.type !== "hist") { chart.yScale = d3.scale.linear() .domain([extents["yMin"], extents["yMax"]]) @@ -253,11 +251,12 @@ var $builtinmodule = function(name) { .interpolate("linear"); // set css classes - chart.svg = d3.select(Sk.console.container).append("div").append("svg"); + let outputTarget = Sk.console.plot(plots); + chart.svg = d3.select(outputTarget.html[0]).append("div").append("svg"); //$(chart.svg.node()).parent().hide(); chart.svg.attr("class", "chart"); - chart.svg.attr("width", Sk.console.width); - chart.svg.attr("height", Sk.console.height); + chart.svg.attr("width", Sk.console.getWidth()); + chart.svg.attr("height", Sk.console.getHeight()); chart.svg.attr("chartCount", chartCounter); var translation = "translate(" + (chart.margin.left + yAxisBuffer) + "," + chart.margin.top + ")"; @@ -319,13 +318,13 @@ var $builtinmodule = function(name) { // Actually draw the chart objects for (var i = 0; i < plots.length; i += 1) { var plot = plots[i]; - if (plot["type"] == "line") { + if (plot["type"] === "line") { chart.canvas.append("path") .style("stroke", plot["style"]["color"]) .attr("class", "line") .data(plot["data"]) .attr("d", chart.mapLine(plot["data"])); - } else if (plot["type"] == "scatter") { + } else if (plot["type"] === "scatter") { chart.canvas.append("g") .attr("class", "series") .selectAll(".point") @@ -337,7 +336,7 @@ var $builtinmodule = function(name) { .attr("cx", chart.mapX) .attr("cy", chart.mapY) .attr("r", 2); - } else if (plot["type"] == "hist") { + } else if (plot["type"] === "hist") { chart.canvas.selectAll(".bar") .data(histMapper) .enter().append("rect") @@ -350,40 +349,43 @@ var $builtinmodule = function(name) { .attr("height", function(d) { return chart.height - chart.yScale(d.y); }); } } - if (Sk.console.pngMode) { - var doctype = '' + "<" + '!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'; - var xml = new XMLSerializer().serializeToString(chart.svg[0][0]); - var blob = new Blob([ doctype + xml], { type: "image/svg+xml" }); - var url = window.URL.createObjectURL(blob); - //var data = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(xml))); - var img = document.createElement("img"); - img.style.display = "block"; - var oldChart = chart; - var oldPlots = plots; - Sk.console.printHtml(img, oldPlots); - resetChart(); - oldChart.svg[0][0].parentNode.replaceChild(img, oldChart.svg[0][0]); - img.onload = function() { - img.onload = null; - //TODO: Make this capture a class descendant. Cross the D3/Jquery barrier! - var canvas = document.createElement("canvas"); - canvas.width = Sk.console.width; - canvas.height = Sk.console.height; - var ctx = canvas.getContext("2d"); - ctx.drawImage(img, 0, 0); - var canvasUrl = canvas.toDataURL("image/png"); - img.setAttribute("src", canvasUrl); - // Snip off this chart, we can now start a new one. - }; - img.onerror = function() { - - }; - img.setAttribute("src", url); - } else { - Sk.console.printHtml(chart.svg, plots); + + var doctype = '' + "<" + '!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'; + var xml = new XMLSerializer().serializeToString(chart.svg[0][0]); + var blob = new Blob([ doctype + xml], { type: "image/svg+xml" }); + var url = window.URL.createObjectURL(blob); + //var data = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(xml))); + let anchor = document.createElement("a"); + var img = document.createElement("img"); + img.style.display = "block"; + var oldChart = chart; + let filename = labels.title; + resetChart(); + //outputTarget.html[0].appendChild(img); + // TODO: Consider using the SVG as the actual image, and using this as the href + // surrounding it instead. + oldChart.svg[0][0].parentNode.replaceChild(anchor, oldChart.svg[0][0]); + img.onload = function() { + img.onload = null; + //TODO: Make this capture a class descendant. Cross the D3/Jquery barrier! + var canvas = document.createElement("canvas"); + canvas.width = Sk.console.getWidth(); + canvas.height = Sk.console.getHeight(); + var ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0); + var canvasUrl = canvas.toDataURL("image/png"); + img.setAttribute("src", canvasUrl); + img.setAttribute("title", "Generated plot titled: "+filename); + img.setAttribute("alt", "Generated plot titled: "+filename); + anchor.setAttribute("href", canvasUrl); + anchor.setAttribute("download", filename); // Snip off this chart, we can now start a new one. - resetChart(); - } + }; + img.onerror = function(e) { + console.error(e); + }; + img.setAttribute("src", url); + anchor.appendChild(img); }; mod.show = new Sk.builtin.func(show_f); @@ -460,10 +462,10 @@ var $builtinmodule = function(name) { // Parse different argument combinations var data = null; var stylestring = null; - if (args.length == 1) { + if (args.length === 1) { // xdata data = Sk.ffi.remapToJs(args[0]); - } else if (args.length == 2) { + } else if (args.length === 2) { // xdata, style data = Sk.ffi.remapToJs(args[0]); stylestring = Sk.ffi.remapToJs(args[1]); @@ -496,10 +498,6 @@ var $builtinmodule = function(name) { } }); updateMinMax("x", data); - - if (Sk.console.skipDrawing) { - return; - } }; hist_f.co_kwargs = true; mod.hist = new Sk.builtin.func(hist_f); @@ -523,18 +521,18 @@ var $builtinmodule = function(name) { var xdata = null; var ydata = null; var stylestring = null; - if (args.length == 2) { + if (args.length === 2) { // xdata, ydata xdata = Sk.ffi.remapToJs(args[0]); ydata = Sk.ffi.remapToJs(args[1]); - } else if (args.length == 3) { + } else if (args.length === 3) { // xdata, ydata, style xdata = Sk.ffi.remapToJs(args[0]); ydata = Sk.ffi.remapToJs(args[1]); stylestring = Sk.ffi.remapToJs(args[2]); } - if (xdata.length > dot_limit) { + if (xdata && xdata.length > dot_limit) { var xdataSampled = [], ydataSampled = []; var LENGTH = xdata.length, RATE = LENGTH / dot_limit; for (var i = 0; i < dot_limit; i+= 1) { @@ -578,9 +576,6 @@ var $builtinmodule = function(name) { // Update min/max updateMinMax("x", xdata); updateMinMax("y", ydata); - if (Sk.console.skipDrawing) { - return; - } }; scatter_f.co_kwargs = true; mod.scatter = new Sk.builtin.func(scatter_f); @@ -589,7 +584,7 @@ var $builtinmodule = function(name) { return mod; }; - +//TODO: Make font size controllable jsplotlib.rc = { "lines.linewidth": 1.0, "lines.linestyle": "-", From 9f8989f50649075d45d468e6ca8aafacaeaa793c Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 19 Aug 2019 15:51:42 -0400 Subject: [PATCH 18/68] Update pedal, fix various skulpt issues --- src/compile.js | 31 +- src/file.js | 12 +- src/import.js | 2 +- src/lib/pedal/cait/ast_map.py | 7 +- src/lib/pedal/cait/cait_node.py | 5 +- src/lib/pedal/cait/stretchy_tree_matching.py | 54 +- src/lib/pedal/mistakes/feedback_mod.py | 15 - src/lib/pedal/mistakes/instructor_append.py | 47 +- src/lib/pedal/mistakes/instructor_filter.py | 16 +- .../pedal/mistakes/instructor_histogram.py | 41 +- .../pedal/mistakes/instructor_iteration.py | 78 +- src/lib/pedal/mistakes/iteration_context.py | 667 +++++++++--------- src/lib/pedal/plugins/cmd_line.py | 129 ++++ src/lib/pedal/report/imperative.py | 14 +- src/lib/pedal/tifa/messages.py | 2 +- src/lib/pedal/tifa/tifa.py | 11 +- src/lib/pedal/toolkit/functions.py | 33 +- src/lib/pedal/toolkit/plotting.py | 96 ++- src/lib/pedal/toolkit/printing.py | 12 +- src/lib/pedal/toolkit/utilities.py | 76 +- src/lib/re.js | 15 +- src/lib/turtle.js | 1 + src/print.js | 1 + test/test_regex.py | 5 + 24 files changed, 806 insertions(+), 564 deletions(-) delete mode 100644 src/lib/pedal/mistakes/feedback_mod.py create mode 100644 src/lib/pedal/plugins/cmd_line.py create mode 100644 test/test_regex.py diff --git a/src/compile.js b/src/compile.js index c0479c33c6..5d26acb58d 100644 --- a/src/compile.js +++ b/src/compile.js @@ -116,7 +116,7 @@ Compiler.prototype.annotateSource = function (ast, shouldStep) { // Do not trace the standard library if (shouldStep && (!this.filename || !this.filename.startsWith('src/lib/'))) { - out("Sk.afterSingleExecution && Sk.afterSingleExecution($gbl,"+lineno+","+col_offset+","+JSON.stringify(this.filename)+");\n"); + out("Sk.afterSingleExecution && Sk.afterSingleExecution($gbl,$loc,"+lineno+","+col_offset+","+JSON.stringify(this.filename)+");\n"); } } }; @@ -324,7 +324,8 @@ Compiler.prototype.outputInterruptTest = function () { // Added by RNL if (Sk.execLimit !== null || Sk.yieldLimit !== null && this.u.canSuspend) { output += "var $dateNow = Date.now();"; if (Sk.execLimit !== null) { - output += "if ($dateNow - Sk.execStart > Sk.execLimit && Sk.execLimit !== null) {throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg())}"; + output += ("if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit){"+ + "throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg())}"); } if (Sk.yieldLimit !== null && this.u.canSuspend) { output += "if ($dateNow - Sk.lastYield > Sk.yieldLimit) {"; @@ -1132,8 +1133,18 @@ Compiler.prototype.outputSuspensionHelpers = function (unit) { } } - output += "try { $ret=susp.child.resume(); } catch(err) { if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now()} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if($exc.length>0) { $err=err; Sk.err=$err; $blk=$exc.pop(); } else { throw err; } }" + - "};"; + output += ("try {"+ + "$ret=susp.child.resume();"+ + "} catch(err) {"+ + "if (err instanceof Sk.builtin.TimeLimitError) {"+ + "Sk.execStart = Date.now();"+ + "Sk.execPaused = 0;"+ + "} if (!(err instanceof Sk.builtin.BaseException)) {"+ + "err = new Sk.builtin.ExternalError(err);"+ + "}"+ + "err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'});"+ + "if($exc.length>0) { $err=err; Sk.err=$err; $blk=$exc.pop(); } else { throw err; } }" + + "};"); output += "var $saveSuspension = function($child, $filename, $lineno, $colno) {" + "var susp = new Sk.misceval.Suspension(); susp.child=$child;" + @@ -1907,7 +1918,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal // all function invocations in call this.u.varDeclsCode += "var $blk=" + entryBlock + ",$exc=[],$loc=" + locals + cells + ",$gbl=this,$err=undefined,$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;"; if (Sk.execLimit !== null) { - this.u.varDeclsCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now()}"; + this.u.varDeclsCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now();Sk.execPaused=0}"; } if (Sk.yieldLimit !== null && this.u.canSuspend) { this.u.varDeclsCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}"; @@ -1988,7 +1999,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal this.u.switchCode = "while(true){try{" this.u.switchCode += this.outputInterruptTest(); this.u.switchCode += "switch($blk){"; - this.u.suffixCode = "} }catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now()} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} }});"; + this.u.suffixCode = "} }catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now();Sk.execPaused=0} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} }});"; // // jump back to the handler so it can do the main actual work of the @@ -2253,7 +2264,7 @@ Compiler.prototype.cclass = function (s) { this.u.switchCode += "var $blk=" + entryBlock + ",$exc=[],$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;" if (Sk.execLimit !== null) { - this.u.switchCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now()}"; + this.u.switchCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now();Sk.execPaused=0}"; } if (Sk.yieldLimit !== null && this.u.canSuspend) { this.u.switchCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}"; @@ -2262,7 +2273,7 @@ Compiler.prototype.cclass = function (s) { this.u.switchCode += "while(true){try{"; this.u.switchCode += this.outputInterruptTest(); this.u.switchCode += "switch($blk){"; - this.u.suffixCode = "}}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now()} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }}}" + this.u.suffixCode = "}}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now();Sk.execPaused=0} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }}}" this.u.suffixCode += "}).call(null, $cell);});"; this.u.private_ = s.name; @@ -2690,7 +2701,7 @@ Compiler.prototype.cmod = function (mod) { "');var $ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;"; if (Sk.execLimit !== null) { - this.u.varDeclsCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now()}"; + this.u.varDeclsCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now();Sk.execPaused=0}"; } if (Sk.yieldLimit !== null && this.u.canSuspend) { @@ -2717,7 +2728,7 @@ Compiler.prototype.cmod = function (mod) { this.u.switchCode += this.outputInterruptTest(); this.u.switchCode += "switch($blk){"; this.u.suffixCode = "}" - this.u.suffixCode += "}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now()} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} } });"; + this.u.suffixCode += "}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now();Sk.execPaused=0} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} } });"; // Note - this change may need to be adjusted for all the other instances of // switchCode and suffixCode in this file. Not knowing how to test those diff --git a/src/file.js b/src/file.js index 3f7fe929bd..be0fcaeb81 100644 --- a/src/file.js +++ b/src/file.js @@ -1,3 +1,7 @@ +var STDOUT_FILENO = 1; +var STDIN_FILENO = 0; +var STDERR_FILENO = 2; + /** * @constructor * @param {Sk.builtin.str} name @@ -18,11 +22,11 @@ Sk.builtin.file = function (name, mode, buffering) { if (this.name === "/dev/stdout") { this.data$ = Sk.builtin.none.none$; - this.fileno = 1; + this.fileno = STDOUT_FILENO; } else if (this.name === "/dev/stdin") { - this.fileno = 0; + this.fileno = STDIN_FILENO; } else if (this.name === "/dev/stderr") { - this.fileno = 2; + this.fileno = STDERR_FILENO; } else { if (Sk.inBrowser) { // todo: Maybe provide a replaceable function for non-import files this.fileno = 10; @@ -241,6 +245,8 @@ Sk.builtin.file.prototype["write"] = new Sk.builtin.func(function write(self, st } else { throw new Sk.builtin.IOError("File not open for writing"); } + + return Sk.builtin.none.none$; }); diff --git a/src/import.js b/src/import.js index 1ec63e2478..c716511ec0 100644 --- a/src/import.js +++ b/src/import.js @@ -161,7 +161,7 @@ Sk.importSetUpPath = function (canSuspend) { Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, relativeToPackage, returnUndefinedOnTopLevelNotFound, canSuspend) { //dumpJS = true; /* TODO: temporary hack, need to delete! */ - if (name == "pedal.sandbox.sandbox") { + if (name === "pedal.sandbox.sandbox") { suppliedPyBody= "class Sandbox: pass\ndef run(): pass\ndef reset(): pass"; } /* End hack */ diff --git a/src/lib/pedal/cait/ast_map.py b/src/lib/pedal/cait/ast_map.py index fb8d51095e..002583cc03 100644 --- a/src/lib/pedal/cait/ast_map.py +++ b/src/lib/pedal/cait/ast_map.py @@ -217,6 +217,9 @@ def merge_map_with(self, other): Returns: AstMap: self modified by adding the contents of other """ + if other is None: + return + if not isinstance(other, type(self)): raise TypeError @@ -252,7 +255,9 @@ def match_lineno(self): def __getitem__(self, id_n): if id_n.startswith('__'): - return self.exp_table[id_n] + expression = self.exp_table[id_n] + expression.map = self + return expression else: if id_n in self.symbol_table: return self.symbol_table[id_n] diff --git a/src/lib/pedal/cait/cait_node.py b/src/lib/pedal/cait/cait_node.py index 50c83409a3..b64d970af9 100644 --- a/src/lib/pedal/cait/cait_node.py +++ b/src/lib/pedal/cait/cait_node.py @@ -324,7 +324,7 @@ def __getattr__(self, item): else: # get added field that may have existed for different node types return self.get_clashing_attr(key) - def find_matches(self, pattern, is_mod=False, check_meta=True): + def find_matches(self, pattern, is_mod=False, check_meta=True, use_previous=True): """ Retrieves any patterns that match against this CaitNode. Expected to be used for subpattern matching. @@ -337,7 +337,8 @@ def find_matches(self, pattern, is_mod=False, check_meta=True): matcher = stm.StretchyTreeMatcher(pattern, report=self.report) if (not is_node and not is_mod) and len(matcher.root_node.children) != 1: raise ValueError("pattern does not evaluate to a singular statement") - return matcher.find_matches(self, check_meta=check_meta) + prev_match = self.map if use_previous else None + return matcher.find_matches(self, check_meta=check_meta, pre_match=prev_match) def find_match(self, pattern, is_mod=False): matches = self.find_matches(pattern, is_mod) diff --git a/src/lib/pedal/cait/stretchy_tree_matching.py b/src/lib/pedal/cait/stretchy_tree_matching.py index 989c1942d9..639aedd19f 100644 --- a/src/lib/pedal/cait/stretchy_tree_matching.py +++ b/src/lib/pedal/cait/stretchy_tree_matching.py @@ -59,7 +59,7 @@ def __init__(self, ast_or_code, report, filename="__main__"): else: self.root_node = CaitNode(ast_node, _NONE_FIELD, report=self.report) - def find_matches(self, ast_or_code, filename="__main__", check_meta=True): + def find_matches(self, ast_or_code, filename="__main__", check_meta=True, pre_match=None): """ Args: ast_or_code (str or AstNode): The students' code or a valid AstNode from @@ -97,12 +97,12 @@ def find_matches(self, ast_or_code, filename="__main__", check_meta=True): other_root_old_field = other_root.field other_root.field = _NONE_FIELD matches = self.any_node_match(explore_root, other_root, - check_meta=check_meta) + check_meta=check_meta, pre_match=pre_match) explore_root.field = explore_root_old_field other_root.field = other_root_old_field return matches - def any_node_match(self, ins_node, std_node, check_meta=True, cut=False): + def any_node_match(self, ins_node, std_node, check_meta=True, cut=False, pre_match=None): """ Finds whether ins_node can be matched to some node in the tree std_node @@ -120,7 +120,7 @@ def any_node_match(self, ins_node, std_node, check_meta=True, cut=False): # @TODO: create a more public function that converts ins_node and std_node into CaitNodes # TODO: Create exhaustive any_node_match # matching: an object representing the mapping and the symbol table - matching = self.deep_find_match(ins_node, std_node, check_meta) + matching = self.deep_find_match(ins_node, std_node, check_meta, pre_match=pre_match) # if a direct matching is found if matching: for match in matching: @@ -131,7 +131,7 @@ def any_node_match(self, ins_node, std_node, check_meta=True, cut=False): # if not matching or exhaust: # otherwise # try to matching ins_node to each child of std_node, recursively for std_child in std_node.children: - matching_c = self.any_node_match(ins_node, std_child, check_meta=check_meta, cut=cut) + matching_c = self.any_node_match(ins_node, std_child, check_meta=check_meta, cut=cut, pre_match=pre_match) if matching_c: for match in matching_c: match.match_root = std_child @@ -141,7 +141,8 @@ def any_node_match(self, ins_node, std_node, check_meta=True, cut=False): return matching return [] - def deep_find_match(self, ins_node, std_node, check_meta=True): + def deep_find_match(self, ins_node, std_node, check_meta=True, + pre_match=None): """ Finds whether ins_node and matches std_node and whether ins_node's children flexibly match std_node's children in order @@ -149,28 +150,30 @@ def deep_find_match(self, ins_node, std_node, check_meta=True): ins_node: The instructor ast that should be included in the student AST std_node: The student AST that we are searching for the included tree check_meta: Flag, if True, check whether the two nodes originated from the same ast field + pre_match: If this was part of a previous match... Returns: a mapping of nodes and a symbol table mapping ins_node to std_node, or [] if no mapping was found """ method_name = "deep_find_match_" + type(ins_node.astNode).__name__ target_func = getattr(self, method_name, self.deep_find_match_generic) - return target_func(ins_node, std_node, check_meta) + return target_func(ins_node, std_node, check_meta, pre_match=pre_match) # noinspection PyPep8Naming - def deep_find_match_Name(self, ins_node, std_node, check_meta=True): + def deep_find_match_Name(self, ins_node, std_node, check_meta=True, pre_match=None): name_id = ins_node.astNode.id match = _name_regex(name_id) mapping = AstMap() matched = False meta_matched = self.metas_match(ins_node, std_node, check_meta) if match[_VAR] and meta_matched: # if variable - # This if body is probably unnecessary. if type(std_node.astNode).__name__ == "Name": - return self.deep_find_match_generic(ins_node, std_node, check_meta=check_meta, ignores=["ctx"]) + return self.deep_find_match_generic(ins_node, std_node, + check_meta=check_meta, ignores=["ctx"],pre_match=pre_match) # could else return False, but shallow_match_generic should do this as well elif match[_EXP]: # and meta_matched: # if expression # terminate recursion, the whole subtree should match since expression nodes match to anything + mapping.merge_map_with(pre_match) mapping.add_exp_to_sym_table(ins_node, std_node) matched = True elif match[_WILD] and meta_matched: # if wild card, don't care @@ -181,20 +184,21 @@ def deep_find_match_Name(self, ins_node, std_node, check_meta=True): mapping.add_node_pairing(ins_node, std_node) return [mapping] # else - return self.deep_find_match_generic(ins_node, std_node, check_meta=check_meta, ignores=["ctx"]) + return self.deep_find_match_generic(ins_node, std_node, + check_meta=check_meta, ignores=["ctx"], pre_match=pre_match) # noinspection PyPep8Naming - def deep_find_match_BinOp(self, ins_node, std_node, check_meta=True): + def deep_find_match_BinOp(self, ins_node, std_node, check_meta=True, pre_match=None): op = ins_node.astNode.op op = type(op).__name__ is_generic = not (op == "Mult" or op == "Add") if is_generic: - return self.deep_find_match_generic(ins_node, std_node, check_meta) + return self.deep_find_match_generic(ins_node, std_node, check_meta, pre_match=pre_match) else: # this means that the node is clearly commutative - return self.deep_find_match_binflex(ins_node, std_node, False) + return self.deep_find_match_binflex(ins_node, std_node, False, pre_match=pre_match) # noinspection PyMethodMayBeStatic - def binflex_helper(self, case_left, case_right, new_mappings, base_mappings): + def binflex_helper(self, case_left, case_right, new_mappings, base_mappings, pre_match=None): """ adds to new_mappings (return/modify by argument) the mappings for both the left and right subtrees as denoted by case_left and case_right @@ -203,19 +207,19 @@ def binflex_helper(self, case_left, case_right, new_mappings, base_mappings): case_right: The mappings for the right opperand new_mappings: The new set of mappings to generate base_mappings: The original mappings of the binop node - + pre_match: A mapping passed down from an initial match Returns: None """ if case_left and case_right: for case_l in case_left: - new_map = base_mappings[0].new_merged_map(case_l) + new_map = base_mappings[0].new_merged_map(case_l).new_merged_map(pre_match) for case_r in case_right: both = new_map.new_merged_map(case_r) if not both.has_conflicts(): new_mappings.append(both) - def deep_find_match_binflex(self, ins_node, std_node, check_meta=False): + def deep_find_match_binflex(self, ins_node, std_node, check_meta=False, pre_match=None): base_mappings = self.shallow_match(ins_node, std_node, check_meta) if not base_mappings: return [] @@ -233,17 +237,17 @@ def deep_find_match_binflex(self, ins_node, std_node, check_meta=False): # case 1: ins_left->std_left and ins_right->std_right case_left = self.deep_find_match(ins_left, std_left, False) case_right = self.deep_find_match(ins_right, std_right, False) - self.binflex_helper(case_left, case_right, new_mappings, base_mappings) + self.binflex_helper(case_left, case_right, new_mappings, base_mappings, pre_match=pre_match) # case 2: ins_left->std_right and ins_right->std_left case_left = self.deep_find_match(ins_left, std_right, False) case_right = self.deep_find_match(ins_right, std_left, False) - self.binflex_helper(case_left, case_right, new_mappings, base_mappings) + self.binflex_helper(case_left, case_right, new_mappings, base_mappings, pre_match=pre_match) if len(new_mappings) == 0: return [] return new_mappings return [] - def deep_find_match_Expr(self, ins_node, std_node, check_meta=True): + def deep_find_match_Expr(self, ins_node, std_node, check_meta=True, pre_match=None): """ An Expression node (not to be confused with expressions denoted by the instructor nodes in Name ast nodes) checks whether it should be generic, or not @@ -251,6 +255,7 @@ def deep_find_match_Expr(self, ins_node, std_node, check_meta=True): ins_node: Instructor ast to find in the student ast std_node: Student AST to search for the instructor ast in check_meta: flag to check whether the fields of the instructor node and the student node should match + pre_match: An AstMap from a previous matching run Returns: AstMap: a mapping between the instructor and student asts, or False if such a mapping doesn't exist @@ -258,7 +263,7 @@ def deep_find_match_Expr(self, ins_node, std_node, check_meta=True): # if check_meta and ins_node.field != std_node.field: if not self.metas_match(ins_node, std_node, check_meta): return [] - mapping = AstMap() + mapping = AstMap() if pre_match is None else pre_match value = ins_node.value ast_type = type(value.astNode).__name__ if ast_type == "Name": @@ -279,7 +284,7 @@ def deep_find_match_Expr(self, ins_node, std_node, check_meta=True): return [mapping] return self.deep_find_match_generic(ins_node, std_node, check_meta) - def deep_find_match_generic(self, ins_node, std_node, check_meta=True, ignores=None): + def deep_find_match_generic(self, ins_node, std_node, check_meta=True, ignores=None, pre_match=None): """ This first uses shallow match to find a base map (match) from which to build off. The algorithm then tracks all the possible mappings that @@ -300,6 +305,7 @@ def deep_find_match_generic(self, ins_node, std_node, check_meta=True, ignores=N std_node: Student AST to search for the instructor ast in check_meta: flag to check whether the fields of the instructor node and the student node should match ignores: List of fields to ignore in the field match + pre_match: a map from a previous match Returns: a mapping between the isntructor and student asts, or [] if such a mapping doesn't exist @@ -308,6 +314,8 @@ def deep_find_match_generic(self, ins_node, std_node, check_meta=True, ignores=N ignores = [] base_mappings = self.shallow_match(ins_node, std_node, check_meta) if base_mappings: + for mapping in base_mappings: + mapping.merge_map_with(pre_match) # base case this runs 0 times because no children # find each child of ins_node that matches IN ORDER base_sibs = [-1] diff --git a/src/lib/pedal/mistakes/feedback_mod.py b/src/lib/pedal/mistakes/feedback_mod.py deleted file mode 100644 index d7f6b67461..0000000000 --- a/src/lib/pedal/mistakes/feedback_mod.py +++ /dev/null @@ -1,15 +0,0 @@ -from pedal.report.imperative import explain, gently - - -def gently_r(message, code, line=None, tldr="explain"): - gently(message + "

({})

".format(code), line, label=tldr) - return message - - -def explain_r(message, code, priority='medium', line=None, tldr="explain"): - explain(message + "

({})

".format(code), priority, line, label=tldr) - return message - - -def codify(code): - return "" + code + "" diff --git a/src/lib/pedal/mistakes/instructor_append.py b/src/lib/pedal/mistakes/instructor_append.py index aa69495f6a..21e0288e53 100644 --- a/src/lib/pedal/mistakes/instructor_append.py +++ b/src/lib/pedal/mistakes/instructor_append.py @@ -1,5 +1,5 @@ from pedal.cait.cait_api import find_matches, find_expr_sub_matches, data_state -from pedal.mistakes.feedback_mod import * +from pedal.report.imperative import gently_r, explain_r def append_group_on_change(): @@ -38,6 +38,9 @@ def missing_append_in_iteration(): def missing_append_in_iteration(): + message = "You must construct a list by appending values one at a time to the list." + code = "app_in_iter" + tldr = "For Loop Append Not Found" matches = find_matches("for ___ in ___:\n" " __expr__") if matches: @@ -46,13 +49,15 @@ def missing_append_in_iteration(): submatch = __expr__.find_matches("___.append(___)") if submatch: return False - explain("You must construct a list by appending values one at a time to the list." - "

(app_in_iter)
") - return True + return explain_r(message, code, label=tldr) return False def wrong_not_append_to_list(): + message = ("Values can only be appended to a list. The variable {0!s} is either not initialized, " + "not initialized correctly, or is confused with another variable.") + code = "app_not_list" + tldr = "Not Appending to List" matches = find_matches("for ___ in ___:\n" " __expr__") for match in matches: @@ -61,14 +66,14 @@ def wrong_not_append_to_list(): for submatch in submatches: _target_ = submatch["_target_"] if not data_state(_target_).was_type('list'): - explain("Values can only be appended to a list. The variable {0!s} is either " - "not initialized, not initialized correctly, or is confused with another variable." - "

(app_not_list)
".format(_target_)) - return True + return explain_r(message.format(_target_), code, label=tldr) return False def missing_append_list_initialization(): + message = "The list variable {0!s} must be initialized." + code = "no_app_list_init" + tldr = "List Not Initialized" matches = find_matches("for ___ in ___:\n" " __expr__") for match in matches: @@ -76,17 +81,21 @@ def missing_append_list_initialization(): submatches = __expr__.find_matches("_new_list_.append(___)", ) for submatch in submatches: _new_list_ = submatch["_new_list_"].astNode + # TODO: In theory revisit this by merging matches matches02 = find_matches("{} = []\n" "for ___ in ___:\n" " __expr__".format(_new_list_.id)) if not matches02: - explain("The list variable {0!s} must be initialized.

" - "(no_app_list_init)
".format(_new_list_.id)) - return True + return explain_r(message.format(_new_list_.id), code, label=tldr) return False def wrong_append_list_initialization(): + message = ("The list variable {0!s} is either not initialized " + "correctly or mistaken for another variable. " + "The list you append to should be initialized to an empty list.") + code = "app_list_init" + tldr = "Incorrect Initialization or Usage of Empty List" matches = find_matches("_list_ = __expr1__\n" "for ___ in ___:\n" " __expr2__") @@ -94,29 +103,25 @@ def wrong_append_list_initialization(): _list_ = match["_list_"].astNode __expr1__ = match["__expr1__"] __expr2__ = match["__expr2__"] - submatch = find_expr_sub_matches("{}.append(___)".format(_list_.id), __expr2__) + submatch = __expr2__.find_matches("_list_.append(___)") if submatch and (__expr1__.ast_name == "List" and len(__expr1__.elts) != 0 or __expr1__.ast_name != "List"): - explain("The list variable {0!s} is either not " - "initialized correctly or mistaken for" - " another variable. The list you append to should be " - "initialized to an empty list.

" - "(app_list_init)
".format(_list_.id)) - return True + return explain_r(message.format(_list_.id), code, label=tldr) return False def append_list_wrong_slot(): + message = "You should not append a list ({0!s}) to {1!s}." + code = "app_list_slot" + tldr = "Appending List Error" matches = find_matches("_target_.append(_item_)") if matches: for match in matches: _item_ = match["_item_"].astNode _target_ = match["_target_"].astNode if data_state(_item_).was_type('list'): - explain("You should not append a list ({0!s}) to {1!s}.

" - "(app_list_slot)
".format(_item_.id, _target_.id)) - return True + return explain_r(message.format(_item_.id, _target_.id), code, label=tldr) return False diff --git a/src/lib/pedal/mistakes/instructor_filter.py b/src/lib/pedal/mistakes/instructor_filter.py index 6fe25d4806..026b28497e 100644 --- a/src/lib/pedal/mistakes/instructor_filter.py +++ b/src/lib/pedal/mistakes/instructor_filter.py @@ -1,5 +1,5 @@ from pedal.cait.cait_api import find_match, find_matches -from pedal.report.imperative import explain +from pedal.report.imperative import gently_r, explain_r def filter_group(): @@ -19,13 +19,14 @@ def missing_if_in_for(): Returns: """ + message = "The arrangement of decision and iteration is not correct for the filter pattern." + code = "missing_if_in_for" + tldr = "Missing if In For" matches = find_matches("for _item_ in ___:\n" " if __expr__:\n" " pass") if not matches: - explain("The arrangement of decision and iteration is not correct for the filter pattern.

" - "(missing_if_in_for)

") - return True + return explain_r(message, code, label=tldr) return False @@ -41,10 +42,11 @@ def append_not_in_if(): Returns: """ + message = "Only items satisfying some condition should be appended to the list." + code = "app_not_in_if" + tldr = "Append not in if" match = find_match("if ___:\n" " ___.append(___)") if not match: - explain( - "Only items satisfying some condition should be appended to the list.

(app_not_in_if)
") - return True + return explain_r(message, code, label=tldr) return False diff --git a/src/lib/pedal/mistakes/instructor_histogram.py b/src/lib/pedal/mistakes/instructor_histogram.py index 139737daaf..7893bee929 100644 --- a/src/lib/pedal/mistakes/instructor_histogram.py +++ b/src/lib/pedal/mistakes/instructor_histogram.py @@ -1,5 +1,5 @@ from pedal.cait.cait_api import find_match, find_matches, data_state -from pedal.report.imperative import explain +from pedal.report.imperative import gently_r, explain_r def histogram_group(): @@ -21,10 +21,12 @@ def histogram_missing(): Returns: """ + message = "The program should display a histogram." + code = "histo_missing" + tldr = "Missing Histogram" match = find_match("plt.hist(___)") if not match: - explain("The program should display a histogram.

(histo_missing)
") - return True + return explain_r(message, code, label=tldr) return False @@ -39,11 +41,12 @@ def plot_show_missing(): Returns: """ + message = "The plot must be explicitly shown to appear in the Printer area." + code = "plot_show_missing" + tldr = "No Plot Shown" match = find_match("plt.show()") if not match: - explain("The plot must be explicitly shown to appear in the Printer area." - "

(plot_show_missing)
") - return True + return explain_r(message, code, label=tldr) return False @@ -60,14 +63,15 @@ def histogram_argument_not_list(): Returns: """ + message = "Making a histogram requires a list; {0!s} is not a list." + code = "hist_arg_not_list" + tldr = "Making Histogram from Non-list" matches = find_matches("plt.hist(_argument_)") if matches: for match in matches: _argument_ = match["_argument_"].astNode - if not data_state(_argument_).was_type('list'): - explain("Making a histogram requires a list; {0!s} is not a list.

" - "(hist_arg_not_list)
".format(_argument_.id)) - return True + if not _argument_.get_data_state() or not _argument_.get_data_state().was_type('list'): + return explain_r(message.format(_argument_.id), code, label=tldr) return False @@ -87,6 +91,9 @@ def histogram_wrong_list(): Returns: """ + message = "The list created in the iteration is not the list being used to create the histogram." + code = "histo_wrong_list" + tldr = "Plotting Wrong List" matches = find_matches("for ___ in ___:\n" " __expr__\n" "plt.hist(_list_)") @@ -94,17 +101,17 @@ def histogram_wrong_list(): for match in matches: _list_ = match["_list_"].astNode __expr__ = match["__expr__"] - submatches = __expr__.find_matches("{}.append(___)".format(_list_.id)) + submatches = __expr__.find_matches("_list_.append(___)") if submatches: return False - explain( - "The list created in the iteration is not the list being used to create the histogram.

" - "(histo_wrong_list)
") - return True + return explain_r(message, code, label=tldr) return False def histogram_wrong_placement(): + message = "The histogram should be plotted only once, after the new list has been created" + code = "histo_wrong_place" + tldr = "Histogram Plot Placed Incorrectly" matches = find_matches("for ___ in ___:\n" " pass\n") if matches: @@ -114,6 +121,4 @@ def histogram_wrong_placement(): for match02 in matches02: if match02.match_lineno > match.match_lineno: return False - explain("The histogram should be plotted only once, after the new list has been created" - "

(histo_wrong_place)
") - return True + return explain_r(message, code, label=tldr) diff --git a/src/lib/pedal/mistakes/instructor_iteration.py b/src/lib/pedal/mistakes/instructor_iteration.py index 3a02e0c2c1..4b1817eb57 100644 --- a/src/lib/pedal/mistakes/instructor_iteration.py +++ b/src/lib/pedal/mistakes/instructor_iteration.py @@ -1,7 +1,7 @@ from pedal.cait.cait_api import (parse_program, find_match, find_matches, find_expr_sub_matches, data_state, def_use_error) -from pedal.report.imperative import explain +from pedal.report.imperative import gently_r, explain_r def iteration_group(): @@ -29,107 +29,125 @@ def all_for_loops(): # this conflics with list_repeated_in_for def wrong_target_is_list(): + message = ('The variable {0!s} is a list and ' + 'should not be placed in the iteration variable slot of the "for" block') + code = "target_is_list" + tldr = "Iteration Variable Overwriting List" match = find_match("for _item_ in ___:\n pass") if match: _item_ = match["_item_"].astNode if data_state(_item_).was_type('list'): - explain('The variable {0!s} is a list and should not be placed in the iteration variable slot' - ' of the "for" block

(target_is_list)
.'.format(_item_.id)) - return True + return explain_r(message.format(_item_.id), code, label=tldr) return False # this conflicts with list_in_wrong_slot_in_for def wrong_list_repeated_in_for(): + message = 'The {0!s} variable can only appear once in the "for" block.' + code = "list_repeat" + tldr = "Duplicate Iteration Variable" match = find_match("for _item_ in _item_:\n pass") if match: _item_ = match["_item_"].astNode if data_state(_item_).was_type('list'): - explain('The {0!s} variable can only appear once in the "for" block

' - '(list_repeat)
'.format(_item_.id)) - return True + return explain_r(message.format(_item_.id), code, label=tldr) return False -# this isn't consistent with the pattern you wrote +# this isn't consistent with the pattern you wrote TODO: Fix this def missing_iterator_initialization(): + message1 = "The slot to hold a list in the iteration is empty." + code1 = "no_iter_init-blank" + tldr1 = "Iteration Variable is Blank" + + message2 = "The variable {0!s} is in the list slot of the iteration but is not a list." + code2 = "no_iter_init" + tldr2 = "Iteration Variable is Not a List" + match = find_match("for ___ in _list_:\n pass") if match: _list_ = match["_list_"].astNode if _list_.id == "___": - explain("The slot to hold a list in the iteration is empty.

(no_iter_init-blank)
") - return True + return explain_r(message1, code1, label=tldr1) elif not data_state(_list_).was_type('list'): - explain("The variable {0!s} is in the list slot of the iteration but is not a list." - "

(no_iter_init)
".format(_list_.id)) - return True + return explain_r(message2.format(_list_.id), code2, label=tldr2) return False # TODO: We need to cover the different cases for these def wrong_iterator_not_list(): + message = ("The variable {0!s} has been set to something that is not a list but is placed " + "in the iteration block that must be a list.") + code = "iter_not_list" + tldr = "Iteration List is not list" + match = find_match("for ___ in _item_:\n pass") if match: _item_ = match["_item_"].astNode if not data_state(_item_).was_type('list'): - explain("The variable {0!s} has been set to something that is not a list but is placed in the " - "iteration block that must be a list.

(iter_not_list)
".format(_item_.id)) - return True + return explain_r(message.format(_item_.id), code, label=tldr) return False def missing_target_slot_empty(): + message = "You must fill in the empty slot in the iteration." + code = "target_empty" + tldr = "Missing Iteration Variable" match = find_match("for _item_ in ___:\n pass") if match: _item_ = match["_item_"].astNode if _item_.id == "___": - explain("You must fill in the empty slot in the iteration.

(target_empty)
") - return True + return explain_r(message, code, label=tldr) return False def list_not_initialized_on_run(): + message = "The list in your for loop has not been initialized." + code = "no_list_init" + tldr = "List Variable Uninitialized" match = find_match("for ___ in _item_:\n pass") if match: _item_ = match["_item_"][0].astNode if def_use_error(_item_): - explain("The list in your for loop has not been initialized

(no_list_init)
") - return True + return explain_r(message, code, label=tldr) return False def list_initialization_misplaced(): + message = "Initialization of {0!s} is a list but either in the wrong place or redefined" + code = "list_init_misplaced" + tldr = "Iterating over Non-list" match = find_match("for ___ in _item_:\n pass") if match: _item_ = match["_item_"][0].astNode if data_state(_item_).was_type('list') and def_use_error(_item_): - explain("Initialization of {0!s} is a list but either in the wrong place or redefined" - "

(list_init_misplaced)
".format(_item_.id)) - return True + return explain_r(message.format(_item_.id), code, label=tldr) return False def missing_for_slot_empty(): + message = "You must fill in the empty slot in the iteration." + code = "for_incomplete" + tldr = "Iteration Incomplete" match = find_match("for _item_ in _list_:\n pass") if match: _item_ = match["_item_"][0].astNode _list_ = match["_list_"][0].astNode if _item_.id == "___" or _list_.id == "___": - explain("You must fill in the empty slot in the iteration.

(for_incomplete)
") - return True + return explain_r(message, code, label=tldr) return False def wrong_target_reassigned(): + message = "The variable {0!s} has been reassigned. The iteration variable shouldn't be reassigned" + code = "target_reassign" + tldr = "Iteration Variable has been Reassigned" matches = find_matches("for _item_ in ___:\n" " __expr__") for match in matches: __expr__ = match["__expr__"] _item_ = match["_item_"][0] - submatches = __expr__.find_matches("{} = ___".format(_item_), ) + submatches = __expr__.find_matches("_item_ = ___") if submatches: - explain("The variable {0!s} has been reassigned. " - "The iteration variable shouldn't be reassigned" - "

(target_reassign)
".format(_item_)) - return True + return explain_r(message.format(_item_), code, label=tldr) return False diff --git a/src/lib/pedal/mistakes/iteration_context.py b/src/lib/pedal/mistakes/iteration_context.py index e4b63aba39..bb4c7df6c5 100644 --- a/src/lib/pedal/mistakes/iteration_context.py +++ b/src/lib/pedal/mistakes/iteration_context.py @@ -5,45 +5,52 @@ import pedal.mistakes.instructor_append as append_api from pedal.toolkit.utilities import * from pedal.sandbox.compatibility import get_output +from pedal.report.imperative import gently_r, explain_r # ################8.2 Start####################### def wrong_list_length_8_2(): + message = "You must have at least three pieces" + code = "list length_8.2" + tldr = "List too short" matches = find_matches("_list_ = __expr__") if matches: for match in matches: __expr__ = match["__expr__"] if __expr__.ast_name == "List" and len(__expr__.elts) < 3: - explain('You must have at least three pieces

(list length_8.2)
') - return True + return explain_r(message, code, label=tldr) return False def missing_list_initialization_8_2(): + message = ('You must set the variable shopping_cart' + 'to a list containing the prices of items in the shopping cart.') + code = "missing_list_init_8.2" + tldr = "Missing list initialization" matches = find_matches("shopping_cart = __expr__") for match in matches: __expr__ = match["__expr__"] if __expr__.ast_name == "List": return False - explain( - 'You must set the variable shopping_cart to a list containing the prices of items in the' - ' shopping cart.

(missing_list_init_8.2)
') - return True + return explain_r(message, code, label=tldr) def wrong_list_is_constant_8_2(): + message = 'You must set shoppping_cart to a list of values not to a single number.' + code = "list_is_const_8.2" + tldr = "Shopping Cart not set to list" matches = find_matches("shopping_cart = __expr__") for match in matches: __expr__ = match["__expr__"] if __expr__.ast_name == "Num": - explain( - 'You must set shoppping_cart to a list of values not to a single number.

' - '(list_is_const_8.2)
') - return True + return explain_r(message, code, label=tldr) return False def list_all_zeros_8_2(): + message = 'Try seeing what happens when you change the numbers in the list.' + code = 'default_list_8.2' + tldr = 'Use different numbers' std_ast = parse_program() lists = std_ast.find_all('List') is_all_zero = True @@ -55,8 +62,7 @@ def list_all_zeros_8_2(): if is_all_zero: break if is_all_zero: - explain('Try seeing what happens when you change the numbers in the list.

(default_list_8.2)
') - return True + return explain_r(message, code, label=tldr) return False @@ -65,6 +71,10 @@ def list_all_zeros_8_2(): # ################8.3 Start####################### def wrong_list_initialization_placement_8_3(): + message = ('The list of episode lengths (episode_length_list)' + ' must be initialized before the iteration which uses this list.') + code = "init_place_8.3" + tldr = "Wrong Initialization Placement" for_matches = find_matches("for ___ in ___:\n" " pass") init_matches = find_matches("episode_length_list = ___") @@ -73,14 +83,15 @@ def wrong_list_initialization_placement_8_3(): for_lineno = for_match.match_lineno for init_match in init_matches: if init_match.match_lineno > for_lineno: - explain( - 'The list of episode lengths (episode_length_list) must be initialized before the' - ' iteration which uses this list.

(init_place_8.3)
') - return True + return explain_r(message, code, label=tldr) return False def wrong_accumulator_initialization_placement_8_3(): + message = ('The variable to hold the sum of the episode lengths (sum_length) ' + 'must be initialized before the iteration which uses this variable.') + code = "accu_init_place_8.3" + tldr = "Accumulator initialization misplaced" for_matches = find_matches("for ___ in ___:" " pass") init_matches = find_matches("sum_length = 0") @@ -89,32 +100,31 @@ def wrong_accumulator_initialization_placement_8_3(): for_lineno = for_match.match_lineno for init_match in init_matches: if init_match.match_lineno > for_lineno: - explain( - 'The variable to hold the sum of the episode lengths (sum_length) must be ' - 'initialized before the iteration which uses this variable.

' - '(accu_init_place_8.3)
') - return True + return explain_r(message, code, label=tldr) return False def wrong_iteration_body_8_3(): + message = "The addition of each episode length to the total length is not in the correct place." + code = "iter_body_8.3" + tldr = "Accumulation Misplaced" match = find_match("for _item_ in _list_:\n" " sum_length = ___ + ___\n") if not match: - explain('The addition of each episode length to the total length is not in the correct place.

' - '(iter_body_8.3)
') - return True + return explain_r(message, code, label=tldr) return False def wrong_print_8_3(): + message = ('The output of the total length of time is not in the correct place. The total length of time should be' + ' output only once after the total length of time has been computed.') + code = "print_8.3" + tldr = "Print statement misplaced" match = find_match("for _item_ in _list_:\n" " pass\n" "print(_total_)") if not match: - explain('The output of the total length of time is not in the correct place. The total length of time should be' - ' output only once after the total length of time has been computed.

(print_8.3)
') - return True + return explain_r(message, code, label=tldr) return False @@ -123,29 +133,36 @@ def wrong_print_8_3(): # ################8.4 Start####################### def missing_target_slot_empty_8_4(): + message = 'You must fill in the empty slot in the iteration.' + code = 'target_empty_8.4' + tldr = "Iteration Variable Empty" matches = find_matches("for _item_ in pages_count_list:\n" " pass") if matches: for match in matches: _item_ = match["_item_"][0] if _item_.id == "___": - explain('You must fill in the empty slot in the iteration.

(target_empty_8.4)
') - return True + return explain_r(message, code, tldr) return False def missing_addition_slot_empty_8_4(): + message = "You must fill in the empty slot in the addition." + code = "add_empty_8.4" + tldr = "Addition Blank" matches = find_matches("sum_pages + _item_") if matches: for match in matches: _item_ = match["_item_"][0] if _item_.id == "___": - explain('You must fill in the empty slot in the addition.

(add_empty_8.4)
') - return True + return explain_r(message, code, label=tldr) return False def wrong_names_not_agree_8_4(): + message = "Each value of {0!s} must be added to {1!s}." + code = "name_agree_8.4" + tldr = "Iteration Variable and Accumulation Mismatch" matches = find_matches("for _item1_ in pages_count_list:\n" " sum_pages = sum_pages + _item2_") if matches: @@ -154,9 +171,7 @@ def wrong_names_not_agree_8_4(): _item1_ = match["_item1_"][0] _item2_ = match["_item2_"][0] if _item1_.id != _item2_.id: - explain('Each value of {0!s} must be added to {1!s}.

' - '(name_agree_8.4)
'.format(_item1_.id, _item2_.id)) - return True + return explain_r(message.format(_item1_.id, _item2_.id), code, label=tldr) return False @@ -178,10 +193,11 @@ def wrong_modifying_list_8_5(): Returns: """ + message = "Don't modify the list" + code = "mod_list_8.5" match = find_match("[20473, 27630, 17849, 19032, 16378]") if not match: - explain('Don\'t modify the list

(mod_list_8.5)
') - return True + return explain_r(message, code) return False @@ -196,10 +212,11 @@ def wrong_modifying_list_8_6(): explain('Don\'t modify the list

(mod_list_8.6)
') Returns: """ + message = "Don't modify the list" + code = "mod_list_8.6" match = find_match("_list_ = [2.9, 1.5, 2.3, 6.1]") if not match: - explain('Don\'t modify the list

(mod_list_8.6)
') - return True + return explain_r(message, code) return False @@ -218,18 +235,18 @@ def wrong_should_be_counting(): 'the list.

(not_count)
') Returns: """ + message = "This problem asks for the number of items in the list not the total of all the values in the list." + code = "not_count" + tldr = "Summing instead of counting" matches = find_matches("for _item_ in ___:\n" " __expr__") if matches: for match in matches: _item_ = match["_item_"][0] __expr__ = match["__expr__"] - submatches = __expr__.find_matches("___ = ___ + {}".format(_item_.id), ) + submatches = __expr__.find_matches("___ = ___ + _item_") if submatches: - explain( - 'This problem asks for the number of items in the list not the total of all the values in the list.' - '

(not_count)
') - return True + return explain_r(message, code, label=tldr) return False @@ -246,6 +263,9 @@ def wrong_should_be_summing(): explain('This problem asks for the total of all the values in the list not the number of items in ' 'the list.

(not_sum)
') """ + message = "This problem asks for the total of all the values in the list not the number of items in the list." + code = "not_sum" + tldr = "Counting instead of summing" matches = find_matches("for _item_ in ___:\n" " __expr__") if matches: @@ -253,9 +273,7 @@ def wrong_should_be_summing(): __expr__ = match["__expr__"] submatches = __expr__.find_matches("___ = 1 + ___", ) if submatches: - explain('This problem asks for the total of all the values in the list not the number of ' - 'items in the list.

(not_sum)
') - return True + return explain_r(message, code, label=tldr) return False @@ -277,13 +295,15 @@ def missing_addition_slot_empty(): return False Returns: """ + message = "You must fill in the empty slot in the addition." + code = "add_empty" + tldr = "Addition Blank" matches = find_matches("___ + _item_") if matches: for match in matches: _item_ = match["_item_"][0] if _item_.id == "___": - explain('You must fill in the empty slot in the addition.

(add_empty)
') - return True + return explain_r(message, code, tldr) return False @@ -303,25 +323,29 @@ def wrong_cannot_sum_list(): ' time.

(sum_list)
') Returns: """ + message = 'Addition can only be done with a single value at a time, not with an entire list at one' + code = "sum_list" + tldr = "Cannot Sum a List" matches = find_matches("for ___ in _list_ :\n" " __expr__") if matches: for match in matches: _list_ = match["_list_"][0] __expr__ = match["__expr__"] - submatches = __expr__.find_matches("___ = ___ + {}".format(_list_.id), ) + # submatches = __expr__.find_matches("___ = ___ + {}".format(_list_.id), ) + submatches = __expr__.find_matches("___ = ___ + _list_") if submatches: - explain('Addition can only be done with a single value at a time, not with an entire list at one' - ' time.

(sum_list)
') - return True + return explain_r(message, code, label=tldr) return False def missing_no_print(): + message = "Program does not output anything." + code = "no_print" + tldr = "Missing Output" prints = find_match('print(___)', cut=True) if not prints: - explain('Program does not output anything.

(no_print)
') - return True + return explain_r(message, code, label=tldr) return False @@ -347,6 +371,9 @@ def missing_counting_list(): explain('Count the total number of items in the list using iteration.

(miss_count_list)
') Returns: """ + message = 'Count the total number of items in the list using iteration.' + code = "miss_count_list" + tldr = "Missing Count in Iteration" matches = find_matches("for _item_ in ___:\n" " __expr__") if matches: @@ -355,9 +382,7 @@ def missing_counting_list(): submatches = __expr__.find_matches("_sum_ = _sum_ + 1", ) if submatches: return False - explain( - 'Count the total number of items in the list using iteration.

(miss_count_list)
') - return True + return explain_r(message, code, label=tldr) def missing_summing_list(): @@ -383,18 +408,19 @@ def missing_summing_list(): explain('Sum the total of all list elements using iteration.

(miss_sum_list)
') Returns: """ + message = 'Sum the total of all list elements using iteration.' + code = "miss_sum_list" + tldr = "Missing Sum in Iteration" matches = find_matches("for _item_ in ___:\n" " __expr__") if matches: for match in matches: _item_ = match["_item_"][0] __expr__ = match["__expr__"] - submatches = find_expr_sub_matches("_sum_ = _sum_ + {}" - .format(_item_.id), __expr__) + submatches = __expr__.find_matches("_sum_ = _sum_ + _item_") if submatches: return False - explain('Sum the total of all list elements using iteration.

(miss_sum_list)
') - return True + return explain_r(message, code, label=tldr) def missing_zero_initialization(): @@ -432,6 +458,11 @@ def missing_zero_initialization(): Returns: """ + message = ('The addition on the first iteration step is not correct because either the variable {0!s} ' + 'has not been initialized to an appropriate initial value ' + 'or it has not been placed in an appropriate location') + code = "miss_zero_init" + tldr = "Missing Initialization for Accumulator" matches01 = find_matches("for ___ in ___:\n" " __expr__") if matches01: @@ -445,28 +476,29 @@ def missing_zero_initialization(): "for ___ in ___:\n" " __expr__").format(_sum_.id)) if not matches02: - explain('The addition on the first iteration step is not correct because either the variable ' - '{0!s} has not been initialized to an appropriate initial value or it has ' - 'not been placed in an appropriate location

' - '(miss_zero_init)
'.format(_sum_.id)) - return True + return explain_r(message.format(_sum_.id), code, label=tldr) return False def wrong_printing_list(): + message = 'You should be printing a single value.' + code = "list_print" + tldr = "Printing in Iteration" matches = find_matches("for ___ in ___:\n" " __expr__") if matches: for match in matches: __expr__ = match["__expr__"] if __expr__.find_matches("print(___)", ): - explain('You should be printing a single value.

(list_print)
') - return True + return explain_r(message, code, label=tldr) return False # TODO: This might be reason to rethink letting instructor symbols map to multiple items def missing_average(): + message = "An average value is not computed.<" + code = "no_avg" + tldr = "Missing Computation" matches_missing = find_matches("for ___ in ___:\n" " pass\n" "__expr__") @@ -482,12 +514,15 @@ def missing_average(): if _total_.id != _count_.id: matches.append(match) if not len(matches) > 0: - explain('An average value is not computed.

(no_avg)
') - return True + return explain_r(message, code, label=tldr) return False def warning_average_in_iteration(): + message = ('An average value is best computed after the properties name {0!s}(total) and ' + '{1!s} are completely known rather than recomputing the average on each iteration.') + code = "avg_in_iter" + tldr = "Redundant Average Calculation" matches = find_matches("for ___ in ___:\n" " __expr__\n") if matches: @@ -500,15 +535,15 @@ def warning_average_in_iteration(): _count_ = submatch["_count_"][0] _average_ = submatch["_average_"][0] if _total_.id != _count_.id != _average_.id and _total_.id != _average_.id: - explain('An average value is best computed after the properties name {0!s}(total)' - ' and {1!s} are completely known rather than recomputing the average on' - ' each iteration.

(avg_in_iter)
'.format(_total_.id, _count_.id)) - return True + return explain_r(message.format(_total_.id, _count_.id), code, label=tldr) return False def wrong_average_denominator(): + message = "The average is not calculated correctly." + code = "avg_denom" + tldr = "Incorrect Average Calculation" matches = find_matches("for ___ in ___:\n" " __expr__\n" # where expr contains _count_ = _count_ + 1 "__expr2__") # where expr2 contains ___/_value_ @@ -526,12 +561,14 @@ def wrong_average_denominator(): _count_ = submatch["_count_"][0] _value_ = submatch02["_value_"][0] if _count_.id != _value_.id: - explain('The average is not calculated correctly.

(avg_denom)
') - return True + return explain_r(message, code, label=tldr) return False def wrong_average_numerator(): + message = "The average is not calculated correctly." + code = "avg_numer" + tldr = "Incorrect Average Calculation" matches = find_matches("for _item_ in ___:\n" " __expr__\n" # where expr contains _total_ = _total_ + 1 "__expr2__") # where expr2 contains _value_/___ @@ -540,21 +577,25 @@ def wrong_average_numerator(): __expr__ = match["__expr__"] __expr2__ = match["__expr2__"] _item_ = match["_item_"][0] - submatches = __expr__.find_matches("_total_ = _total_ + {}".format(_item_.id), ) - submatches02 = find_expr_sub_matches("_value_/___", __expr2__) + # TODO: In theory, we could merge these matches to match variables... + submatches = __expr__.find_matches("_total_ = _total_ + _item_") + # submatches02 = find_expr_sub_matches("_value_/___", __expr2__) + submatches02 = __expr2__.find_matches("_value_/___") if submatches and submatches02: for submatch in submatches: for submatch02 in submatches02: _value_ = submatch02["_value_"][0] _total_ = submatch["_total_"][0] if _total_.id != _value_.id: - explain('The average is not calculated correctly.

(avg_numer)
') - return True + return explain_r(message, code, label=tldr) return False # #######################AVERAGE END########################### def wrong_compare_list(): + message = "Each item in the list {0!s} must be compared one item at a time." + code = "comp_list" + tldr = "Not Comparing Each Item" matches = find_matches("for ___ in _list_:\n" " if __expr__:\n" " pass") @@ -563,23 +604,26 @@ def wrong_compare_list(): _list_ = match["_list_"][0] __expr__ = match["__expr__"] if __expr__.has(_list_.astNode): - explain('Each item in the list {0!s} must be compared one item at a time.

' - '(comp_list)
'.format(_list_.id)) - return True + return explain_r(message.format(_list_.id), code, label=tldr) return False def wrong_for_inside_if(): + message = "The iteration should not be inside the decision block." + code = "for_in_if" + tldr = "For inside if" match = find_match("if ___:\n" " for ___ in ___:\n" " pass") if match: - explain('The iteration should not be inside the decision block.

(for_in_if)
') - return True + return explain_r(message, code, label=tldr) return False def iterator_is_function(): + message = "You should make a variable for the list instead of using a function call for the list" + code = "iter_is_func" + tldr = "Using Function Call instead of List" std_ast = parse_program() for_loops = std_ast.find_all('For') # noinspection PyBroadException @@ -587,9 +631,7 @@ def iterator_is_function(): for loop in for_loops: list_prop = loop.iter if list_prop.ast_name == 'Call': - explain('You should make a variable for the list instead of using a function call for the list' - '

(iter_is_func)
') - return True + return explain_r(message, code, label=tldr) except Exception: return False return False @@ -597,117 +639,89 @@ def iterator_is_function(): # ##########################9.1 START############################ def wrong_list_initialization_9_1(): + message = "The list of rainfall amounts (rainfall_list) is not initialized properly." + code = "list_init_9.1" + tldr = "Incorrect List Initialization" match = find_match('rainfall_list = weather.get("Precipitation","Location","Blacksburg, VA")') if not match: - explain('The list of rainfall amounts (rainfall_list) is not initialized properly.' - '

(list_init_9.1)
') - return True + return explain_r(message, code, label=tldr) return False def wrong_accumulator_initialization_9_1(): + message = ("The variable to hold the total value of the rainfall amounts (rainfall_sum) " + "is not initialized properly.") + code = "accu_init_9.1" + tldr = "Incorrect Accumulation Variable initialization" match = find_match("rainfall_sum = 0") if not match: - explain('The variable to hold the total value of the rainfall amounts (rainfall_sum) is not ' - 'initialized properly.

(accu_init_9.1)
') - return True + return explain_r(message, code, label=tldr) return False def wrong_accumulation_9_1(): + message = "The addition of each rainfall amount to rainfall_sum is not correct." + code = "accu_9.1" + tldr = "Incorrect Accumulation Statement" matches = find_matches("rainfall_sum = _item_ + rainfall") if matches: for match in matches: _item_ = match["_item_"][0] if _item_.id != "rainfall_sum": - explain('The addition of each rainfall amount to rainfall_sum is not correct.' - '

(accu_9.1)
') - return True + return explain_r(message, code, label=tldr) return False def wrong_list_initialization_placement_9_1(): + message = ("The list of rainfall amount (rainfall_list) " + "must be initialized before the iteration that uses this list.") + code = "list_init_place_9.1" + tldr = "List initialization Misplaced or Missing" match = find_match("rainfall_list = ___\n" "for _item_ in _list_:\n" " pass") if not match: - explain('The list of rainfall amount (rainfall_list) must be initialized before the iteration that' - ' uses this list.

(list_init_place_9.1)
') - return True + return explain_r(message, code, label=tldr) return False -# TODO: Convert this to matching API def wrong_accumulator_initialization_placement_9_1(): - std_ast = parse_program() - assignments = std_ast.find_all('Assign') - loops = std_ast.find_all('For') - list_init = None - init_after_loop = False - for assignment in assignments: - if assignment.target.id == 'rainfall_sum': - list_init = assignment - break - for loop in loops: - if list_init is not None and loop.lineno > list_init.lineno: - init_after_loop = True - break - if list_init is None or not init_after_loop: - explain('The variable for the sum of all the rainfall amounts (rainfall_sum) must be initialized ' - 'before the iteration which uses this variable.

(accu_init_place_9.1)
') + message = ("The variable for the sum of all the rainfall amounts (rainfall_sum) " + "must be initialized before the iteration which uses this variable.") + code = "accu_init_place_9.1" + tldr = "Accumulator Initialization Misplaced or missing" + matches = find_matches("rainfall_sum = ___\n" + "for _item_ in _list_:\n" + " pass") + if not matches: + return explain_r(message, code, label=tldr) + return False -# TODO: Convert this to matching API def wrong_iteration_body_9_1(): - std_ast = parse_program() - loops = std_ast.find_all('For') - assignment_in_for = False - for loop in loops: - assignments = loop.find_all('Assign') - for assignment in assignments: - if assignment.target.id == 'rainfall_sum': - assignment_in_for = True - break - if assignment_in_for: - break - if not assignment_in_for: - explain('The addition of each rainfall amount to the total rainfall is not in the correct place.

' - '(iter_body_9.1)
') + message = "The addition of each rainfall amount to the total rainfall is not in the correct place." + code = "iter_body_9.1" + tldr = "Accumulation Statement Misplaced or Missing" + matches = find_matches("for _item_ in _list_:\n" + " rainfall_sum = ___") + if not matches: + return explain_r(message, code, label=tldr) + return False def wrong_print_9_1(): """ - - std_ast = parse_program() - for_loops = std_ast.find_all('For') - # has_for = len(for_loops) > 0 - for_loc = [] - wrong_print_placement = True - for loop in for_loops: - end_node = loop.next_tree - if end_node is not None: - for_loc.append(end_node.lineno) - calls = std_ast.find_all('Call') - for call in calls: - if call.func.id == 'print': - for loc in for_loc: - if call.func.lineno >= loc: - wrong_print_placement = False - break - if not wrong_print_placement: - break - if wrong_print_placement: - explain('The output of the total rainfall amount is not in the correct place. The total rainfall should be ' - 'output only once after the total rainfall has been computed.

(print_9.1)
') Returns: """ + message = ('The output of the total rainfall amount is not in the correct place. The total rainfall should be ' + 'output only once after the total rainfall has been computed.') + code = "print_9.1" + tldr = "Print Statement Misplaced or Missing" match = find_match("for _item_ in _list_:\n" " pass\n" "print(_total_)") if not match: - explain('The output of the total rainfall amount is not in the correct place. The total rainfall should be ' - 'output only once after the total rainfall has been computed.

(print_9.1)
') - return True + return explain_r(message, code, label=tldr) return False @@ -715,101 +729,71 @@ def wrong_print_9_1(): # ##########################9.2 START############################ -# TODO: Convert this to matching API def wrong_list_initialization_9_2(): - std_ast = parse_program() - assignments = std_ast.find_all('Assign') - has_call = False - for assignment in assignments: - if assignment.target.id == 'rainfall_list': - call = assignment.find_all('Call') - if len(call) == 1: - args = call[0].args - if len(args) == 3: - if args[0].s == 'Precipitation' and args[1].s == 'Location' and args[2].s == 'Blacksburg, VA': - has_call = True - break - if not has_call: - explain('The list of rainfall amounts (rainfall_list) is not initialized properly.' - '

(list_init_9.2)
') - return not has_call - - -# TODO: Convert this to matching API + message = "The list of rainfall amounts (rainfall_list) is not initialized properly." + code = "list_init_9.2" + tldr = "Incorrect List Initialization" + matches = find_matches('rainfall_list = weather.get("Precipitation","Location","Blacksburg, VA")') + if not matches: + return explain_r(message, code, label=tldr) + return False + + def wrong_accumulator_initialization_9_2(): - std_ast = parse_program() - assignments = std_ast.find_all('Assign') - has_assignment = False - for assignment in assignments: - if assignment.target.id == 'rainfall_count' and assignment.value.ast_name == 'Num': - if assignment.value.n == 0: - has_assignment = True - break - if not has_assignment: - explain('The variable to hold the total value of the rainfall amounts (rainfall_count) is not ' - 'initialized properly.

(accu_init_9.2)
') - return not has_assignment + message = ("The variable to hold the total value of the rainfall amounts " + "(rainfall_count) is not initialized properly.") + code = "accu_init_9.2" + tldr = "Incorrect Initialization" + if not find_matches("rainfall_count = 0"): + return explain_r(message, code, label=tldr) + return False def wrong_accumulation_9_2(): + message = ('The adding of another day with rainfall to the total ' + 'count of days with rainfall (rainfall_count) is not correct.') + code = "accu_9.2" + tldr = "Accumulation Statement Incorrect" matches = find_matches("rainfall_count = _item_ + 1") if matches: for match in matches: _item_ = match["_item_"][0] if _item_.id != "rainfall_count": - explain( - 'The adding of another day with rainfall to the total count of days with rainfall ' - '(rainfall_count) is not correct.

(accu_9.2)
') - return True + return explain_r(message, code, label=tldr) return False -# TODO: Convert this to matching API def wrong_list_initialization_placement_9_2(): - std_ast = parse_program() - assignments = std_ast.find_all('Assign') - loops = std_ast.find_all('For') - list_init = None - init_after_loop = False - for assignment in assignments: - if assignment.target.id == 'rainfall_list': - list_init = assignment - break - for loop in loops: - if list_init is not None and loop.lineno > list_init.lineno: - init_after_loop = True - break - if list_init is None or not init_after_loop: - explain('The list of rainfall amount (rainfall_list) must be initialized before the iteration that' - ' uses this list.

(list_init_place_9.2)
') - return True + message = ("The list of rainfall amount (rainfall_list) " + "must be initialized before the iteration that uses this list.") + code = "list_init_place_9.2" + tldr = "Incorrect List Initialization Placement" + matches = find_matches("rainfall_list = ___\n" + "for _item_ in _list_:\n" + " pass") + if not matches: + return explain_r(message, code, label=tldr) return False -# TODO: Convert this to matching API def wrong_accumulator_initialization_placement_9_2(): - std_ast = parse_program() - assignments = std_ast.find_all('Assign') - loops = std_ast.find_all('For') - list_init = None - init_after_loop = False - for assignment in assignments: - if assignment.target.id == 'rainfall_count': - list_init = assignment - break - if list_init is not None: - for loop in loops: - if loop.lineno > list_init.lineno: - init_after_loop = True - break - if list_init is None or not init_after_loop: - explain('The variable for the count of the number of days having rain (rainfall_count) must be ' - 'initialized before the iteration which uses this variable.

(accu_init_place_9.2)
') - return True + message = ("The variable for the count of the number of days having rain (rainfall_count) " + "must be initialized before the iteration which uses this variable.") + code = "accu_init_place_9.2" + tldr = "Accumulator Initialization Misplaced" + matches = find_matches("rainfall_count = ___\n" + "for _item_ in _list_:\n" + " pass") + if not matches: + return explain_r(message, code, label=tldr) return False def wrong_iteration_body_9_2(): + message = ("The test (if) to determine if a given amount " + "of rainfall is greater than (>) zero is not in the correct place.") + code = "iter_body_9.2" + tldr = "If statement misplaced" matches = find_matches("for _item_ in _list_:\n" " if __expr__:\n" " pass") @@ -818,12 +802,14 @@ def wrong_iteration_body_9_2(): __expr__ = match["__expr__"] if __expr__.numeric_logic_check(1, 'var > 0'): return False - explain('The test (if) to determine if a given amount of rainfall is greater than (>) zero is not in the ' - 'correct place.

(iter_body_9.2)
') - return True + return explain_r(message, code, label=tldr) def wrong_decision_body_9_2(): + message = ("The increase by 1 in the number of days having rainfall " + "(rainfall_count) is not in the correct place.") + code = "dec_body_9.2" + tldr = "Accumulation Statement Misplaced" matches = find_matches("if __expr__:\n" " rainfall_count = rainfall_count + 1") if matches: @@ -831,20 +817,19 @@ def wrong_decision_body_9_2(): __expr__ = match["__expr__"] if __expr__.numeric_logic_check(1, 'var > 0'): return False - explain('The increase by 1 in the number of days having rainfall (rainfall_count) is not in the ' - 'correct place.

(dec_body_9.2)
') - return True + return explain_r(message, code, label=tldr) def wrong_print_9_2(): + message = ("The output of the total number of days with rainfall is not in the correct place. The total number of " + "days should be output only once after the total number of days has been computed.") + code = "print_9.2" + tldr = "Misplaced Print Statement" match = find_match("for _item_ in _list_:\n" " pass\n" "print(_total_)") if not match: - explain('The output of the total number of days with rainfall is not in the correct place. The total number of ' - 'days should be output only once after the total number of days has been computed.

' - '(print_9.2)
') - return True + return explain_r(message, code, label=tldr) return False @@ -853,15 +838,16 @@ def wrong_print_9_2(): # ##########################9.6 START############################ def wrong_comparison_9_6(): + message = "In this problem you should be finding temperatures above 80 degrees." + code = "comp_9.6" + tldr = "Incorrect Comparison Statement" matches = find_matches("if __comp__:\n" " pass") if matches: for match in matches: __comp__ = match["__comp__"] if not __comp__.numeric_logic_check(1, 'var > 80'): - explain( - 'In this problem you should be finding temperatures above 80 degrees.

(comp_9.6)
') - return True + return explain_r(message, code, label=tldr) return False @@ -871,33 +857,25 @@ def wrong_comparison_9_6(): # ##########################10.2 START############################ def wrong_conversion_10_2(): """ - ''' - # code version 2 start - binops = __expr__.find_all('BinOp') - for binop in binops: - if binop.has(_target_.astNode) and binop.has(0.04) and binop.op_name == 'Mult': - return False - # code version 2 end + '''missing + for _target_ in ____ : + _target_ * 0.4 ''' Returns: """ + message = "The conversion of {0!s} to inches is either missing, incorrect, or misplaced." + code = "conv_10.2" + tldr = "Incorrect/Missing Conversion" matches = find_matches("for _target_ in ___:\n" " __expr__") - if matches: - for match in matches: - # code version 1 start - _target_ = match["_target_"][0] - __expr__ = match["__expr__"] - matches02 = __expr__.find_matches("_target_*0.04", ) - if matches02: - for match02 in matches02: - _target_02 = match02["_target_"][0] - if _target_.id == _target_02.id: - return False - # code version 1 end - explain('The conversion of {0!s} to inches is not correct.

' - '(conv_10.2)
'.format(_target_.id)) - return True + for match in matches: + # code version 1 start + _target_ = match["_target_"][0] + __expr__ = match["__expr__"] + matches02 = __expr__.find_matches("_target_*0.04".format(_target_.id)) + if matches02: + return False + return explain_r(message.format(_target_.id), code, label=tldr) return False @@ -906,6 +884,9 @@ def wrong_conversion_10_2(): # ##########################10.3 START############################ def wrong_filter_condition_10_3(): + message = "The condition used to filter the year when artists died is not correct." + code = "filt_10.3" + tldr = "Incorrect Condition" matches = find_matches("if __expr__:\n" " pass") if matches: @@ -913,8 +894,7 @@ def wrong_filter_condition_10_3(): __expr__ = match["__expr__"] if __expr__.numeric_logic_check(1, "var > 0") or __expr__.numeric_logic_check(1, "var != 0"): return False - explain('The condition used to filter the year when artists died is not correct.

(filt_10.3)
') - return True + return explain_r(message, code, label=tldr) return False @@ -923,6 +903,10 @@ def wrong_filter_condition_10_3(): # ##########################10.4 START############################ def wrong_and_filter_condition_10_4(): + message = ("The condition used to filter the temperatures " + "into the specified range of temperatures is not correct.") + code = "filt_and_10.4" + tldr = "Incorrect Condition Statement" matches = find_matches("for _temp_ in _list_:\n" " if __expr__:\n" " pass") @@ -932,14 +916,15 @@ def wrong_and_filter_condition_10_4(): __expr__ = match["__expr__"] if (__expr__.has(_temp_.astNode) and not __expr__.numeric_logic_check(1, "32 <= temp <= 50")): - explain( - 'The condition used to filter the temperatures into the specified range of temperatures is not ' - 'correct.

(filt_and_10.4)
') - return True + return explain_r(message, code, label=tldr) return False def wrong_nested_filter_condition_10_4(): + message = ("The decisions used to filter the temperatures into " + "the specified range of temperatures is not correct.") + code = "nest_filt_10.4" + tldr = "Incorrect Set of Decisions" matches = find_matches("for _temp_ in _list_:\n" " if __cond1__:\n" " if __cond2__:\n" @@ -949,21 +934,11 @@ def wrong_nested_filter_condition_10_4(): _temp_ = match["_temp_"][0].astNode __cond1__ = match["__cond1__"] __cond2__ = match["__cond2__"] - if not ( - __cond1__.has(_temp_) and __cond2__.has(_temp_) and ( - __cond1__.numeric_logic_check( - 1, - "32 <= temp") and __cond2__.numeric_logic_check( - 1, - "temp <= 50") or __cond2__.numeric_logic_check( - 1, - "32 <= temp") and __cond1__.numeric_logic_check( - 1, - "temp <= 50"))): - explain( - 'The decisions used to filter the temperatures into the specified range of temperatures is not ' - 'correct.

(nest_filt_10.4)
') - return True + if not (__cond1__.has(_temp_) and __cond2__.has(_temp_) and ( + __cond1__.numeric_logic_check(1, "32 <= temp") and __cond2__.numeric_logic_check(1, "temp <= 50") or + __cond2__.numeric_logic_check(1, "32 <= temp") and + __cond1__.numeric_logic_check(1, "temp <= 50"))): + return explain_r(message, code, label=tldr) return False @@ -972,20 +947,19 @@ def wrong_nested_filter_condition_10_4(): # ########################10.5 START############################### def wrong_conversion_problem_10_5(): + message = "The conversion from kilometers to miles is not correct." + code = "conv_10.5" + tldr = "Incorrect Conversion" matches = find_matches("for _item_ in ___:\n" " __expr__") if matches: for match in matches: _item_ = match["_item_"][0] __expr__ = match["__expr__"] - matches02 = __expr__.find_matches("_item_*0.62", ) + matches02 = __expr__.find_matches("_item_*0.62") if matches02: - for match02 in matches02: - _item_02 = match02["_item_"][0] - if _item_02.id == _item_.id: - return False - explain('The conversion from kilometers to miles is not correct.

(conv_10.5)
') - return True + return False + return explain_r(message, code, label=tldr) return False @@ -995,7 +969,9 @@ def wrong_filter_problem_atl1_10_5(): where the condition is not equivalent to _expr_ > 10 Returns: """ - + message = "You are not correctly filtering out values from the list." + code = "filt_alt1_10.5" + tldr = "Incorrect Filter Statement" matches = find_matches("for _item_ in ___:\n" " if __cond__:\n" " _list_.append(__expr__)") @@ -1004,20 +980,20 @@ def wrong_filter_problem_atl1_10_5(): _item_ = match["_item_"][0].astNode __cond__ = match["__cond__"] __expr__ = match["__expr__"] - matches02 = __expr__.find_matches("_item_*0.62", ) + # matches02 = __expr__.find_matches("{0!s}*0.62".format(_item_.id)) + matches02 = __expr__.find_matches("_item_*0.62") if matches02: for match02 in matches02: - _item_02 = match02["_item_"][0].astNode - if (_item_.id == _item_02.id and - __cond__.has(_item_) and + if (__cond__.has(_item_) and not __cond__.numeric_logic_check(0.1, "item > 16.1290322580645")): - explain('You are not correctly filtering out values from the list.

' - '(filt_alt1_10.5)
') - return True + return explain_r(message, code, label=tldr) return False def wrong_filter_problem_atl2_10_5(): + message = "You are not correctly filtering out values from the list." + code = "filt_alt2_10.5" + tldr = "Incorrect Filter Statement" matches = find_matches("for _item_ in ___:\n" " _miles_ = __expr__\n" " if __cond__:\n" @@ -1028,20 +1004,18 @@ def wrong_filter_problem_atl2_10_5(): __cond__ = match["__cond__"] _item_ = match["_item_"][0].astNode _miles_ = match["_miles_"][0].astNode - matches02 = __expr__.find_matches("_item_*0.62", ) - if matches02: - for match02 in matches02: - _item_02 = match02["_item_"][0].astNode - if _item_.id == _item_02.id: - if not (__cond__.has(_miles_) and - __cond__.numeric_logic_check(1, "_item_ > 10")): - explain('You are not correctly filtering out values from the list.

' - '(filt_alt2_10.5)
') - return True + matches02 = __expr__.find_matches("_item_*0.62") + for _ in matches02: + if not (__cond__.has(_miles_) and + __cond__.numeric_logic_check(1, "_item_ > 10")): + return explain_r(message, code, label=tldr) return False def wrong_append_problem_atl1_10_5(): + message = "You are not appending the correct values.

(app_alt1_10.5" + code = "app_alt1_10.5" + tldr = "Incorrect Value Appended" matches = find_matches("for _item_ in ___:\n" " if __cond__:\n" " _list_.append(__expr__)") @@ -1052,15 +1026,18 @@ def wrong_append_problem_atl1_10_5(): __expr__ = match["__expr__"] if (__cond__.numeric_logic_check(0.1, "item > 16.1290322580645") and __cond__.has(_item_)): - new_code = "{}*0.62".format(_item_.id) - matches02 = __expr__.find_matches(new_code, ) + # new_code = "{}*0.62".format(_item_.id) + new_code = "_item_*0.62" + matches02 = __expr__.find_matches(new_code) if not matches02: - explain('You are not appending the correct values.

(app_alt1_10.5)
') - return True + return explain_r(message, code, label=tldr) return False def wrong_append_problem_atl2_10_5(): + message = "You are not appending the correct values." + code = "app_alt2_10.5" + tldr = "Incorrect Value Appended" matches = find_matches("for _item_ in ___:\n" " _miles_ = _item_ * 0.62\n" " if __cond__:\n" @@ -1071,20 +1048,19 @@ def wrong_append_problem_atl2_10_5(): _var_ = match["_var_"][0] if __cond__.has(_miles_) and __cond__.numeric_logic_check(1, "_miles_ > 10"): if _var_.id != _miles_.id: - explain('You are not appending the correct values

(app_alt2_10.5)
') - return True + return explain_r(message, code, label=tldr) return False # ########################10.5 END############################### def wrong_debug_10_6(): """ - - - - + Should be on change feedback as opposed to on-run Returns: """ + message = "This is not one of the two changes needed. Undo the change and try again." + code = "debug_10.6" + tldr = "At least one unnecessary change" matches = find_matches('quakes = earthquakes.get("depth","(None)","")\n' 'quakes_in_miles = []\n' 'for quake in _list1_:\n' @@ -1102,11 +1078,13 @@ def wrong_debug_10_6(): name1 != "quakes_in_miles" and name2 != "quakes" and (name1 != "quake" or name2 != "quake")): return False - explain('This is not one of the two changes needed. Undo the change and try again.

(debug_10.6)
') - return True + return explain_r(message, code, label=tldr) def wrong_debug_10_7(): + message = "This is not the change needed. Undo the change and try again." + code = "debug_10.7" + tldr = "At least one unnecessary change" match = find_match("filtered_sentence_counts = []\n" "book_sentence_counts = classics.get('sentences','(None)','')\n" "for book in book_sentence_counts:\n" @@ -1119,13 +1097,16 @@ def wrong_debug_10_7(): "plt.show()\n") if not match: - explain('This is not the change needed. Undo the change and try again.

(debug_10.7)
') - return True + return explain_r(message, code, label=tldr) return False # ########################.....############################### def wrong_initialization_in_iteration(): + message = ("You only need to initialize {0!s} once. " + "Remember that statements in an iteration block happens multiple times") + code = "wrong_init_in_iter" + tldr = "Initialization in Iteration" matches = find_matches("for ___ in ___:\n" " __expr__") if matches: @@ -1137,20 +1118,17 @@ def wrong_initialization_in_iteration(): __expr__sub = submatch["__expr__"] _assign_ = submatch["_assign_"][0].astNode if len(__expr__sub.find_all("Name")) == 0: - explain( - 'You only need to initialize {0!s} once. Remember that statements in an ' - 'iteration block happens multiple times' - '

(wrong_init_in_iter)
'.format(_assign_.id)) - return True + return explain_r(message.format(_assign_.id), code, label=tldr) return False def wrong_duplicate_var_in_add(): + message = "You are adding the same variable twice; you need two different variables in your addition." + code = "dup_var" + tldr = "Duplicate Division" match = find_match("_item_ + _item_") if match: - explain('You are adding the same variable twice; you need two different variables in your addition.' - '

(dup_var)
') - return True + return explain_r(message, code, label=tldr) return False @@ -1159,16 +1137,16 @@ def plot_group_error(output=None): if output is None: output = get_output() if len(output) > 1: - explain('You should only be printing/plotting one thing!

(print_one)
') + explain_r('You should only be printing/plotting one thing!', "print_one", "Multiple Calls to print or plot") return True elif len(output) == 0: - explain('The algorithm is plotting an empty list. Check your logic.

(blank_plot)
') + explain_r('The algorithm is plotting an empty list. Check your logic.', 'blank_plot', "Blank Plot") return True elif not isinstance(output[0], list): - explain('You should be plotting, not printing!

(printing)
') + explain('You should be plotting, not printing!', 'printing', "Printing instead of Plotting") return True elif len(output[0]) != 1: - explain('You should only be plotting one thing!

(one_plot)
') + explain('You should only be plotting one thing!', 'one_plot', "Too Many Plots") return True @@ -1180,25 +1158,26 @@ def all_labels_present(): # TODO: make sure it's before the show, maybe check f plt.show() Returns: """ - + message = "Make sure you supply labels to all your axes and provide a title and then call show" + code = "labels_present" + tldr = "Missing Label(s)" match = find_match("plt.title(___)\nplt.show()") match02 = find_match("plt.xlabel(___)\nplt.show()") match03 = find_match("plt.ylabel(___)\nplt.show()") if (not match) or (not match02) or (not match03): - gently('Make sure you supply labels to all your axes and provide a title and then call show' - '

(labels_present)
') - return True + return gently_r(message, code, label=tldr) return False -# TODO: Convert this to matching API def hard_code_8_5(): # TODO: This one's weird + message = "Use iteration to calculate the sum." + code = "hard_code_8.5" + tldr = "Hard Coded Answer" match = find_matches("print(__num__)") if match: for m in match: __num__ = m["__num__"] if len(__num__.find_all("Num")) > 0: - explain("Use iteration to calculate the sum.

(hard_code_8.5)
") - return True + return explain_r(message, code, label=tldr) return False diff --git a/src/lib/pedal/plugins/cmd_line.py b/src/lib/pedal/plugins/cmd_line.py new file mode 100644 index 0000000000..029ab48689 --- /dev/null +++ b/src/lib/pedal/plugins/cmd_line.py @@ -0,0 +1,129 @@ +from pedal.cait.cait_api import * +from pedal.report import MAIN_REPORT +from pedal.source import set_source +from pedal.tifa import tifa_analysis +from pedal.sandbox.compatibility import * +import importlib.util +import numpy as np +import pandas as pd + +import sys +import os +import re + + +def setup(student_code, input_vals): + """ + Clears MAIN_REPORT, sets source, and runs TIFA + Args: + student_code: String of student code + input_vals: list of inputs to be queued. + Returns: + None + """ + MAIN_REPORT.clear() + set_source(student_code) + tifa_analysis() + if len(input_vals) != 0: + queue_input(*input_vals) + run_student(True) + return get_sandbox() + + +def process(file, module, ins_code, report): + student_code1 = file.read() + setup(student_code1, inputs) # setup returns a sandbox object + module.loader.exec_module(ins_code) + feedback = report.feedback + return feedback + + +p2Flag = True +secrets = False +assignment_id = -1 +if __name__ == "__main__": + # processing args + feedback_code = sys.argv[1] + code_dir = sys.argv[2] + flag = sys.argv[3] + if flag == "-p2": + p2Flag = True + inputs = sys.argv[4:] + elif flag == "-secrets": + p2Flag = True + secrets = True + inputs = sys.argv[4:] + else: + inputs = sys.argv[3:] +else: + # feedback_suffix = "prequiz.py" + # assignment_id = 409 + feedback_suffix = "postquiz1.py" + assignment_id = 410 # Pass Count = 1 + # feedback_suffix = "postquiz2-1.py" + # assignment_id = 411 # Pass Count = 2 + # feedback_suffix = "postquiz2-2.py" + # assignment_id = 412 + # feedback_code = ("C:/Users/User/Documents/Luke_Stuff/Research/ComputationalThinking/DictionaryUnit/test_cmd/" + # "ins_script.py") + feedback_code = ("C:/Users/User/Documents/Luke_Stuff/Research/ComputationalThinking/" + "DictionaryUnit/ID/Assessments/") + feedback_code += feedback_suffix + + code_dir = ("C:/Users/User/Documents/Luke_Stuff/Research/ComputationalThinking/ResearchData/" + "ComputationalThinking/Tests/results/") + code_dir += "Spring2019/DictionaryData/cs1014_spr2019_log-v1/" + # code_dir += "Fall2018/DictionaryData/exported-f18/" + p2Flag = True + secrets = True + inputs = [] + +# Grabbing instructor feedback code +ins_mod = re.match("(?:.*/)(.*).py", feedback_code)[1] +my_spec = importlib.util.spec_from_file_location(ins_mod, feedback_code) +foo = importlib.util.module_from_spec(my_spec) + +# preparing to process + + +# Grabbing student files +if p2Flag: + student_feedback = [] + pass_count = 0 + main_table = "MainTable" + if secrets: + main_table += "-2" + main_table += ".csv" + df = pd.read_csv(code_dir + main_table) + code_states = code_dir + "CodeStates/" + for index, row in df.iterrows(): + scan = True + if assignment_id >= 0: + if secrets: + if int(row["AssignmentID"]) != assignment_id: + scan = False + if scan: + code_f = code_states + str(int(row['CodeStateID'])) + "/__main__.py" + # check assignment and find corresponding answer key in DictionaryUnit/ID/Assessments/... + with open(code_f) as code: + feedback_result = process(code, my_spec, foo, MAIN_REPORT) + # df.at[index, 'InterventionMessage'] = feedback_result + student_feedback.append(feedback_result) + score = 0.0 + if not feedback_result: + score = 1.0 + pass_count += 1 + df.at[index, 'Score'] = score + df.to_csv(code_dir + "processed.csv", index=False) +else: + student_feedback = [] + print(os.getcwd()) + student_files_base = os.listdir(code_dir) + student_files = [] + for code_name in student_files_base: + student_files.append(code_dir + code_name) + for code_name in student_files: + with open(code_name) as code_f: + student_feedback.append(process(code_f, my_spec, foo, MAIN_REPORT)) + if __name__ == "__main__": + print(student_feedback) diff --git a/src/lib/pedal/report/imperative.py b/src/lib/pedal/report/imperative.py index 28b88eb225..a3672d76bc 100644 --- a/src/lib/pedal/report/imperative.py +++ b/src/lib/pedal/report/imperative.py @@ -3,8 +3,8 @@ Uses a global report object (MAIN_REPORT). """ -__all__ = ['set_success', 'compliment', 'give_partial', 'explain', - 'gently', 'hide_correctness', 'suppress', 'log', 'debug', +__all__ = ['set_success', 'compliment', 'give_partial', 'explain', 'explain_r', + 'gently', 'gently_r', 'hide_correctness', 'suppress', 'log', 'debug', 'clear_report', 'get_all_feedback', 'MAIN_REPORT'] from pedal.report.report import Report @@ -56,6 +56,16 @@ def gently(message, line=None, label='explain'): MAIN_REPORT.gently(message, line, label=label) +def gently_r(message, code, line=None, label="explain"): + gently(message + "

({})

".format(code), line, label=label) + return message + + +def explain_r(message, code, priority='medium', line=None, label="explain"): + explain(message + "

({})

".format(code), priority, line, label=label) + return message + + def hide_correctness(): MAIN_REPORT.hide_correctness() diff --git a/src/lib/pedal/tifa/messages.py b/src/lib/pedal/tifa/messages.py index 655b248e07..7ba869c3fe 100644 --- a/src/lib/pedal/tifa/messages.py +++ b/src/lib/pedal/tifa/messages.py @@ -97,7 +97,7 @@ def _format_message(issue, data): "was used. One of the times that you gave {name} " "a value was incorrect." ).format(line=data['position']['line'], name=data['name']) - elif issue == 'Iterating over non-list': + elif issue == 'Iterating over Non-list': if 'name' not in data or data['name'] is None: expression = "expression" else: diff --git a/src/lib/pedal/tifa/tifa.py b/src/lib/pedal/tifa/tifa.py index baccb032db..ac7729d421 100644 --- a/src/lib/pedal/tifa/tifa.py +++ b/src/lib/pedal/tifa/tifa.py @@ -40,7 +40,6 @@ def __init__(self, python_3=True, report=None): report = MAIN_REPORT self.report = report self._initialize_report() - self.PYTHON_3 = python_3 def _initialize_report(self): """ @@ -119,7 +118,7 @@ def process_code(self, code, filename="__main__"): self.report['tifa']['error'] = error self.report.attach('tifa_error', category='Analyzer', tool='TIFA', mistake={ - 'message': "Could not process code", + 'message': "Could not process code: "+str(error), 'error': error }) return self.report['tifa'] @@ -333,6 +332,12 @@ def _walk_target(self, target, type): if potential_name is not None and result is None: result = potential_name return result + + def visit_AnnAssign(self, node): + """ + TODO: Implement! + """ + pass def visit_Assign(self, node): """ @@ -529,7 +534,7 @@ def _visit_collection_loop(self, node): "position": self.locate(iter)}) if not isinstance(iter_type, INDEXABLE_TYPES): - self.report_issue("Iterating over non-list", + self.report_issue("Iterating over Non-list", {"name": iter_list_name, "position": self.locate(iter)}) diff --git a/src/lib/pedal/toolkit/functions.py b/src/lib/pedal/toolkit/functions.py index a4a92da1e6..a9a4b83a9e 100644 --- a/src/lib/pedal/toolkit/functions.py +++ b/src/lib/pedal/toolkit/functions.py @@ -1,5 +1,5 @@ from pedal.cait.cait_api import parse_program -from pedal.report.imperative import gently, explain, MAIN_REPORT +from pedal.report.imperative import gently, explain, gently_r, explain_r, MAIN_REPORT from pedal.sandbox import compatibility import ast @@ -69,25 +69,25 @@ def match_signature(name, length, *parameters): if a_def._name == name: found_length = len(a_def.args.args) if found_length < length: - gently("The function named {} has fewer parameters ({}) than expected ({}). " - "

(insuff_args)".format(name, found_length, length)) + gently_r("The function named {} has fewer parameters ({}) " + "than expected ({}). ".format(name, found_length, length), "insuff_args") elif found_length > length: - gently("The function named {} has more parameters ({}) than expected ({}). " - "

(excess_args)".format(name, found_length, length)) + gently_r("The function named {} has more parameters ({}) " + "than expected ({}). ".format(name, found_length, length), "excess_args") elif parameters: for parameter, arg in zip(parameters, a_def.args.args): arg_name = get_arg_name(arg) if arg_name != parameter: - gently("Error in definition of {}. Expected a parameter named {}, instead " - "found {}.

(name_missing)".format(name, parameter, arg_name)) + gently_r("Error in definition of {}. Expected a parameter named {}, " + "instead found {}.".format(name, parameter, arg_name), "name_missing") return None else: return a_def else: return a_def else: - gently("No function named {name} was found." - "

(missing_func_{name})".format(name=name)) + gently_r("No function named {name} was found.".format(name=name), + "missing_func_{name}".format(name=name)) return None @@ -157,14 +157,13 @@ def output_test(name, *tests): else: result = ("I ran your function {} on some new arguments, and it gave the wrong output " "{}/{} times.".format(name, len(tests) - success_count, len(tests)) + result) - gently(result + "") + gently_r(result + "", "wrong_output") return None else: - gently("You defined {}, but did not define it as a function." - "

(not_func_def)".format(name)) + gently_r("You defined {}, but did not define it as a function.".format(name), "not_func_def") return None else: - gently("The function {} was not defined.

(no_func_def)".format(name)) + gently_r("The function {} was not defined.".format(name), "no_func_def") return None @@ -215,7 +214,7 @@ def unit_test(name, *tests): else: result = "I ran your function {} on some new arguments, " \ "and it failed {}/{} tests.".format(name, len(tests) - success_count, len(tests)) + result - gently(result + "") + gently_r(result + "", "tests_failed") return None else: gently("You defined {}, but did not define it as a function.".format(name)) @@ -302,14 +301,15 @@ def check_coverage(report=None): else: return False, 1 + def ensure_coverage(percentage=.5, destructive=False, report=None): - ''' + """ Note that this avoids destroying the current sandbox instance stored on the report, if there is one present. Args: destructive (bool): Whether or not to remove the sandbox. - ''' + """ if report is None: report = MAIN_REPORT student_code = report['source']['code'] @@ -320,6 +320,7 @@ def ensure_coverage(percentage=.5, destructive=False, report=None): return False return True + def ensure_cisc108_tests(test_count, report=None): student = compatibility.get_student_data() if 'assert_equal' not in student.data: diff --git a/src/lib/pedal/toolkit/plotting.py b/src/lib/pedal/toolkit/plotting.py index a1a932db57..e1263a700b 100644 --- a/src/lib/pedal/toolkit/plotting.py +++ b/src/lib/pedal/toolkit/plotting.py @@ -1,6 +1,6 @@ from pedal.toolkit.utilities import function_is_called from pedal.cait.cait_api import parse_program, def_use_error -from pedal.report.imperative import explain, gently +from pedal.report.imperative import gently, explain_r, gently_r from pedal.sandbox import compatibility PLOT_LABEL = {'plot': 'line plot', @@ -12,9 +12,15 @@ def prevent_incorrect_plt(): ast = parse_program() plts = [n for n in ast.find_all("Name") if n.id == 'plt'] if plts and def_use_error(plts[0]): - explain("You have imported the matplotlib.pyplot module, " - "but you did not rename it to plt using " - "import matplotlib.pyplot as plt.

(plt_rename_err)", 'verifier') + # TODO: I converted this to the explain_r function, but I wasn't sure about the priority thing ~Luke Gusukuma + # explain("You have imported the matplotlib.pyplot module, " + # "but you did not rename it to plt using " + # "import matplotlib.pyplot as plt.

(plt_rename_err)

", 'verifier') + explain_r("You have imported the matplotlib.pyplot module, " + "but you did not rename it to plt using " + "import matplotlib.pyplot as plt.", + "plt_rename_err", + priority='verifier') return True matplotlib_names = ['plot', 'hist', 'scatter', 'title', 'xlabel', 'ylabel', 'show'] @@ -22,13 +28,22 @@ def prevent_incorrect_plt(): for n in ast.find_all("Name"): if n.id == name: if def_use_error(n): - explain(("You have attempted to use the MatPlotLib " - "function named {0}. However, you " - "imported MatPlotLib in a way that does not " - "allow you to use the function directly. I " - "recommend you use plt.{0} instead, " - "after you use import matplotlib.pyplot as " - "plt.

(plt_wrong_import)").format(name), 'verifier') + # explain(("You have attempted to use the MatPlotLib " + # "function named {0}. However, you " + # "imported MatPlotLib in a way that does not " + # "allow you to use the function directly. I " + # "recommend you use plt.{0} instead, " + # "after you use import matplotlib.pyplot as " + # "plt.

(plt_wrong_import)

").format(name), 'verifier') + explain_r(("You have attempted to use the MatPlotLib " + "function named {0}. However, you " + "imported MatPlotLib in a way that does not " + "allow you to use the function directly. I " + "recommend you use plt.{0} instead, " + "after you use import matplotlib.pyplot as " + "plt.").format(name), + "plt_wrong_import", + priority='verifier') return True return False @@ -37,20 +52,20 @@ def ensure_correct_plot(function_name): for a_plot, label in PLOT_LABEL.items(): if function_name == a_plot: if not function_is_called(function_name): - gently("You are not calling the {func_name} function." - "

(no_{func_name}_call)".format(func_name=function_name)) + gently_r("You are not calling the {func_name} function.".format(func_name=function_name), + "no_{func_name}_call".format(func_name=function_name)) return True elif function_is_called(a_plot): - gently("You have called the {} function, which makes a {}." - "

(wrong_plt)".format(a_plot, label)) + gently_r("You have called the {} function, which makes a {}.".format(a_plot, label), + "wrong_plt") return True return False def ensure_show(): if not function_is_called("show"): - gently("You have not called show function, which " - "actually creates the graph.

(no_show)") + gently_r("You have not called show function, which " + "actually creates the graph.", "no_show") return True return False @@ -117,13 +132,52 @@ def check_for_plot(plt_type, data): plt_type = GRAPH_TYPES.get(plt_type, plt_type) if type_found and data_found: return ("You have created a {}, but it does not have the right data. That data appears to have been plotted " - "in another graph.

(other_plt)".format(plt_type)) + "in another graph.

(other_plt)

".format(plt_type)) elif type_found: return ("You have created a {}, but it does not have the right data." - "

(wrong_plt_data)".format(plt_type)) + "

(wrong_plt_data)

".format(plt_type)) elif data_found: return ("You have plotted the right data, but you appear to have not plotted it as a {}." - "

(wrong_plt_type)".format(plt_type)) + "

(wrong_plt_type)

".format(plt_type)) else: return ("You have not created a {} with the proper data." - "

(no_plt)".format(plt_type)) + "

(no_plt)

".format(plt_type)) + + +def check_for_plot_r(plt_type, data): + """ + Returns any errors found for this plot type and data. + In other words, if it returns False, the plot was found correctly. + """ + if plt_type == 'plot': + plt_type = 'line' + type_found = False + data_found = False + for graph in compatibility.get_plots(): + for a_plot in graph['data']: + data_found_here = compare_data(plt_type, data, a_plot) + if a_plot['type'] == plt_type and data_found_here: + return False + if a_plot['type'] == plt_type: + type_found = True + if data_found_here: + data_found = True + plt_type = GRAPH_TYPES.get(plt_type, plt_type) + if type_found and data_found: + return {"message": "You have created a {}, but it does not have the right data. " + "That data appears to have been plotted in another graph.".format(plt_type), + "code": "other_plt", + "label": "Plotting Another Graph"} + elif type_found: + return {"message": "You have created a {}, but it does not have the right data.".format(plt_type), + "code": "wrong_plt_data", + "label": "Plot Data Incorrect"} + elif data_found: + return {"message": "You have plotted the right data, but you appear to have not plotted it as a {}.".format(plt_type), + "code": "wrong_plt_type", + "label": "Wrong Plot Type" + } + else: + return {"message": "You have not created a {} with the proper data.".format(plt_type), + "code": "no_plt", + "label": "Missing Plot"} diff --git a/src/lib/pedal/toolkit/printing.py b/src/lib/pedal/toolkit/printing.py index f045f4c506..a0191fc2dc 100644 --- a/src/lib/pedal/toolkit/printing.py +++ b/src/lib/pedal/toolkit/printing.py @@ -1,22 +1,22 @@ -from pedal.report.imperative import gently +from pedal.report.imperative import gently_r from pedal.toolkit.utilities import find_function_calls, is_top_level def ensure_prints(count): prints = find_function_calls('print') if not prints: - gently("You are not using the print function!

(no_print)") + gently_r("You are not using the print function!", "no_print", label="Missing Print") return False elif len(prints) > count: - gently("You are printing too many times!

(multiple_print)") + gently_r("You are printing too many times!", "multiple_print", label="Too Many Prints") return False elif len(prints) < count: - gently("You are not printing enough things!

(too_few_print)") + gently_r("You are not printing enough things!", "too_few_print", label="Too Few Prints") return False else: for a_print in prints: if not is_top_level(a_print): - gently("You have a print function that is not at the top level. That is incorrect for this problem!" - "

(not_top_level_print)") + gently_r("You have a print function that is not at the top level. That is incorrect for this problem!", + "not_top_level_print", label="Non-Top Level Print") return False return prints diff --git a/src/lib/pedal/toolkit/utilities.py b/src/lib/pedal/toolkit/utilities.py index d0d0443846..b7c850c003 100644 --- a/src/lib/pedal/toolkit/utilities.py +++ b/src/lib/pedal/toolkit/utilities.py @@ -1,6 +1,6 @@ from pedal.cait.cait_api import parse_program from pedal.report.imperative import gently, explain - +from pedal.report.imperative import gently_r, explain_r def is_top_level(ast_node): ast = parse_program() @@ -20,7 +20,7 @@ def no_nested_function_definitions(): if not is_top_level(a_def): gently("You have defined a function inside of another block. For instance, you may have placed it inside " "another function definition, or inside of a loop. Do not nest your function definition!" - "

(nest_func)") + "

(nest_func)

") return False return True @@ -98,34 +98,41 @@ def prevent_unused_result(): pass elif a_call.func.attr in ('replace', 'strip', 'lstrip', 'rstrip'): gently("Remember! You cannot modify a string directly. Instead, you should assign the result back " - "to the string variable.

(str_mutate)") + "to the string variable.

(str_mutate)

") def prevent_builtin_usage(function_names): + message = "You cannot use the builtin function {}." + code = "builtin_use" + label = "Builtin Usage" # Prevent direction calls ast = parse_program() all_calls = ast.find_all('Call') for a_call in all_calls: if a_call.func.ast_name == 'Name': if a_call.func.id in function_names: - explain("You cannot use the builtin function {}.

(builtin_use)".format( - a_call.func.id)) + explain_r(message.format(a_call.func.id), code, label=label) return a_call.func.id return None + # TODO: UGLY HACK. This is to avoid muted=False kwargs in the following # functions. Apparently skulpt doesn't support this syntax. muted = False + def prevent_literal(*literals): - ''' + """ Confirms that the literal is not in the code, returning False if it is not. Args: *literals (Any...): A series of literal values to look for. Returns: AstNode or False: If the literal is found in the code, then it is returned. - ''' + """ + message = "Do not use the literal value {} in your code." + code = "hard_code" + label = "Hard Coding" ast = parse_program() str_values = [s.s for s in ast.find_all("Str")] num_values = [n.n for n in ast.find_all("Num")] @@ -135,32 +142,33 @@ def prevent_literal(*literals): if isinstance(literal, (int, float)): if literal in num_values: if not muted: - explain("Do not use the literal value {} in your code." - "

(hard_code)".format(repr(literal))) + explain_r(message.format(repr(literal)), code, label=label) return literal elif isinstance(literal, str): if literal in str_values: if not muted: - explain("Do not use the literal value {} in your code." - "

(hard_code)".format(repr(literal))) + explain_r(message.format(repr(literal)), code, label=label) return literal elif literal in (True, False, None): if str(literal) in name_values: if not muted: - explain("Do not use the literal value {} in your code." - "

(hard_code)".format(repr(literal))) + explain_r(message.format(repr(literal)), code, label=label) return literal return False + def ensure_literal(*literals): - ''' + """ Confirms that the literal IS in the code, returning False if it is not. Args: *literals (Any...): A series of literal values to look for. Returns: AstNode or False: If the literal is found in the code, then it is returned. - ''' + """ + message = "You need the literal value {} in your code." + code = "missing_literal" + label = "Missing Literal" ast = parse_program() str_values = [s.s for s in ast.find_all("Str")] num_values = [n.n for n in ast.find_all("Num")] @@ -170,29 +178,28 @@ def ensure_literal(*literals): if literal in (True, False, None): if str(literal) not in name_values: if not muted: - explain("You need the literal value {} in your code." - "

(missing_literal)".format(repr(literal))) + explain_r(message.format(repr(literal)), code, label=label) return True elif isinstance(literal, (int, float)): if literal not in num_values: if not muted: - explain("You need the literal value {} in your code." - "

(missing_literal)".format(repr(literal))) + explain_r(message.format(repr(literal)), code, label=label) return literal elif isinstance(literal, str): if literal not in str_values: if not muted: - explain("You need the literal value {} in your code." - "

(missing_literal)".format(repr(literal))) + explain_r(message.format(repr(literal)), code, label=label) return literal return False def prevent_advanced_iteration(): + message = "You should not use a while loop to solve this problem." + code = "while_usage" + label = "Usage of while" ast = parse_program() if ast.find_all('While'): - explain("You should not use a while loop to solve this problem." - "

(while_usage)") + explain_r(message, code, label=label) prevent_builtin_usage(['sum', 'map', 'filter', 'reduce', 'len', 'max', 'min', 'max', 'sorted', 'all', 'any', 'getattr', 'setattr', 'eval', 'exec', 'iter']) @@ -235,20 +242,26 @@ def prevent_advanced_iteration(): def ensure_operation(op_name, root=None): + message = "You are not using the {} operator.".format(op_name) + code = "missing_op" + label = "Missing {} Operator".format(op_name) if root is None: root = parse_program() result = find_operation(op_name, root) if not result: - gently("You are not using the {} operator.

(missing_op)".format(op_name)) + gently_r(message, code, label) return result def prevent_operation(op_name, root=None): + message = "You may not use the {} operator.".format(op_name) + code = "bad_op" + label = "Bad Operator".format(op_name) if root is None: root = parse_program() result = find_operation(op_name, root) if result: - gently("You may not use the {} operator.

(bad_op)".format(op_name)) + gently_r(message, code, label=label) return result @@ -326,12 +339,13 @@ def ensure_assignment(variable_name, type=None, value=None, root=None): elif assign.value.ast_name == type: return assign if potentials and potentials[0].value.ast_name not in ("Str", "Bool", "Num", "List", "Tuple"): - explain(("You needed to assign a literal value to {variable}, but you " - "created an expression instead.").format(variable=variable_name)) + explain_r(("You needed to assign a literal value to {variable}, but you " + "created an expression instead.").format(variable=variable_name), "exp_vs_lit", + label="Expression Instead of Literal") elif type is None: - explain(("You have not properly assigned anything to the variable " - "{variable}.").format(variable=variable_name)) + explain_r(("You have not properly assigned anything to the variable " + "{variable}.").format(variable=variable_name), "no_assign", label="No Proper Assignment") else: - explain(("You have not assigned a {type} to the variable {variable}." - "").format(type=type, variable=variable_name)) + explain_r(("You have not assigned a {type} to the variable {variable}." + "").format(type=type, variable=variable_name), "type_assign", label="Unexpected Variable Type") return False diff --git a/src/lib/re.js b/src/lib/re.js index 6e919ab62b..f587c7a4da 100644 --- a/src/lib/re.js +++ b/src/lib/re.js @@ -203,14 +203,13 @@ var $builtinmodule = function (name) { $loc.groups = new Sk.builtin.func(function (self) { var _groups = self.thematch.v.slice(1); - return new Sk.builtin.tuple(_groups) + return new Sk.builtin.tuple(_groups); }); $loc.group = new Sk.builtin.func(function (self, grpnum) { if (grpnum === undefined) { grpnum = 0; - } - else { + } else { grpnum = Sk.builtin.asnum$(grpnum); } if (grpnum >= self.thematch.v.length) { @@ -235,8 +234,7 @@ var $builtinmodule = function (name) { if (str.match(patt)) { matches = str.slice(0, -1).match(re); - } - else { + } else { matches = str.match(re); } retval = new Sk.builtin.list(); @@ -319,8 +317,7 @@ var $builtinmodule = function (name) { self.re = pattern; if (flags === undefined) { self.flags = 0; - } - else { + } else { self.flags = flags; } }); @@ -343,9 +340,9 @@ var $builtinmodule = function (name) { var end = endpos == undefined ? str.length : Sk.ffi.remapToJs(endpos); if (start == "^") { - start = str.indexOf('\n') + 1; + start = str.indexOf("\n") + 1; } - if (end == Sk.builtin.none.none$) { + if (end == Sk.builtin.none.none$ || end === null) { end = str.length; } return Sk.ffi.remapToPy(str.substring(start, end)); diff --git a/src/lib/turtle.js b/src/lib/turtle.js index 1b001344f0..ba57b4d8fc 100644 --- a/src/lib/turtle.js +++ b/src/lib/turtle.js @@ -2288,6 +2288,7 @@ var $builtinmodule = function (name) { addModuleMethod(Screen, _module, "$delay", getScreen); addModuleMethod(Screen, _module, "$window_width", getScreen); addModuleMethod(Screen, _module, "$window_height", getScreen); + addModuleMethod(Screen, _module, "$bgpic", getScreen); _module.Turtle = Sk.misceval.buildClass(_module, TurtleWrapper, "Turtle", []); _module.Screen = Sk.misceval.buildClass(_module, ScreenWrapper, "Screen", []); diff --git a/src/print.js b/src/print.js index e8c76d42ef..b504626a89 100644 --- a/src/print.js +++ b/src/print.js @@ -78,6 +78,7 @@ var print_f = function function_print(kwa) { } // ToDo: // cpython print function may receive another flush kwarg that flushes the output stream immediatelly + return Sk.builtin.none.none$; }; print_f.co_kwargs = true; diff --git a/test/test_regex.py b/test/test_regex.py new file mode 100644 index 0000000000..04cdcbdd35 --- /dev/null +++ b/test/test_regex.py @@ -0,0 +1,5 @@ +import re + +exp_match = re.compile("__.*__") +print(exp_match) +assert exp_match.match("__expr__") is not None \ No newline at end of file From a55d93b35d9e7692bf9f5adcd1d1eb8550e11698 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 22 Aug 2019 08:00:27 -0400 Subject: [PATCH 19/68] Add latest skulpt features, correct bugs in compilation, and rename some modules --- deleteme.js | Bin 0 -> 58840 bytes example/stupid.html | 40 +++++++++++++++++ src/abstract.js | 12 ++++++ src/ast.js | 25 +++++++++-- src/builtin.js | 3 +- src/builtindict.js | 18 ++++++-- src/compile.js | 39 ++++++++++++----- src/env.js | 14 ++++++ src/filter.js | 67 +++++++++++++++++++++++++++++ src/float.js | 7 ++- src/function.js | 3 +- src/lib/ast.js | 5 ++- src/lib/pedal/tifa/tifa.py | 11 +++-- src/lib/turtle.js | 2 +- src/list.js | 17 ++++++++ src/long.js | 8 +++- src/main.js | 3 ++ src/map.js | 85 +++++++++++++++++++++++++++++++++++++ src/misceval.js | 32 ++++++++++---- src/object.js | 4 +- src/pgen/parser/main.py | 1 + src/str.js | 19 ++++++++- src/type.js | 15 +++++++ src/zip.js | 63 +++++++++++++++++++++++++++ test/test_ast2.py | 4 ++ test/test_names.py | 10 +++++ test/test_tifa.py | 27 ++++++++---- 27 files changed, 479 insertions(+), 55 deletions(-) create mode 100644 deleteme.js create mode 100644 example/stupid.html create mode 100644 src/filter.js create mode 100644 src/map.js create mode 100644 src/zip.js create mode 100644 test/test_names.py diff --git a/deleteme.js b/deleteme.js new file mode 100644 index 0000000000000000000000000000000000000000..f2b8de28929c85d304c039ab3a1e807accc3de7a GIT binary patch literal 58840 zcmdU2YmZ#ValM}dDVY>WELWxpntwe> zom+d@kE*_1eP@#0W{qyus^MCsJ9eIj>``7R8OuN&=>BaQ@^isZyZ#|N~U&#M=reDf` zkEb7m_CulmojiR$-I+d>r*9pK2k$A8$KOPfOL@90GJ(fSp}s#IO?TxPXy0+D@5-|m z@~wyR*(Y;9TKo#T6*uHeEymI+n-KE+Fv-mJqmDt zEmWQg*Ds|%7~%UemapbfcrLAu=jbROH~H4Re|61GIoLValX+1Iv3j1k(s?~U=}NGU zm&%FN_DII;v5e1iX&$K%e!w*R!(#Rjita4pn{bQN?P99&&zc zyL-LTt*oj6mg@fWMS$wX^mkWQeeO|Bs9>ekC%ZBym$O#abs&9FJzXk zuIF8~?~OCg{^vZdZG0eiAm0yjZ+?Hh+Wsg~*jZ3~y^!_z!dZ<#wEhf})4vIgXOqtd z82PNprLXz-WxekSh4XnN|4aIGA+Lz!|1Qt3f^^U-~W&ifxU+fQ_f$wbJ<4^@_A?0-oeyI1%8|J_LKK(VQlVxBmF^d?#))^ zLJ*ri;it{H>thL}W1CwS*#FlucK%v|9Kf?nSy`|bX(gq#?&(e+T_}q;-hHItx3wpG zBIBjV2hDgex4`I^^UC;EX2Rmh!2q6+S8kzxE+gh!wx5~xa~MDp@?e{+RLLn>Hjhu= zvmkx8@BpzNq3;fEw`J`9JkL51=kxb}ee?cPur)6Qsc$=djU&lB7k3W_FoD_ze+ga~ z>@xcDL}UgY|Ev7>MMG~610DrOiEZhZ>Dsfa|8M{=NSSDwygu^s=#OhWJ#xj!G+yxZ zUN*dx!)Zi|u}gk7W0z;Y52v_Jb4)OY*wX=>+GE}K{7KIu%~3!zy>GSk?44tMj;6Lw z2K1{k^L(Wh(3+(w`+aoE?oC{@U8Vp!WZ8oI0|SXW#_(6*H&py;C~=4aUp$! z{n&H+=X+pB0SzVZy=)}7#?#nX9Xbl=Xri5ai%LBju{o01MsMH3^|{H8p0av9jcc*` z&DgCy7pbj0t$FI}wyzg`-DSV_#J_`|eJ*m9d*U?9=`(f|jsu#Wc-3gxie?-KJo=n! z9@o&l+Cv8~ju-HPYbB?u@e8i{Ry5-{pwT&1wWy@3ue@>Xd)6h@h=n^I($3|$M(bU# zFMVCj`%+^q$Etqraq8=2&MEgi$t8d0ap>!0&LPLZ#94}-fH*Z}2_xaS&N zf7bFo8PsG(N0jSZ($~Eb|G#jl^!1Bg zwywOdr@y##`kF*bLCsWzw>>)zXa+n+`03O_e~eB8I>G&kN)TBc(KkJ-Od&;!0u$v7W8~+to_rE@v%NzKm@KP9Rr`EKh}0k+EHe2Oxn82 zF|j+N$T{!M^b=W)*uB`*=5Via*{B__U<8Zm#;a*J{+=Mg8mbsI85g^`xzfUJu$j z_WC+Di5#grvw!x3%#nux$h7F^TC`4w@$I;#Bri@QN*8mU!0T&2Gh(~JFRn!;ZqZ9g zXS2yFJQus5p7|%!CnC@8b<8VC_?bM_)k!+}xvPD*^cZj&Qhqy0(VCUE<>VTx+dOK& zos=) zdSj-&eYr>{n)uK zPl;^iR{2ia;o03ZB#q-FWrlC98JsDPkn7iv!-LN_PUU>+sH5_Oy@AE5Ju~o&V-5RN zM$8g-k9r)7y!8=Bj8}5Diu@o%U}2*f_wud5{t7fegcA8IIEVkaL4p0iUYp}rw;JjE zO!Bc|_cWUt_7l9213+(qpJ0}DijZO+*CWE`*IC~=y-3bj-4DJ7y&aKXPim1nZpUjR zgN$_9D*Z_q_o*xYI3ASp|F>B~YjVhPnq{;Ah(+DD@5J?z9M~?!-PCe1cNHE1{HBz> zy6+&+!NSggJX>v&C84(>UMWfauFb5&Xs!1UN^W0DzjZz7EdrHjB%W)L^k4&gQR&V_ zTQvXe{yZ;;321g6VvX=gdBZ?`_OWjPaT(2rzpv-R(4_rY({S>zGu@K-_Md|pS+_** zym45Mb7iax=jh0_cp><~oH3oLC13y5^tD9hHAa71&Os2TFA)gQSS$CM%sH<5w%qC; zQ~Fmj!idR1!YB&fzV@dS3Hd5VLq5w95k2?%IPTA}kIB=g|A(QNSf3I2>IyX4uL(CNGF7?kF%W*%DW9ixRH93Mgz0#h09?+Ux z!IDF%M|T7*a2M-bTEz}z!Q$T8(;2_`RP(}tY`7)Gr?CH6#Xu26jB5*cxPjGC|HInv zkt{>@oUK1++*w-bH$lO+eJ<^oXTLqm8Oc3lKkIgz7W_!iVst-AdCH#Ogau#9oum}4 zcFY#B-g1kJKWB{P*1qiV z2Kbb`N9(Jq4Q%_m&^m9e(&ujVuKheoQ^%W1S?YIvIo4}^$?Yb5*+}McYn+IbiW=MT zR7yPZP;?|Wx|W=02`cfy4Osn=9BN9Jtp0%$pN?cqlTzLOf3^CNZ11|S_3Iyt)jt_H z#jpEdt^Uad(GJh*YkoFja@1yvq$eBTx@SR7Ht^}x%Jr;1YWJ*-A1H?pY4syH^7YE# zYn9eo{YbVvY1FMZXDLs!5zk{WUV+md*-rbN9_0rMQqN$h|q{fxRoDHrT!ARU&v_ z8}B0mgxI~lk0=mMUhoPPI}jN_t*56##q&TS+4$^{Mes-1~Po? zIyf6ZD$d%6s^Ef|8_z4?n!;!;Ywf)i6C2!F z0&po=q1-0c_qV`n+{bw+JhaYO$thVh%V*?90q5hfAVr=x0d6C)wA#nGJAflz1sCL0 z#m|AMZM-WPuiyecsP3{mpPi`Ri&K<5cd7hLp26h# z#8`W6jL<#vw<0(4ta$glv^k*AMgxu$@D85NWs$j>&JuMWfipvJvZsXL(ngJCE*PN} z|GFRgF3<-~FTkjK9zZ$WX-T$1&wT%ObYx@i=?^(X1 zu$+K7JwGMwLDJ5^_ATuuJT86B*B!TaD-A0kveQ2h?T+X-G2ZI~Jk4j$I*8=(`}k|Q zu-?yJ7_n2vKWE>zyiM}9icDZk`dUnSjQiJ8=M|6;^TP^5^vAok4~~5eeEtXCb^0-rd(kk5AGT96@*B>q1CaAE&Iso7H7QXDAXVJ%fAhBfMY4dlbN z^u2WLS+rS`>rGVa*KLI4;~GC6Hv^v$EnbRnfmU3nIokaQ7wE?I*dk3Nqt*LXThDB@ zJ;>PB3GNlKV{7MdswZRjsPJlUqB1dL?ptQcPETE>EbWQSN57wCbu&}e_rN0gpyWMP znHb(=-=-(pxwqJw3qM{kk`KDcuAQ=4G#beltxv-1m|1I{`nv7uMI?`t{Yq7bm%Mu1 z$U^mEZxs4AIPt2{QX?52)ix3k#d4~7?1FdkJgRNH@Tlgo3*H>`sP;8Fmo<-F(7sx# zk!;J7YQ(}tG9q)TeoR7FN~_|d*O$Is&AHaT6ZzH8Jx+bgnsdrMPjbnhc^vvWnR7_~ zCh4aq9`(M}P1OCmAL;#M!}jFVt*m0~@gCFUGExGI*@}-uvI3LTyMi>h@%c*jP}oPk zH8G2ktiW51IEvOF_M6(^ zVLDr0onXXg`dUNW=B(SAMWEBykF*k(DfL%7F<HwQ3=|jzfe?B+o2Gj`Lmq2#>y}nRulAeT#$Dxux7N?vPoV$l z+PzKtPPD(@isW|j)OEa;FMiLgt<5+MXlME0yKP%Xzdl(7>}au@Q5?{}t?oy%vvQhw zo`F)%pLq1!I%ikHQl?eBPANvJZ}r+|MI+f%$;Z&WW2yogKgGDa5Z~dvvZX$hI0eh1 zc{V@ZI;EJEww|+pb(_cV%E(ap>jK-YQ;PGRcyza3$M|+z%j+I;MsL1ODdg%SNG)Gl z;q=;7@0TNvwoWOy_w9Vmov#_0!D+_Z%6|vtHp-Afuce2hnYpT8?K-94+4s>~Jgv_U zdVS1l(aYnDkl#jn)!KP0weJtqTt7~oQjAdBtEgqiAG z%8<*gzj~e5_bB~dNU$Cg>>WeUTaO8v)5$2`jv#+ssTB0bwV$)C^!W;-g-R!WqzQ3< zkNdbjlV8cbzU_&gjq7kuyPnyUBWB-;>lZn&P5X8m8&C_>&TqqAeJjhjeYdjIF@#c* z`rRW}j&a?l93{6eWku!pW)w?DZq9h4v>qo^x{p{foQK*W+jMtLd$!s+rK@UT&3EYvp%Yx5}>y zs_TYvUgXKAJ-xlpi;-QA%>E2=&p>(ezIqzEUG#=e7tO#U4zaE6=&57LU~e{~Pj01O z=Z|HL`fD=y$ggKdM>6q9lPzdlS&6;&olVfFZ6`~6V(sjyqt|OP?%3;O(`!pvN>ab; z+0iu_d6c8%b`y4VBv+48vHiGqR4XlR%Z`p@9J2px8U67!9jH0-TXd*|ydLQ&Q#|JO zpRzA{AYOo;mHl4+!Vc|ORd+(OR`7gh{(bpN@#*+W1v^qPT`N=ATVr@fYc4((F9XPk z+S05@f&B(5SKyZUjxs@CjyE|T!Nl9uEq_>uFB&U*`*p?hs z1M4U%?%ZQ0_T>Ni*RF$)YqBvZMSOLA_(~?wpSTtB)LR;8j-z%RM6yGv51h3Rb>jnl z8;=rjP4#vqdxHIc7xcyFv4<*c0av=6)sZYyZ@lSMa#Y)`$}oE41EUs426AWgZLE?B zc=d6FJlJA4K32&DeB$UrK6Q4Yo|oXaPcZAKgYfzLztXRd;Mc!jN@c40_ue2w97ntx zAD|FN1Zo?zx#E>fuqTLP0!ORNhtjoYSAQg5l@!^QS#7C5?Mf!-!MNsQUBRD%hw4=_ z0o8c457w`p;{aN5P3B1C9z}bE9`2uhIWKSnN*Q8ljN|)|4Qfde^^{@0t3BX1Qn-{V}xbNH{(Q#sYit#j@TzDtWk&IFD=LsZZVD${4rh!U``H0nVz}WXS#fx(G zd8wQ>|Ir)$zE+hu#j$ADaX`=d*00Q>&8hlz96+_Lja)N~km?BJRQ);*plY?2)YuZ6 zEmgmc1E}^bT4`BwSz8zw%T`^w3z2j2KWEoSq~c+Unc4VU&NKI?JWhSqu}67pc1>_W*Y*%FUW-$NjMw%gugk7V9@dk&l`#4&-|M!zS%#|EGAlho1^^1UBW z?t2Fuf3`%m{+(0M@B0O$%eM55(|EP_@Zi(;3rK%?w+&ixohHrxZX0y_9z5yQNP}

K6eB!#g$sU@r*1iuUh--0qZfE83sw#j(U$;HIh~%8IUz=4`fQ)@jPrPch z^s6d>Y8wfNIyu!yRRvIOuS!m_MOPDe(rJV>txO;_dLlZf97%M>txO$`J1Glo_N&zRyR@iZz-hr z^L=^!S&#RaCNnyr{d!wP7O;Z*I!vv*l|?wT-a(3Zl<{WJ?Q5}zijfS$5~n$mcC41N z1%2Lay`!}+vC-BzK;B4SkLZEwtavpJK&P)IWpwuKWbZqHPG1+Wm(yw+Tqi!I{<<$C zyYOMX8V8W7zmAEJDwB)y76r>!#lggTt+lHa%Sr9JeIPb>WG9L zsio|scntvbvafNilMQ^kuSfJhk_}#40|5N`){y+TUP?ViwDZSa0|5Q(+iLccZ60wd z;}~`J>R0m^Tuf;!?#YQi&iwHUcsgD<*~j@3vX-x=KS+e<$y~wvQ{#h+N$Elo8 zy<=8=Y1IG#zrN4H{;)0gn~A&ien%<^dg~*O7$Y?R;HQnF!BPzXP+X4!+o%Bm3D#qR zy)g%ReLsdPqHZ+@k1?qop;X_(2mQFGg|SqnQmR%+1K({?L9{-9M*LCT6@J; zZ0i~T=-IdqlbSyh)KS};xUDA#wrO7ruLb_iAH_FOyig0&zT0?c?&hG^ac!jxY?CbY z9=nvJe)r6gBa&@KzI9u2`%=bbyEOoCcV#_JsB|Bz2EgH51K@1_|E4tnFb?a{DtUzu z=9LSZhf^n5XymYPoF0h%h2_JYGURqWkTrfe{bv5opX57tviL2l*6AH}FH;r~@aHo{ z@X|Y+x2IoAy_QGroj>$(GX$-*JyJ)qUeSDABl3}h$V(L^NvGBN*qGPo11{tDAs@>> z;El!Rk*vO24xhip=13l2O`EShY))BxcDA;GhvV36?{nif5yt%;_IjJxJd$}w{%mg^ zn@2MHC>2{`&g1zG(xqUK=G4P};AR@`9b(`A-HakT(Kz$-o7Rg}{6WBjdX zv@L92lS9aHXkjxvnDyA4<46>tPi9NCjL;*QiR>X;4-J;;i1K@_RuL@u%*PHwugOy+ zKOZ4q&QU5<)47z@`DXf^Sh#!F_7cx{UmoavF3;b{r*MXa`yv*>*W@-X%V!aMq-rT? zv-RhUI9(z*qSf0#&2a=jHeM}cS?@P)55XbFdcWIG`eK}zHUuQIbA|qL&IJmEmD)`)M6bBEYZoFswq(yO@{1K~b6p!SMl5*W9 zg3d8V}te4S*eqaY=&*6-RF2}WyEoMdch`!Nzow;sJdE{dzqxg9?o5aem(xiPmMo(ogwrr zj_vh{48Gk*?RPTA(KN-8OW^_wiU=R}CCJyy^QY>0Upk@pvQ;42 zxW}z)4!P26>kNVKxQAWx?PpejOWXq3b%5X5Y-P38^Tt{GPA zT=R7|iO8&9X9!eBba$!F5NO3UnIn;V6zvg?Yp=zZNF`DGt)&cY@6%Qf{PphTOh+OC zSYv(s)-%C7;)iNB-fQ>t4kvt88zNF*t&B*KpNV<3Lm`RPo`@t^5hIe+@!gg*R>%6% kX#U-ZG$}&SlEZ3cxdSMr!YP + + + + + + + +

+ + + + + diff --git a/src/abstract.js b/src/abstract.js index 97fbcf242a..4f23daa270 100644 --- a/src/abstract.js +++ b/src/abstract.js @@ -55,6 +55,10 @@ Sk.abstr.boNameToSlotFuncLhs_ = function (obj, name) { return obj.nb$subtract ? obj.nb$subtract : obj["__sub__"]; case "Mult": return obj.nb$multiply ? obj.nb$multiply : obj["__mul__"]; + case "MatMult": + if (Sk.__future__.python3) { + return obj.tp$matmul ? obj.tp$matmul : obj["__matmul__"]; + } case "Div": return obj.nb$divide ? obj.nb$divide : obj["__div__"]; case "FloorDiv": @@ -90,6 +94,10 @@ Sk.abstr.boNameToSlotFuncRhs_ = function (obj, name) { return obj.nb$reflected_subtract ? obj.nb$reflected_subtract : obj["__rsub__"]; case "Mult": return obj.nb$reflected_multiply ? obj.nb$reflected_multiply : obj["__rmul__"]; + case "MatMult": + if (Sk.__future__.python3) { + return obj.tp$reflected_matmul ? obj.tp$reflected_matmul : obj["__rmatmul__"]; + } case "Div": return obj.nb$reflected_divide ? obj.nb$reflected_divide : obj["__rdiv__"]; case "FloorDiv": @@ -121,6 +129,10 @@ Sk.abstr.iboNameToSlotFunc_ = function (obj, name) { return obj.nb$inplace_subtract ? obj.nb$inplace_subtract : obj["__isub__"]; case "Mult": return obj.nb$inplace_multiply ? obj.nb$inplace_multiply : obj["__imul__"]; + case "MatMult": + if (Sk.__future__.python3) { + return obj.tp$inplace_matmul ? obj.tp$inplace_matmul : obj["__imatmul__"]; + } case "Div": return obj.nb$inplace_divide ? obj.nb$inplace_divide : obj["__idiv__"]; case "FloorDiv": diff --git a/src/ast.js b/src/ast.js index f3fab2ae1f..dbdff6266b 100644 --- a/src/ast.js +++ b/src/ast.js @@ -247,8 +247,21 @@ var operatorMap = {}; operatorMap[TOK.T_PERCENT] = Sk.astnodes.Mod; }()); +Sk.setupOperators = function (py3) { + if (py3) { + operatorMap[TOK.T_AT] = Sk.astnodes.MatMult; + } else { + if (operatorMap[TOK.T_AT]) { + delete operatorMap[TOK.T_AT]; + } + } +} +Sk.exportSymbol("Sk.setupOperators", Sk.setupOperators); + function getOperator (n) { - Sk.asserts.assert(operatorMap[n.type] !== undefined, "Operator missing from operatorMap"); + if (operatorMap[n.type] === undefined) { + throw new Sk.builtin.SyntaxError("invalid syntax", n.type, n.lineno); + } return operatorMap[n.type]; } @@ -2033,6 +2046,10 @@ function astForAugassign (c, n) { return Sk.astnodes.Pow; } return Sk.astnodes.Mult; + case "@": + if (Sk.__future__.python3) { + return Sk.astnodes.MatMult; + } default: Sk.asserts.fail("invalid augassign"); } @@ -2282,14 +2299,14 @@ function parsestr (c, s) { else if (c === "x") { d0 = s.charAt(++i); d1 = s.charAt(++i); - ret += String.fromCharCode(parseInt(d0 + d1, 16)); + ret += encodeUtf8(String.fromCharCode(parseInt(d0 + d1, 16))); } else if (c === "u" || c === "U") { d0 = s.charAt(++i); d1 = s.charAt(++i); d2 = s.charAt(++i); d3 = s.charAt(++i); - ret += String.fromCharCode(parseInt(d0 + d1, 16), parseInt(d2 + d3, 16)); + ret += encodeUtf8(String.fromCharCode(parseInt(d0 + d1, 16), parseInt(d2 + d3, 16))); } else { // Leave it alone @@ -2301,7 +2318,7 @@ function parsestr (c, s) { ret += c; } } - return ret; + return decodeUtf8(ret); }; //print("parsestr", s); diff --git a/src/builtin.js b/src/builtin.js index c6b7ddc7ee..0d60ae1ea4 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -536,10 +536,9 @@ Sk.builtin.int2str_ = function helper_ (x, radix, prefix) { var str = ""; if (x instanceof Sk.builtin.lng) { suffix = ""; - if (radix !== 2) { + if (radix !== 2 && (!(Sk.__future__.python3))) { suffix = "L"; } - str = x.str$(radix, false); if (x.nb$isnegative()) { return new Sk.builtin.str("-" + prefix + str + suffix); diff --git a/src/builtindict.js b/src/builtindict.js index 75252edbcb..de76eb80a0 100644 --- a/src/builtindict.js +++ b/src/builtindict.js @@ -8,7 +8,6 @@ Sk.builtins = { "min" : new Sk.builtin.func(Sk.builtin.min), "max" : new Sk.builtin.func(Sk.builtin.max), "sum" : new Sk.builtin.func(Sk.builtin.sum), - "zip" : new Sk.builtin.func(Sk.builtin.zip), "abs" : new Sk.builtin.func(Sk.builtin.abs), "fabs" : new Sk.builtin.func(Sk.builtin.fabs), "ord" : new Sk.builtin.func(Sk.builtin.ord), @@ -25,8 +24,6 @@ Sk.builtins = { "hasattr" : new Sk.builtin.func(Sk.builtin.hasattr), "id" : new Sk.builtin.func(Sk.builtin.id), - "map" : new Sk.builtin.func(Sk.builtin.map), - "filter" : new Sk.builtin.func(Sk.builtin.filter), "reduce" : new Sk.builtin.func(Sk.builtin.reduce), "sorted" : new Sk.builtin.func(Sk.builtin.sorted), "any" : new Sk.builtin.func(Sk.builtin.any), @@ -111,4 +108,17 @@ Sk.builtins = { "coerce" : Sk.builtin.coerce, "intern" : Sk.builtin.intern }; -Sk.exportSymbol("Sk.builtins", Sk.builtins); + +Sk.setupObjects = function (py3) { + if (py3) { + Sk.builtins["filter"] = Sk.builtin.filter_; + Sk.builtins["map"] = Sk.builtin.map_; + Sk.builtins["zip"] = Sk.builtin.zip_; + } else { + Sk.builtins["filter"] = new Sk.builtin.func(Sk.builtin.filter); + Sk.builtins["map"] = new Sk.builtin.func(Sk.builtin.map); + Sk.builtins["zip"] = new Sk.builtin.func(Sk.builtin.zip); + } +}; +Sk.exportSymbol("Sk.setupObjects", Sk.setupObjects); +Sk.exportSymbol("Sk.builtins", Sk.builtins); \ No newline at end of file diff --git a/src/compile.js b/src/compile.js index 5d26acb58d..28d3c6272d 100644 --- a/src/compile.js +++ b/src/compile.js @@ -391,21 +391,34 @@ Compiler.prototype.cunpackstarstoarray = function(elts, permitEndOnly) { if (!elts || elts.length == 0) { return "[]"; } - let arr = this._gr("unpack", "[]"); + let hasStars = false; + // If there are no stars, we have a nice fast path here for (let elt of elts) { if (permitEndOnly && hasStars) { throw new Sk.builtin.SyntaxError("Extended argument unpacking is not permitted in Python 2"); } - if (elt.constructor !== Sk.astnodes.Starred) { - out(arr,".push(",this.vexpr(elt),");"); - } else { - out("$ret = Sk.misceval.iterFor(Sk.abstr.iter(",this.vexpr(elt.value),"), function(e) { ",arr,".push(e); });"); - this._checkSuspension(); + if (elt.constructor === Sk.astnodes.Starred) { hasStars = true; } } - return arr; + + if (hasStars) { + // Slow path + let arr = this._gr("unpack", "[]"); + for (let elt of elts) { + if (elt.constructor !== Sk.astnodes.Starred) { + out(arr,".push(",this.vexpr(elt),");"); + } else { + out("$ret = Sk.misceval.iterFor(Sk.abstr.iter(",this.vexpr(elt.value),"), function(e) { ",arr,".push(e); });"); + this._checkSuspension(); + } + } + return arr; + } else { + // Fast path + return "[" + elts.map((expr) => this.vexpr(expr)).join(",") + "]"; + } } Compiler.prototype.ctuplelistorset = function(e, data, tuporlist) { @@ -1955,17 +1968,20 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal for (i = 0; args && i < args.args.length; ++i) { id = args.args[i].arg; if (this.isCell(id)) { - this.u.varDeclsCode += "$cell." + id.v + "=" + id.v + ";"; + let mangled = fixReservedNames(mangleName(this.u.private_, id).v); + this.u.varDeclsCode += "$cell." + mangled + "=" + mangled + ";"; } } for (i = 0; args && args.kwonlyargs && i < args.kwonlyargs.length; ++i) { id = args.kwonlyargs[i].arg; if (this.isCell(id)) { - this.u.varDeclsCode += "$cell." + id.v + "=" + id.v + ";"; + let mangled = fixReservedNames(mangleName(this.u.private_, id).v); + this.u.varDeclsCode += "$cell." + mangled + "=" + mangled + ";"; } } if (vararg && this.isCell(vararg.arg)) { - this.u.varDeclsCode += "$cell." + vararg.arg.v + "=" + vararg.arg.v + ";"; + let mangled = fixReservedNames(mangleName(this.u.private_, vararg.arg).v); + this.u.varDeclsCode += "$cell." + mangled + "=" + mangled + ";"; } // @@ -2449,6 +2465,7 @@ var D_CELLVARS = 2; Compiler.prototype.isCell = function (name) { var mangled = mangleName(this.u.private_, name).v; + mangled = fixReservedNames(mangled); var scope = this.u.ste.getScope(mangled); var dict = null; return scope === Sk.SYMTAB_CONSTS.CELL; @@ -2513,7 +2530,7 @@ Compiler.prototype.nameop = function (name, ctx, dataToStore) { // have to do this after looking it up in the scope mangled = fixReservedWords(mangled); - //print("mangled", mangled); + //console.log("mangled", mangled); // TODO TODO TODO todo; import * at global scope failing here Sk.asserts.assert(scope || name.v.charAt(1) === "_"); diff --git a/src/env.js b/src/env.js index 9365445d4c..f2ea21f60d 100644 --- a/src/env.js +++ b/src/env.js @@ -210,10 +210,21 @@ Sk.configure = function (options) { Sk.switch_version("clear$", Sk.__future__.list_clear); Sk.builtin.lng.tp$name = Sk.__future__.no_long_type ? "int" : "long"; + + Sk.setupOperators(Sk.__future__.python3); + Sk.setupDunderMethods(Sk.__future__.python3); + Sk.setupObjects(Sk.__future__.python3); }; Sk.exportSymbol("Sk.configure", Sk.configure); +/* +* Replaceable handler for uncaught exceptions +*/ +Sk.uncaughtException = function(err) { + throw err; +}; + /* * Replaceable handler for uncaught exceptions */ @@ -349,6 +360,9 @@ Sk.setup_method_mappings = function () { Sk.builtin.tuple_iter_, Sk.builtin.generator, Sk.builtin.enumerate, + Sk.builtin.filter_, + Sk.builtin.zip_, + Sk.builtin.map_, Sk.builtin.iterator], 2: "next", 3: "__next__" diff --git a/src/filter.js b/src/filter.js new file mode 100644 index 0000000000..539de068c6 --- /dev/null +++ b/src/filter.js @@ -0,0 +1,67 @@ +/** + * @constructor + * @param {Object} iterable + * @extends Sk.builtin.object + */ + +Sk.builtin.filter_ = function filter_ (fun, iterable) { + var it; + var getitem; + var result; + var item; + Sk.builtin.pyCheckArgsLen("filter_", arguments.length, 2, 2); + + if (!(this instanceof Sk.builtin.filter_)) { + return new Sk.builtin.filter_(fun, iterable); + } + //don't need to check if iterable is an iterable because Sk.abstr.iter will throw the right error msg + it = Sk.abstr.iter(iterable); + getitem = function (item) { + if (fun === Sk.builtin.none.none$) { + result = item; + } else { + result = Sk.misceval.callsimArray(fun, [item]); + } + + if (Sk.misceval.isTrue(result)) { + return result; + } + return undefined; + }; + this.tp$iter = function () { + return this; + }; + this.tp$iternext = function () { + item = it.tp$iternext(); + if (item === undefined) { + return undefined; + } + result = getitem(item); + while (result === undefined) { + item = it.tp$iternext(); + if (item === undefined) { + return undefined; + } + result = getitem(item); + } + return item; + }; + this.__class__ = Sk.builtin.filter_; + return this; +}; + +Sk.abstr.setUpInheritance("filter", Sk.builtin.filter_, Sk.builtin.object); + +Sk.builtin.filter_.prototype["__iter__"] = new Sk.builtin.func(function (self) { + return self.tp$iter(); +}); + +Sk.builtin.filter_.prototype.next$ = function (self) { + return self.tp$iternext(); +}; + +Sk.builtin.filter_.prototype["$r"] = function () { + return new Sk.builtin.str(""); +}; + +Sk.exportSymbol("Sk.builtin.filter_", Sk.builtin.filter_); diff --git a/src/float.js b/src/float.js index aa7373048e..d60e589230 100644 --- a/src/float.js +++ b/src/float.js @@ -883,7 +883,12 @@ Sk.builtin.float_.prototype.str$ = function (base, sign) { if (base === undefined || base === 10) { - tmp = work.toPrecision(12); + if (Sk.__future__.python3) { + tmp = work.toPrecision(16); + } else { + tmp = work.toPrecision(12); + } + // transform fractions with 4 or more leading zeroes into exponents idx = tmp.indexOf("."); diff --git a/src/function.js b/src/function.js index 5ff63684a5..20cb150b0a 100644 --- a/src/function.js +++ b/src/function.js @@ -299,6 +299,7 @@ Sk.builtin.func.prototype.tp$call = function (posargs, kw) { co_argcount = this.func_code.co_varnames ? this.func_code.co_varnames.length : posargs.length; } let varnames = this.func_code.co_varnames || []; + let co_kwonlyargcount = this.func_code.co_kwonlyargcount || 0; let totalArgs = co_argcount + co_kwonlyargcount; let kwargs; @@ -312,7 +313,6 @@ Sk.builtin.func.prototype.tp$call = function (posargs, kw) { let nposargs = posargs.length; let args = (posargs.length <= co_argcount) ? posargs : posargs.slice(0, co_argcount); - /* Pack other positional arguments into the *args argument */ if (this.func_code.co_varargs) { let vararg = (posargs.length > args.length) ? posargs.slice(args.length) : []; @@ -393,6 +393,7 @@ Sk.builtin.func.prototype.tp$call = function (posargs, kw) { } } + if (this.func_closure) { // todo; OK to modify? if (varnames) { diff --git a/src/lib/ast.js b/src/lib/ast.js index da59b28840..3ccc8ac6a0 100644 --- a/src/lib/ast.js +++ b/src/lib/ast.js @@ -1,5 +1,5 @@ var $builtinmodule = function (name) { - var mod = {}; + var mod = {__name__: Sk.builtin.str("_ast")}; function mangleAppropriately(name) { switch (name) { @@ -29,7 +29,8 @@ var $builtinmodule = function (name) { }; var convertValue = function(value) { - if (value === null) { + // acbart: kwarg field for lambdas (and functions perhaps?) can be undefined + if (value === null || value === undefined) { return Sk.builtin.none.none$; } else if (isSpecialPyAst(value)) { var constructorName = functionName(value); diff --git a/src/lib/pedal/tifa/tifa.py b/src/lib/pedal/tifa/tifa.py index ac7729d421..fb4c57ee0a 100644 --- a/src/lib/pedal/tifa/tifa.py +++ b/src/lib/pedal/tifa/tifa.py @@ -552,7 +552,8 @@ def _visit_collection_loop(self, node): def visit_comprehension(self, node): self._visit_collection_loop(node) # Handle the bodies - self.visit_statements(node.ifs) + if node.ifs: + self.visit_statements(node.ifs) def visit_Dict(self, node): """ @@ -840,11 +841,9 @@ def visit_UnaryOp(self, node): return UnknownType() elif type(node.op) in VALID_UNARYOP_TYPES: op_lookup = VALID_UNARYOP_TYPES[type(node.op)] - if type(node.op) in op_lookup: - op_lookup = op_lookup[type(node.op)] - if type(operand) in op_lookup: - op_lookup = op_lookup[type(operand)] - return op_lookup(operand) + if type(operand) in op_lookup: + op_lookup = op_lookup[type(operand)] + return op_lookup(operand) return UnknownType() def visit_While(self, node): diff --git a/src/lib/turtle.js b/src/lib/turtle.js index ba57b4d8fc..914e5d8027 100644 --- a/src/lib/turtle.js +++ b/src/lib/turtle.js @@ -19,7 +19,7 @@ var $builtinmodule = function (name) { } function generateTurtleModule(_target) { - var _module = {}, + var _module = {__name__: Sk.builtin.str("turtle")}, _durationSinceRedraw = 0, _focus = true, OPTIMAL_FRAME_RATE = 1000/30, diff --git a/src/list.js b/src/list.js index 8f5574b98c..1949323fc3 100644 --- a/src/list.js +++ b/src/list.js @@ -644,6 +644,23 @@ Sk.builtin.list.prototype["count"] = new Sk.builtin.func(function (self, item) { return new Sk.builtin.int_(count); }); +Sk.builtin.list.prototype["copy"] = new Sk.builtin.func(function (self) { + var it; + var k; + var items; + Sk.builtin.pyCheckArgsLen("copy", arguments.length - 1, 0, 0); + + items = []; + for (it = Sk.abstr.iter(self), k = it.tp$iternext(); + k !== undefined; + k = it.tp$iternext()) { + items.push(k); + + } + return new Sk.builtin.list(items); + +}); + Sk.builtin.list.prototype["reverse"] = new Sk.builtin.func(Sk.builtin.list.prototype.list_reverse_); Sk.builtin.list.prototype["sort"] = new Sk.builtin.func(Sk.builtin.list.prototype.list_sort_); diff --git a/src/long.js b/src/long.js index ea05ea58da..edd6e8a55b 100644 --- a/src/long.js +++ b/src/long.js @@ -814,7 +814,13 @@ Sk.builtin.lng.prototype.ob$ge = function (other) { }; Sk.builtin.lng.prototype.$r = function () { - return new Sk.builtin.str(this.str$(10, true) + "L"); + var suffix; + if (Sk.__future__.python3) { + suffix = ""; + } else { + suffix = "L"; + } + return new Sk.builtin.str(this.str$(10, true) + suffix); }; Sk.builtin.lng.prototype.tp$str = function () { diff --git a/src/main.js b/src/main.js index 7594376761..8d5df2f111 100644 --- a/src/main.js +++ b/src/main.js @@ -47,6 +47,9 @@ require("./file.js"); require("./ffi.js"); require("./iterator.js"); require("./enumerate.js"); +require("./filter.js"); +require("./zip.js"); +require("./map.js"); require("./token.js"); require("./tokenize.js"); require("../gen/parse_tables.js"); diff --git a/src/map.js b/src/map.js new file mode 100644 index 0000000000..e373ff140d --- /dev/null +++ b/src/map.js @@ -0,0 +1,85 @@ +/** + * @constructor + * @param {Object} iterable + * @extends Sk.builtin.object + */ +Sk.builtin.map_ = function map_ (fun, seq) { + var next; + var args; + var getnext; + var i; + var item; + var iterables; + var combined; + var args; + Sk.builtin.pyCheckArgsLen("map_", arguments.length, 2); + + if (!(this instanceof Sk.builtin.map_)) { + args = Array.prototype.slice.apply(arguments).slice(1); + return new Sk.builtin.map_(fun, ...args); + } + if (arguments.length > 2) { + // Pack sequences into one list of Javascript Arrays + iterables = Array.prototype.slice.apply(arguments).slice(1); + for (i = 0; i < iterables.length; i++) { + //don't need to check if iterables[i] is an iterable bc Sk.abstr.iter will check and throw the correct error msg + iterables[i] = Sk.abstr.iter(iterables[i]); + } + getnext = function () { + combined = []; + for (i = 0; i < iterables.length; i++) { + next = iterables[i].tp$iternext(); + if (next === undefined) { + return undefined; + } else { + combined.push(next); + } + } + return combined; + }; + } else { + //don't need to check if seq is iterable bc Sk.abstr.iter will throw the right error msg + seq = Sk.abstr.iter(seq); + getnext = function () { + return seq.tp$iternext(); + }; + } + this.tp$iternext = function () { + item = getnext(); + if (item === undefined) { + return undefined; + } + if (fun === Sk.builtin.none.none$) { + if (item instanceof Array) { + item = new Sk.builtin.tuple(item); + return item; + } + return item; + } + if (!(item instanceof Array)) { + item = [item]; + } + return Sk.misceval.applyOrSuspend(fun, undefined, undefined, undefined, item); + }; + this.tp$iter = function () { + return this; + }; + this.__class__ = Sk.builtin.map_; + return this; +}; + +Sk.abstr.setUpInheritance("map", Sk.builtin.map_, Sk.builtin.object); + +Sk.builtin.map_.prototype["__iter__"] = new Sk.builtin.func(function (self) { + return self.tp$iter(); +}); + +Sk.builtin.map_.prototype.next$ = function (self) { + return self.tp$iternext(); +}; + +Sk.builtin.map_.prototype["$r"] = function () { + return new Sk.builtin.str(""); +}; + +Sk.exportSymbol("Sk.builtin.map_", Sk.builtin.map_); \ No newline at end of file diff --git a/src/misceval.js b/src/misceval.js index ca6135f6bb..364e4eea66 100644 --- a/src/misceval.js +++ b/src/misceval.js @@ -259,9 +259,12 @@ Sk.misceval.richCompareBool = function (v, w, op, canSuspend) { Sk.builtin.bool.prototype.ob$type]; sequence_types = [Sk.builtin.dict.prototype.ob$type, Sk.builtin.enumerate.prototype.ob$type, + Sk.builtin.filter_.prototype.ob$type, Sk.builtin.list.prototype.ob$type, + Sk.builtin.map_.prototype.ob$type, Sk.builtin.str.prototype.ob$type, - Sk.builtin.tuple.prototype.ob$type]; + Sk.builtin.tuple.prototype.ob$type, + Sk.builtin.zip_.prototype.ob$type]; v_num_type = numeric_types.indexOf(v_type); v_seq_type = sequence_types.indexOf(v_type); @@ -540,7 +543,8 @@ Sk.misceval.richCompareBool = function (v, w, op, canSuspend) { vname = Sk.abstr.typeName(v); wname = Sk.abstr.typeName(w); - throw new Sk.builtin.ValueError("don't know how to compare '" + vname + "' and '" + wname + "'"); + throw new Sk.builtin.TypeError("'" + "OPERATION SYMBOL" + "' not supported between instances of '" + vname + "' and '" + wname + "'"); + //throw new Sk.builtin.ValueError("don't know how to compare '" + vname + "' and '" + wname + "'"); }; Sk.exportSymbol("Sk.misceval.richCompareBool", Sk.misceval.richCompareBool); @@ -624,15 +628,25 @@ Sk.misceval.isTrue = function (x) { if (x.constructor === Sk.builtin.float_) { return x.v !== 0; } - if (x["__nonzero__"]) { - ret = Sk.misceval.callsimArray(x["__nonzero__"], [x]); - if (!Sk.builtin.checkInt(ret)) { - throw new Sk.builtin.TypeError("__nonzero__ should return an int"); + if (Sk.__future__.python3) { + if (x.__bool__) { + ret = Sk.misceval.callsimArray(x.__bool__, [x]); + if (!(ret instanceof Sk.builtin.bool)) { + throw new Sk.builtin.TypeError("__bool__ should return bool, returned " + Sk.abstr.typeName(ret)); + } + return ret.v; + } + } else { + if (x.__nonzero__) { + ret = Sk.misceval.callsimArray(x.__nonzero__, [x]); + if (!Sk.builtin.checkInt(ret)) { + throw new Sk.builtin.TypeError("__nonzero__ should return an int"); + } + return Sk.builtin.asnum$(ret) !== 0; } - return Sk.builtin.asnum$(ret) !== 0; } - if (x["__len__"]) { - ret = Sk.misceval.callsimArray(x["__len__"], [x]); + if (x.__len__) { + ret = Sk.misceval.callsimArray(x.__len__, [x]); if (!Sk.builtin.checkInt(ret)) { throw new Sk.builtin.TypeError("__len__ should return an int"); } diff --git a/src/object.js b/src/object.js index de4b362aba..2ec918c400 100644 --- a/src/object.js +++ b/src/object.js @@ -56,11 +56,11 @@ Sk.builtin.object.prototype.GenericGetAttr = function (pyName, canSuspend) { if (tp !== null) { return tp; } - } else if (jsName === "__name__") { + }/* else if (jsName === "__name__") { if (this.tp$name !== null) { return Sk.ffi.remapToPy(this.tp$name); } - } + }*/ // todo; assert? force? if (dict) { diff --git a/src/pgen/parser/main.py b/src/pgen/parser/main.py index ecf8e54bc2..b96534c037 100644 --- a/src/pgen/parser/main.py +++ b/src/pgen/parser/main.py @@ -24,6 +24,7 @@ "{": Sk.token.tokens.T_LBRACE, "}": Sk.token.tokens.T_RBRACE, "@": Sk.token.tokens.T_AT, +"@=": Sk.token.tokens.T_ATEQUAL, "==": Sk.token.tokens.T_EQEQUAL, "!=": Sk.token.tokens.T_NOTEQUAL, "<>": Sk.token.tokens.T_NOTEQUAL, diff --git a/src/str.js b/src/str.js index ee9d0e0b72..a107e5dead 100755 --- a/src/str.js +++ b/src/str.js @@ -909,10 +909,11 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { var replFunc; var index; var regex; + var val; + if (rhs.constructor !== Sk.builtin.tuple && (rhs.mp$subscript === undefined || rhs.constructor === Sk.builtin.str)) { rhs = new Sk.builtin.tuple([rhs]); } - // general approach is to use a regex that matches the format above, and // do an re.sub with a function as replacement to make the subs. @@ -1039,6 +1040,7 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { var prefix = args[0]; var r = args[1]; var j; + if (fieldWidth) { fieldWidth = parseInt(fieldWidth, 10); totLen = r.length + prefix.length; @@ -1097,12 +1099,17 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { } convName = ["toExponential", "toFixed", "toPrecision"]["efg".indexOf(conversionType.toLowerCase())]; if (precision === undefined || precision === "") { + if (conversionType === "e" || conversionType === "E") { precision = 6; } else if (conversionType === "f" || conversionType === "F") { + if (Sk.__future__.python3) { + precision = 6; + } else { precision = 7; } } + } result = (convValue)[convName](precision); // possible loose of negative zero sign // apply sign to negative zeros, floats only! @@ -1111,7 +1118,16 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { result = "-" + result; // add sign for zero } } + if (Sk.__future__.python3) { + if ((result.length >= 7) && (result.slice(0, 6) == "0.0000")) { + val = parseFloat(result); + result = val.toExponential(); + } + if (result.charAt(result.length -2) == "-") { + result = result.slice(0, result.length - 1) + "0" + result.charAt(result.length - 1); + } + } if ("EFG".indexOf(conversionType) !== -1) { result = result.toUpperCase(); } @@ -1150,7 +1166,6 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { return "%"; } }; - ret = this.v.replace(regex, replFunc); return new Sk.builtin.str(ret); }; diff --git a/src/type.js b/src/type.js index 5e481ab809..13d74571d9 100644 --- a/src/type.js +++ b/src/type.js @@ -57,6 +57,21 @@ Sk.dunderToSkulpt = { "__set__": ["tp$descr_set", 3] }; +Sk.setupDunderMethods = function (py3) { + if (py3) { + Sk.dunderToSkulpt["__matmul__"] = "tp$matmul"; + Sk.dunderToSkulpt["__rmatmul__"] = "tp$reflected_matmul"; + } else { + if (Sk.dunderToSkulpt["__matmul__"]) { + delete Sk.dunderToSkulpt["__matmul__"]; + } + if (Sk.dunderToSkulpt["__rmatmul__"]) { + delete Sk.dunderToSkulpt["__rmatmul__"]; + } + } +}; + +Sk.exportSymbol("Sk.setupDunderMethods", Sk.setupDunderMethods); /** * * @constructor diff --git a/src/zip.js b/src/zip.js new file mode 100644 index 0000000000..be104a78fd --- /dev/null +++ b/src/zip.js @@ -0,0 +1,63 @@ +/** + * @constructor + * @param {Object} iterable + * @extends Sk.builtin.object + */ +Sk.builtin.zip_ = function zip_ () { + var i; + var iters; + var next; + if (!(this instanceof Sk.builtin.zip_)) { + return new Sk.builtin.zip_(...arguments); + } + if (arguments.length === 0) { + return new Sk.builtin.zip_(new Sk.builtin.list([])); + } + iters = []; + for (i = 0; i < arguments.length; i++) { + try { + iters.push(Sk.abstr.iter(arguments[i])); + } catch (e) { + if (e instanceof Sk.builtin.TypeError) { + throw new Sk.builtin.TypeError("zip argument #" + (i + 1) + " must support iteration"); + } else { + throw e; + } + } + } + + this.tp$iter = function () { + return this; + }; + + this.tp$iternext = function () { + var tup = []; + for (i = 0; i < iters.length; i++) { + next = iters[i].tp$iternext(); + if (next === undefined) { + return undefined; + } + tup.push(next); + } + return new Sk.builtin.tuple(tup); + }; + this.__class__ = Sk.builtin.zip_; + + return this; +}; + +Sk.abstr.setUpInheritance("zip", Sk.builtin.zip_, Sk.builtin.object); + +Sk.builtin.zip_.prototype["__iter__"] = new Sk.builtin.func(function (self) { + return self.tp$iter(); +}); + +Sk.builtin.zip_.prototype.next$ = function (self) { + return self.tp$iternext(); +}; + +Sk.builtin.zip_.prototype["$r"] = function () { + return new Sk.builtin.str(""); +}; + +Sk.exportSymbol("Sk.builtin.zip_", Sk.builtin.zip_); \ No newline at end of file diff --git a/test/test_ast2.py b/test/test_ast2.py index 2d18d289e9..9b69530fcc 100644 --- a/test/test_ast2.py +++ b/test/test_ast2.py @@ -3,3 +3,7 @@ func = ast.parse("def alpha(): pass") assert func.body[0].name == "alpha" + +assert func.body[0].__name__ == "FunctionDef" + +func = ast.parse("lambda: 0") \ No newline at end of file diff --git a/test/test_names.py b/test/test_names.py new file mode 100644 index 0000000000..8bdcb32c15 --- /dev/null +++ b/test/test_names.py @@ -0,0 +1,10 @@ +def a_scope(propertyIsEnumerable, cat, length, banana): + def inner_scope(): + cat+"meow" + banana+"puffin" + propertyIsEnumerable+"anything" + length+"jango" + return inner_scope + + +a_scope("ada", "pumpkin", "mittens", "pele")() \ No newline at end of file diff --git a/test/test_tifa.py b/test/test_tifa.py index 8a97fbf869..d6363279bf 100644 --- a/test/test_tifa.py +++ b/test/test_tifa.py @@ -42,9 +42,9 @@ 'iterate_through_empty_list': ['y = []\nfor x in y:\n\tpass', ['Unused Variable', 'Initialization Problem'], ['Iterating over empty list']], 'double_iterate_through_strings': - ['ss = ["Testing", "Here"]\nfor a in ss:\n print(a)\nfor b in a:\n print(b)', ['Iterating over non-list', 'Iterating over empty list'], []], + ['ss = ["Testing", "Here"]\nfor a in ss:\n print(a)\nfor b in a:\n print(b)', ['Iterating over Non-list', 'Iterating over empty list'], []], 'iterate_through_number': - ['y = 5\nfor x in y:\n\tpass', ['Unused Variable', 'Initialization Problem'], ['Iterating over non-list']], + ['y = 5\nfor x in y:\n\tpass', ['Unused Variable', 'Initialization Problem'], ['Iterating over Non-list']], 'iterate_over_iteration_variable': ['y = [1,2,3]\nfor y in y:\n\tpass', [], ['Iteration Problem']], 'type_change': @@ -84,21 +84,25 @@ # Iterating over the result of a builtin 'print_range': - ['x = range(100)\nprint(x)', ['Iterating over non-list'], []], + ['x = range(100)\nprint(x)', ['Iterating over Non-list'], []], 'iterate_range': - ['x = range(100)\nfor y in x:\n print(y)', ['Iterating over non-list'], []], + ['x = range(100)\nfor y in x:\n print(y)', ['Iterating over Non-list'], []], 'iterate_over_ranges_atomic_subtype': - ['x = range(100)\nfor y in x:\n pass\nfor z in y:\n print(z)', [], ['Iterating over non-list']], + ['x = range(100)\nfor y in x:\n pass\nfor z in y:\n print(z)', [], ['Iterating over Non-list']], 'iterate_over_split': - ['for x in "a,b,c".split(","):\n x+""', ['Iterating over non-list', 'Iterating over empty list'], []], + ['for x in "a,b,c".split(","):\n x+""', ['Iterating over Non-list', 'Iterating over empty list'], []], 'iterate_over_string_upper': - ['for l in "abc".upper():\n l+""', ['Iterating over non-list', 'Iterating over empty list'], []], + ['for l in "abc".upper():\n l+""', ['Iterating over Non-list', 'Iterating over empty list'], []], # Incompatible types 'add_int_str': ['a = 5 + "ERROR"', [], ['Incompatible types']], 'multiply_str_int': ['a = "ERROR" * 5', ['Incompatible types'], []], + 'simple_unary_op': + ['+1', ['Incompatible types'], []], + 'unary_compare': + ['-1 < 5', ['Incompatible types'], []], 'unary_and_sub_int_int': ['-(5)+0', ['Incompatible types'], []], 'iadd_int_int': @@ -111,6 +115,8 @@ ['a=0\na+=5', ['Initialization Problem', 'Overwritten Variable'], ['Unused Variable']], # Lambda + 'simple_lambda': + ['lambda: 0', [], []], 'lambda_add': ['a = lambda: 0\nb=a()\nb+5', ['Incompatible types'], []], @@ -132,7 +138,7 @@ 'function_with_subtypes_add_int_list_str': ['def add_first(a_list):\n for element in a_list:\n return element + 5\nprint(add_first(["1"]))', [], ['Incompatible types']], 'function_with_subtypes_add_int_primitive_int': - ['def add_first(a_list):\n for element in a_list:\n return element + 5\nprint(add_first(1))', [], ['Iterating over non-list']], + ['def add_first(a_list):\n for element in a_list:\n return element + 5\nprint(add_first(1))', [], ['Iterating over Non-list']], 'function_with_subtypes_add_int_primitive_str': ['def add_first(a_list):\n for element in a_list:\n return element + 5\nprint(add_first("1"))', [], ['Incompatible types']], # Out of scope @@ -200,8 +206,10 @@ ['with open("A") as a:\n print(a)', ['Initialization Problem'], []], # List comprehensions + 'simple_comprehension': + ['[0 for y in range(100)]',[], []], 'list_comprehension': - ['a = [5 for x in range(100)]\nfor i in a:\n 5+i', ['Iterating over non-list', 'Incompatible types'], []], + ['a = [5 for x in range(100)]\nfor i in a:\n 5+i', ['Iterating over Non-list', 'Incompatible types'], []], # Return outside function 'no_return_outside_function': @@ -312,6 +320,7 @@ def test_code(self): self.fail(name+": Incorrectly detected "+none+"\n"+code+"\n") for some in somes: if not tifa.report['tifa']['issues'].get(some, []): + print(name+"Test") self.fail(name+": Failed to detect "+some+"\n"+code+"\n") return test_code for name, (code, nones, somes) in unit_tests.items(): From 8db371662dbe8a37fb07098ee03c7e574a72e255 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 28 Aug 2019 21:02:22 -0400 Subject: [PATCH 20/68] Pedal updates, image processing stuff, basic exec and traceback support --- .gitignore | 1 + loose_compile.py | 34 +- src/builtin.js | 50 +++ src/builtin/sys.js | 23 +- src/builtindict.js | 1 + src/compile.js | 210 ++++++------ src/errors.js | 56 +++- src/import.js | 2 +- src/lib/PIL/__init__.js | 142 ++++++++ src/lib/{image.js => image_old.js} | 0 src/lib/matplotlib/pyplot/__init__.js | 2 +- src/lib/pedal/report/imperative.py | 6 +- src/lib/pedal/report/report.py | 8 + src/lib/pedal/tifa/tifa.py | 5 +- src/lib/pedal/toolkit/utilities.py | 17 +- src/lib/sound/sample.js | 82 ++--- src/lib/sound/sound.js | 460 +++++++++++++------------- src/lib/traceback.py | 110 +++--- test/test_traceback.py | 22 ++ webpack.config.js | 62 ++-- 20 files changed, 814 insertions(+), 479 deletions(-) create mode 100644 src/lib/PIL/__init__.js rename src/lib/{image.js => image_old.js} (100%) create mode 100644 test/test_traceback.py diff --git a/.gitignore b/.gitignore index b631a3f8f5..247318dab6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +deleteme* support/tmp/ doc/static/skulpt-stdlib.js doc/static/skulpt.min.js diff --git a/loose_compile.py b/loose_compile.py index 7f50224a5c..1a3635001b 100644 --- a/loose_compile.py +++ b/loose_compile.py @@ -11,6 +11,7 @@ def __init__(self): self.stack = [] def generic_visit(self, node): + print(type(node).__name__) self.unhandled.add(type(node).__name__) return NodeVisitor.generic_visit(self, node) @@ -28,7 +29,7 @@ def exit(self): def visit_Module(self, node): self.add_statement("var $builtinmodule = function (name) {") self.enter("mod") - self.add_statement("let mod = {};") + self.add_statement("let mod = {{__name__: {name} }};".format(name=name)) for statement in node.body: self.visit(statement) self.add_statement("return mod;") @@ -54,7 +55,13 @@ def visit_ClassDef(self, node): )) def visit_Name(self, node): - return "Sk.misceval.loadname('{}', $gbl)".format(node.id) + if isinstance(node.ctx, ast.Load): + return node.id + elif isinstance(node.ctx, ast.Store): + return "var {name}".format(node.id) + elif isinstance(node.ctx, ast.Delete): + pass + #return "Sk.misceval.loadname('{}', $gbl)".format(node.id) def visit_FunctionDef(self, node): owner = self.context @@ -62,17 +69,17 @@ def visit_FunctionDef(self, node): str_args = ", ".join("'{}'".format(arg.arg) for arg in node.args.args) defaults = ", ".join(self.visit(default) for default in node.args.defaults) self.add_statement( - "var _{name} = new Sk.builtin.func(function({args}) {{".format( + "var {name} = new Sk.builtin.func(function({args}) {{".format( name=node.name, args=args)) self.enter(node.name) for statement in node.body: self.visit(statement) self.exit() self.add_statement("}});") - self.add_statement("_{name}.co_varnames = [{args}]".format( + self.add_statement("{name}.co_varnames = [{args}]".format( name=node.name, args=str_args )) - self.add_statement("_{name}.$defaults = [{defaults}]".format( + self.add_statement("{name}.$defaults = [{defaults}]".format( name=node.name, defaults=defaults )) self.add_statement("{owner}.{name} = _{name}".format( @@ -118,6 +125,23 @@ def visit_Attribute(self, node): def visit_Return(self, node): self.add_statement("return {};".format(self.visit(node.value))); + + def visit_ImportFrom(self, node): + module = node.module + names = node.names + self.add_statement("var {module} = Sk.builtin.__import__('{module}', $gbl, $loc, [{names}], -1);".format( + module=module, + names=", ".join(repr(name) for name in names), + )) + for name in names: + self.add_statement("var {name} = Sk.abstr.gattr({module}, new Sk.builtin.str({name!r}))".format( + name=name, + module=module + )) + + def visit_Call(self, node): + func = node.func + if __name__ == '__main__': target = sys.argv[1] diff --git a/src/builtin.js b/src/builtin.js index 0d60ae1ea4..a308c0c6ad 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -1356,6 +1356,56 @@ Sk.builtin.execfile = function execfile () { throw new Sk.builtin.NotImplementedError("execfile is not yet implemented"); }; +var extractDict = function(obj) { + var ret = {}; + var k, v, kAsJs, iter; + for (iter = obj.tp$iter(), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { + v = obj.mp$subscript(k); + if (v === undefined) { + v = null; + } + kAsJs = Sk.ffi.remapToJs(k); + // todo; assert that this is a reasonble lhs? + ret[kAsJs] = v; + } + return ret; +}; + +Sk.builtin.exec = function execf(python_code, new_globals) { + Sk.builtin.pyCheckArgs("exec", arguments, 1, 2); + var backupRG = Sk.retainGlobals; + Sk.retainGlobals = true; + var filename = "test"; + var new_globals_copy = extractDict(new_globals); + if (!new_globals_copy.__file__) { + new_globals_copy.__file__ = Sk.ffi.remapToPy(filename); + } + if (!new_globals_copy.__name__) { + new_globals_copy.__name__ = Sk.ffi.remapToPy(filename); + } + var backupGlobals = Sk.globals, + backupSysmodules = new Sk.builtin.dict([]); + Sk.misceval.iterFor(Sk.sysmodules.tp$iter(), function(key) { + var value = Sk.sysmodules.mp$subscript(key); + backupSysmodules.mp$ass_subscript(key, value); + }); + Sk.globals = new_globals_copy; // Possibly copy over some "default" ones? + python_code = Sk.ffi.remapToJs(python_code); + Sk.importMainWithBody(filename, false, python_code, true); + Sk.globals = backupGlobals; + Sk.misceval.iterFor(backupSysmodules.tp$iter(), function(key) { + var value = backupSysmodules.mp$subscript(key); + Sk.sysmodules.mp$ass_subscript(key, value); + }); + for (var key in new_globals_copy) { + if (new_globals_copy.hasOwnProperty(key)) { + var pykey = Sk.ffi.remapToPy(key); + Sk.builtin.dict.prototype.mp$ass_subscript.call(new_globals, pykey, new_globals_copy[key]) + } + } + Sk.retainGlobals = backupRG; +}; + Sk.builtin.frozenset = function frozenset () { throw new Sk.builtin.NotImplementedError("frozenset is not yet implemented"); }; diff --git a/src/builtin/sys.js b/src/builtin/sys.js index 7a0889712d..d6c3bce032 100644 --- a/src/builtin/sys.js +++ b/src/builtin/sys.js @@ -73,13 +73,22 @@ var $builtinmodule = function (name) { sys.stdin = sys.__stdin__; sys.exc_info = new Sk.builtin.func(function () { - var type = Sk.err.ob$type; - var value = Sk.builtin.none.none$; - var traceback = new Sk.builtin.traceback(Sk.err); - //print(traceback.tp$setattr) - //traceback.tp$setattr('tb_lineno', traceback.tb_lineno); - var vals = [type, Sk.err, traceback]; - return new Sk.builtin.tuple(vals); + console.log(Sk.scopes); + if (Sk.err.length && Sk.err[0]) { + console.log(">>>", Sk.err[0], "<<<"); + console.log("%%%", Sk.err[1], "$$$"); + console.log(Sk.err.traceback); + console.log(Sk.ffi.remapToJs(Sk.sysmodules)); + var type = Sk.err.ob$type; + var value = Sk.builtin.none.none$; + var traceback = new Sk.builtin.traceback(Sk.err[0]); + //print(traceback.tp$setattr) + //traceback.tp$setattr('tb_lineno', traceback.tb_lineno); + var vals = [type, Sk.err[0], traceback]; + return new Sk.builtin.tuple(vals); + } else { + return new Sk.builtin.tuple(Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.none.none$); + } }); return sys; diff --git a/src/builtindict.js b/src/builtindict.js index de76eb80a0..ebcd103ee4 100644 --- a/src/builtindict.js +++ b/src/builtindict.js @@ -90,6 +90,7 @@ Sk.builtins = { "callable" : Sk.builtin.callable, "delattr" : Sk.builtin.delattr, "eval_$rn$" : Sk.builtin.eval_, + "exec" : Sk.builtin.exec, "execfile" : Sk.builtin.execfile, "frozenset" : Sk.builtin.frozenset, "help" : Sk.builtin.help, diff --git a/src/compile.js b/src/compile.js index 28d3c6272d..105dbf2bc7 100644 --- a/src/compile.js +++ b/src/compile.js @@ -38,6 +38,8 @@ function Compiler (filename, st, flags, canSuspend, sourceCodeForAnnotation) { * when returning to a block. * * Corresponds to the body of a module, class, or function. + * + * Effectively a frame. */ function CompilerUnit () { @@ -112,10 +114,10 @@ Compiler.prototype.annotateSource = function (ast, shouldStep) { Sk.asserts.assert(ast.lineno !== undefined && ast.col_offset !== undefined); out("\n$currLineNo=Sk.currLineNo=",lineno, ";$currColNo=Sk.currColNo=",col_offset,";"); - out("Sk.currFilename='",this.filename,"';"); + out("Sk.currFilename='",this.filename,"';"); // Do not trace the standard library if (shouldStep && (!this.filename || - !this.filename.startsWith('src/lib/'))) { + !this.filename.startsWith("src/lib/"))) { out("Sk.afterSingleExecution && Sk.afterSingleExecution($gbl,$loc,"+lineno+","+col_offset+","+JSON.stringify(this.filename)+");\n"); } } @@ -297,7 +299,7 @@ Compiler.prototype.makeConstant = function (rest) { v = this.u.scopename + "." + this.gensym("const"); this.u.consts[v] = val; return v; -} +}; /** * @param {string} hint basename for gensym @@ -322,7 +324,7 @@ Compiler.prototype._gr = function (hint, rest) { Compiler.prototype.outputInterruptTest = function () { // Added by RNL var output = ""; if (Sk.execLimit !== null || Sk.yieldLimit !== null && this.u.canSuspend) { - output += "var $dateNow = Date.now();"; + output += "var $dateNow = Date.now();"; if (Sk.execLimit !== null) { output += ("if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit){"+ "throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg())}"); @@ -419,7 +421,7 @@ Compiler.prototype.cunpackstarstoarray = function(elts, permitEndOnly) { // Fast path return "[" + elts.map((expr) => this.vexpr(expr)).join(",") + "]"; } -} +}; Compiler.prototype.ctuplelistorset = function(e, data, tuporlist) { var i; @@ -442,16 +444,14 @@ Compiler.prototype.ctuplelistorset = function(e, data, tuporlist) { for (i = 0; i < e.elts.length; ++i) { this.vexpr(e.elts[i], items + "[" + i + "]"); } - } - else if (e.ctx === Sk.astnodes.Load || tuporlist === "set") { //because set's can't be assigned to. + } else if (e.ctx === Sk.astnodes.Load || tuporlist === "set") { //because set's can't be assigned to. if (hasStars) { if (!Sk.__future__.python3) { throw new Sk.builtin.SyntaxError("List packing with stars is not supported in Python 2"); } return this._gr("load" + tuporlist, "new Sk.builtins['", tuporlist, "'](", this.cunpackstarstoarray(e.elts), ")"); - } - else if (tuporlist === "tuple") { + } else if (tuporlist === "tuple") { allconsts = true; items = []; for (i = 0; i < e.elts.length; ++i) { @@ -462,7 +462,7 @@ Compiler.prototype.ctuplelistorset = function(e, data, tuporlist) { // this requires seeing if "$const" is contained // within it. A better solution would require a // change to vexpr, which would be more invasive. - if (allconsts && (item.indexOf('$const') == -1)) { + if (allconsts && (item.indexOf("$const") == -1)) { allconsts = false; } items.push(item); @@ -563,11 +563,9 @@ Compiler.prototype.ccompgen = function (type, tmpname, generators, genIndex, val if (type === "dict") { lkey = this.vexpr(key); out(tmpname, ".mp$ass_subscript(", lkey, ",", lvalue, ");"); - } - else if (type === "list") { + } else if (type === "list") { out(tmpname, ".v.push(", lvalue, ");"); // todo; - } - else if (type === "set") { + } else if (type === "set") { out(tmpname, ".v.mp$ass_subscript(", lvalue, ", true);"); } this._jump(skip); @@ -581,8 +579,7 @@ Compiler.prototype.ccompgen = function (type, tmpname, generators, genIndex, val return tmpname; }; -Compiler.prototype.cyield = function(e) -{ +Compiler.prototype.cyield = function(e) { if (this.u.ste.blockType !== Sk.SYMTAB_CONSTS.FunctionBlock) { throw new SyntaxError("'yield' outside function"); } @@ -670,7 +667,7 @@ Compiler.prototype.ccall = function (e) { // note that it's part of the js API spec: https://developer.mozilla.org/en/docs/Web/API/Window/self // so we should probably add self to the mangling // TODO: feel free to ignore the above - out("if (typeof self === \"undefined\" || self.toString().indexOf(\"Window\") > 0) { throw new Sk.builtin.RuntimeError(\"super(): no arguments\") };") + out("if (typeof self === \"undefined\" || self.toString().indexOf(\"Window\") > 0) { throw new Sk.builtin.RuntimeError(\"super(): no arguments\") };"); positionalArgs = "[$gbl.__class__,self]"; } if (keywordArgs !== "undefined") { @@ -748,15 +745,12 @@ Compiler.prototype.chandlesubscr = function (ctx, obj, subs, data) { out("$ret = Sk.abstr.objectGetItem(", obj, ",", subs, ", true);"); this._checkSuspension(); return this._gr("lsubscr", "$ret"); - } - else if (ctx === Sk.astnodes.Store || ctx === Sk.astnodes.AugStore) { + } else if (ctx === Sk.astnodes.Store || ctx === Sk.astnodes.AugStore) { out("$ret = Sk.abstr.objectSetItem(", obj, ",", subs, ",", data, ", true);"); this._checkSuspension(); - } - else if (ctx === Sk.astnodes.Del) { + } else if (ctx === Sk.astnodes.Del) { out("Sk.abstr.objectDelItem(", obj, ",", subs, ");"); - } - else { + } else { Sk.asserts.fail("handlesubscr fail"); } }; @@ -773,8 +767,7 @@ Compiler.prototype.cboolop = function (e) { Sk.asserts.assert(e instanceof Sk.astnodes.BoolOp); if (e.op === Sk.astnodes.And) { jtype = this._jumpfalse; - } - else { + } else { jtype = this._jumptrue; } end = this.newBlock("end of boolop"); @@ -850,19 +843,16 @@ Compiler.prototype.vexpr = function (e, data, augvar, augsubs) { case Sk.astnodes.Num: if (typeof e.n === "number") { return e.n; - } - else if (e.n instanceof Sk.builtin.int_) { + } else if (e.n instanceof Sk.builtin.int_) { return this.makeConstant("new Sk.builtin.int_(" + e.n.v + ")"); } else if (e.n instanceof Sk.builtin.float_) { // Preserve sign of zero for floats nStr = e.n.v === 0 && 1/e.n.v === -Infinity ? "-0" : e.n.v; return this.makeConstant("new Sk.builtin.float_(" + nStr + ")"); - } - else if (e.n instanceof Sk.builtin.lng) { + } else if (e.n instanceof Sk.builtin.lng) { // long uses the tp$str() method which delegates to nmber.str$ which preserves the sign return this.makeConstant("Sk.longFromStr('" + e.n.tp$str().v + "')"); - } - else if (e.n instanceof Sk.builtin.complex) { + } else if (e.n instanceof Sk.builtin.complex) { // preserve sign of zero here too var real_val = e.n.real.v === 0 && 1/e.n.real.v === -Infinity ? "-0" : e.n.real.v; var imag_val = e.n.imag.v === 0 && 1/e.n.imag.v === -Infinity ? "-0" : e.n.imag.v; @@ -916,7 +906,7 @@ Compiler.prototype.vexpr = function (e, data, augvar, augsubs) { switch (e.ctx) { case Sk.astnodes.AugLoad: out("$ret = Sk.abstr.objectGetItem(",augvar,",",augsubs,", true);"); - this._checkSuspension(e) + this._checkSuspension(e); return this._gr("gitem", "$ret"); case Sk.astnodes.Load: case Sk.astnodes.Store: @@ -953,15 +943,15 @@ Compiler.prototype.vexpr = function (e, data, augvar, augsubs) { case Sk.builtin.bool.false$: return "Sk.builtin.bool.false$"; default: - Sk.asserts.fail("invalid named constant") + Sk.asserts.fail("invalid named constant"); } break; case Sk.astnodes.List: - return this.ctuplelistorset(e, data, 'list'); + return this.ctuplelistorset(e, data, "list"); case Sk.astnodes.Tuple: - return this.ctuplelistorset(e, data, 'tuple'); + return this.ctuplelistorset(e, data, "tuple"); case Sk.astnodes.Set: - return this.ctuplelistorset(e, data, 'set'); + return this.ctuplelistorset(e, data, "set"); case Sk.astnodes.Starred: break; default: @@ -1148,16 +1138,9 @@ Compiler.prototype.outputSuspensionHelpers = function (unit) { output += ("try {"+ "$ret=susp.child.resume();"+ - "} catch(err) {"+ - "if (err instanceof Sk.builtin.TimeLimitError) {"+ - "Sk.execStart = Date.now();"+ - "Sk.execPaused = 0;"+ - "} if (!(err instanceof Sk.builtin.BaseException)) {"+ - "err = new Sk.builtin.ExternalError(err);"+ - "}"+ - "err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'});"+ - "if($exc.length>0) { $err=err; Sk.err=$err; $blk=$exc.pop(); } else { throw err; } }" + - "};"); + this.handleTraceback(false, unit.scopename)+ + // Close out function + ";"); output += "var $saveSuspension = function($child, $filename, $lineno, $colno) {" + "var susp = new Sk.misceval.Suspension(); susp.child=$child;" + @@ -1180,7 +1163,7 @@ Compiler.prototype.outputSuspensionHelpers = function (unit) { "};"; return output; -} +}; Compiler.prototype.outputAllUnits = function () { var i; @@ -1203,8 +1186,7 @@ Compiler.prototype.outputAllUnits = function () { generatedBlocks = Object.create(null); for (i = 0; i < blocks.length; ++i) { block = i; - if (block in generatedBlocks) - continue; + if (block in generatedBlocks) {continue;} while (true) { generatedBlocks[block] = true; @@ -1215,13 +1197,11 @@ Compiler.prototype.outputAllUnits = function () { if (!(blocks[block]._next in generatedBlocks)) { ret += "/* allowing case fallthrough */"; block = blocks[block]._next; - } - else { + } else { ret += "/* jump */ continue;"; break; } - } - else { + } else { ret += "throw new Sk.builtin.SystemError('internal error: unterminated block');"; break; } @@ -1243,11 +1223,9 @@ Compiler.prototype.cif = function (s) { if (s.orelse && s.orelse.length > 0) { this.vseqstmt(s.orelse); } - } - else if (constant === 1) { + } else if (constant === 1) { this.vseqstmt(s.body); - } - else { + } else { end = this.newBlock("end of if"); if (s.orelse && s.orelse.length > 0) { next = this.newBlock("next branch of if"); @@ -1262,8 +1240,7 @@ Compiler.prototype.cif = function (s) { this.setBlock(next); this.vseqstmt(s.orelse); - } - else { + } else { this._jumpfalse(test, end); this.vseqstmt(s.body); } @@ -1283,8 +1260,7 @@ Compiler.prototype.cwhile = function (s) { if (s.orelse) { this.vseqstmt(s.orelse); } - } - else { + } else { top = this.newBlock("while test"); this._jump(top); this.setBlock(top); @@ -1303,7 +1279,7 @@ Compiler.prototype.cwhile = function (s) { this.setBlock(body); if ((Sk.debugging || Sk.killableWhile) && this.u.canSuspend) { - var suspType = 'Sk.delay'; + var suspType = "Sk.delay"; var debugBlock = this.newBlock("debug breakpoint for line "+s.lineno); out("if (Sk.breakpoints('"+this.filename+"',"+s.lineno+","+s.col_offset+")) {", "var $susp = $saveSuspension({data: {type: '"+suspType+"'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+");", @@ -1352,8 +1328,7 @@ Compiler.prototype.cfor = function (s) { // so it's preserved (as we cross blocks here and assume it survives) iter = "$loc." + this.gensym("iter"); out(iter, "=Sk.abstr.iter(", toiter, ");"); - } - else { + } else { iter = this._gr("iter", "Sk.abstr.iter(", toiter, ")"); this.u.tempsToSave.push(iter); // Save it across suspensions } @@ -1372,7 +1347,7 @@ Compiler.prototype.cfor = function (s) { target = this.vexpr(s.target, nexti); if ((Sk.debugging || Sk.killableFor) && this.u.canSuspend) { - var suspType = 'Sk.delay'; + var suspType = "Sk.delay"; var debugBlock = this.newBlock("debug breakpoint for line "+s.lineno); out("if (Sk.breakpoints('"+this.filename+"',"+s.lineno+","+s.col_offset+")) {", "var $susp = $saveSuspension({data: {type: '"+suspType+"'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+");", @@ -1436,8 +1411,7 @@ Compiler.prototype.craise = function (s) { // (and perhaps traceback for py2 if we care before it gets fully deprecated) out("throw ",exc,";"); - } - else { + } else { // re-raise out("throw $err;"); } @@ -1478,13 +1452,13 @@ Compiler.prototype.outputFinallyCascade = function (thisFinally) { nextFinally = this.peekFinallyBlock(); out("if($postfinally!==undefined) {", - "if ($postfinally.returning", - (nextFinally.breakDepth == thisFinally.breakDepth) ? "|| $postfinally.isBreak" : "", ") {", + "if ($postfinally.returning", + (nextFinally.breakDepth == thisFinally.breakDepth) ? "|| $postfinally.isBreak" : "", ") {", - "$blk=",nextFinally.blk,";continue;", - "} else {", - "$blk=$postfinally.gotoBlock;$postfinally=undefined;continue;", - "}", + "$blk=",nextFinally.blk,";continue;", + "} else {", + "$blk=$postfinally.gotoBlock;$postfinally=undefined;continue;", + "}", "}"); } }; @@ -1505,7 +1479,7 @@ Compiler.prototype.ctry = function (s) { if (s.finalbody) { finalBody = this.newBlock("finalbody"); - finalExceptionHandler = this.newBlock("finalexh") + finalExceptionHandler = this.newBlock("finalexh"); finalExceptionToReRaise = this._gr("finally_reraise", "undefined"); this.u.tempsToSave.push(finalExceptionToReRaise); @@ -1725,8 +1699,7 @@ Compiler.prototype.cimport = function (s) { if (alias.asname) { this.cimportas(alias.name, alias.asname, mod); - } - else { + } else { tmp = alias.name; lastDot = tmp.v.indexOf("."); if (lastDot !== -1) { @@ -1839,7 +1812,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal defaults = this.vseqexpr(args.defaults); } if (args && args.kw_defaults) { - kw_defaults = args.kw_defaults.map(e => e ? this.vexpr(e) : 'undefined'); + kw_defaults = args.kw_defaults.map(e => e ? this.vexpr(e) : "undefined"); } if (args && args.vararg) { vararg = args.vararg; @@ -1877,8 +1850,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal throw new SyntaxError(coname.v + "(): variable number of arguments in generators not supported"); } funcArgs.push("$gen"); - } - else { + } else { if (kwarg) { funcArgs.push("$kwa"); this.u.tempsToSave.push("$kwa"); @@ -1911,6 +1883,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal if (hasCell) { this.u.prefixCode += "\n// has cell\n"; } + this.u.prefixCode += "Sk.scopes=Sk.scopes||[];Sk.scopes.push('"+coname.v+"');\n"; // // set up standard dicts/variables @@ -2012,10 +1985,20 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal // this.u.suffixCode = "}break;}});"; // New switch code to catch exceptions - this.u.switchCode = "while(true){try{" - this.u.switchCode += this.outputInterruptTest(); - this.u.switchCode += "switch($blk){"; - this.u.suffixCode = "} }catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now();Sk.execPaused=0} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} }});"; + this.u.switchCode = "while(true){try{"; + this.u.switchCode += this.outputInterruptTest(); + this.u.switchCode += "switch($blk){"; + this.u.suffixCode = "}" + this.handleTraceback(true, coname.v); + this.u.suffixCode += "Sk.scopes.pop();"; + this.u.suffixCode += "});"; + /*this.u.suffixCode = ("} }catch(err){ "+ + "if (err instanceof Sk.builtin.TimeLimitError) {"+ + "Sk.execStart = Date.now();Sk.execPaused=0"+ + "} if (!(err instanceof Sk.builtin.BaseException)) {"+ + "err = new Sk.builtin.ExternalError(err);" + + "} err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'});"+ + "if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }}");*/ + // // jump back to the handler so it can do the main actual work of the @@ -2106,21 +2089,18 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal frees += ",$free"; } } - if (isGenerator) + if (isGenerator) { // Keyword and variable arguments are not currently supported in generators. // The call to pyCheckArgs assumes they can't be true. - { if (args && args.args.length > 0) { return this._gr("gener", "new Sk.builtins['function']((function(){var $origargs=Array.prototype.slice.call(arguments);Sk.builtin.pyCheckArgsLen(\"", - coname.v, "\",arguments.length,", args.args.length - defaults.length, ",", args.args.length, - ");return new Sk.builtins['generator'](", scopename, ",$gbl,$origargs", frees, ");}))"); - } - else { + coname.v, "\",arguments.length,", args.args.length - defaults.length, ",", args.args.length, + ");return new Sk.builtins['generator'](", scopename, ",$gbl,$origargs", frees, ");}))"); + } else { return this._gr("gener", "new Sk.builtins['function']((function(){Sk.builtin.pyCheckArgsLen(\"", coname.v, - "\",arguments.length,0,0);return new Sk.builtins['generator'](", scopename, ",$gbl,[]", frees, ");}))"); + "\",arguments.length,0,0);return new Sk.builtins['generator'](", scopename, ",$gbl,[]", frees, ");}))"); } - } - else { + } else { var res; if (decos.length > 0) { out("$ret = Sk.misceval.callsimOrSuspendArray(", scopename, ".$decorators[0], [new Sk.builtins['function'](", scopename, ",$gbl", frees, ")]);"); @@ -2192,8 +2172,7 @@ Compiler.prototype.cgenexpgen = function (generators, genIndex, elt) { // have to evaluate it outside and store it into the generator as a // local, which we retrieve here. iter = "$loc.$iter0"; - } - else { + } else { toiter = this.vexpr(ge.iter); iter = "$loc." + this.gensym("iter"); out(iter, "=", "Sk.abstr.iter(", toiter, ");"); @@ -2276,8 +2255,9 @@ Compiler.prototype.cclass = function (s) { entryBlock = this.newBlock("class entry"); this.u.prefixCode = "var " + scopename + "=(function $" + s.name.v + "$class_outer($globals,$locals,$cell){var $gbl=$globals,$loc=$locals;$free=$globals;"; + this.u.prefixCode += "Sk.scopes=Sk.scopes||[];Sk.scopes.push('"+s.name.v+"');\n"; this.u.switchCode += "(function $" + s.name.v + "$_closure($cell){"; - this.u.switchCode += "var $blk=" + entryBlock + ",$exc=[],$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;" + this.u.switchCode += "var $blk=" + entryBlock + ",$exc=[],$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;"; if (Sk.execLimit !== null) { this.u.switchCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now();Sk.execPaused=0}"; @@ -2289,7 +2269,14 @@ Compiler.prototype.cclass = function (s) { this.u.switchCode += "while(true){try{"; this.u.switchCode += this.outputInterruptTest(); this.u.switchCode += "switch($blk){"; - this.u.suffixCode = "}}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now();Sk.execPaused=0} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }}}" + this.u.suffixCode = "}" + this.handleTraceback(true, s.name.v); + this.u.suffixCode += "Sk.scopes.pop();"; + /*this.u.suffixCode = ("}}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) {"+ + "Sk.execStart = Date.now();Sk.execPaused=0"+ + "} if (!(err instanceof Sk.builtin.BaseException)) {"+ + " err = new Sk.builtin.ExternalError(err);"+ + "} err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '" + this.filename + "'});"+ + "if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }}}");*/ this.u.suffixCode += "}).call(null, $cell);});"; this.u.private_ = s.name; @@ -2539,8 +2526,7 @@ Compiler.prototype.nameop = function (name, ctx, dataToStore) { mangledNoPre = mangled; if (this.u.ste.generator || this.u.ste.blockType !== Sk.SYMTAB_CONSTS.FunctionBlock) { mangled = "$loc." + mangled; - } - else if (optype === OP_FAST || optype === OP_NAME) { + } else if (optype === OP_FAST || optype === OP_NAME) { this.u.localnames.push(mangled); } @@ -2648,8 +2634,7 @@ Compiler.prototype.exitScope = function () { this.nestlevel--; if (this.stack.length - 1 >= 0) { this.u = this.stack.pop(); - } - else { + } else { this.u = null; } if (this.u) { @@ -2701,7 +2686,6 @@ Compiler.prototype.cprint = function (s) { out("$ret = Sk.misceval.print_(", /*dest, ',*/ "\"\\n\");"); this._checkSuspension(s); } - }; Compiler.prototype.cmod = function (mod) { @@ -2714,6 +2698,7 @@ Compiler.prototype.cmod = function (mod) { this.u.varDeclsCode = "var $gbl = $forcegbl || {}, $blk=" + entryBlock + ",$exc=[],$loc=$gbl,$cell={},$err=undefined;" + + "Sk.scopes=Sk.scopes||[];Sk.scopes.push('');"+ "$loc.__file__=new Sk.builtins.str('" + this.filename + "');var $ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;"; @@ -2744,8 +2729,10 @@ Compiler.prototype.cmod = function (mod) { this.u.switchCode = "while(true){try{"; this.u.switchCode += this.outputInterruptTest(); this.u.switchCode += "switch($blk){"; - this.u.suffixCode = "}" - this.u.suffixCode += "}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now();Sk.execPaused=0} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} } });"; + this.u.suffixCode = "}" + this.handleTraceback(true, ""); + this.u.suffixCode += "Sk.scopes.pop();"; + this.u.suffixCode += "});"; + //this.u.suffixCode += "}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now();Sk.execPaused=0} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} } });"; // Note - this change may need to be adjusted for all the other instances of // switchCode and suffixCode in this file. Not knowing how to test those @@ -2774,6 +2761,23 @@ Compiler.prototype.cmod = function (mod) { return modf; }; +Compiler.prototype.handleTraceback = function(doContinue, scopeName) { + doContinue = doContinue ? "continue" : ""; + return ("}catch(err){ "+ + "Sk.err=[err,$blk];"+ + "if(err instanceof Sk.builtin.TimeLimitError){"+ + "Sk.execStart=Date.now();Sk.execPaused=0"+ + "}if(!(err instanceof Sk.builtin.BaseException)) {"+ + "err=new Sk.builtin.ExternalError(err);" + + "}err.traceback.push({"+ + "lineno:$currLineNo,"+ + "colno:$currColNo,"+ + "filename:'"+this.filename+"'," + + "scope:'"+scopeName+"'"+ + "});"+ + "if($exc.length>0){$err=err;$blk=$exc.pop();"+doContinue+"}else{throw err;}}}"); +}; + /** * @param {string} source the code * @param {string} filename where it came from diff --git a/src/errors.js b/src/errors.js index ecb3f11059..33e1ba9636 100644 --- a/src/errors.js +++ b/src/errors.js @@ -30,6 +30,9 @@ Sk.builtin.BaseException = function (args) { } this.args = new Sk.builtin.tuple(args); this.traceback = []; + // TODO: Hack, this exception isn't guaranteed to be thrown! + this.err = Sk.err; + //Sk.err = this; // For errors occurring during normal execution, the line/col/etc // of the error are populated by each stack frame of the runtime code, @@ -568,6 +571,49 @@ Sk.builtin.StopIteration = function (args) { Sk.abstr.setUpInheritance("StopIteration", Sk.builtin.StopIteration, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.StopIteration", Sk.builtin.StopIteration); +/** + * @constructor + */ +Sk.builtin.frame = function() { + if (!(this instanceof Sk.builtin.frame)) { + return new Sk.builtin.frame(); + } + + this.__class__ = Sk.builtin.frame; + return this; +}; + +Sk.abstr.setUpInheritance("frame", Sk.builtin.frame, Sk.builtin.object); + +Sk.builtin.frame.prototype.tp$getattr = function (name) { + if (name != null && (Sk.builtin.checkString(name) || typeof name === "string")) { + var _name = name; + + // get javascript string + if (Sk.builtin.checkString(name)) { + _name = Sk.ffi.remapToJs(name); + } + + switch (_name) { + case "f_back": return Sk.builtin.none.none$; + case "f_builtins": return Sk.builtin.none.none$; + case "f_code": return Sk.builtin.none.none$; + case "f_globals": return Sk.builtin.none.none$; + case "f_lasti": return Sk.builtin.none.none$; + case "f_lineno": return Sk.builtin.none.none$; + case "f_locals": return Sk.builtin.none.none$; + case "f_trace": return Sk.builtin.none.none$; + } + } + + // if we have not returned yet, try the genericgetattr + return Sk.builtin.object.prototype.GenericGetAttr(name); +}; +Sk.builtin.frame.prototype["$r"] = function () { + return new Sk.builtin.str(""); +}; +Sk.exportSymbol("Sk.builtin.frame", Sk.builtin.frame); + /** * @constructor * @param {Object} err @@ -583,6 +629,8 @@ Sk.builtin.traceback = function(err) { } this.tb_lineno = new Sk.builtin.int_(lineno); + // TODO: Hack, you know this isn't right + this.tb_frame = Sk.builtin.none.none$; //tb_frame, tb_lasti, tb_lineno, tb_next @@ -601,8 +649,10 @@ Sk.builtin.traceback.prototype.tp$getattr = function (name) { _name = Sk.ffi.remapToJs(name); } - if (_name === "tb_lineno") { - return this[_name]; + switch (_name) { + case "tb_lineno": return this[_name]; + case "tb_frame": return new Sk.builtin.frame(); + case "tb_next": return Sk.builtin.none.none$; } } @@ -610,7 +660,7 @@ Sk.builtin.traceback.prototype.tp$getattr = function (name) { return Sk.builtin.object.prototype.GenericGetAttr(name); }; Sk.builtin.traceback.prototype["$r"] = function () { - return new Sk.builtin.str(""); + return new Sk.builtin.str(""); }; Sk.exportSymbol("Sk.builtin.traceback", Sk.builtin.traceback); diff --git a/src/import.js b/src/import.js index c716511ec0..2ab374ec3b 100644 --- a/src/import.js +++ b/src/import.js @@ -337,7 +337,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela // } finalcode += "\n" + co.funcname + ";"; - + //console.log(finalcode); modscope = Sk.global["eval"](finalcode); module["$d"] = { diff --git a/src/lib/PIL/__init__.js b/src/lib/PIL/__init__.js new file mode 100644 index 0000000000..6b3ca50337 --- /dev/null +++ b/src/lib/PIL/__init__.js @@ -0,0 +1,142 @@ +var $builtinmodule = function(name) { + var mod, sampleWrapper; + mod = {__name__: "PIL"}; + + if (!Sk.PIL) { + Sk.PIL = {assets: {}}; + } + + // InstantPromise is a workaround to allow usage of the clean promise-style + // then/catch syntax but to instantly call resolve the then/catch chain so we + // can avoid creating Suspensions in unnecessary cases. This is desirable + // because Suspensions have a fairly large negative impact on overall + // performance. These 'instant promises' come into play when a tracer() + // call is made with a value other than 1. When tracer is 0 or greater than 1 + // , we can bypass the creation of a Suspension and proceed to the next line of + // code immediately if the current line is not going to involve a screen + // update. We determine if a real promise or InstantPromise is necessary by + // checking FrameManager.willRenderNext() + function InstantPromise(err, result) { + this.lastResult = result; + this.lastError = err; + } + + InstantPromise.prototype.then = function(cb) { + if (this.lastError) { + return this; + } + + try { + this.lastResult = cb(this.lastResult); + } catch(e) { + this.lastResult = undefined; + this.lastError = e; + } + + return this.lastResult instanceof Promise ? this.lastResult : this; + }; + + InstantPromise.prototype.catch = function(cb) { + if (this.lastError) { + try { + this.lastResult = cb(this.lastError); + this.lastError = undefined; + } catch(e) { + this.lastResult = undefined; + this.lastError = e; + } + } + + return this.lastResult instanceof Promise ? this.lastResult : this; + }; + + var buildImage = function(imageData) { + + }; + + function getAsset(name) { + return new Promise(function(resolve, reject) { + if (Sk.PIL.assets[name] !== undefined) { + //return Sk.PIL.assets[name]; + resolve(Sk.PIL.assets[name]); + } else { + var img = new Image(); + img.onload = function () { + Sk.PIL.assets[name] = this; + resolve(this); + }; + img.onerror = function () { + //throw new Error("Failed to load asset: " + name); + reject(name); + }; + img.src = name; + } + }); + } + + var image = function($gbl, $loc) { + // open(filename) or open(url) + // show() + + $loc.__init__ = new Sk.builtin.func(function (self) { + // + }); + + $loc.open = new Sk.builtin.func(function(self, file_or_url) { + Sk.builtin.pyCheckArgs("open", arguments, 2, 2); + Sk.builtin.pyCheckType("file_or_url", "string", Sk.builtin.checkString(file_or_url)); + self.file_or_url = file_or_url; + // TODO: Change to suspension + var imagePromise = getAsset(Sk.ffi.remapToJs(file_or_url)); + var susp = new Sk.misceval.Suspension(); + self.image = Sk.builtin.none.none$; + susp.resume = function() { + if (susp.data["error"]) { + //throw new Sk.builtin.IOError(susp.data["error"].message); + throw susp.data["error"]; + } else { + console.log("RESUMED"); + return self.image; + } + }; + susp.data = { + type: "Sk.promise", + promise: imagePromise.then(function(value) { + console.log("PROMISED"); + self.image = value; + self.canvas = document.createElement("canvas"); + self.canvas.width = self.image.width; + self.canvas.height = self.image.height; + console.log(self.image); + console.log(self.image.width, self.image.height); + self.canvas.getContext("2d").drawImage(self.image, 0, 0, self.image.width, self.image.height); + self.pixels = self.canvas.getContext("2d").getImageData(0, 0, self.image.width, self.image.height).data; + console.log(self.pixels); + return value; + }, function(err) { + self.image = ""; + throw err; + //return err; + }) + }; + + return susp; + }); + + $loc.show = new Sk.builtin.func(function(self) { + if (Sk.console === undefined) { + throw new Sk.builtin.NameError("Can not resolve drawing area. Sk.console is undefined!"); + } + + var consoleData = { + image: self.image, + file_or_url: self.file_or_url + }; + + Sk.console.printPILImage(consoleData); + }); + }; + mod.Image = Sk.misceval.buildClass(mod, image, "Image", []); + + return mod; +}; \ No newline at end of file diff --git a/src/lib/image.js b/src/lib/image_old.js similarity index 100% rename from src/lib/image.js rename to src/lib/image_old.js diff --git a/src/lib/matplotlib/pyplot/__init__.js b/src/lib/matplotlib/pyplot/__init__.js index f60c9f650b..29929122f9 100644 --- a/src/lib/matplotlib/pyplot/__init__.js +++ b/src/lib/matplotlib/pyplot/__init__.js @@ -2,7 +2,7 @@ var jsplotlib = {}; // Skulpt translation var $builtinmodule = function(name) { - var mod = {}; + var mod = {__name__: "matplotlib.pyplot"}; // Unique ID generator for charts var chartCounter = 0; diff --git a/src/lib/pedal/report/imperative.py b/src/lib/pedal/report/imperative.py index a3672d76bc..ef8cb76839 100644 --- a/src/lib/pedal/report/imperative.py +++ b/src/lib/pedal/report/imperative.py @@ -5,7 +5,7 @@ __all__ = ['set_success', 'compliment', 'give_partial', 'explain', 'explain_r', 'gently', 'gently_r', 'hide_correctness', 'suppress', 'log', 'debug', - 'clear_report', 'get_all_feedback', 'MAIN_REPORT'] + 'clear_report', 'get_all_feedback', 'MAIN_REPORT', 'guidance'] from pedal.report.report import Report @@ -52,6 +52,10 @@ def explain(message, priority='medium', line=None, label='explain'): MAIN_REPORT.explain(message, priority, line, label=label) +def guidance(message, priority='medium', line=None, label='Guidance'): + MAIN_REPORT.guidance(message, priority, line, label=label) + + def gently(message, line=None, label='explain'): MAIN_REPORT.gently(message, line, label=label) diff --git a/src/lib/pedal/report/report.py b/src/lib/pedal/report/report.py index dd7690a97c..2b14bd30df 100644 --- a/src/lib/pedal/report/report.py +++ b/src/lib/pedal/report/report.py @@ -89,6 +89,14 @@ def gently(self, message, line=None, group=None, label='explain'): self.explain(message, priority='student', line=line, group=group, label=label) + def guidance(self, message, line=None, group=None, label='guidance'): + hint = {'message': message} + if line is not None: + hint['line'] = line + if group is None: + group = self.group + self.attach(label, priority='instructions', category='instructions', group=group, hint=hint) + def compliment(self, message, line=None, group=None, label='explain'): self.explain(message, priority='positive', line=line, group=group, label=label) diff --git a/src/lib/pedal/tifa/tifa.py b/src/lib/pedal/tifa/tifa.py index fb4c57ee0a..0d723fd6f0 100644 --- a/src/lib/pedal/tifa/tifa.py +++ b/src/lib/pedal/tifa/tifa.py @@ -551,7 +551,7 @@ def _visit_collection_loop(self, node): def visit_comprehension(self, node): self._visit_collection_loop(node) - # Handle the bodies + # Handle ifs, unless they're blank (None in Skulpt :) if node.ifs: self.visit_statements(node.ifs) @@ -842,8 +842,7 @@ def visit_UnaryOp(self, node): elif type(node.op) in VALID_UNARYOP_TYPES: op_lookup = VALID_UNARYOP_TYPES[type(node.op)] if type(operand) in op_lookup: - op_lookup = op_lookup[type(operand)] - return op_lookup(operand) + return op_lookup[type(operand)]() return UnknownType() def visit_While(self, node): diff --git a/src/lib/pedal/toolkit/utilities.py b/src/lib/pedal/toolkit/utilities.py index b7c850c003..04c85dae16 100644 --- a/src/lib/pedal/toolkit/utilities.py +++ b/src/lib/pedal/toolkit/utilities.py @@ -116,6 +116,13 @@ def prevent_builtin_usage(function_names): return None +def find_negatives(root=None): + if root is None: + root = parse_program() + return [-op.operand.n for op in root.find_all("UnaryOp") + if op.op.ast_name == "USub" and op.operand.ast_name == "Num"] + + # TODO: UGLY HACK. This is to avoid muted=False kwargs in the following # functions. Apparently skulpt doesn't support this syntax. muted = False @@ -136,11 +143,12 @@ def prevent_literal(*literals): ast = parse_program() str_values = [s.s for s in ast.find_all("Str")] num_values = [n.n for n in ast.find_all("Num")] + negative_values = find_negatives(ast) name_values = ([name.id for name in ast.find_all("Name")]+ [name.value for name in ast.find_all("NameConstant")]) for literal in literals: if isinstance(literal, (int, float)): - if literal in num_values: + if literal in num_values or literal in negative_values: if not muted: explain_r(message.format(repr(literal)), code, label=label) return literal @@ -172,6 +180,7 @@ def ensure_literal(*literals): ast = parse_program() str_values = [s.s for s in ast.find_all("Str")] num_values = [n.n for n in ast.find_all("Num")] + negative_values = find_negatives(ast) name_values = ([str(name.id) for name in ast.find_all("Name")]+ [str(name.value) for name in ast.find_all("NameConstant")]) for literal in literals: @@ -181,7 +190,7 @@ def ensure_literal(*literals): explain_r(message.format(repr(literal)), code, label=label) return True elif isinstance(literal, (int, float)): - if literal not in num_values: + if literal not in num_values and literal not in negative_values: if not muted: explain_r(message.format(repr(literal)), code, label=label) return literal @@ -234,8 +243,8 @@ def prevent_advanced_iteration(): "&": "BitAnd", "@": "MatMult"} UNARY_OP_NAMES = { - # "+=": "UAdd", - # "-=": "USub", + # "+": "UAdd", + # "-": "USub", "not": "Not", "~": "Invert" } diff --git a/src/lib/sound/sample.js b/src/lib/sound/sample.js index 83b1ac1796..4464a2cf36 100644 --- a/src/lib/sound/sample.js +++ b/src/lib/sound/sample.js @@ -1,49 +1,49 @@ var $builtinmodule = function() { - var mod, sampleWrapper; - - mod = {}; - - sampleWrapper = { - getSound : new Sk.builtin.func(function (sample) { - Sk.builtin.pyCheckArgs('getSound', arguments, 1); - return sample._sound; - }), - - getSampleValue : new Sk.builtin.func(function (sample) { - Sk.builtin.pyCheckArgs('getSampleValue', arguments, 1); - return new Sk.builtin.float_(sample._internalSound.getLeftSample(sample._index)); - }), - - setSampleValue : new Sk.builtin.func(function (sample, value) { - Sk.builtin.pyCheckArgs('setSampleValue', arguments, 2); - sample._internalSound.setLeftSample(sample._index, Sk.ffi.unwrapo(value)); - }), - }; - - mod.Sample = Sk.misceval.buildClass(mod, function ($gbl, $loc) { - $loc.__init__ = new Sk.builtin.func(function (self, sound, index) { - Sk.builtin.pyCheckArgs('__init__', arguments, 3); - self._sound = sound; - self._internalSound = sound._sound; - self._index = Sk.ffi.unwrapo(index); - }); - - $loc.__str__ = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgs('__str__', arguments, 1); - return new Sk.builtin.str('Sample at ' + self._index + ' with value ' + + var mod, sampleWrapper; + + mod = {}; + + sampleWrapper = { + getSound : new Sk.builtin.func(function (sample) { + Sk.builtin.pyCheckArgs("getSound", arguments, 1); + return sample._sound; + }), + + getSampleValue : new Sk.builtin.func(function (sample) { + Sk.builtin.pyCheckArgs("getSampleValue", arguments, 1); + return new Sk.builtin.float_(sample._internalSound.getLeftSample(sample._index)); + }), + + setSampleValue : new Sk.builtin.func(function (sample, value) { + Sk.builtin.pyCheckArgs("setSampleValue", arguments, 2); + sample._internalSound.setLeftSample(sample._index, Sk.ffi.unwrapo(value)); + }), + }; + + mod.Sample = Sk.misceval.buildClass(mod, function ($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self, sound, index) { + Sk.builtin.pyCheckArgs("__init__", arguments, 3); + self._sound = sound; + self._internalSound = sound._sound; + self._index = Sk.ffi.unwrapo(index); + }); + + $loc.__str__ = new Sk.builtin.func(function (self) { + Sk.builtin.pyCheckArgs("__str__", arguments, 1); + return new Sk.builtin.str("Sample at " + self._index + " with value " + self._internalSound.getLeftSample(self._index)); - }); + }); - $loc.__repr__ = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgs('__repr__', arguments, 1); - return new Sk.builtin.str('Sample at ' + self._index + ' with value ' + + $loc.__repr__ = new Sk.builtin.func(function (self) { + Sk.builtin.pyCheckArgs("__repr__", arguments, 1); + return new Sk.builtin.str("Sample at " + self._index + " with value " + self._internalSound.getLeftSample(self._index)); - }); + }); - goog.object.extend($loc, sampleWrapper); - }, 'Sample', []); + goog.object.extend($loc, sampleWrapper); + }, "Sample", []); - goog.object.extend(mod, sampleWrapper); + goog.object.extend(mod, sampleWrapper); - return mod; + return mod; }; diff --git a/src/lib/sound/sound.js b/src/lib/sound/sound.js index 171a7c42bc..fe6ed9d93b 100644 --- a/src/lib/sound/sound.js +++ b/src/lib/sound/sound.js @@ -1,317 +1,317 @@ // Do not include this module directly as it has dependencies var $builtinmodule = function() { - var soundWrapper, mod, Sample; + var soundWrapper, mod, Sample; - mod = {}; + mod = {}; - // Dependency - Sample = Sk.sysmodules.mp$subscript('sound.sample').$d.Sample; + // Dependency + Sample = Sk.sysmodules.mp$subscript("sound.sample").$d.Sample; - soundWrapper = { - stopPlaying: new Sk.builtin.func(function (sound) { - Sk.builtin.pyCheckArgs('stopPlaying', arguments, 1); - sound._sound.stop(); - }), + soundWrapper = { + stopPlaying: new Sk.builtin.func(function (sound) { + Sk.builtin.pyCheckArgs("stopPlaying", arguments, 1); + sound._sound.stop(); + }), - play : new Sk.builtin.func(function(sound) { - Sk.builtin.pyCheckArgs('play', arguments, 1); - sound._sound.play(); - }), + play : new Sk.builtin.func(function(sound) { + Sk.builtin.pyCheckArgs("play", arguments, 1); + sound._sound.play(); + }), - blockingPlay : new Sk.builtin.func(function(sound) { - Sk.builtin.pyCheckArgs('blockingPlay', arguments, 1); - Sk.future(function (continueWith) { - sound._sound.play(continueWith); - }); - }), + blockingPlay : new Sk.builtin.func(function(sound) { + Sk.builtin.pyCheckArgs("blockingPlay", arguments, 1); + Sk.future(function (continueWith) { + sound._sound.play(continueWith); + }); + }), - getDuration : new Sk.builtin.func(function(sound) { - Sk.builtin.pyCheckArgs('getDuration', arguments, 1); - return new Sk.builtin.float_(sound._sound.getDuration()); - }), + getDuration : new Sk.builtin.func(function(sound) { + Sk.builtin.pyCheckArgs("getDuration", arguments, 1); + return new Sk.builtin.float_(sound._sound.getDuration()); + }), - getNumSamples : new Sk.builtin.func(function(sound) { - Sk.builtin.pyCheckArgs('getNumSamples', arguments, 1); - return new Sk.builtin.int_(sound._sound.getLength()); - }), + getNumSamples : new Sk.builtin.func(function(sound) { + Sk.builtin.pyCheckArgs("getNumSamples", arguments, 1); + return new Sk.builtin.int_(sound._sound.getLength()); + }), - getLength : new Sk.builtin.func(function(sound) { - Sk.builtin.pyCheckArgs('getLength', arguments, 1); - return new Sk.builtin.int_(sound._sound.getLength()); - }), + getLength : new Sk.builtin.func(function(sound) { + Sk.builtin.pyCheckArgs("getLength", arguments, 1); + return new Sk.builtin.int_(sound._sound.getLength()); + }), - getSamplingRate : new Sk.builtin.func(function(sound) { - Sk.builtin.pyCheckArgs('getSamplingRate', arguments, 1); - return new Sk.builtin.int_(sound._sound.getSamplingRate()); - }), + getSamplingRate : new Sk.builtin.func(function(sound) { + Sk.builtin.pyCheckArgs("getSamplingRate", arguments, 1); + return new Sk.builtin.int_(sound._sound.getSamplingRate()); + }), - setSampleValueAt : new Sk.builtin.func(function(sound, index, value) { - var length; + setSampleValueAt : new Sk.builtin.func(function(sound, index, value) { + var length; - Sk.builtin.pyCheckArgs('setSampleValueAt', arguments, 3); + Sk.builtin.pyCheckArgs("setSampleValueAt", arguments, 3); - length = sound._sound.getLength(); + length = sound._sound.getLength(); - if(index < 0 || index >= length) { - throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); - } + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); + } - if(!(value instanceof Sk.builtin.int_)) { - throw new Sk.builtin.TypeError('Value must be an integer'); - } + if(!(value instanceof Sk.builtin.int_)) { + throw new Sk.builtin.TypeError("Value must be an integer"); + } - value = Sk.ffi.unwrapo(value); + value = Sk.ffi.unwrapo(value); - if(value < -32768) { value = -32768; } - if(value > 32767) { value = 32767; } + if(value < -32768) { value = -32768; } + if(value > 32767) { value = 32767; } - sound._sound.setLeftSample(Sk.ffi.unwrapo(index), pythy.Sound.map16BitIntToFloat(value)); - }), + sound._sound.setLeftSample(Sk.ffi.unwrapo(index), pythy.Sound.map16BitIntToFloat(value)); + }), - setLeftSample : new Sk.builtin.func(function(sound, index, value) { - var length; + setLeftSample : new Sk.builtin.func(function(sound, index, value) { + var length; - Sk.builtin.pyCheckArgs('setLeftSample', arguments, 3); + Sk.builtin.pyCheckArgs("setLeftSample", arguments, 3); - length = sound._sound.getLength(); + length = sound._sound.getLength(); - if(index < 0 || index >= length) { - throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); - } + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); + } - if(!(value instanceof Sk.builtin.int_)) { - throw new Sk.builtin.TypeError('Value must be an integer'); - } + if(!(value instanceof Sk.builtin.int_)) { + throw new Sk.builtin.TypeError("Value must be an integer"); + } - value = Sk.ffi.unwrapo(value); + value = Sk.ffi.unwrapo(value); - if(value < -32768) { value = -32768; } - if(value > 32767) { value = 32767; } + if(value < -32768) { value = -32768; } + if(value > 32767) { value = 32767; } - sound._sound.setLeftSample(Sk.ffi.unwrapo(index), pythy.Sound.map16BitIntToFloat(value)); - }), + sound._sound.setLeftSample(Sk.ffi.unwrapo(index), pythy.Sound.map16BitIntToFloat(value)); + }), - setRightSample : new Sk.builtin.func(function(sound, index, value) { - var length; + setRightSample : new Sk.builtin.func(function(sound, index, value) { + var length; - Sk.builtin.pyCheckArgs('setRightSample', arguments, 3); + Sk.builtin.pyCheckArgs("setRightSample", arguments, 3); - length = sound._sound.getLength(); + length = sound._sound.getLength(); - if(index < 0 || index >= length) { - throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); - } + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); + } - if(!(value instanceof Sk.builtin.int_)) { - throw new Sk.builtin.TypeError('Value must be an integer'); - } + if(!(value instanceof Sk.builtin.int_)) { + throw new Sk.builtin.TypeError("Value must be an integer"); + } - value = Sk.ffi.unwrapo(value); + value = Sk.ffi.unwrapo(value); - if(value < -32768) { value = -32768; } - if(value > 32767) { value = 32767; } + if(value < -32768) { value = -32768; } + if(value > 32767) { value = 32767; } - sound._sound.setRightSample(Sk.ffi.unwrapo(index), pythy.Sound.map16BitIntToFloat(value)); - }), + sound._sound.setRightSample(Sk.ffi.unwrapo(index), pythy.Sound.map16BitIntToFloat(value)); + }), - getSampleValueAt : new Sk.builtin.func(function(sound, index) { - var length; + getSampleValueAt : new Sk.builtin.func(function(sound, index) { + var length; - Sk.builtin.pyCheckArgs('getSampleValueAt', arguments, 2); + Sk.builtin.pyCheckArgs("getSampleValueAt", arguments, 2); - length = sound._sound.getLength(); + length = sound._sound.getLength(); - if(index < 0 || index >= length) { - throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); - } + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); + } - return new Sk.builtin.int_(pythy.Sound.mapFloatTo16BitInt(sound._sound.getLeftSample(Sk.ffi.unwrapo(index)))); - }), + return new Sk.builtin.int_(pythy.Sound.mapFloatTo16BitInt(sound._sound.getLeftSample(Sk.ffi.unwrapo(index)))); + }), - getLeftSample : new Sk.builtin.func(function(sound, index) { - var length; + getLeftSample : new Sk.builtin.func(function(sound, index) { + var length; - Sk.builtin.pyCheckArgs('getLeftSample', arguments, 2); + Sk.builtin.pyCheckArgs("getLeftSample", arguments, 2); - length = sound._sound.getLength(); + length = sound._sound.getLength(); - if(index < 0 || index >= length) { - throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); - } + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); + } - return new Sk.builtin.int_(pythy.Sound.mapFloatTo16BitInt(sound._sound.getLeftSample(Sk.ffi.unwrapo(index)))); - }), + return new Sk.builtin.int_(pythy.Sound.mapFloatTo16BitInt(sound._sound.getLeftSample(Sk.ffi.unwrapo(index)))); + }), - getRightSample : new Sk.builtin.func(function(sound, index) { - var length; + getRightSample : new Sk.builtin.func(function(sound, index) { + var length; - Sk.builtin.pyCheckArgs('getRightSample', arguments, 2); + Sk.builtin.pyCheckArgs("getRightSample", arguments, 2); - length = sound._sound.getLength(); + length = sound._sound.getLength(); - if(index < 0 || index >= length) { - throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); - } + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); + } - return new Sk.builtin.int_(pythy.Sound.mapFloatTo16BitInt(sound._sound.getRightSample(Sk.ffi.unwrapo(index)))); - }), + return new Sk.builtin.int_(pythy.Sound.mapFloatTo16BitInt(sound._sound.getRightSample(Sk.ffi.unwrapo(index)))); + }), - getSampleObjectAt : new Sk.builtin.func(function (sound, index) { - var length; + getSampleObjectAt : new Sk.builtin.func(function (sound, index) { + var length; - Sk.builtin.pyCheckArgs('getSampleObjectAt', arguments, 2); + Sk.builtin.pyCheckArgs("getSampleObjectAt", arguments, 2); - length = sound._sound.getLength(); + length = sound._sound.getLength(); - if(index < 0 || index >= length) { - throw new Sk.builtin.ValueError('Index must have a value between 0 and ' + length); - } + if(index < 0 || index >= length) { + throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); + } - return Sk.misceval.callsim(Sample, sound, index); - }), + return Sk.misceval.callsim(Sample, sound, index); + }), - getSamples : new Sk.builtin.func(function (sound) { - var samples, len; + getSamples : new Sk.builtin.func(function (sound) { + var samples, len; - Sk.builtin.pyCheckArgs('getSamples', arguments, 1); - - samples = []; - len = sound._sound.getLength(); - - for(var i = 0; i < len; i++) { - samples.push(Sk.misceval.callsim(Sample, sound, Sk.builtin.int_(i))); - } - - return new Sk.builtin.list(samples); - }) - }; - - mod.Sound = Sk.misceval.buildClass(mod, function ($gbl, $loc) { - var onError; - - onError = function (continueWith) { - return function (errorMsg) { - if(errorMsg.indexOf('File') !== -1) { - continueWith(new Sk.builtin.ValueError(errorMsg + '. Is the URL incorrect?')); - } else { - continueWith(new Sk.builtin.ValueError(errorMsg)); - } - } - }; + Sk.builtin.pyCheckArgs("getSamples", arguments, 1); - $loc.__init__ = new Sk.builtin.func(function (sound) { - var arg0, res, arg1, arg2; + samples = []; + len = sound._sound.getLength(); - Sk.builtin.pyCheckArgs('__init__', arguments, [2, 3]); + for(var i = 0; i < len; i++) { + samples.push(Sk.misceval.callsim(Sample, sound, Sk.builtin.int_(i))); + } - arg0 = arguments[1]; + return new Sk.builtin.list(samples); + }) + }; - if(arg0 instanceof Sk.builtin.str) { - arg0 = Sk.ffi.unwrapo(arg0); //url - res = Sk.future(function (continueWith) { - new window.pythy.Sound(continueWith, onError(continueWith), arg0); - }); - } else if(arg0.tp$name === 'Sound') { - res = Sk.future(function (continueWith) { - new window.pythy.Sound(continueWith, onError(continueWith), arg0._sound); + mod.Sound = Sk.misceval.buildClass(mod, function ($gbl, $loc) { + var onError; + + onError = function (continueWith) { + return function (errorMsg) { + if(errorMsg.indexOf("File") !== -1) { + continueWith(new Sk.builtin.ValueError(errorMsg + ". Is the URL incorrect?")); + } else { + continueWith(new Sk.builtin.ValueError(errorMsg)); + } + }; + }; + + $loc.__init__ = new Sk.builtin.func(function (sound) { + var arg0, res, arg1, arg2; + + Sk.builtin.pyCheckArgs("__init__", arguments, [2, 3]); + + arg0 = arguments[1]; + + if(arg0 instanceof Sk.builtin.str) { + arg0 = Sk.ffi.unwrapo(arg0); //url + res = Sk.future(function (continueWith) { + new window.pythy.Sound(continueWith, onError(continueWith), arg0); + }); + } else if(arg0.tp$name === "Sound") { + res = Sk.future(function (continueWith) { + new window.pythy.Sound(continueWith, onError(continueWith), arg0._sound); + }); + } else { + arg1 = Sk.ffi.unwrapo(arguments[1]); //numSamples + arg2 = Sk.ffi.unwrapo(arguments[2]); //samplingRate + res = Sk.future(function (continueWith) { + new window.pythy.Sound(continueWith, onError(continueWith), arg1, arg2); + }); + } + + if(res instanceof window.pythy.Sound) { + sound._sound = res; + } else if(res) { + throw res; + } }); - } else { - arg1 = Sk.ffi.unwrapo(arguments[1]); //numSamples - arg2 = Sk.ffi.unwrapo(arguments[2]); //samplingRate - res = Sk.future(function (continueWith) { - new window.pythy.Sound(continueWith, onError(continueWith), arg1, arg2); - }); - } - if(res instanceof window.pythy.Sound) { - sound._sound = res; - } else if(res) { - throw res; - } - }); + $loc.__str__ = new Sk.builtin.func(function(sound) { + var str; - $loc.__str__ = new Sk.builtin.func(function(sound) { - var str; + Sk.builtin.pyCheckArgs("__str__", arguments, 1); - Sk.builtin.pyCheckArgs('__str__', arguments, 1); + str = "Sound, "; - str = 'Sound, '; + if(sound._sound.url) { + str += "File: " + sound._sound.url + ", "; + } - if(sound._sound.url) { - str += 'File: ' + sound._sound.url + ', '; - } - - return new Sk.builtin.str(str + 'Number of samples: ' + sound._sound.getLength()); - }); + return new Sk.builtin.str(str + "Number of samples: " + sound._sound.getLength()); + }); - $loc.__repr__ = new Sk.builtin.func(function(sound) { - var str; + $loc.__repr__ = new Sk.builtin.func(function(sound) { + var str; - Sk.builtin.pyCheckArgs('__repr__', arguments, 1); + Sk.builtin.pyCheckArgs("__repr__", arguments, 1); - str = 'Sound, '; + str = "Sound, "; - if(sound._sound.url) { - str += 'File: ' + sound._sound.url + ', '; - } + if(sound._sound.url) { + str += "File: " + sound._sound.url + ", "; + } - return new Sk.builtin.str(str + 'Number of samples: ' + sound._sound.getLength()); - }); + return new Sk.builtin.str(str + "Number of samples: " + sound._sound.getLength()); + }); - $loc.writeToFile = new Sk.builtin.func(function(sound, path) { - Sk.builtin.pyCheckArgs('writeToFile', arguments, 2); - sound._sound.save(Sk.ffi.unwrapo(path)); - }); + $loc.writeToFile = new Sk.builtin.func(function(sound, path) { + Sk.builtin.pyCheckArgs("writeToFile", arguments, 2); + sound._sound.save(Sk.ffi.unwrapo(path)); + }); - $loc.duplicate = new Sk.builtin.func(function (sound) { - Sk.builtin.pyCheckArgs('duplicate', arguments, 1); - return Sk.misceval.callsim(mod.Sound, sound); - }); + $loc.duplicate = new Sk.builtin.func(function (sound) { + Sk.builtin.pyCheckArgs("duplicate", arguments, 1); + return Sk.misceval.callsim(mod.Sound, sound); + }); - goog.object.extend($loc, soundWrapper); + goog.object.extend($loc, soundWrapper); - }, 'Sound', []); + }, "Sound", []); - goog.object.extend(mod, soundWrapper); + goog.object.extend(mod, soundWrapper); - goog.object.extend(mod, { - duplicateSound: new Sk.builtin.func(function (sound) { - Sk.builtin.pyCheckArgs('duplicateSound', arguments, 1); - return Sk.misceval.callsim(mod.Sound, sound); - }), + goog.object.extend(mod, { + duplicateSound: new Sk.builtin.func(function (sound) { + Sk.builtin.pyCheckArgs("duplicateSound", arguments, 1); + return Sk.misceval.callsim(mod.Sound, sound); + }), - makeSound: new Sk.builtin.func(function (url) { - Sk.builtin.pyCheckArgs('makeSound', arguments, 1); - return Sk.misceval.callsim(mod.Sound, url); - }), + makeSound: new Sk.builtin.func(function (url) { + Sk.builtin.pyCheckArgs("makeSound", arguments, 1); + return Sk.misceval.callsim(mod.Sound, url); + }), - makeEmptySound: new Sk.builtin.func(function (numSamples, samplingRate) { - Sk.builtin.pyCheckArgs('makeEmptySound', arguments, [1, 2]); - return Sk.misceval.callsim(mod.Sound, numSamples, samplingRate); - }), + makeEmptySound: new Sk.builtin.func(function (numSamples, samplingRate) { + Sk.builtin.pyCheckArgs("makeEmptySound", arguments, [1, 2]); + return Sk.misceval.callsim(mod.Sound, numSamples, samplingRate); + }), - makeEmptySoundBySeconds: new Sk.builtin.func(function (seconds, samplingRate) { - var numSamples; + makeEmptySoundBySeconds: new Sk.builtin.func(function (seconds, samplingRate) { + var numSamples; - Sk.builtin.pyCheckArgs('makeEmptySoundBySeconds', arguments, [1, 2]); + Sk.builtin.pyCheckArgs("makeEmptySoundBySeconds", arguments, [1, 2]); - if(Sk.ffi.unwrapo(seconds) < 0) { - throw new Sk.builtin.ValueError('Duration can not be negative'); - } - numSamples = Sk.ffi.unwrapo(seconds) * (Sk.ffi.unwrapo(samplingRate) || window.pythy.Sound.SAMPLE_RATE); - return Sk.misceval.callsim(mod.Sound, new Sk.builtin.int_(numSamples), samplingRate); - }), + if(Sk.ffi.unwrapo(seconds) < 0) { + throw new Sk.builtin.ValueError("Duration can not be negative"); + } + numSamples = Sk.ffi.unwrapo(seconds) * (Sk.ffi.unwrapo(samplingRate) || window.pythy.Sound.SAMPLE_RATE); + return Sk.misceval.callsim(mod.Sound, new Sk.builtin.int_(numSamples), samplingRate); + }), - openSoundTool: new Sk.builtin.func(function (sound) { - Sk.builtin.pyCheckArgs('openSoundTool', arguments, 1); - window.pythy.soundTool.start(sound._sound); - }), + openSoundTool: new Sk.builtin.func(function (sound) { + Sk.builtin.pyCheckArgs("openSoundTool", arguments, 1); + window.pythy.soundTool.start(sound._sound); + }), - writeSoundTo : new Sk.builtin.func(function(sound, path) { - Sk.builtin.pyCheckArgs('writeSoundTo', arguments, 2); - sound._sound.save(Sk.ffi.unwrapo(path)); - }) - }); + writeSoundTo : new Sk.builtin.func(function(sound, path) { + Sk.builtin.pyCheckArgs("writeSoundTo", arguments, 2); + sound._sound.save(Sk.ffi.unwrapo(path)); + }) + }); - return mod; + return mod; }; diff --git a/src/lib/traceback.py b/src/lib/traceback.py index a71d1abb37..9fdba670f6 100644 --- a/src/lib/traceback.py +++ b/src/lib/traceback.py @@ -1,7 +1,18 @@ """Extract, format and print information about Python stack traces.""" import collections -import linecache + +class linecache: + @staticmethod + def getline(filename, lineno, module_globals=None): + return "Apples and bananas" + @staticmethod + def lazycache(filename, globals): + return "Frogs and ketchup" + @staticmethod + def checkcache(filename): + return "Weird ocean city" + import sys from itertools import islice @@ -69,7 +80,7 @@ def extract_tb(tb, limit=None): trace. The line is a string with leading and trailing whitespace stripped; if the source is not available it is None. """ - return StackSummary.extract(walk_tb(tb), limit=limit) + return extract(walk_tb(tb), limit=limit) # # Exception formatting and output. @@ -315,56 +326,57 @@ def walk_tb(tb): _RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. + +def extract(frame_gen, limit=None, lookup_lines=True, capture_locals=False): + """Create a StackSummary from a traceback or stack object. + + :param frame_gen: A generator that yields (frame, lineno) tuples to + include in the stack. + :param limit: None to include all frames or the number of frames to + include. + :param lookup_lines: If True, lookup lines for each frame immediately, + otherwise lookup is deferred until the frame is rendered. + :param capture_locals: If True, the local variables from each frame will + be captured as object representations into the FrameSummary. + """ + if limit is None: + limit = getattr(sys, 'tracebacklimit', None) + if limit is not None and limit < 0: + limit = 0 + if limit is not None: + if limit >= 0: + frame_gen = islice(frame_gen, limit) + else: + frame_gen = collections.deque(frame_gen, maxlen=-limit) + + result = StackSummary() + fnames = set() + for f, lineno in frame_gen: + co = f.f_code + filename = "Apple.py" #co.co_filename + name = "Ada" #co.co_name + + fnames.add(filename) + linecache.lazycache(filename, f.f_globals) + # Must defer line lookups until we have called checkcache. + if capture_locals: + f_locals = f.f_locals + else: + f_locals = None + result.append(FrameSummary( + filename, lineno, name, lookup_line=False, locals=f_locals)) + for filename in fnames: + linecache.checkcache(filename) + # If immediate lookup was desired, trigger lookups now. + if lookup_lines: + for f in result: + f.line + return result + class StackSummary(list): """A stack of frames.""" - @classmethod - def extract(klass, frame_gen, *, limit=None, lookup_lines=True, - capture_locals=False): - """Create a StackSummary from a traceback or stack object. - - :param frame_gen: A generator that yields (frame, lineno) tuples to - include in the stack. - :param limit: None to include all frames or the number of frames to - include. - :param lookup_lines: If True, lookup lines for each frame immediately, - otherwise lookup is deferred until the frame is rendered. - :param capture_locals: If True, the local variables from each frame will - be captured as object representations into the FrameSummary. - """ - if limit is None: - limit = getattr(sys, 'tracebacklimit', None) - if limit is not None and limit < 0: - limit = 0 - if limit is not None: - if limit >= 0: - frame_gen = islice(frame_gen, limit) - else: - frame_gen = collections.deque(frame_gen, maxlen=-limit) - - result = klass() - fnames = set() - for f, lineno in frame_gen: - co = f.f_code - filename = co.co_filename - name = co.co_name - - fnames.add(filename) - linecache.lazycache(filename, f.f_globals) - # Must defer line lookups until we have called checkcache. - if capture_locals: - f_locals = f.f_locals - else: - f_locals = None - result.append(FrameSummary( - filename, lineno, name, lookup_line=False, locals=f_locals)) - for filename in fnames: - linecache.checkcache(filename) - # If immediate lookup was desired, trigger lookups now. - if lookup_lines: - for f in result: - f.line - return result + @classmethod def from_list(klass, a_list): diff --git a/test/test_traceback.py b/test/test_traceback.py new file mode 100644 index 0000000000..d5fdbc674f --- /dev/null +++ b/test/test_traceback.py @@ -0,0 +1,22 @@ +import sys + +def another_dumb_approach(): + 1+"" + +bad_code = ''' +def be_stupid(): + return 1+"" +be_stupid() +''' + +def win_dumb_prize(): + exec(bad_code, {'wrong': another_dumb_approach}) + +try: + win_dumb_prize() +except Exception as e: + result = sys.exc_info() + print(result[2]) + print(result[2].tb_lineno) + import traceback + print(traceback.extract_tb(result[2])) \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index c6eb27e55c..3cfcf79fa3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,23 +1,23 @@ -const path = require('path'); -const webpack = require('webpack'); -const shell = require('shelljs'); -const chalk = require('chalk'); +const path = require("path"); +const webpack = require("webpack"); +const shell = require("shelljs"); +const chalk = require("chalk"); -const ClosureWebpackPlugin = require('closure-webpack-plugin'); -const CleanWebpackPlugin = require('clean-webpack-plugin'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); -const GitRevisionPlugin = require('git-revision-webpack-plugin'); -const CompressionWebpackPlugin = require('compression-webpack-plugin'); +const ClosureWebpackPlugin = require("closure-webpack-plugin"); +const CleanWebpackPlugin = require("clean-webpack-plugin"); +const CopyWebpackPlugin = require("copy-webpack-plugin"); +const GitRevisionPlugin = require("git-revision-webpack-plugin"); +const CompressionWebpackPlugin = require("compression-webpack-plugin"); const git = new GitRevisionPlugin({branch: true}); const styleexcludes = /(node_modules)|(support)|(gen)|(tokenize.js)|(symtable.js)|(compile.js)|(ast.js)|(internalpython.js)/; -if (!shell.which('git')) { +if (!shell.which("git")) { console.log(chalk.red("WARNING: Cannot find git! Unsure if working directory is clean.")); } -var output = shell.exec('git diff-index --quiet HEAD'); +var output = shell.exec("git diff-index --quiet HEAD"); if (output.code !== 0) { console.log(chalk.red("WARNING: Working directory is not clean.")); } else { @@ -28,27 +28,27 @@ module.exports = (env, argv) => { var opt = { minimize: false }; - var outfile = 'skulpt.js'; - var assertfile = './assert-dev.js'; + var outfile = "skulpt.js"; + var assertfile = "./assert-dev.js"; var mod = {}; - if (argv.mode === 'production') { + if (argv.mode === "production") { opt = { noEmitOnErrors: true, minimizer: [ - new ClosureWebpackPlugin({mode: 'STANDARD', platform: 'java'}, { - jscomp_error: ['accessControls', 'checkRegExp', 'checkTypes', 'checkVars', - 'invalidCasts', 'missingProperties', - 'nonStandardJsDocs', 'strictModuleDepCheck', 'undefinedVars', - 'unknownDefines', 'visibility'], - jscomp_off: ['fileoverviewTags', 'deprecated'], - languageOut: (env && env.languageOut) ? env.languageOut : 'ECMASCRIPT_2015', - externs: 'support/externs/sk.js' + new ClosureWebpackPlugin({mode: "STANDARD", platform: "java"}, { + jscomp_error: ["accessControls", "checkRegExp", "checkTypes", "checkVars", + "invalidCasts", "missingProperties", + "nonStandardJsDocs", "strictModuleDepCheck", "undefinedVars", + "unknownDefines", "visibility"], + jscomp_off: ["fileoverviewTags", "deprecated"], + languageOut: (env && env.languageOut) ? env.languageOut : "ECMASCRIPT_2015", + externs: "support/externs/sk.js" }) ] }; - outfile = 'skulpt.min.js'; - assertfile = './assert-prod.js'; + outfile = "skulpt.min.js"; + assertfile = "./assert-prod.js"; mod = { rules: [ /*{ @@ -62,18 +62,18 @@ module.exports = (env, argv) => { } var config = { - entry: './src/main.js', + entry: "./src/main.js", output: { - path: path.resolve(__dirname, 'dist'), - devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]', + path: path.resolve(__dirname, "dist"), + devtoolFallbackModuleFilenameTemplate: "[absolute-resource-path]?[hash]", filename: outfile }, - devtool: 'source-map', + devtool: "source-map", module: mod, plugins: [ new CleanWebpackPlugin(), new CopyWebpackPlugin([ - { from: 'debugger/debugger.js', to: 'debugger.js' } + { from: "debugger/debugger.js", to: "debugger.js" } ]), new webpack.DefinePlugin({ GITVERSION: JSON.stringify(git.version()), @@ -83,13 +83,13 @@ module.exports = (env, argv) => { }), new CompressionWebpackPlugin({ include: /^skulpt\.min\.js$/, - algorithm: 'gzip' + algorithm: "gzip" }) ], optimization: opt, resolve: { alias: { - 'assert': assertfile + "assert": assertfile } } }; From 28512af6778fca0068832c88804d88586f7e5475 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 28 Aug 2019 23:28:04 -0400 Subject: [PATCH 21/68] BASIC TRACEBACK SUPPORT! --- src/builtin.js | 52 ++++++++++++++++++++++++++++++++++++++---- src/builtin/sys.js | 11 +++------ src/builtindict.js | 1 + src/compile.js | 9 +------- src/errors.js | 45 +++++++++++++++++++++--------------- src/lib/traceback.py | 4 ++-- test/test_traceback.py | 11 +++++---- 7 files changed, 89 insertions(+), 44 deletions(-) diff --git a/src/builtin.js b/src/builtin.js index a308c0c6ad..0149bc4dfc 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -1356,6 +1356,42 @@ Sk.builtin.execfile = function execfile () { throw new Sk.builtin.NotImplementedError("execfile is not yet implemented"); }; +/** + * Okay, I'll be honest, this is 100% not a real code object. It just holds the source + * code and filename. I'm relying on the `exec` call to actually compile it. IT'S ALL + * A BIG LIE. I'm sorry. -acbart + * @param filename + * @param source + * @returns {Sk.builtin|*} + */ +Sk.builtin.code = function(filename, source) { + if (!(this instanceof Sk.builtin.code)) { + return new Sk.builtin.code(filename, source); + } + this.filename = filename; + this.source = source; + this.__class__ = Sk.builtin.code; + return this; +}; +Sk.abstr.setUpInheritance("code", Sk.builtin.code, Sk.builtin.object); +Sk.builtin.code.prototype["$r"] = function () { + return new Sk.builtin.str(""); +}; +Sk.exportSymbol("Sk.builtin.code", Sk.builtin.code); + +Sk.builtin.compile = function(source, filename, mode, flags, dont_inherit, optimize) { + Sk.builtin.pyCheckArgsLen("compile", arguments.length, 3, 6); + source = Sk.ffi.remapToJs(source); + filename = Sk.ffi.remapToJs(filename); + if (!Sk.compiledSources) { + Sk.compiledSources = {}; + } + Sk.compiledSources[filename] = source; + return new Sk.builtin.code(filename, source); +}; + + + var extractDict = function(obj) { var ret = {}; var k, v, kAsJs, iter; @@ -1371,11 +1407,17 @@ var extractDict = function(obj) { return ret; }; -Sk.builtin.exec = function execf(python_code, new_globals) { +Sk.builtin.exec = function execf(pythonCode, new_globals) { Sk.builtin.pyCheckArgs("exec", arguments, 1, 2); var backupRG = Sk.retainGlobals; Sk.retainGlobals = true; - var filename = "test"; + var filename = ""; + if (pythonCode instanceof Sk.builtin.code) { + filename = pythonCode.filename; + pythonCode = pythonCode.source; + } else { + pythonCode = Sk.ffi.remapToJs(pythonCode); + } var new_globals_copy = extractDict(new_globals); if (!new_globals_copy.__file__) { new_globals_copy.__file__ = Sk.ffi.remapToPy(filename); @@ -1390,8 +1432,10 @@ Sk.builtin.exec = function execf(python_code, new_globals) { backupSysmodules.mp$ass_subscript(key, value); }); Sk.globals = new_globals_copy; // Possibly copy over some "default" ones? - python_code = Sk.ffi.remapToJs(python_code); - Sk.importMainWithBody(filename, false, python_code, true); + //Sk.importMainWithBody(filename, false, python_code, true); + var name = filename.endsWith(".py") ? filename.slice(0, -3) : filename; + var modname = name; + Sk.importModuleInternal_(name, false, modname, pythonCode, undefined, false, true) Sk.globals = backupGlobals; Sk.misceval.iterFor(backupSysmodules.tp$iter(), function(key) { var value = backupSysmodules.mp$subscript(key); diff --git a/src/builtin/sys.js b/src/builtin/sys.js index d6c3bce032..69ae29654a 100644 --- a/src/builtin/sys.js +++ b/src/builtin/sys.js @@ -73,18 +73,13 @@ var $builtinmodule = function (name) { sys.stdin = sys.__stdin__; sys.exc_info = new Sk.builtin.func(function () { - console.log(Sk.scopes); - if (Sk.err.length && Sk.err[0]) { - console.log(">>>", Sk.err[0], "<<<"); - console.log("%%%", Sk.err[1], "$$$"); - console.log(Sk.err.traceback); - console.log(Sk.ffi.remapToJs(Sk.sysmodules)); + if (Sk.err) { var type = Sk.err.ob$type; var value = Sk.builtin.none.none$; - var traceback = new Sk.builtin.traceback(Sk.err[0]); + var traceback = new Sk.builtin.traceback.fromList(Sk.err.traceback); //print(traceback.tp$setattr) //traceback.tp$setattr('tb_lineno', traceback.tb_lineno); - var vals = [type, Sk.err[0], traceback]; + var vals = [type, Sk.err, traceback]; return new Sk.builtin.tuple(vals); } else { return new Sk.builtin.tuple(Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.none.none$); diff --git a/src/builtindict.js b/src/builtindict.js index ebcd103ee4..8039231bda 100644 --- a/src/builtindict.js +++ b/src/builtindict.js @@ -90,6 +90,7 @@ Sk.builtins = { "callable" : Sk.builtin.callable, "delattr" : Sk.builtin.delattr, "eval_$rn$" : Sk.builtin.eval_, + "compile" : Sk.builtin.compile, "exec" : Sk.builtin.exec, "execfile" : Sk.builtin.execfile, "frozenset" : Sk.builtin.frozenset, diff --git a/src/compile.js b/src/compile.js index 105dbf2bc7..b3f3c9cd12 100644 --- a/src/compile.js +++ b/src/compile.js @@ -1883,7 +1883,6 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal if (hasCell) { this.u.prefixCode += "\n// has cell\n"; } - this.u.prefixCode += "Sk.scopes=Sk.scopes||[];Sk.scopes.push('"+coname.v+"');\n"; // // set up standard dicts/variables @@ -1989,7 +1988,6 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal this.u.switchCode += this.outputInterruptTest(); this.u.switchCode += "switch($blk){"; this.u.suffixCode = "}" + this.handleTraceback(true, coname.v); - this.u.suffixCode += "Sk.scopes.pop();"; this.u.suffixCode += "});"; /*this.u.suffixCode = ("} }catch(err){ "+ "if (err instanceof Sk.builtin.TimeLimitError) {"+ @@ -2255,7 +2253,6 @@ Compiler.prototype.cclass = function (s) { entryBlock = this.newBlock("class entry"); this.u.prefixCode = "var " + scopename + "=(function $" + s.name.v + "$class_outer($globals,$locals,$cell){var $gbl=$globals,$loc=$locals;$free=$globals;"; - this.u.prefixCode += "Sk.scopes=Sk.scopes||[];Sk.scopes.push('"+s.name.v+"');\n"; this.u.switchCode += "(function $" + s.name.v + "$_closure($cell){"; this.u.switchCode += "var $blk=" + entryBlock + ",$exc=[],$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;"; @@ -2270,7 +2267,6 @@ Compiler.prototype.cclass = function (s) { this.u.switchCode += this.outputInterruptTest(); this.u.switchCode += "switch($blk){"; this.u.suffixCode = "}" + this.handleTraceback(true, s.name.v); - this.u.suffixCode += "Sk.scopes.pop();"; /*this.u.suffixCode = ("}}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) {"+ "Sk.execStart = Date.now();Sk.execPaused=0"+ "} if (!(err instanceof Sk.builtin.BaseException)) {"+ @@ -2698,7 +2694,6 @@ Compiler.prototype.cmod = function (mod) { this.u.varDeclsCode = "var $gbl = $forcegbl || {}, $blk=" + entryBlock + ",$exc=[],$loc=$gbl,$cell={},$err=undefined;" + - "Sk.scopes=Sk.scopes||[];Sk.scopes.push('');"+ "$loc.__file__=new Sk.builtins.str('" + this.filename + "');var $ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;"; @@ -2730,7 +2725,6 @@ Compiler.prototype.cmod = function (mod) { this.u.switchCode += this.outputInterruptTest(); this.u.switchCode += "switch($blk){"; this.u.suffixCode = "}" + this.handleTraceback(true, ""); - this.u.suffixCode += "Sk.scopes.pop();"; this.u.suffixCode += "});"; //this.u.suffixCode += "}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now();Sk.execPaused=0} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} } });"; @@ -2764,12 +2758,11 @@ Compiler.prototype.cmod = function (mod) { Compiler.prototype.handleTraceback = function(doContinue, scopeName) { doContinue = doContinue ? "continue" : ""; return ("}catch(err){ "+ - "Sk.err=[err,$blk];"+ "if(err instanceof Sk.builtin.TimeLimitError){"+ "Sk.execStart=Date.now();Sk.execPaused=0"+ "}if(!(err instanceof Sk.builtin.BaseException)) {"+ "err=new Sk.builtin.ExternalError(err);" + - "}err.traceback.push({"+ + "}Sk.err=err;err.traceback.push({"+ "lineno:$currLineNo,"+ "colno:$currColNo,"+ "filename:'"+this.filename+"'," + diff --git a/src/errors.js b/src/errors.js index 33e1ba9636..e68bc567da 100644 --- a/src/errors.js +++ b/src/errors.js @@ -574,11 +574,11 @@ Sk.exportSymbol("Sk.builtin.StopIteration", Sk.builtin.StopIteration); /** * @constructor */ -Sk.builtin.frame = function() { +Sk.builtin.frame = function(trace) { if (!(this instanceof Sk.builtin.frame)) { - return new Sk.builtin.frame(); + return new Sk.builtin.frame(trace); } - + this.trace = trace; this.__class__ = Sk.builtin.frame; return this; }; @@ -600,9 +600,11 @@ Sk.builtin.frame.prototype.tp$getattr = function (name) { case "f_code": return Sk.builtin.none.none$; case "f_globals": return Sk.builtin.none.none$; case "f_lasti": return Sk.builtin.none.none$; - case "f_lineno": return Sk.builtin.none.none$; + case "f_lineno": return this.trace.lineno; case "f_locals": return Sk.builtin.none.none$; case "f_trace": return Sk.builtin.none.none$; + case "co_filename": return this.trace.filename; + case "co_name": return this.trace.scope; } } @@ -618,28 +620,35 @@ Sk.exportSymbol("Sk.builtin.frame", Sk.builtin.frame); * @constructor * @param {Object} err */ -Sk.builtin.traceback = function(err) { +Sk.builtin.traceback = function(trace) { if (!(this instanceof Sk.builtin.traceback)) { - return new Sk.builtin.traceback(err); - } - - var lineno = null; - if (err.traceback.length > 0) { - lineno = err.traceback[0].lineno; + return new Sk.builtin.traceback(trace); } - - this.tb_lineno = new Sk.builtin.int_(lineno); + + this.trace = trace; + + this.tb_lineno = new Sk.builtin.int_(trace.lineno); // TODO: Hack, you know this isn't right - this.tb_frame = Sk.builtin.none.none$; + this.tb_frame = new Sk.builtin.frame(trace); //tb_frame, tb_lasti, tb_lineno, tb_next this.__class__ = Sk.builtin.traceback; - + return this; }; Sk.abstr.setUpInheritance("traceback", Sk.builtin.traceback, Sk.builtin.object); +Sk.builtin.traceback.fromList = function(traces) { + var current = Sk.builtin.traceback(traces[0]), + first = current; + for (var i=1; i Date: Sun, 1 Sep 2019 15:53:55 -0400 Subject: [PATCH 22/68] Many bug fixes and improvements to skulpt --- src/builtin.js | 4 ++ src/builtindict.js | 5 +- src/classmethod.js.disabled | 50 +++++++++++++++++++ src/constants.js | 7 +++ src/import.js | 32 ++++++++---- src/lib/functools.py | 3 ++ src/lib/pedal/assertions/assertions.py | 14 +++++- src/lib/pedal/sandbox/exceptions.py | 5 ++ src/lib/pedal/sandbox/mocked.py | 4 +- src/lib/pedal/sandbox/sandbox.py | 1 + src/lib/pedal/sandbox/tracer.py | 2 +- src/lib/pedal/source/__init__.py | 3 +- src/lib/posixpath.py | 68 +++++++++++++------------ src/lib/sound/sound.js | 2 +- src/lib/turtle.js | 1 + src/lib/unittest/mock.py | 69 +++++++++++++++++++++++++- src/main.js | 1 + src/module.js | 7 ++- src/parser.js | 2 +- src/str.js | 19 ++++++- src/tokenize.js | 16 +++--- test/test_pedal.py | 5 ++ test/test_sandbox.py | 25 ++++++++++ 23 files changed, 283 insertions(+), 62 deletions(-) create mode 100644 src/classmethod.js.disabled create mode 100644 test/test_sandbox.py diff --git a/src/builtin.js b/src/builtin.js index 0149bc4dfc..1c9d63abeb 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -846,6 +846,7 @@ Sk.builtin.setattr = function setattr (obj, pyName, value) { throw new Sk.builtin.TypeError("attribute name must be string"); } jsName = pyName.$jsstr(); + if (obj.tp$setattr) { obj.tp$setattr(new Sk.builtin.str(Sk.fixReservedWords(jsName)), value); } else { @@ -1425,6 +1426,9 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { if (!new_globals_copy.__name__) { new_globals_copy.__name__ = Sk.ffi.remapToPy(filename); } + if (!new_globals_copy.__package__) { + new_globals_copy.__package__ = Sk.builtin.none.none$; + } var backupGlobals = Sk.globals, backupSysmodules = new Sk.builtin.dict([]); Sk.misceval.iterFor(Sk.sysmodules.tp$iter(), function(key) { diff --git a/src/builtindict.js b/src/builtindict.js index 8039231bda..34829daef8 100644 --- a/src/builtindict.js +++ b/src/builtindict.js @@ -29,6 +29,7 @@ Sk.builtins = { "any" : new Sk.builtin.func(Sk.builtin.any), "all" : new Sk.builtin.func(Sk.builtin.all), + "BaseException" : Sk.builtin.BaseException, "AttributeError" : Sk.builtin.AttributeError, "ValueError" : Sk.builtin.ValueError, "Exception" : Sk.builtin.Exception, @@ -89,6 +90,7 @@ Sk.builtins = { "bytearray" : Sk.builtin.bytearray, "callable" : Sk.builtin.callable, "delattr" : Sk.builtin.delattr, + "__import__": Sk.builtin.__import__, "eval_$rn$" : Sk.builtin.eval_, "compile" : Sk.builtin.compile, "exec" : Sk.builtin.exec, @@ -108,7 +110,8 @@ Sk.builtins = { "apply_$rn$": Sk.builtin.apply_, "buffer" : Sk.builtin.buffer, "coerce" : Sk.builtin.coerce, - "intern" : Sk.builtin.intern + "intern" : Sk.builtin.intern, + //"classmethod": Sk.builtin.classmethod, }; Sk.setupObjects = function (py3) { diff --git a/src/classmethod.js.disabled b/src/classmethod.js.disabled new file mode 100644 index 0000000000..72463013bf --- /dev/null +++ b/src/classmethod.js.disabled @@ -0,0 +1,50 @@ +Sk.builtin.classmethod = function classmethod(f) { + if (!(this instanceof Sk.builtin.classmethod)) { + Sk.builtin.pyCheckArgsLen("classmethod", arguments.length, 1, 1); + return new Sk.builtin.classmethod(f); + } + this["$d"] = new Sk.builtin.dict([]); + this["$d"].mp$ass_subscript(Sk.builtin.str("f"), f); + this["$d"].mp$ass_subscript(Sk.builtin.str("__dict__"), this.$d); + var __get__ = new Sk.builtin.func(function __get__(self, obj, klass) { + Sk.builtin.pyCheckArgsLen("__get__", arguments.length, 1, 2, false, true); + if (obj === Sk.builtin.none.none$ && klass === Sk.builtin.none.none$) { + throw new Sk.builtin.TypeError("__get__(None, None) is invalid"); + } + + if (Sk.builtin.checkNone(klass)) { + klass = Sk.builtin.type(obj); + } + + var newFunction = new Sk.builtins.function(function(args) { + var f = Sk.abstr.gattr(self, Sk.builtin.str("f"), true); + var fArgs = [klass]; + Sk.misceval.iterFor(Sk.abstr.iter(args), function (e) { + fArgs.push(e); + }); + return Sk.misceval.callsimArray(f, fArgs); + }); + newFunction.co_name = new Sk.builtin["str"]("newfunc"); + newFunction.co_varnames = []; + newFunction.co_varargs = 1; + + return newFunction; + }); + __get__.co_name = new Sk.builtin.str("__get__"); + __get__.$defaults = [Sk.builtin.none.none$]; + __get__.co_varnames = ["self", "obj", "klass"]; + this["$d"].mp$ass_subscript(Sk.builtin.str("__get__"), __get__); + this.__class__ = Sk.builtin.classmethod; + //return Sk.misceval.buildClass({}, this, "classmethod", [Sk.builtin.object]); + return this; +}; +Sk.builtin.classmethod.co_name = new Sk.builtin.str("__init__"); +Sk.builtin.classmethod.co_varnames = ["f"]; +Sk.abstr.setUpInheritance("classmethod", Sk.builtin.classmethod, Sk.builtin.object); + +/*var fields = new Sk.builtin.dict([]); +fields.mp$ass_subscript(Sk.builtin.str("__module__"), new Sk.builtin.str("classmethod")); +Sk.builtin.classmethod = Sk.builtin.type(new Sk.builtin.str("classmethod"), new Sk.builtin.tuple([Sk.builtin.object]), fields);*/ +//Sk.builtin.classmethod = Sk.builtin.type.makeIntoTypeObj("classmethod", Sk.builtin.classmethod); + +Sk.exportSymbol("Sk.builtin.classmethod", Sk.builtin.classmethod); \ No newline at end of file diff --git a/src/constants.js b/src/constants.js index a19d5b3db9..585975fe54 100644 --- a/src/constants.js +++ b/src/constants.js @@ -134,3 +134,10 @@ var builtinNames = [ for (var i = 0; i < builtinNames.length; i++) { Sk.builtin[builtinNames[i]].co_name = new Sk.builtin.str(builtinNames[i]); } + +Sk.builtin.str.prototype["split"].co_varnames = ["sep", "maxsplit"]; +Sk.builtin.str.prototype["split"].$defaults = [Sk.builtin.none.none$, Sk.builtin.int_(-1)]; +Sk.builtin.str.prototype["split"].co_kwargs = true; +Sk.builtin.str.prototype["rsplit"].co_varnames = ["sep", "maxsplit"]; +Sk.builtin.str.prototype["rsplit"].$defaults = [Sk.builtin.none.none$, Sk.builtin.int_(-1)]; +Sk.builtin.str.prototype["rsplit"].co_kwargs = true; \ No newline at end of file diff --git a/src/import.js b/src/import.js index 2ab374ec3b..0fb2810296 100644 --- a/src/import.js +++ b/src/import.js @@ -161,8 +161,11 @@ Sk.importSetUpPath = function (canSuspend) { Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, relativeToPackage, returnUndefinedOnTopLevelNotFound, canSuspend) { //dumpJS = true; /* TODO: temporary hack, need to delete! */ - if (name === "pedal.sandbox.sandbox") { + /*if (name === "pedal.sandbox.sandbox") { suppliedPyBody= "class Sandbox: pass\ndef run(): pass\ndef reset(): pass"; + }*/ + if (name === "pedal.sandbox.timeout") { + suppliedPyBody = "def timeout(delay, func, *args, **kwargs):\n return func(*args, **kwargs)"; } /* End hack */ var filename; @@ -195,10 +198,10 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela // if leaf is already in sys.modules, early out try { - prev = Sk.sysmodules.mp$subscript(modname); + prev = Sk.sysmodules.mp$subscript(new Sk.builtin.str(modname)); // if we're a dotted module, return the top level, otherwise ourselves if (modNameSplit.length > 1) { - return Sk.sysmodules.mp$subscript(absolutePackagePrefix + modNameSplit[0]); + return Sk.sysmodules.mp$subscript(new Sk.builtin.str(absolutePackagePrefix + modNameSplit[0])); } else { return prev; } @@ -227,7 +230,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela if (!topLevelModuleToReturn) { return undefined; } - parentModule = Sk.sysmodules.mp$subscript(absolutePackagePrefix + parentModName); + parentModule = Sk.sysmodules.mp$subscript(new Sk.builtin.str(absolutePackagePrefix + parentModName)); searchFileName = modNameSplit[modNameSplit.length-1]; searchPath = parentModule.tp$getattr(Sk.builtin.str.$path); } @@ -297,7 +300,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela } // Now we know this module exists, we can add it to the cache - Sk.sysmodules.mp$ass_subscript(modname, module); + Sk.sysmodules.mp$ass_subscript(new Sk.builtin.str(modname), module); module.$js = co.code; // todo; only in DEBUG? finalcode = co.code; @@ -337,7 +340,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela // } finalcode += "\n" + co.funcname + ";"; - //console.log(finalcode); + modscope = Sk.global["eval"](finalcode); module["$d"] = { @@ -472,6 +475,14 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { // a Python language module. var saveSk = Sk.globals; + if (Sk.builtin.checkString(name)) { + name = name.v; + } + + if (globals === undefined) { + globals = Sk.globals; + } + // This might be a relative import, so first we get hold of the module object // representing this module's package (so we can search its __path__). // module.__package__ contains its name, so we use that to look it up in sys.modules. @@ -496,7 +507,7 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { relativeToPackageName = relativeToPackageNames.join("."); } try { - relativeToPackage = Sk.sysmodules.mp$subscript(relativeToPackageName); + relativeToPackage = Sk.sysmodules.mp$subscript(new Sk.builtin.str(relativeToPackageName)); } catch(e) { relativeToPackageName = undefined; } @@ -544,9 +555,10 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { var importChain; leafModule = Sk.sysmodules.mp$subscript( - (relativeToPackageName || "") + - ((relativeToPackageName && name) ? "." : "") + - name); + new Sk.builtin.str((relativeToPackageName || "") + + ((relativeToPackageName && name) ? "." : "") + + name) + ); for (i = 0; i < fromlist.length; i++) { fromName = fromlist[i]; diff --git a/src/lib/functools.py b/src/lib/functools.py index b3eaa455bf..d5dff1e81e 100644 --- a/src/lib/functools.py +++ b/src/lib/functools.py @@ -1,5 +1,8 @@ from reprlib import recursive_repr +def wraps(wrapper, wrapped): + return wrapper + # Purely functional, no descriptor behaviour class partial: """New function with partial application of the given arguments diff --git a/src/lib/pedal/assertions/assertions.py b/src/lib/pedal/assertions/assertions.py index f916c01970..734a0c60af 100644 --- a/src/lib/pedal/assertions/assertions.py +++ b/src/lib/pedal/assertions/assertions.py @@ -25,7 +25,17 @@ def safe_repr(obj, short=False): return result -punctuation_table = str.maketrans(string.punctuation, ' ' * len(string.punctuation)) +try: + punctuation_table = str.maketrans(string.punctuation, ' ' * len(string.punctuation)) +except AttributeError: + punctuation_table = None + +if punctuation_table is None: + def strip_punctuation(a_string): + return ''.join(ch for ch in a_string if ch not in set(string.punctuation)) +else: + def strip_punctuation(a_string): + return a_string.translate(punctuation_table) def _normalize_string(a_string, numeric_endings=False): @@ -35,7 +45,7 @@ def _normalize_string(a_string, numeric_endings=False): if numeric_endings: a_string = re.sub(r"(\s*[0-9]+)\.[0-9]+(\s*)", r"\1\2", a_string) # Remove punctuation - a_string = a_string.translate(punctuation_table) + a_string = strip_punctuation(a_string) # Split lines lines = a_string.split("\n") normalized = [[piece diff --git a/src/lib/pedal/sandbox/exceptions.py b/src/lib/pedal/sandbox/exceptions.py index 2961915195..5de27ce608 100644 --- a/src/lib/pedal/sandbox/exceptions.py +++ b/src/lib/pedal/sandbox/exceptions.py @@ -2,6 +2,11 @@ import os import sys +try: + TimeoutError +except NameError: + TimeoutError = Exception + class SandboxException(Exception): """ Generic base exception for sandbox errors. diff --git a/src/lib/pedal/sandbox/mocked.py b/src/lib/pedal/sandbox/mocked.py index 5ab898f899..61cb9144f8 100644 --- a/src/lib/pedal/sandbox/mocked.py +++ b/src/lib/pedal/sandbox/mocked.py @@ -17,7 +17,7 @@ def _disabled_compile(source, filename, mode, flags=0, dont_inherit=False): raise RuntimeError("You are not allowed to call 'compile'.") -def _disabled_eval(object, globals=globals(), locals=locals()): +def _disabled_eval(object, globals=globals(), locals=None): """ A version of the built-in `eval` method that fails with a runtime error. @@ -28,7 +28,7 @@ def _disabled_eval(object, globals=globals(), locals=locals()): # ------------------------------------------------------------- -def _disabled_exec(object, globals=globals(), locals=locals()): +def _disabled_exec(object, globals=globals(), locals=None): """ A version of the built-in `exec` method that fails with a runtime error. diff --git a/src/lib/pedal/sandbox/sandbox.py b/src/lib/pedal/sandbox/sandbox.py index 7efd0ae469..3d13474c9d 100644 --- a/src/lib/pedal/sandbox/sandbox.py +++ b/src/lib/pedal/sandbox/sandbox.py @@ -649,6 +649,7 @@ def run(self, code, as_filename=None, modules=None, inputs=None, with self.trace._as_filename(as_filename, code): exec(compiled_code, self.data) except Exception as user_exception: + print(user_exception) self._stop_patches() info = sys.exc_info() self._capture_exception(user_exception, info, diff --git a/src/lib/pedal/sandbox/tracer.py b/src/lib/pedal/sandbox/tracer.py index 9deb134617..6547204939 100644 --- a/src/lib/pedal/sandbox/tracer.py +++ b/src/lib/pedal/sandbox/tracer.py @@ -8,7 +8,7 @@ try: from bdb import Bdb, BdbQuit -except ImportError: +except Exception: class Bdb: pass diff --git a/src/lib/pedal/source/__init__.py b/src/lib/pedal/source/__init__.py index 54a9f902b5..7eeaf4b358 100644 --- a/src/lib/pedal/source/__init__.py +++ b/src/lib/pedal/source/__init__.py @@ -79,7 +79,8 @@ def _check_issues(code, report): report.attach('Syntax error', category='Syntax', tool='Source', group=report['source']['section'], mistake={'message': "Invalid syntax on line " - + str(e.lineno), + + str(e.lineno) + + "\n\n"+str(e), 'error': e, 'position': {"line": e.lineno}}) report['source']['success'] = False diff --git a/src/lib/posixpath.py b/src/lib/posixpath.py index cb650cb46f..5fd5b6f9cd 100644 --- a/src/lib/posixpath.py +++ b/src/lib/posixpath.py @@ -40,8 +40,15 @@ "commonpath"] +def fspath(val): + return val + + +os.getcwd = lambda: "" + + def _get_sep(path): - if isinstance(path, bytes): + if isinstance(path, str): return '/' #'/' else: return '/' @@ -53,7 +60,7 @@ def _get_sep(path): def normcase(s): """Normalize case of pathname. Has no effect under Posix""" - return os.fspath(s) + return fspath(s) # Return whether a path is absolute. @@ -61,7 +68,7 @@ def normcase(s): def isabs(s): """Test whether a path is absolute""" - s = os.fspath(s) + s = fspath(s) sep = _get_sep(s) return s.startswith(sep) @@ -75,13 +82,13 @@ def join(a, *p): If any component is an absolute path, all previous path components will be discarded. An empty last part will result in a path that ends with a separator.""" - a = os.fspath(a) + a = fspath(a) sep = _get_sep(a) path = a try: if not p: path[:0] + sep #23780: Ensure compatible data type even if p is null. - for b in map(os.fspath, p): + for b in map(fspath, p): if b.startswith(sep): path = b elif not path or path.endswith(sep): @@ -102,7 +109,7 @@ def join(a, *p): def split(p): """Split a pathname. Returns tuple "(head, tail)" where "tail" is everything after the final slash. Either part may be empty.""" - p = os.fspath(p) + p = fspath(p) sep = _get_sep(p) i = p.rfind(sep) + 1 head, tail = p[:i], p[i:] @@ -117,8 +124,8 @@ def split(p): # It is always true that root + ext == p. def splitext(p): - p = os.fspath(p) - if isinstance(p, bytes): + p = fspath(p) + if isinstance(p, str): sep = '/' extsep = '.' else: @@ -133,7 +140,7 @@ def splitext(p): def splitdrive(p): """Split a pathname into drive and path. On Posix, drive is always empty.""" - p = os.fspath(p) + p = fspath(p) return p[:0], p @@ -141,7 +148,7 @@ def splitdrive(p): def basename(p): """Returns the final component of a pathname""" - p = os.fspath(p) + p = fspath(p) sep = _get_sep(p) i = p.rfind(sep) + 1 return p[i:] @@ -151,7 +158,7 @@ def basename(p): def dirname(p): """Returns the directory component of a pathname""" - p = os.fspath(p) + p = fspath(p) sep = _get_sep(p) i = p.rfind(sep) + 1 head = p[:i] @@ -197,7 +204,7 @@ def ismount(path): if stat.S_ISLNK(s1.st_mode): return False - if isinstance(path, bytes): + if isinstance(path, str): parent = join(path, '..') else: parent = join(path, '..') @@ -230,8 +237,8 @@ def ismount(path): def expanduser(path): """Expand ~ and ~user constructions. If user or $HOME is unknown, do nothing.""" - path = os.fspath(path) - if isinstance(path, bytes): + path = fspath(path) + if isinstance(path, str): tilde = '~' else: tilde = '~' @@ -255,7 +262,7 @@ def expanduser(path): else: import pwd name = path[1:i] - if isinstance(name, bytes): + if isinstance(name, str): name = str(name, 'ASCII') try: pwent = pwd.getpwnam(name) @@ -264,7 +271,7 @@ def expanduser(path): # password database, return the path unchanged return path userhome = pwent.pw_dir - if isinstance(path, bytes): + if isinstance(path, str): userhome = os.fsencode(userhome) root = '/' else: @@ -283,9 +290,9 @@ def expanduser(path): def expandvars(path): """Expand shell variables of form $var and ${var}. Unknown variables are left unchanged.""" - path = os.fspath(path) + path = fspath(path) global _varprog, _varprogb - if isinstance(path, bytes): + if isinstance(path, str): if '$' not in path: return path if not _varprogb: @@ -335,8 +342,8 @@ def expandvars(path): def normpath(path): """Normalize path, eliminating double slashes, etc.""" - path = os.fspath(path) - if isinstance(path, bytes): + path = fspath(path) + if isinstance(path, str): sep = '/' empty = '' dot = '.' @@ -373,12 +380,9 @@ def normpath(path): def abspath(path): """Return an absolute path.""" - path = os.fspath(path) + path = fspath(path) if not isabs(path): - if isinstance(path, bytes): - cwd = os.getcwdb() - else: - cwd = os.getcwd() + cwd = os.getcwd() path = join(cwd, path) return normpath(path) @@ -389,14 +393,14 @@ def abspath(path): def realpath(filename): """Return the canonical path of the specified filename, eliminating any symbolic links encountered in the path.""" - filename = os.fspath(filename) + filename = fspath(filename) path, ok = _joinrealpath(filename[:0], filename, {}) return abspath(path) # Join two paths, normalizing and eliminating any symbolic links # encountered in the second path. def _joinrealpath(path, rest, seen): - if isinstance(path, bytes): + if isinstance(path, str): sep = '/' curdir = '.' pardir = '..' @@ -454,8 +458,8 @@ def relpath(path, start=None): if not path: raise ValueError("no path specified") - path = os.fspath(path) - if isinstance(path, bytes): + path = fspath(path) + if isinstance(path, str): curdir = '.' sep = '/' pardir = '..' @@ -467,7 +471,7 @@ def relpath(path, start=None): if start is None: start = curdir else: - start = os.fspath(start) + start = fspath(start) try: start_list = [x for x in abspath(start).split(sep) if x] @@ -495,8 +499,8 @@ def commonpath(paths): if not paths: raise ValueError('commonpath() arg is an empty sequence') - paths = tuple(map(os.fspath, paths)) - if isinstance(paths[0], bytes): + paths = tuple(map(fspath, paths)) + if isinstance(paths[0], str): sep = '/' curdir = '.' else: diff --git a/src/lib/sound/sound.js b/src/lib/sound/sound.js index fe6ed9d93b..c025a13903 100644 --- a/src/lib/sound/sound.js +++ b/src/lib/sound/sound.js @@ -5,7 +5,7 @@ var $builtinmodule = function() { mod = {}; // Dependency - Sample = Sk.sysmodules.mp$subscript("sound.sample").$d.Sample; + Sample = Sk.sysmodules.mp$subscript(new Sk.builtin.str("sound.sample")).$d.Sample; soundWrapper = { stopPlaying: new Sk.builtin.func(function (sound) { diff --git a/src/lib/turtle.js b/src/lib/turtle.js index 914e5d8027..3378c5f824 100644 --- a/src/lib/turtle.js +++ b/src/lib/turtle.js @@ -2289,6 +2289,7 @@ var $builtinmodule = function (name) { addModuleMethod(Screen, _module, "$window_width", getScreen); addModuleMethod(Screen, _module, "$window_height", getScreen); addModuleMethod(Screen, _module, "$bgpic", getScreen); + addModuleMethod(Screen, _module, "$setup", getScreen); _module.Turtle = Sk.misceval.buildClass(_module, TurtleWrapper, "Turtle", []); _module.Screen = Sk.misceval.buildClass(_module, ScreenWrapper, "Screen", []); diff --git a/src/lib/unittest/mock.py b/src/lib/unittest/mock.py index 379ba4e30d..2d0bb54246 100644 --- a/src/lib/unittest/mock.py +++ b/src/lib/unittest/mock.py @@ -1,5 +1,70 @@ -def pass_through(*args, **kwargs): - return pass_through(*args, **kwargs) +def _dot_lookup(thing, comp, import_path): + try: + return getattr(thing, comp) + except AttributeError: + __import__(import_path) + return getattr(thing, comp) + + +def _importer(target): + components = target.split('.') + import_path = components.pop(0) + thing = __import__(import_path) + + for comp in components: + import_path += ".%s" % comp + thing = _dot_lookup(thing, comp, import_path) + return thing + +def rsplit(a_str, sep, howmany): + broken = a_str.split(sep) + where = len(broken) - howmany + if len(broken) == 1: + return broken + front, back = broken[:where], broken[where:] + back.insert(0, sep.join(front)) + return back + +def _get_target(target): + try: + target, attribute = rsplit(target, '.', 1) + except (TypeError, ValueError): + raise TypeError("Need a valid target to patch. You supplied: %r" % + (target,)) + getter = lambda: _importer(target) + return getter, attribute + +class Patch: + def __init__(self, target, new, return_value): + self.target = target + self.new = new + self.return_value = return_value + self.getter, self.attribute = _get_target(target) + self.backup = None + + def get_original(self): + target = self.getter() + name = self.attribute + try: + original = target.__dict__[name] + except (AttributeError, KeyError): + original = getattr(target, name, None) + return original + + def start(self): + self.backup = self.get_original() + if self.new: + new_attr = self.new + else: + new_attr = self.return_value + setattr(self.getter(), self.attribute, new_attr) + + + def stop(self): + setattr(self.getter(), self.attribute, self.backup) + +def pass_through(target, new=None, return_value=None): + return Patch(target, new, return_value) patch = pass_through patch.dict = pass_through \ No newline at end of file diff --git a/src/main.js b/src/main.js index 8d5df2f111..4c45e3237a 100644 --- a/src/main.js +++ b/src/main.js @@ -50,6 +50,7 @@ require("./enumerate.js"); require("./filter.js"); require("./zip.js"); require("./map.js"); +//require("./classmethod.js"); require("./token.js"); require("./tokenize.js"); require("../gen/parse_tables.js"); diff --git a/src/module.js b/src/module.js index 76b54cde0d..cebece9f9e 100644 --- a/src/module.js +++ b/src/module.js @@ -1,7 +1,12 @@ /** * @constructor */ -Sk.builtin.module = function module () { +Sk.builtin.module = function module (name) { + if (!(this instanceof Sk.builtin.module)) { + return new Sk.builtin.module(name); + } + this["$d"] = {__name__: name}; + return this; }; Sk.exportSymbol("Sk.builtin.module", Sk.builtin.module); diff --git a/src/parser.js b/src/parser.js index 77edaa0fd9..8b594b54cd 100644 --- a/src/parser.js +++ b/src/parser.js @@ -360,7 +360,7 @@ Sk.parse = function parse (filename, input) { endmarker_seen = true; } } - }); + }, filename); if (!endmarker_seen) { throw new Sk.builtin.SyntaxError("incomplete input", this.filename); diff --git a/src/str.js b/src/str.js index a107e5dead..d718e4a9a8 100755 --- a/src/str.js +++ b/src/str.js @@ -281,7 +281,7 @@ Sk.builtin.str.prototype["join"] = new Sk.builtin.func(function (self, seq) { return new Sk.builtin.str(arrOfStrs.join(self.v)); }); -Sk.builtin.str.prototype["split"] = new Sk.builtin.func(function (self, on, howmany) { +var genericsSplit = function genericsSplit(self, on, howmany) { var splits; var index; var match; @@ -339,6 +339,23 @@ Sk.builtin.str.prototype["split"] = new Sk.builtin.func(function (self, on, howm } return new Sk.builtin.list(result); +}; + +Sk.builtin.str.prototype["split"] = new Sk.builtin.func(function (self, sep, maxsplit) { + return genericsSplit(self, sep, maxsplit); +}); + +Sk.builtin.str.prototype["rsplit"] = new Sk.builtin.func(function (self, sep, maxsplit) { + var allSplit = genericsSplit(self, sep, undefined); + if (maxsplit !== undefined) { + if (!Sk.builtin.checkInt(maxsplit)) { + throw new Sk.builtin.TypeError("an integer is required"); + } + // TODO + return allSplit; + } else { + return allSplit; + } }); Sk.builtin.str.prototype["strip"] = new Sk.builtin.func(function (self, chars) { diff --git a/src/tokenize.js b/src/tokenize.js index 2c159ff592..c9bfb5f385 100644 --- a/src/tokenize.js +++ b/src/tokenize.js @@ -1,7 +1,7 @@ -var tokens = Sk.token.tokens +var tokens = Sk.token.tokens; -const TokenError = Error; -const IndentationError = Error; +const TokenError = Sk.builtin.SyntaxError; +const IndentationError = Sk.builtin.SyntaxError; /** * @@ -235,7 +235,7 @@ Sk.exportSymbol("Sk._setupTokenRegexes", Sk._setupTokenRegexes); * @param {string} encoding * @param {function(TokenInfo): void} yield_ */ -function _tokenize(readline, encoding, yield_) { +function _tokenize(readline, encoding, yield_, filename) { var lnum = 0, @@ -284,7 +284,8 @@ function _tokenize(readline, encoding, yield_) { if (contstr) { // continued string if (!line) { - throw new TokenError("EOF in multi-line string", strstart); + //throw new TokenError("EOF in multi-line string", strstart); + throw new TokenError("EOF in multi-line string", filename, spos[0], [spos, epos]); } endprog.lastIndex = 0; var endmatch = endprog.exec(line); @@ -348,7 +349,7 @@ function _tokenize(readline, encoding, yield_) { if (!contains(indents, column)) { throw new IndentationError( "unindent does not match any outer indentation level", - ["", lnum, pos, line]); + filename, spos[0], [spos, epos]); //["", lnum, pos, line]); } indents = indents.slice(0, -1); @@ -357,7 +358,8 @@ function _tokenize(readline, encoding, yield_) { } } else { // continued statement if (!line) { - throw new TokenError("EOF in multi-line statement", [lnum, 0]); + //throw new TokenError("EOF in multi-line statement", [lnum, 0]); + throw new TokenError("EOF in multi-line statement", filename, spos[0], [spos, epos]); } continued = 0; } diff --git a/test/test_pedal.py b/test/test_pedal.py index 0940ec51dc..8257f1d985 100644 --- a/test/test_pedal.py +++ b/test/test_pedal.py @@ -33,6 +33,11 @@ def click(phase): print(ast.find_all("Assign")) click("Found assignments") +from pedal.sandbox.sandbox import run + +student = run() +print(student) + from pedal.resolvers import simple click("Imported resolver") diff --git a/test/test_sandbox.py b/test/test_sandbox.py new file mode 100644 index 0000000000..2da04c2f61 --- /dev/null +++ b/test/test_sandbox.py @@ -0,0 +1,25 @@ +import sys +from pprint import pprint + + +from pedal.sandbox import Sandbox + +student = Sandbox() +student.run(""" +def add_together(a, b): + return -7 +""", as_filename='student.py') +#pprint(student.data) +print(student.data) +print(student.output) + + +from pedal.assertions import assertEqual + +#assertEqual(student.data['a'], 2) + +assertEqual(student.call("add_together", 2, 2), 4) + +from pedal.report import MAIN_REPORT + +print(MAIN_REPORT.feedback[0].mistake) \ No newline at end of file From b358af4824d08ce74dc6dd9bfffe9df9d619f806 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 11 Sep 2019 01:16:00 -0400 Subject: [PATCH 23/68] Oh man. Exec, tracebacks, mocking... Intense update --- records.txt | 16622 ++++++++++++++++++++ src/builtin.js | 28 +- src/builtin/sys.js | 3 +- src/builtindict.js | 1 + src/compile.js | 34 +- src/constants.js | 45 +- src/errors.js | 71 +- src/import.js | 63 +- src/int.js | 4 + src/lib/functools.py | 7 +- src/lib/itertools.py | 19 +- src/lib/parking/__init__.js | 210 + src/lib/pedal/assertions/assertions.py | 13 +- src/lib/pedal/assertions/setup.py | 3 +- src/lib/pedal/sandbox/__init__.py | 2 +- src/lib/pedal/sandbox/compatibility.py | 6 +- src/lib/pedal/sandbox/exceptions.py | 6 +- src/lib/pedal/sandbox/mocked.py | 13 +- src/lib/pedal/sandbox/sandbox.py | 63 +- src/lib/pedal/tifa/builtin_definitions.py | 42 +- src/lib/pedal/tifa/messages.py | 13 + src/lib/pedal/tifa/tifa.py | 20 +- src/lib/pedal/tifa/type_definitions.py | 38 + src/lib/pedal/toolkit/functions.py | 30 + src/lib/pedal/toolkit/signatures.py | 27 + src/lib/traceback.py | 149 +- src/lib/unittest/mock.py | 9 +- src/misceval.js | 10 +- src/parser.js | 4 +- test/test_matplotlib_capture.py | 27 + test/test_pedal.py | 1 + test/test_sandbox.py | 18 +- test/test_sandbox2.py | 33 + test/test_slowdowns.py | 1 + 34 files changed, 17435 insertions(+), 200 deletions(-) create mode 100644 records.txt create mode 100644 src/lib/parking/__init__.js create mode 100644 test/test_matplotlib_capture.py create mode 100644 test/test_sandbox2.py create mode 100644 test/test_slowdowns.py diff --git a/records.txt b/records.txt new file mode 100644 index 0000000000..8f01185af7 --- /dev/null +++ b/records.txt @@ -0,0 +1,16622 @@ + +> skulpt@1.0.0 start C:\Users\acbart\Projects\blockpy-edu\skulpt +> node support/run/runfile.js "py3" "src/lib/traceback.py" + +Using skulpt.js +----- + + +import collections + +class linecache: + @staticmethod + def getline(filename, lineno, module_globals=None): + return "Apples and bananas" + @staticmethod + def lazycache(filename, globals): + return "Frogs and ketchup" + @staticmethod + def checkcache(filename): + return "Weird ocean city" + +import sys +from itertools import islice + +__all__ = ['extract_stack', 'extract_tb', 'format_exception', + 'format_exception_only', 'format_list', 'format_stack', + 'format_tb', 'print_exc', 'format_exc', 'print_exception', + 'print_last', 'print_stack', 'print_tb', 'clear_frames', + 'FrameSummary', 'StackSummary', 'TracebackException', + 'walk_stack', 'walk_tb'] + +# +# Formatting and printing lists of traceback lines. +# + +def print_list(extracted_list, file=None): + + if file is None: + file = sys.stderr + for item in StackSummary.from_list(extracted_list).format(): + print(item, file=file, end="") + +def format_list(extracted_list): + + return StackSummary.from_list(extracted_list).format() + +# +# Printing and Extracting Tracebacks. +# + +def print_tb(tb, limit=None, file=None): + + print_list(extract_tb(tb, limit=limit), file=file) + +def format_tb(tb, limit=None): + + return extract_tb(tb, limit=limit).format() + +def extract_tb(tb, limit=None): + + return extract(walk_tb(tb), limit=limit) + +# +# Exception formatting and output. +# + +_cause_message = ( + "\nThe above exception was the direct cause " + "of the following exception:\n\n") + +_context_message = ( + "\nDuring handling of the above exception, " + "another exception occurred:\n\n") + + +def print_exception(etype, value, tb, limit=None, file=None, chain=True): + + # format_exception has ignored etype for some time, and code such as cgitb + # passes in bogus values as a result. For compatibility with such code we + # ignore it here (rather than in the new TracebackException API). + if file is None: + file = sys.stderr + for line in TracebackException( + type(value), value, tb, limit=limit).format(chain=chain): + print(line, file=file, end="") + + +def format_exception(etype, value, tb, limit=None, chain=True): + + # format_exception has ignored etype for some time, and code such as cgitb + # passes in bogus values as a result. For compatibility with such code we + # ignore it here (rather than in the new TracebackException API). + return list(TracebackException( + type(value), value, tb, limit=limit).format(chain=chain)) + + +def format_exception_only(etype, value): + + return list(TracebackException(etype, value, None).format_exception_only()) + + +# -- not official API but folk probably use these two functions. + +def _format_final_exc_line(etype, value): + valuestr = _some_str(value) + if value is None or not valuestr: + line = "%s\n" % etype + else: + line = "%s: %s\n" % (etype, valuestr) + return line + +def _some_str(value): + try: + return str(value) + except: + return '' % type(value).__name__ + +# -- + +def print_exc(limit=None, file=None, chain=True): + + print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain) + +def format_exc(limit=None, chain=True): + + return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain)) + +def print_last(limit=None, file=None, chain=True): + + if not hasattr(sys, "last_type"): + raise ValueError("no last exception") + print_exception(sys.last_type, sys.last_value, sys.last_traceback, + limit, file, chain) + +# +# Printing and Extracting Stacks. +# + +def print_stack(f=None, limit=None, file=None): + + if f is None: + f = sys._getframe().f_back + print_list(extract_stack(f, limit=limit), file=file) + + +def format_stack(f=None, limit=None): + + if f is None: + f = sys._getframe().f_back + return format_list(extract_stack(f, limit=limit)) + + +def extract_stack(f=None, limit=None): + + if f is None: + f = sys._getframe().f_back + stack = StackSummary.extract(walk_stack(f), limit=limit) + stack.reverse() + return stack + + +def clear_frames(tb): + "Clear all references to local variables in the frames of a traceback." + while tb is not None: + try: + tb.tb_frame.clear() + except RuntimeError: + # Ignore the exception raised if the frame is still executing. + pass + tb = tb.tb_next + + +class FrameSummary: + + + __slots__ = ('filename', 'lineno', 'name', '_line', 'locals') + + def __init__(self, filename, lineno, name, *, lookup_line=True, + locals=None, line=None): + + self.filename = filename + self.lineno = lineno + self.name = name + self._line = line + if lookup_line: + self.line + self.locals = {k: repr(v) for k, v in locals.items()} if locals else None + + def __eq__(self, other): + if isinstance(other, FrameSummary): + return (self.filename == other.filename and + self.lineno == other.lineno and + self.name == other.name and + self.locals == other.locals) + if isinstance(other, tuple): + return (self.filename, self.lineno, self.name, self.line) == other + return NotImplemented + + def __getitem__(self, pos): + return (self.filename, self.lineno, self.name, self.line)[pos] + + def __iter__(self): + return iter([self.filename, self.lineno, self.name, self.line]) + + def __repr__(self): + return "".format( + filename=self.filename, lineno=self.lineno, name=self.name) + + def __len__(self): + return 4 + + @property + def line(self): + if self._line is None: + self._line = linecache.getline(self.filename, self.lineno).strip() + return self._line + + +def walk_stack(f): + + if f is None: + f = sys._getframe().f_back.f_back + while f is not None: + yield f, f.f_lineno + f = f.f_back + + +def walk_tb(tb): + + while tb is not None: + yield tb.tb_frame, tb.tb_lineno + tb = tb.tb_next + + +_RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. + + +def extract(frame_gen, limit=None, lookup_lines=True, capture_locals=False): + + if limit is None: + limit = getattr(sys, 'tracebacklimit', None) + if limit is not None and limit < 0: + limit = 0 + if limit is not None: + if limit >= 0: + frame_gen = islice(frame_gen, limit) + else: + frame_gen = collections.deque(frame_gen, maxlen=-limit) + + result = StackSummary() + fnames = set() + for f, lineno in frame_gen: + co = f.f_code + filename = f.co_filename #co.co_filename + name = f.co_name #co.co_name + + fnames.add(filename) + linecache.lazycache(filename, f.f_globals) + # Must defer line lookups until we have called checkcache. + if capture_locals: + f_locals = f.f_locals + else: + f_locals = None + result.append(FrameSummary( + filename, lineno, name, lookup_line=False, locals=f_locals)) + for filename in fnames: + linecache.checkcache(filename) + # If immediate lookup was desired, trigger lookups now. + if lookup_lines: + for f in result: + f.line + return result + +class StackSummary(list): + + + + + @classmethod + def from_list(klass, a_list): + + # While doing a fast-path check for isinstance(a_list, StackSummary) is + # appealing, idlelib.run.cleanup_traceback and other similar code may + # break this by making arbitrary frames plain tuples, so we need to + # check on a frame by frame basis. + result = StackSummary() + for frame in a_list: + if isinstance(frame, FrameSummary): + result.append(frame) + else: + filename, lineno, name, line = frame + result.append(FrameSummary(filename, lineno, name, line=line)) + return result + + def format(self): + + result = [] + last_file = None + last_line = None + last_name = None + count = 0 + for frame in self: + if (last_file is None or last_file != frame.filename or + last_line is None or last_line != frame.lineno or + last_name is None or last_name != frame.name): + if count > _RECURSIVE_CUTOFF: + count -= _RECURSIVE_CUTOFF + result.append(( + ' [Previous line repeated {count} more ' + 'time{s_count}]\n' + ).format(count=count, s_count="s" if count > 1 else "")) + last_file = frame.filename + last_line = frame.lineno + last_name = frame.name + count = 0 + count += 1 + if count > _RECURSIVE_CUTOFF: + continue + row = [] + row.append(' File "{}", line {}, in {}\n'.format( + frame.filename, frame.lineno, frame.name)) + if frame.line: + row.append(' {}\n'.format(frame.line.strip())) + if frame.locals: + for name, value in sorted(frame.locals.items()): + row.append(' {name} = {value}\n'.format(name=name, value=value)) + result.append(''.join(row)) + if count > _RECURSIVE_CUTOFF: + count -= _RECURSIVE_CUTOFF + result.append(( + ' [Previous line repeated {count} more ' + 'time{s_count}]\n' + ).format(count=count, s_count='s' if count > 1 else '')) + return result + + +class TracebackException: + + + def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, + lookup_lines=True, capture_locals=False, _seen=None): + # NB: we need to accept exc_traceback, exc_value, exc_traceback to + # permit backwards compat with the existing API, otherwise we + # need stub thunk objects just to glue it together. + # Handle loops in __cause__ or __context__. + if _seen is None: + _seen = set() + _seen.add(id(exc_value)) + # Gracefully handle (the way Python 2.4 and earlier did) the case of + # being called with no type or value (None, None, None). + if (exc_value and exc_value.__cause__ is not None + and id(exc_value.__cause__) not in _seen): + cause = TracebackException( + type(exc_value.__cause__), + exc_value.__cause__, + exc_value.__cause__.__traceback__, + limit=limit, + lookup_lines=False, + capture_locals=capture_locals, + _seen=_seen) + else: + cause = None + if (exc_value and exc_value.__context__ is not None + and id(exc_value.__context__) not in _seen): + context = TracebackException( + type(exc_value.__context__), + exc_value.__context__, + exc_value.__context__.__traceback__, + limit=limit, + lookup_lines=False, + capture_locals=capture_locals, + _seen=_seen) + else: + context = None + self.exc_traceback = exc_traceback + self.__cause__ = cause + self.__context__ = context + self.__suppress_context__ = \ + exc_value.__suppress_context__ if exc_value else False + # TODO: locals. + self.stack = StackSummary.extract( + walk_tb(exc_traceback), limit=limit, lookup_lines=lookup_lines, + capture_locals=capture_locals) + self.exc_type = exc_type + # Capture now to permit freeing resources: only complication is in the + # unofficial API _format_final_exc_line + self._str = _some_str(exc_value) + if exc_type and issubclass(exc_type, SyntaxError): + # Handle SyntaxError's specially + self.filename = exc_value.filename + self.lineno = str(exc_value.lineno) + self.text = exc_value.text + self.offset = exc_value.offset + self.msg = exc_value.msg + if lookup_lines: + self._load_lines() + + @classmethod + def from_exception(cls, exc, *args, **kwargs): + + return cls(type(exc), exc, exc.__traceback__, *args, **kwargs) + + def _load_lines(self): + + for frame in self.stack: + frame.line + if self.__context__: + self.__context__._load_lines() + if self.__cause__: + self.__cause__._load_lines() + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + def __str__(self): + return self._str + + def format_exception_only(self): + + if self.exc_type is None: + yield _format_final_exc_line(None, self._str) + return + + stype = self.exc_type.__qualname__ + smod = self.exc_type.__module__ + if smod not in ("__main__", "builtins"): + stype = smod + '.' + stype + + if not issubclass(self.exc_type, SyntaxError): + yield _format_final_exc_line(stype, self._str) + return + + # It was a syntax error; show exactly where the problem was found. + filename = self.filename or "" + lineno = str(self.lineno) or '?' + yield ' File "{}", line {}\n'.format(filename, lineno) + + badline = self.text + offset = self.offset + if badline is not None: + yield ' {}\n'.format(badline.strip()) + if offset is not None: + caretspace = badline.rstrip('\n') + offset = min(len(caretspace), offset) - 1 + caretspace = caretspace[:offset].lstrip() + # non-space whitespace (likes tabs) must be kept for alignment + caretspace = ((c.isspace() and c or ' ') for c in caretspace) + yield ' {}^\n'.format(''.join(caretspace)) + msg = self.msg or "" + yield "{}: {}\n".format(stype, msg) + + def format(self, *, chain=True): + + if chain: + if self.__cause__ is not None: + #yield from self.__cause__.format(chain=chain) + for g in self.__cause__.format(chain=chain): + yield g + yield _cause_message + elif (self.__context__ is not None and + not self.__suppress_context__): + #yield from self.__context__.format(chain=chain) + for g in self.__cause__.format(chain=chain): + yield g + yield _context_message + if self.exc_traceback is not None: + yield 'Traceback (most recent call last):\n' + #yield from self.stack.format() + for g in self.stack.format(): + yield g + #yield from self.format_exception_only() + for g in self.format_exception_only(): + yield g + +----- +Parse classmethod.py 8 +AST classmethod.py 6 +Parse property.py 11 +AST property.py 4 +Parse staticmethod.py 1 +AST staticmethod.py 0 +Parse src/lib/traceback.py 79 +AST src/lib/traceback.py 41 +/* 1 */ $compiledmod = function() { +/* 2 */ var $scope149 = (function($forcegbl) { +/* 3 */ var $wakeFromSuspension = function() { +/* 4 */ var susp = $scope149.$wakingSuspension; +/* 5 */ $scope149.$wakingSuspension = undefined; +/* 6 */ $blk = susp.$blk; +/* 7 */ $loc = susp.$loc; +/* 8 */ $gbl = susp.$gbl; +/* 9 */ $exc = susp.$exc; +/* 10 */ $err = susp.$err; +/* 11 */ $postfinally = susp.$postfinally; +/* 12 */ $currLineNo = susp.$lineno; +/* 13 */ $currColNo = susp.$colno; +/* 14 */ Sk.lastYield = Date.now(); +/* 15 */ try { +/* 16 */ $ret = susp.child.resume(); +/* 17 */ } catch (err) { +/* 18 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 19 */ Sk.execStart = Date.now(); +/* 20 */ Sk.execPaused = 0 +/* 21 */ } +/* 22 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 23 */ err = new Sk.builtin.ExternalError(err); +/* 24 */ } +/* 25 */ Sk.err = err; +/* 26 */ err.traceback.push({ +/* 27 */ lineno: $currLineNo, +/* 28 */ colno: $currColNo, +/* 29 */ filename: 'src/lib/traceback.py', +/* 30 */ scope: '$scope149' +/* 31 */ }); +/* 32 */ if ($exc.length > 0) { +/* 33 */ $err = err; +/* 34 */ $blk = $exc.pop(); +/* 35 */ } else { +/* 36 */ throw err; +/* 37 */ } +/* 38 */ } +/* 39 */ }; +/* 40 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 41 */ var susp = new Sk.misceval.Suspension(); +/* 42 */ susp.child = $child; +/* 43 */ susp.resume = function() { +/* 44 */ $scope149.$wakingSuspension = susp; +/* 45 */ return $scope149(); +/* 46 */ }; +/* 47 */ susp.data = susp.child.data; +/* 48 */ susp.$blk = $blk; +/* 49 */ susp.$loc = $loc; +/* 50 */ susp.$gbl = $gbl; +/* 51 */ susp.$exc = $exc; +/* 52 */ susp.$err = $err; +/* 53 */ susp.$postfinally = $postfinally; +/* 54 */ susp.$filename = $filename; +/* 55 */ susp.$lineno = $lineno; +/* 56 */ susp.$colno = $colno; +/* 57 */ susp.optional = susp.child.optional; +/* 58 */ susp.$tmps = {}; +/* 59 */ return susp; +/* 60 */ }; +/* 61 */ var $gbl = $forcegbl || {}, +/* 62 */ $blk = 0, +/* 63 */ $exc = [], +/* 64 */ $loc = $gbl, +/* 65 */ $cell = {}, +/* 66 */ $err = undefined; +/* 67 */ $loc.__file__ = new Sk.builtins.str('src/lib/traceback.py'); +/* 68 */ var $ret = undefined, +/* 69 */ $postfinally = undefined, +/* 70 */ $currLineNo = undefined, +/* 71 */ $currColNo = undefined; +/* 72 */ if (typeof Sk.execStart === 'undefined') { +/* 73 */ Sk.execStart = Date.now(); +/* 74 */ Sk.execPaused = 0 +/* 75 */ } +/* 76 */ if (typeof Sk.lastYield === 'undefined') { +/* 77 */ Sk.lastYield = Date.now() +/* 78 */ } +/* 79 */ if ($scope149.$wakingSuspension !== undefined) { +/* 80 */ $wakeFromSuspension(); +/* 81 */ } +/* 82 */ if (Sk.retainGlobals) { +/* 83 */ if (Sk.globals) { +/* 84 */ $gbl = Sk.globals; +/* 85 */ Sk.globals = $gbl; +/* 86 */ $loc = $gbl; +/* 87 */ $loc.__file__ = new Sk.builtins.str('src/lib/traceback.py'); +/* 88 */ } else { +/* 89 */ Sk.globals = $gbl; +/* 90 */ } +/* 91 */ } else { +/* 92 */ Sk.globals = $gbl; +/* 93 */ } +/* 94 */ while (true) { +/* 95 */ try { +/* 96 */ var $dateNow = Date.now(); +/* 97 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 98 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 99 */ } +/* 100 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 101 */ var $susp = $saveSuspension({ +/* 102 */ data: { +/* 103 */ type: 'Sk.yield' +/* 104 */ }, +/* 105 */ resume: function() {} +/* 106 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 107 */ $susp.$blk = $blk; +/* 108 */ $susp.optional = true; +/* 109 */ return $susp; +/* 110 */ } +/* 111 */ switch ($blk) { +/* 112 */ case 0: +/* 113 */ /* --- module entry --- */ +/* 114 */ // +/* 115 */ // line 3: +/* 116 */ // import collections +/* 117 */ // ^ +/* 118 */ // +/* 119 */ +/* 120 */ $currLineNo = Sk.currLineNo = 3; +/* 121 */ $currColNo = Sk.currColNo = 0; +/* 122 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 123 */ $ret = Sk.builtin.__import__('collections', $gbl, $loc, [], -1); +/* 124 */ $blk = 1; /* allowing case fallthrough */ +/* 125 */ case 1: +/* 126 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 127 */ return $saveSuspension($ret, 'src/lib/traceback.py', 3, 0); +/* 128 */ } +/* 129 */ var $module150 = $ret; +/* 130 */ $loc.collections = $module150; +/* 131 */ // +/* 132 */ // line 5: +/* 133 */ // class linecache: +/* 134 */ // ^ +/* 135 */ // +/* 136 */ +/* 137 */ $currLineNo = Sk.currLineNo = 5; +/* 138 */ $currColNo = Sk.currColNo = 0; +/* 139 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 140 */ $scope151.co_name = new Sk.builtins['str']('linecache'); +/* 141 */ var $built167 = Sk.misceval.buildClass($gbl, $scope151, 'linecache', [], $cell); +/* 142 */ $loc.linecache = $built167; +/* 143 */ // +/* 144 */ // line 16: +/* 145 */ // import sys +/* 146 */ // ^ +/* 147 */ // +/* 148 */ +/* 149 */ $currLineNo = Sk.currLineNo = 16; +/* 150 */ $currColNo = Sk.currColNo = 0; +/* 151 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 152 */ $ret = Sk.builtin.__import__('sys', $gbl, $loc, [], -1); +/* 153 */ $blk = 2; /* allowing case fallthrough */ +/* 154 */ case 2: +/* 155 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 156 */ return $saveSuspension($ret, 'src/lib/traceback.py', 16, 0); +/* 157 */ } +/* 158 */ var $module168 = $ret; +/* 159 */ $loc.sys = $module168; +/* 160 */ // +/* 161 */ // line 17: +/* 162 */ // from itertools import islice +/* 163 */ // ^ +/* 164 */ // +/* 165 */ +/* 166 */ $currLineNo = Sk.currLineNo = 17; +/* 167 */ $currColNo = Sk.currColNo = 0; +/* 168 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 169 */ $ret = Sk.builtin.__import__('itertools', $gbl, $loc, ['islice'], -1); +/* 170 */ $blk = 3; /* allowing case fallthrough */ +/* 171 */ case 3: +/* 172 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 173 */ return $saveSuspension($ret, 'src/lib/traceback.py', 17, 0); +/* 174 */ } +/* 175 */ var $module169 = $ret; +/* 176 */ var $item170 = Sk.abstr.gattr($module169, new Sk.builtin.str('islice')); +/* 177 */ $loc.islice = $item170; +/* 178 */ // +/* 179 */ // line 19: +/* 180 */ // __all__ = ['extract_stack', 'extract_tb', 'format_exception', +/* 181 */ // ^ +/* 182 */ // +/* 183 */ +/* 184 */ $currLineNo = Sk.currLineNo = 19; +/* 185 */ $currColNo = Sk.currColNo = 0; +/* 186 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 187 */ var $elem172 = $scope149.$const171; +/* 188 */ var $elem174 = $scope149.$const173; +/* 189 */ var $elem176 = $scope149.$const175; +/* 190 */ var $elem178 = $scope149.$const177; +/* 191 */ var $elem180 = $scope149.$const179; +/* 192 */ var $elem182 = $scope149.$const181; +/* 193 */ var $elem184 = $scope149.$const183; +/* 194 */ var $elem186 = $scope149.$const185; +/* 195 */ var $elem188 = $scope149.$const187; +/* 196 */ var $elem190 = $scope149.$const189; +/* 197 */ var $elem192 = $scope149.$const191; +/* 198 */ var $elem194 = $scope149.$const193; +/* 199 */ var $elem196 = $scope149.$const195; +/* 200 */ var $elem198 = $scope149.$const197; +/* 201 */ var $elem200 = $scope149.$const199; +/* 202 */ var $elem202 = $scope149.$const201; +/* 203 */ var $elem204 = $scope149.$const203; +/* 204 */ var $elem206 = $scope149.$const205; +/* 205 */ var $elem208 = $scope149.$const207; +/* 206 */ var $loadlist209 = new Sk.builtins['list']([$elem172, $elem174, $elem176, $elem178, $elem180, $elem182, $elem184, $elem186, $elem188, $elem190, $elem192, $elem194, $elem196, $elem198, $elem200, $elem202, $elem204, $elem206, $elem208]); +/* 207 */ $loc.__all__ = $loadlist209; +/* 208 */ // +/* 209 */ // line 30: +/* 210 */ // def print_list(extracted_list, file=None): +/* 211 */ // ^ +/* 212 */ // +/* 213 */ +/* 214 */ $currLineNo = Sk.currLineNo = 30; +/* 215 */ $currColNo = Sk.currColNo = 0; +/* 216 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 217 */ $scope210.co_name = new Sk.builtins['str']('print_list'); +/* 218 */ $scope210.$defaults = [Sk.builtin.none.none$]; +/* 219 */ $scope210.co_varnames = ['extracted_list', 'file']; +/* 220 */ var $funcobj230 = new Sk.builtins['function']($scope210, $gbl); +/* 221 */ $loc.print_list = $funcobj230; +/* 222 */ // +/* 223 */ // line 37: +/* 224 */ // def format_list(extracted_list): +/* 225 */ // ^ +/* 226 */ // +/* 227 */ +/* 228 */ $currLineNo = Sk.currLineNo = 37; +/* 229 */ $currColNo = Sk.currColNo = 0; +/* 230 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 231 */ $scope231.co_name = new Sk.builtins['str']('format_list'); +/* 232 */ $scope231.co_varnames = ['extracted_list']; +/* 233 */ var $funcobj240 = new Sk.builtins['function']($scope231, $gbl); +/* 234 */ $loc.format_list = $funcobj240; +/* 235 */ // +/* 236 */ // line 45: +/* 237 */ // def print_tb(tb, limit=None, file=None): +/* 238 */ // ^ +/* 239 */ // +/* 240 */ +/* 241 */ $currLineNo = Sk.currLineNo = 45; +/* 242 */ $currColNo = Sk.currColNo = 0; +/* 243 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 244 */ $scope241.co_name = new Sk.builtins['str']('print_tb'); +/* 245 */ $scope241.$defaults = [Sk.builtin.none.none$, Sk.builtin.none.none$]; +/* 246 */ $scope241.co_varnames = ['tb', 'limit', 'file']; +/* 247 */ var $funcobj247 = new Sk.builtins['function']($scope241, $gbl); +/* 248 */ $loc.print_tb = $funcobj247; +/* 249 */ // +/* 250 */ // line 49: +/* 251 */ // def format_tb(tb, limit=None): +/* 252 */ // ^ +/* 253 */ // +/* 254 */ +/* 255 */ $currLineNo = Sk.currLineNo = 49; +/* 256 */ $currColNo = Sk.currColNo = 0; +/* 257 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 258 */ $scope248.co_name = new Sk.builtins['str']('format_tb'); +/* 259 */ $scope248.$defaults = [Sk.builtin.none.none$]; +/* 260 */ $scope248.co_varnames = ['tb', 'limit']; +/* 261 */ var $funcobj255 = new Sk.builtins['function']($scope248, $gbl); +/* 262 */ $loc.format_tb = $funcobj255; +/* 263 */ // +/* 264 */ // line 53: +/* 265 */ // def extract_tb(tb, limit=None): +/* 266 */ // ^ +/* 267 */ // +/* 268 */ +/* 269 */ $currLineNo = Sk.currLineNo = 53; +/* 270 */ $currColNo = Sk.currColNo = 0; +/* 271 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 272 */ $scope256.co_name = new Sk.builtins['str']('extract_tb'); +/* 273 */ $scope256.$defaults = [Sk.builtin.none.none$]; +/* 274 */ $scope256.co_varnames = ['tb', 'limit']; +/* 275 */ var $funcobj262 = new Sk.builtins['function']($scope256, $gbl); +/* 276 */ $loc.extract_tb = $funcobj262; +/* 277 */ // +/* 278 */ // line 61: +/* 279 */ // _cause_message = ( +/* 280 */ // ^ +/* 281 */ // +/* 282 */ +/* 283 */ $currLineNo = Sk.currLineNo = 61; +/* 284 */ $currColNo = Sk.currColNo = 0; +/* 285 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 286 */ $loc._cause_message = $scope149.$const263; +/* 287 */ // +/* 288 */ // line 65: +/* 289 */ // _context_message = ( +/* 290 */ // ^ +/* 291 */ // +/* 292 */ +/* 293 */ $currLineNo = Sk.currLineNo = 65; +/* 294 */ $currColNo = Sk.currColNo = 0; +/* 295 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 296 */ $loc._context_message = $scope149.$const264; +/* 297 */ // +/* 298 */ // line 70: +/* 299 */ // def print_exception(etype, value, tb, limit=None, file=None, chain=True): +/* 300 */ // ^ +/* 301 */ // +/* 302 */ +/* 303 */ $currLineNo = Sk.currLineNo = 70; +/* 304 */ $currColNo = Sk.currColNo = 0; +/* 305 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 306 */ $scope265.co_name = new Sk.builtins['str']('print_exception'); +/* 307 */ $scope265.$defaults = [Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.bool.true$]; +/* 308 */ $scope265.co_varnames = ['etype', 'value', 'tb', 'limit', 'file', 'chain']; +/* 309 */ var $funcobj285 = new Sk.builtins['function']($scope265, $gbl); +/* 310 */ $loc.print_exception = $funcobj285; +/* 311 */ // +/* 312 */ // line 82: +/* 313 */ // def format_exception(etype, value, tb, limit=None, chain=True): +/* 314 */ // ^ +/* 315 */ // +/* 316 */ +/* 317 */ $currLineNo = Sk.currLineNo = 82; +/* 318 */ $currColNo = Sk.currColNo = 0; +/* 319 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 320 */ $scope286.co_name = new Sk.builtins['str']('format_exception'); +/* 321 */ $scope286.$defaults = [Sk.builtin.none.none$, Sk.builtin.bool.true$]; +/* 322 */ $scope286.co_varnames = ['etype', 'value', 'tb', 'limit', 'chain']; +/* 323 */ var $funcobj297 = new Sk.builtins['function']($scope286, $gbl); +/* 324 */ $loc.format_exception = $funcobj297; +/* 325 */ // +/* 326 */ // line 91: +/* 327 */ // def format_exception_only(etype, value): +/* 328 */ // ^ +/* 329 */ // +/* 330 */ +/* 331 */ $currLineNo = Sk.currLineNo = 91; +/* 332 */ $currColNo = Sk.currColNo = 0; +/* 333 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 334 */ $scope298.co_name = new Sk.builtins['str']('format_exception_only'); +/* 335 */ $scope298.co_varnames = ['etype', 'value']; +/* 336 */ var $funcobj307 = new Sk.builtins['function']($scope298, $gbl); +/* 337 */ $loc.format_exception_only = $funcobj307; +/* 338 */ // +/* 339 */ // line 98: +/* 340 */ // def _format_final_exc_line(etype, value): +/* 341 */ // ^ +/* 342 */ // +/* 343 */ +/* 344 */ $currLineNo = Sk.currLineNo = 98; +/* 345 */ $currColNo = Sk.currColNo = 0; +/* 346 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 347 */ $scope308.co_name = new Sk.builtins['str']('_format_final_exc_line'); +/* 348 */ $scope308.co_varnames = ['etype', 'value']; +/* 349 */ var $funcobj326 = new Sk.builtins['function']($scope308, $gbl); +/* 350 */ $loc._format_final_exc_line = $funcobj326; +/* 351 */ // +/* 352 */ // line 106: +/* 353 */ // def _some_str(value): +/* 354 */ // ^ +/* 355 */ // +/* 356 */ +/* 357 */ $currLineNo = Sk.currLineNo = 106; +/* 358 */ $currColNo = Sk.currColNo = 0; +/* 359 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 360 */ $scope327.co_name = new Sk.builtins['str']('_some_str'); +/* 361 */ $scope327.co_varnames = ['value']; +/* 362 */ var $funcobj337 = new Sk.builtins['function']($scope327, $gbl); +/* 363 */ $loc._some_str = $funcobj337; +/* 364 */ // +/* 365 */ // line 114: +/* 366 */ // def print_exc(limit=None, file=None, chain=True): +/* 367 */ // ^ +/* 368 */ // +/* 369 */ +/* 370 */ $currLineNo = Sk.currLineNo = 114; +/* 371 */ $currColNo = Sk.currColNo = 0; +/* 372 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 373 */ $scope338.co_name = new Sk.builtins['str']('print_exc'); +/* 374 */ $scope338.$defaults = [Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.bool.true$]; +/* 375 */ $scope338.co_varnames = ['limit', 'file', 'chain']; +/* 376 */ var $funcobj347 = new Sk.builtins['function']($scope338, $gbl); +/* 377 */ $loc.print_exc = $funcobj347; +/* 378 */ // +/* 379 */ // line 118: +/* 380 */ // def format_exc(limit=None, chain=True): +/* 381 */ // ^ +/* 382 */ // +/* 383 */ +/* 384 */ $currLineNo = Sk.currLineNo = 118; +/* 385 */ $currColNo = Sk.currColNo = 0; +/* 386 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 387 */ $scope348.co_name = new Sk.builtins['str']('format_exc'); +/* 388 */ $scope348.$defaults = [Sk.builtin.none.none$, Sk.builtin.bool.true$]; +/* 389 */ $scope348.co_varnames = ['limit', 'chain']; +/* 390 */ var $funcobj361 = new Sk.builtins['function']($scope348, $gbl); +/* 391 */ $loc.format_exc = $funcobj361; +/* 392 */ // +/* 393 */ // line 122: +/* 394 */ // def print_last(limit=None, file=None, chain=True): +/* 395 */ // ^ +/* 396 */ // +/* 397 */ +/* 398 */ $currLineNo = Sk.currLineNo = 122; +/* 399 */ $currColNo = Sk.currColNo = 0; +/* 400 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 401 */ $scope362.co_name = new Sk.builtins['str']('print_last'); +/* 402 */ $scope362.$defaults = [Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.bool.true$]; +/* 403 */ $scope362.co_varnames = ['limit', 'file', 'chain']; +/* 404 */ var $funcobj386 = new Sk.builtins['function']($scope362, $gbl); +/* 405 */ $loc.print_last = $funcobj386; +/* 406 */ // +/* 407 */ // line 133: +/* 408 */ // def print_stack(f=None, limit=None, file=None): +/* 409 */ // ^ +/* 410 */ // +/* 411 */ +/* 412 */ $currLineNo = Sk.currLineNo = 133; +/* 413 */ $currColNo = Sk.currColNo = 0; +/* 414 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 415 */ $scope387.co_name = new Sk.builtins['str']('print_stack'); +/* 416 */ $scope387.$defaults = [Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.none.none$]; +/* 417 */ $scope387.co_varnames = ['f', 'limit', 'file']; +/* 418 */ var $funcobj402 = new Sk.builtins['function']($scope387, $gbl); +/* 419 */ $loc.print_stack = $funcobj402; +/* 420 */ // +/* 421 */ // line 140: +/* 422 */ // def format_stack(f=None, limit=None): +/* 423 */ // ^ +/* 424 */ // +/* 425 */ +/* 426 */ $currLineNo = Sk.currLineNo = 140; +/* 427 */ $currColNo = Sk.currColNo = 0; +/* 428 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 429 */ $scope403.co_name = new Sk.builtins['str']('format_stack'); +/* 430 */ $scope403.$defaults = [Sk.builtin.none.none$, Sk.builtin.none.none$]; +/* 431 */ $scope403.co_varnames = ['f', 'limit']; +/* 432 */ var $funcobj418 = new Sk.builtins['function']($scope403, $gbl); +/* 433 */ $loc.format_stack = $funcobj418; +/* 434 */ // +/* 435 */ // line 147: +/* 436 */ // def extract_stack(f=None, limit=None): +/* 437 */ // ^ +/* 438 */ // +/* 439 */ +/* 440 */ $currLineNo = Sk.currLineNo = 147; +/* 441 */ $currColNo = Sk.currColNo = 0; +/* 442 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 443 */ $scope419.co_name = new Sk.builtins['str']('extract_stack'); +/* 444 */ $scope419.$defaults = [Sk.builtin.none.none$, Sk.builtin.none.none$]; +/* 445 */ $scope419.co_varnames = ['f', 'limit']; +/* 446 */ var $funcobj439 = new Sk.builtins['function']($scope419, $gbl); +/* 447 */ $loc.extract_stack = $funcobj439; +/* 448 */ // +/* 449 */ // line 156: +/* 450 */ // def clear_frames(tb): +/* 451 */ // ^ +/* 452 */ // +/* 453 */ +/* 454 */ $currLineNo = Sk.currLineNo = 156; +/* 455 */ $currColNo = Sk.currColNo = 0; +/* 456 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 457 */ $scope440.co_name = new Sk.builtins['str']('clear_frames'); +/* 458 */ $scope440.co_varnames = ['tb']; +/* 459 */ var $funcobj456 = new Sk.builtins['function']($scope440, $gbl); +/* 460 */ $loc.clear_frames = $funcobj456; +/* 461 */ // +/* 462 */ // line 167: +/* 463 */ // class FrameSummary: +/* 464 */ // ^ +/* 465 */ // +/* 466 */ +/* 467 */ $currLineNo = Sk.currLineNo = 167; +/* 468 */ $currColNo = Sk.currColNo = 0; +/* 469 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 470 */ $scope457.co_name = new Sk.builtins['str']('FrameSummary'); +/* 471 */ var $built607 = Sk.misceval.buildClass($gbl, $scope457, 'FrameSummary', [], $cell); +/* 472 */ $loc.FrameSummary = $built607; +/* 473 */ // +/* 474 */ // line 213: +/* 475 */ // def walk_stack(f): +/* 476 */ // ^ +/* 477 */ // +/* 478 */ +/* 479 */ $currLineNo = Sk.currLineNo = 213; +/* 480 */ $currColNo = Sk.currColNo = 0; +/* 481 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 482 */ $scope608.co_name = new Sk.builtins['str']('walk_stack'); +/* 483 */ $scope608.co_varnames = ['f']; +/* 484 */ var $gener634 = new Sk.builtins['function']((function() { +/* 485 */ var $origargs = Array.prototype.slice.call(arguments); +/* 486 */ Sk.builtin.pyCheckArgsLen("walk_stack", arguments.length, 1, 1); +/* 487 */ return new Sk.builtins['generator']($scope608, $gbl, $origargs); +/* 488 */ })); +/* 489 */ $loc.walk_stack = $gener634; +/* 490 */ // +/* 491 */ // line 222: +/* 492 */ // def walk_tb(tb): +/* 493 */ // ^ +/* 494 */ // +/* 495 */ +/* 496 */ $currLineNo = Sk.currLineNo = 222; +/* 497 */ $currColNo = Sk.currColNo = 0; +/* 498 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 499 */ $scope635.co_name = new Sk.builtins['str']('walk_tb'); +/* 500 */ $scope635.co_varnames = ['tb']; +/* 501 */ var $gener653 = new Sk.builtins['function']((function() { +/* 502 */ var $origargs = Array.prototype.slice.call(arguments); +/* 503 */ Sk.builtin.pyCheckArgsLen("walk_tb", arguments.length, 1, 1); +/* 504 */ return new Sk.builtins['generator']($scope635, $gbl, $origargs); +/* 505 */ })); +/* 506 */ $loc.walk_tb = $gener653; +/* 507 */ // +/* 508 */ // line 229: +/* 509 */ // _RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. +/* 510 */ // ^ +/* 511 */ // +/* 512 */ +/* 513 */ $currLineNo = Sk.currLineNo = 229; +/* 514 */ $currColNo = Sk.currColNo = 0; +/* 515 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 516 */ $loc._RECURSIVE_CUTOFF = $scope149.$const654; +/* 517 */ // +/* 518 */ // line 232: +/* 519 */ // def extract(frame_gen, limit=None, lookup_lines=True, capture_locals=False): +/* 520 */ // ^ +/* 521 */ // +/* 522 */ +/* 523 */ $currLineNo = Sk.currLineNo = 232; +/* 524 */ $currColNo = Sk.currColNo = 0; +/* 525 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 526 */ $scope655.co_name = new Sk.builtins['str']('extract'); +/* 527 */ $scope655.$defaults = [Sk.builtin.none.none$, Sk.builtin.bool.true$, Sk.builtin.bool.false$]; +/* 528 */ $scope655.co_varnames = ['frame_gen', 'limit', 'lookup_lines', 'capture_locals']; +/* 529 */ var $funcobj727 = new Sk.builtins['function']($scope655, $gbl); +/* 530 */ $loc.extract = $funcobj727; +/* 531 */ // +/* 532 */ // line 268: +/* 533 */ // class StackSummary(list): +/* 534 */ // ^ +/* 535 */ // +/* 536 */ +/* 537 */ $currLineNo = Sk.currLineNo = 268; +/* 538 */ $currColNo = Sk.currColNo = 0; +/* 539 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 540 */ var $loadname728 = $loc.list !== undefined ? $loc.list : Sk.misceval.loadname('list', $gbl);; +/* 541 */ $scope729.co_name = new Sk.builtins['str']('StackSummary'); +/* 542 */ var $built868 = Sk.misceval.buildClass($gbl, $scope729, 'StackSummary', [$loadname728], $cell); +/* 543 */ $loc.StackSummary = $built868; +/* 544 */ // +/* 545 */ // line 331: +/* 546 */ // class TracebackException: +/* 547 */ // ^ +/* 548 */ // +/* 549 */ +/* 550 */ $currLineNo = Sk.currLineNo = 331; +/* 551 */ $currColNo = Sk.currColNo = 0; +/* 552 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 553 */ $scope869.co_name = new Sk.builtins['str']('TracebackException'); +/* 554 */ var $built1225 = Sk.misceval.buildClass($gbl, $scope869, 'TracebackException', [], $cell); +/* 555 */ $loc.TracebackException = $built1225; +/* 556 */ return $loc; +/* 557 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 558 */ } +/* 559 */ } catch (err) { +/* 560 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 561 */ Sk.execStart = Date.now(); +/* 562 */ Sk.execPaused = 0 +/* 563 */ } +/* 564 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 565 */ err = new Sk.builtin.ExternalError(err); +/* 566 */ } +/* 567 */ Sk.err = err; +/* 568 */ err.traceback.push({ +/* 569 */ lineno: $currLineNo, +/* 570 */ colno: $currColNo, +/* 571 */ filename: 'src/lib/traceback.py', +/* 572 */ scope: '' +/* 573 */ }); +/* 574 */ if ($exc.length > 0) { +/* 575 */ $err = err; +/* 576 */ $blk = $exc.pop(); +/* 577 */ continue +/* 578 */ } else { +/* 579 */ throw err; +/* 580 */ } +/* 581 */ } +/* 582 */ } +/* 583 */ }); +/* 584 */ $scope149.$const171 = new Sk.builtin.str('extract_stack'); +/* 585 */ $scope149.$const173 = new Sk.builtin.str('extract_tb'); +/* 586 */ $scope149.$const175 = new Sk.builtin.str('format_exception'); +/* 587 */ $scope149.$const177 = new Sk.builtin.str('format_exception_only'); +/* 588 */ $scope149.$const179 = new Sk.builtin.str('format_list'); +/* 589 */ $scope149.$const181 = new Sk.builtin.str('format_stack'); +/* 590 */ $scope149.$const183 = new Sk.builtin.str('format_tb'); +/* 591 */ $scope149.$const185 = new Sk.builtin.str('print_exc'); +/* 592 */ $scope149.$const187 = new Sk.builtin.str('format_exc'); +/* 593 */ $scope149.$const189 = new Sk.builtin.str('print_exception'); +/* 594 */ $scope149.$const191 = new Sk.builtin.str('print_last'); +/* 595 */ $scope149.$const193 = new Sk.builtin.str('print_stack'); +/* 596 */ $scope149.$const195 = new Sk.builtin.str('print_tb'); +/* 597 */ $scope149.$const197 = new Sk.builtin.str('clear_frames'); +/* 598 */ $scope149.$const199 = new Sk.builtin.str('FrameSummary'); +/* 599 */ $scope149.$const201 = new Sk.builtin.str('StackSummary'); +/* 600 */ $scope149.$const203 = new Sk.builtin.str('TracebackException'); +/* 601 */ $scope149.$const205 = new Sk.builtin.str('walk_stack'); +/* 602 */ $scope149.$const207 = new Sk.builtin.str('walk_tb'); +/* 603 */ $scope149.$const263 = new Sk.builtin.str('\nThe above exception was the direct cause of the following exception:\n\n'); +/* 604 */ $scope149.$const264 = new Sk.builtin.str('\nDuring handling of the above exception, another exception occurred:\n\n'); +/* 605 */ $scope149.$const654 = new Sk.builtin.int_(3); +/* 606 */ var $scope151 = (function $linecache$class_outer($globals, $locals, $cell) { +/* 607 */ var $gbl = $globals, +/* 608 */ $loc = $locals; +/* 609 */ $free = $globals; +/* 610 */ (function $linecache$_closure($cell) { +/* 611 */ var $blk = 0, +/* 612 */ $exc = [], +/* 613 */ $ret = undefined, +/* 614 */ $postfinally = undefined, +/* 615 */ $currLineNo = undefined, +/* 616 */ $currColNo = undefined; +/* 617 */ if (typeof Sk.execStart === 'undefined') { +/* 618 */ Sk.execStart = Date.now(); +/* 619 */ Sk.execPaused = 0 +/* 620 */ } +/* 621 */ while (true) { +/* 622 */ try { +/* 623 */ var $dateNow = Date.now(); +/* 624 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 625 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 626 */ } +/* 627 */ switch ($blk) { +/* 628 */ case 0: +/* 629 */ /* --- class entry --- */ +/* 630 */ // +/* 631 */ // line 6: +/* 632 */ // @staticmethod +/* 633 */ // ^ +/* 634 */ // +/* 635 */ +/* 636 */ $currLineNo = Sk.currLineNo = 6; +/* 637 */ $currColNo = Sk.currColNo = 4; +/* 638 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 639 */ var $loadname152 = $loc.staticmethod !== undefined ? $loc.staticmethod : Sk.misceval.loadname('staticmethod', $gbl);; +/* 640 */ $scope153.co_name = new Sk.builtins['str']('getline'); +/* 641 */ $scope153.$defaults = [Sk.builtin.none.none$]; +/* 642 */ $scope153.$decorators = [$loadname152]; +/* 643 */ $scope153.co_varnames = ['filename', 'lineno', 'module_globals']; +/* 644 */ $ret = Sk.misceval.callsimOrSuspendArray($scope153.$decorators[0], [new Sk.builtins['function']($scope153, $gbl)]); +/* 645 */ if ($ret && $ret.$isSuspension) { +/* 646 */ $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); +/* 647 */ } +/* 648 */ var $funcobj156 = $ret; +/* 649 */ $loc.getline = $funcobj156; +/* 650 */ // +/* 651 */ // line 9: +/* 652 */ // @staticmethod +/* 653 */ // ^ +/* 654 */ // +/* 655 */ +/* 656 */ $currLineNo = Sk.currLineNo = 9; +/* 657 */ $currColNo = Sk.currColNo = 4; +/* 658 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 659 */ var $loadname157 = $loc.staticmethod !== undefined ? $loc.staticmethod : Sk.misceval.loadname('staticmethod', $gbl);; +/* 660 */ $scope158.co_name = new Sk.builtins['str']('lazycache'); +/* 661 */ $scope158.$decorators = [$loadname157]; +/* 662 */ $scope158.co_varnames = ['filename', 'globals']; +/* 663 */ $ret = Sk.misceval.callsimOrSuspendArray($scope158.$decorators[0], [new Sk.builtins['function']($scope158, $gbl)]); +/* 664 */ if ($ret && $ret.$isSuspension) { +/* 665 */ $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); +/* 666 */ } +/* 667 */ var $funcobj161 = $ret; +/* 668 */ $loc.lazycache = $funcobj161; +/* 669 */ // +/* 670 */ // line 12: +/* 671 */ // @staticmethod +/* 672 */ // ^ +/* 673 */ // +/* 674 */ +/* 675 */ $currLineNo = Sk.currLineNo = 12; +/* 676 */ $currColNo = Sk.currColNo = 4; +/* 677 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 678 */ var $loadname162 = $loc.staticmethod !== undefined ? $loc.staticmethod : Sk.misceval.loadname('staticmethod', $gbl);; +/* 679 */ $scope163.co_name = new Sk.builtins['str']('checkcache'); +/* 680 */ $scope163.$decorators = [$loadname162]; +/* 681 */ $scope163.co_varnames = ['filename']; +/* 682 */ $ret = Sk.misceval.callsimOrSuspendArray($scope163.$decorators[0], [new Sk.builtins['function']($scope163, $gbl)]); +/* 683 */ if ($ret && $ret.$isSuspension) { +/* 684 */ $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); +/* 685 */ } +/* 686 */ var $funcobj166 = $ret; +/* 687 */ $loc.checkcache = $funcobj166; +/* 688 */ return; +/* 689 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 690 */ } +/* 691 */ } catch (err) { +/* 692 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 693 */ Sk.execStart = Date.now(); +/* 694 */ Sk.execPaused = 0 +/* 695 */ } +/* 696 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 697 */ err = new Sk.builtin.ExternalError(err); +/* 698 */ } +/* 699 */ Sk.err = err; +/* 700 */ err.traceback.push({ +/* 701 */ lineno: $currLineNo, +/* 702 */ colno: $currColNo, +/* 703 */ filename: 'src/lib/traceback.py', +/* 704 */ scope: 'linecache' +/* 705 */ }); +/* 706 */ if ($exc.length > 0) { +/* 707 */ $err = err; +/* 708 */ $blk = $exc.pop(); +/* 709 */ continue +/* 710 */ } else { +/* 711 */ throw err; +/* 712 */ } +/* 713 */ } +/* 714 */ } +/* 715 */ }).call(null, $cell); +/* 716 */ }); +/* 717 */ var $scope153 = (function $getline154$(filename, lineno, module_globals) { +/* 718 */ var filename, lineno, module_globals; +/* 719 */ var $wakeFromSuspension = function() { +/* 720 */ var susp = $scope153.$wakingSuspension; +/* 721 */ $scope153.$wakingSuspension = undefined; +/* 722 */ $blk = susp.$blk; +/* 723 */ $loc = susp.$loc; +/* 724 */ $gbl = susp.$gbl; +/* 725 */ $exc = susp.$exc; +/* 726 */ $err = susp.$err; +/* 727 */ $postfinally = susp.$postfinally; +/* 728 */ $currLineNo = susp.$lineno; +/* 729 */ $currColNo = susp.$colno; +/* 730 */ Sk.lastYield = Date.now(); +/* 731 */ filename = susp.$tmps.filename; +/* 732 */ lineno = susp.$tmps.lineno; +/* 733 */ module_globals = susp.$tmps.module_globals; +/* 734 */ try { +/* 735 */ $ret = susp.child.resume(); +/* 736 */ } catch (err) { +/* 737 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 738 */ Sk.execStart = Date.now(); +/* 739 */ Sk.execPaused = 0 +/* 740 */ } +/* 741 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 742 */ err = new Sk.builtin.ExternalError(err); +/* 743 */ } +/* 744 */ Sk.err = err; +/* 745 */ err.traceback.push({ +/* 746 */ lineno: $currLineNo, +/* 747 */ colno: $currColNo, +/* 748 */ filename: 'src/lib/traceback.py', +/* 749 */ scope: '$scope153' +/* 750 */ }); +/* 751 */ if ($exc.length > 0) { +/* 752 */ $err = err; +/* 753 */ $blk = $exc.pop(); +/* 754 */ } else { +/* 755 */ throw err; +/* 756 */ } +/* 757 */ } +/* 758 */ }; +/* 759 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 760 */ var susp = new Sk.misceval.Suspension(); +/* 761 */ susp.child = $child; +/* 762 */ susp.resume = function() { +/* 763 */ $scope153.$wakingSuspension = susp; +/* 764 */ return $scope153(); +/* 765 */ }; +/* 766 */ susp.data = susp.child.data; +/* 767 */ susp.$blk = $blk; +/* 768 */ susp.$loc = $loc; +/* 769 */ susp.$gbl = $gbl; +/* 770 */ susp.$exc = $exc; +/* 771 */ susp.$err = $err; +/* 772 */ susp.$postfinally = $postfinally; +/* 773 */ susp.$filename = $filename; +/* 774 */ susp.$lineno = $lineno; +/* 775 */ susp.$colno = $colno; +/* 776 */ susp.optional = susp.child.optional; +/* 777 */ susp.$tmps = { +/* 778 */ "filename": filename, +/* 779 */ "lineno": lineno, +/* 780 */ "module_globals": module_globals +/* 781 */ }; +/* 782 */ return susp; +/* 783 */ }; +/* 784 */ var $blk = 0, +/* 785 */ $exc = [], +/* 786 */ $loc = {}, +/* 787 */ $cell = {}, +/* 788 */ $gbl = this, +/* 789 */ $err = undefined, +/* 790 */ $ret = undefined, +/* 791 */ $postfinally = undefined, +/* 792 */ $currLineNo = undefined, +/* 793 */ $currColNo = undefined; +/* 794 */ if (typeof Sk.execStart === 'undefined') { +/* 795 */ Sk.execStart = Date.now(); +/* 796 */ Sk.execPaused = 0 +/* 797 */ } +/* 798 */ if (typeof Sk.lastYield === 'undefined') { +/* 799 */ Sk.lastYield = Date.now() +/* 800 */ } +/* 801 */ if ($scope153.$wakingSuspension !== undefined) { +/* 802 */ $wakeFromSuspension(); +/* 803 */ } else {} +/* 804 */ $gbl.__class__ = this.linecache; +/* 805 */ while (true) { +/* 806 */ try { +/* 807 */ var $dateNow = Date.now(); +/* 808 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 809 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 810 */ } +/* 811 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 812 */ var $susp = $saveSuspension({ +/* 813 */ data: { +/* 814 */ type: 'Sk.yield' +/* 815 */ }, +/* 816 */ resume: function() {} +/* 817 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 818 */ $susp.$blk = $blk; +/* 819 */ $susp.optional = true; +/* 820 */ return $susp; +/* 821 */ } +/* 822 */ switch ($blk) { +/* 823 */ case 0: +/* 824 */ /* --- codeobj entry --- */ if (filename === undefined) { +/* 825 */ throw new Sk.builtin.UnboundLocalError('local variable \'filename\' referenced before assignment'); +/* 826 */ } +/* 827 */ if (lineno === undefined) { +/* 828 */ throw new Sk.builtin.UnboundLocalError('local variable \'lineno\' referenced before assignment'); +/* 829 */ } +/* 830 */ if (module_globals === undefined) { +/* 831 */ throw new Sk.builtin.UnboundLocalError('local variable \'module_globals\' referenced before assignment'); +/* 832 */ } +/* 833 */ +/* 834 */ // +/* 835 */ // line 8: +/* 836 */ // return "Apples and bananas" +/* 837 */ // ^ +/* 838 */ // +/* 839 */ +/* 840 */ $currLineNo = Sk.currLineNo = 8; +/* 841 */ $currColNo = Sk.currColNo = 8; +/* 842 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 843 */ return $scope153.$const155; +/* 844 */ return Sk.builtin.none.none$; +/* 845 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 846 */ } +/* 847 */ } catch (err) { +/* 848 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 849 */ Sk.execStart = Date.now(); +/* 850 */ Sk.execPaused = 0 +/* 851 */ } +/* 852 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 853 */ err = new Sk.builtin.ExternalError(err); +/* 854 */ } +/* 855 */ Sk.err = err; +/* 856 */ err.traceback.push({ +/* 857 */ lineno: $currLineNo, +/* 858 */ colno: $currColNo, +/* 859 */ filename: 'src/lib/traceback.py', +/* 860 */ scope: 'getline' +/* 861 */ }); +/* 862 */ if ($exc.length > 0) { +/* 863 */ $err = err; +/* 864 */ $blk = $exc.pop(); +/* 865 */ continue +/* 866 */ } else { +/* 867 */ throw err; +/* 868 */ } +/* 869 */ } +/* 870 */ } +/* 871 */ }); +/* 872 */ $scope153.$const155 = new Sk.builtin.str('Apples and bananas'); +/* 873 */ var $scope158 = (function $lazycache159$(filename, globals) { +/* 874 */ var filename, globals; +/* 875 */ var $wakeFromSuspension = function() { +/* 876 */ var susp = $scope158.$wakingSuspension; +/* 877 */ $scope158.$wakingSuspension = undefined; +/* 878 */ $blk = susp.$blk; +/* 879 */ $loc = susp.$loc; +/* 880 */ $gbl = susp.$gbl; +/* 881 */ $exc = susp.$exc; +/* 882 */ $err = susp.$err; +/* 883 */ $postfinally = susp.$postfinally; +/* 884 */ $currLineNo = susp.$lineno; +/* 885 */ $currColNo = susp.$colno; +/* 886 */ Sk.lastYield = Date.now(); +/* 887 */ filename = susp.$tmps.filename; +/* 888 */ globals = susp.$tmps.globals; +/* 889 */ try { +/* 890 */ $ret = susp.child.resume(); +/* 891 */ } catch (err) { +/* 892 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 893 */ Sk.execStart = Date.now(); +/* 894 */ Sk.execPaused = 0 +/* 895 */ } +/* 896 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 897 */ err = new Sk.builtin.ExternalError(err); +/* 898 */ } +/* 899 */ Sk.err = err; +/* 900 */ err.traceback.push({ +/* 901 */ lineno: $currLineNo, +/* 902 */ colno: $currColNo, +/* 903 */ filename: 'src/lib/traceback.py', +/* 904 */ scope: '$scope158' +/* 905 */ }); +/* 906 */ if ($exc.length > 0) { +/* 907 */ $err = err; +/* 908 */ $blk = $exc.pop(); +/* 909 */ } else { +/* 910 */ throw err; +/* 911 */ } +/* 912 */ } +/* 913 */ }; +/* 914 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 915 */ var susp = new Sk.misceval.Suspension(); +/* 916 */ susp.child = $child; +/* 917 */ susp.resume = function() { +/* 918 */ $scope158.$wakingSuspension = susp; +/* 919 */ return $scope158(); +/* 920 */ }; +/* 921 */ susp.data = susp.child.data; +/* 922 */ susp.$blk = $blk; +/* 923 */ susp.$loc = $loc; +/* 924 */ susp.$gbl = $gbl; +/* 925 */ susp.$exc = $exc; +/* 926 */ susp.$err = $err; +/* 927 */ susp.$postfinally = $postfinally; +/* 928 */ susp.$filename = $filename; +/* 929 */ susp.$lineno = $lineno; +/* 930 */ susp.$colno = $colno; +/* 931 */ susp.optional = susp.child.optional; +/* 932 */ susp.$tmps = { +/* 933 */ "filename": filename, +/* 934 */ "globals": globals +/* 935 */ }; +/* 936 */ return susp; +/* 937 */ }; +/* 938 */ var $blk = 0, +/* 939 */ $exc = [], +/* 940 */ $loc = {}, +/* 941 */ $cell = {}, +/* 942 */ $gbl = this, +/* 943 */ $err = undefined, +/* 944 */ $ret = undefined, +/* 945 */ $postfinally = undefined, +/* 946 */ $currLineNo = undefined, +/* 947 */ $currColNo = undefined; +/* 948 */ if (typeof Sk.execStart === 'undefined') { +/* 949 */ Sk.execStart = Date.now(); +/* 950 */ Sk.execPaused = 0 +/* 951 */ } +/* 952 */ if (typeof Sk.lastYield === 'undefined') { +/* 953 */ Sk.lastYield = Date.now() +/* 954 */ } +/* 955 */ if ($scope158.$wakingSuspension !== undefined) { +/* 956 */ $wakeFromSuspension(); +/* 957 */ } else {} +/* 958 */ $gbl.__class__ = this.linecache; +/* 959 */ while (true) { +/* 960 */ try { +/* 961 */ var $dateNow = Date.now(); +/* 962 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 963 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 964 */ } +/* 965 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 966 */ var $susp = $saveSuspension({ +/* 967 */ data: { +/* 968 */ type: 'Sk.yield' +/* 969 */ }, +/* 970 */ resume: function() {} +/* 971 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 972 */ $susp.$blk = $blk; +/* 973 */ $susp.optional = true; +/* 974 */ return $susp; +/* 975 */ } +/* 976 */ switch ($blk) { +/* 977 */ case 0: +/* 978 */ /* --- codeobj entry --- */ if (filename === undefined) { +/* 979 */ throw new Sk.builtin.UnboundLocalError('local variable \'filename\' referenced before assignment'); +/* 980 */ } +/* 981 */ if (globals === undefined) { +/* 982 */ throw new Sk.builtin.UnboundLocalError('local variable \'globals\' referenced before assignment'); +/* 983 */ } +/* 984 */ +/* 985 */ // +/* 986 */ // line 11: +/* 987 */ // return "Frogs and ketchup" +/* 988 */ // ^ +/* 989 */ // +/* 990 */ +/* 991 */ $currLineNo = Sk.currLineNo = 11; +/* 992 */ $currColNo = Sk.currColNo = 8; +/* 993 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 994 */ return $scope158.$const160; +/* 995 */ return Sk.builtin.none.none$; +/* 996 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 997 */ } +/* 998 */ } catch (err) { +/* 999 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 1000 */ Sk.execStart = Date.now(); +/* 1001 */ Sk.execPaused = 0 +/* 1002 */ } +/* 1003 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 1004 */ err = new Sk.builtin.ExternalError(err); +/* 1005 */ } +/* 1006 */ Sk.err = err; +/* 1007 */ err.traceback.push({ +/* 1008 */ lineno: $currLineNo, +/* 1009 */ colno: $currColNo, +/* 1010 */ filename: 'src/lib/traceback.py', +/* 1011 */ scope: 'lazycache' +/* 1012 */ }); +/* 1013 */ if ($exc.length > 0) { +/* 1014 */ $err = err; +/* 1015 */ $blk = $exc.pop(); +/* 1016 */ continue +/* 1017 */ } else { +/* 1018 */ throw err; +/* 1019 */ } +/* 1020 */ } +/* 1021 */ } +/* 1022 */ }); +/* 1023 */ $scope158.$const160 = new Sk.builtin.str('Frogs and ketchup'); +/* 1024 */ var $scope163 = (function $checkcache164$(filename) { +/* 1025 */ var filename; +/* 1026 */ var $wakeFromSuspension = function() { +/* 1027 */ var susp = $scope163.$wakingSuspension; +/* 1028 */ $scope163.$wakingSuspension = undefined; +/* 1029 */ $blk = susp.$blk; +/* 1030 */ $loc = susp.$loc; +/* 1031 */ $gbl = susp.$gbl; +/* 1032 */ $exc = susp.$exc; +/* 1033 */ $err = susp.$err; +/* 1034 */ $postfinally = susp.$postfinally; +/* 1035 */ $currLineNo = susp.$lineno; +/* 1036 */ $currColNo = susp.$colno; +/* 1037 */ Sk.lastYield = Date.now(); +/* 1038 */ filename = susp.$tmps.filename; +/* 1039 */ try { +/* 1040 */ $ret = susp.child.resume(); +/* 1041 */ } catch (err) { +/* 1042 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 1043 */ Sk.execStart = Date.now(); +/* 1044 */ Sk.execPaused = 0 +/* 1045 */ } +/* 1046 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 1047 */ err = new Sk.builtin.ExternalError(err); +/* 1048 */ } +/* 1049 */ Sk.err = err; +/* 1050 */ err.traceback.push({ +/* 1051 */ lineno: $currLineNo, +/* 1052 */ colno: $currColNo, +/* 1053 */ filename: 'src/lib/traceback.py', +/* 1054 */ scope: '$scope163' +/* 1055 */ }); +/* 1056 */ if ($exc.length > 0) { +/* 1057 */ $err = err; +/* 1058 */ $blk = $exc.pop(); +/* 1059 */ } else { +/* 1060 */ throw err; +/* 1061 */ } +/* 1062 */ } +/* 1063 */ }; +/* 1064 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 1065 */ var susp = new Sk.misceval.Suspension(); +/* 1066 */ susp.child = $child; +/* 1067 */ susp.resume = function() { +/* 1068 */ $scope163.$wakingSuspension = susp; +/* 1069 */ return $scope163(); +/* 1070 */ }; +/* 1071 */ susp.data = susp.child.data; +/* 1072 */ susp.$blk = $blk; +/* 1073 */ susp.$loc = $loc; +/* 1074 */ susp.$gbl = $gbl; +/* 1075 */ susp.$exc = $exc; +/* 1076 */ susp.$err = $err; +/* 1077 */ susp.$postfinally = $postfinally; +/* 1078 */ susp.$filename = $filename; +/* 1079 */ susp.$lineno = $lineno; +/* 1080 */ susp.$colno = $colno; +/* 1081 */ susp.optional = susp.child.optional; +/* 1082 */ susp.$tmps = { +/* 1083 */ "filename": filename +/* 1084 */ }; +/* 1085 */ return susp; +/* 1086 */ }; +/* 1087 */ var $blk = 0, +/* 1088 */ $exc = [], +/* 1089 */ $loc = {}, +/* 1090 */ $cell = {}, +/* 1091 */ $gbl = this, +/* 1092 */ $err = undefined, +/* 1093 */ $ret = undefined, +/* 1094 */ $postfinally = undefined, +/* 1095 */ $currLineNo = undefined, +/* 1096 */ $currColNo = undefined; +/* 1097 */ if (typeof Sk.execStart === 'undefined') { +/* 1098 */ Sk.execStart = Date.now(); +/* 1099 */ Sk.execPaused = 0 +/* 1100 */ } +/* 1101 */ if (typeof Sk.lastYield === 'undefined') { +/* 1102 */ Sk.lastYield = Date.now() +/* 1103 */ } +/* 1104 */ if ($scope163.$wakingSuspension !== undefined) { +/* 1105 */ $wakeFromSuspension(); +/* 1106 */ } else {} +/* 1107 */ $gbl.__class__ = this.linecache; +/* 1108 */ while (true) { +/* 1109 */ try { +/* 1110 */ var $dateNow = Date.now(); +/* 1111 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 1112 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 1113 */ } +/* 1114 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 1115 */ var $susp = $saveSuspension({ +/* 1116 */ data: { +/* 1117 */ type: 'Sk.yield' +/* 1118 */ }, +/* 1119 */ resume: function() {} +/* 1120 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 1121 */ $susp.$blk = $blk; +/* 1122 */ $susp.optional = true; +/* 1123 */ return $susp; +/* 1124 */ } +/* 1125 */ switch ($blk) { +/* 1126 */ case 0: +/* 1127 */ /* --- codeobj entry --- */ if (filename === undefined) { +/* 1128 */ throw new Sk.builtin.UnboundLocalError('local variable \'filename\' referenced before assignment'); +/* 1129 */ } +/* 1130 */ +/* 1131 */ // +/* 1132 */ // line 14: +/* 1133 */ // return "Weird ocean city" +/* 1134 */ // ^ +/* 1135 */ // +/* 1136 */ +/* 1137 */ $currLineNo = Sk.currLineNo = 14; +/* 1138 */ $currColNo = Sk.currColNo = 8; +/* 1139 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1140 */ return $scope163.$const165; +/* 1141 */ return Sk.builtin.none.none$; +/* 1142 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 1143 */ } +/* 1144 */ } catch (err) { +/* 1145 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 1146 */ Sk.execStart = Date.now(); +/* 1147 */ Sk.execPaused = 0 +/* 1148 */ } +/* 1149 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 1150 */ err = new Sk.builtin.ExternalError(err); +/* 1151 */ } +/* 1152 */ Sk.err = err; +/* 1153 */ err.traceback.push({ +/* 1154 */ lineno: $currLineNo, +/* 1155 */ colno: $currColNo, +/* 1156 */ filename: 'src/lib/traceback.py', +/* 1157 */ scope: 'checkcache' +/* 1158 */ }); +/* 1159 */ if ($exc.length > 0) { +/* 1160 */ $err = err; +/* 1161 */ $blk = $exc.pop(); +/* 1162 */ continue +/* 1163 */ } else { +/* 1164 */ throw err; +/* 1165 */ } +/* 1166 */ } +/* 1167 */ } +/* 1168 */ }); +/* 1169 */ $scope163.$const165 = new Sk.builtin.str('Weird ocean city'); +/* 1170 */ var $scope210 = (function $print_list211$(extracted_list, file) { +/* 1171 */ var item; /* locals */ +/* 1172 */ var extracted_list, extracted_list, file, file, file, file, item, item, $compareres212, $loadgbl215, $loadgbl218, $loadgbl218, $lattr220, $loadgbl218, $lattr220, $call221, $loadgbl218, $lattr220, $call221, $lattr223, $iter225, $loadgbl218, $lattr220, $call221, $lattr223, $call224, $iter225, $loadgbl227; +/* 1173 */ var $wakeFromSuspension = function() { +/* 1174 */ var susp = $scope210.$wakingSuspension; +/* 1175 */ $scope210.$wakingSuspension = undefined; +/* 1176 */ $blk = susp.$blk; +/* 1177 */ $loc = susp.$loc; +/* 1178 */ $gbl = susp.$gbl; +/* 1179 */ $exc = susp.$exc; +/* 1180 */ $err = susp.$err; +/* 1181 */ $postfinally = susp.$postfinally; +/* 1182 */ $currLineNo = susp.$lineno; +/* 1183 */ $currColNo = susp.$colno; +/* 1184 */ Sk.lastYield = Date.now(); +/* 1185 */ extracted_list = susp.$tmps.extracted_list; +/* 1186 */ file = susp.$tmps.file; +/* 1187 */ item = susp.$tmps.item; +/* 1188 */ $compareres212 = susp.$tmps.$compareres212; +/* 1189 */ $loadgbl215 = susp.$tmps.$loadgbl215; +/* 1190 */ $loadgbl218 = susp.$tmps.$loadgbl218; +/* 1191 */ $lattr220 = susp.$tmps.$lattr220; +/* 1192 */ $call221 = susp.$tmps.$call221; +/* 1193 */ $lattr223 = susp.$tmps.$lattr223; +/* 1194 */ $iter225 = susp.$tmps.$iter225; +/* 1195 */ $call224 = susp.$tmps.$call224; +/* 1196 */ $loadgbl227 = susp.$tmps.$loadgbl227; +/* 1197 */ try { +/* 1198 */ $ret = susp.child.resume(); +/* 1199 */ } catch (err) { +/* 1200 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 1201 */ Sk.execStart = Date.now(); +/* 1202 */ Sk.execPaused = 0 +/* 1203 */ } +/* 1204 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 1205 */ err = new Sk.builtin.ExternalError(err); +/* 1206 */ } +/* 1207 */ Sk.err = err; +/* 1208 */ err.traceback.push({ +/* 1209 */ lineno: $currLineNo, +/* 1210 */ colno: $currColNo, +/* 1211 */ filename: 'src/lib/traceback.py', +/* 1212 */ scope: '$scope210' +/* 1213 */ }); +/* 1214 */ if ($exc.length > 0) { +/* 1215 */ $err = err; +/* 1216 */ $blk = $exc.pop(); +/* 1217 */ } else { +/* 1218 */ throw err; +/* 1219 */ } +/* 1220 */ } +/* 1221 */ }; +/* 1222 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 1223 */ var susp = new Sk.misceval.Suspension(); +/* 1224 */ susp.child = $child; +/* 1225 */ susp.resume = function() { +/* 1226 */ $scope210.$wakingSuspension = susp; +/* 1227 */ return $scope210(); +/* 1228 */ }; +/* 1229 */ susp.data = susp.child.data; +/* 1230 */ susp.$blk = $blk; +/* 1231 */ susp.$loc = $loc; +/* 1232 */ susp.$gbl = $gbl; +/* 1233 */ susp.$exc = $exc; +/* 1234 */ susp.$err = $err; +/* 1235 */ susp.$postfinally = $postfinally; +/* 1236 */ susp.$filename = $filename; +/* 1237 */ susp.$lineno = $lineno; +/* 1238 */ susp.$colno = $colno; +/* 1239 */ susp.optional = susp.child.optional; +/* 1240 */ susp.$tmps = { +/* 1241 */ "extracted_list": extracted_list, +/* 1242 */ "file": file, +/* 1243 */ "item": item, +/* 1244 */ "$compareres212": $compareres212, +/* 1245 */ "$loadgbl215": $loadgbl215, +/* 1246 */ "$loadgbl218": $loadgbl218, +/* 1247 */ "$lattr220": $lattr220, +/* 1248 */ "$call221": $call221, +/* 1249 */ "$lattr223": $lattr223, +/* 1250 */ "$iter225": $iter225, +/* 1251 */ "$call224": $call224, +/* 1252 */ "$loadgbl227": $loadgbl227 +/* 1253 */ }; +/* 1254 */ return susp; +/* 1255 */ }; +/* 1256 */ var $blk = 0, +/* 1257 */ $exc = [], +/* 1258 */ $loc = {}, +/* 1259 */ $cell = {}, +/* 1260 */ $gbl = this, +/* 1261 */ $err = undefined, +/* 1262 */ $ret = undefined, +/* 1263 */ $postfinally = undefined, +/* 1264 */ $currLineNo = undefined, +/* 1265 */ $currColNo = undefined; +/* 1266 */ if (typeof Sk.execStart === 'undefined') { +/* 1267 */ Sk.execStart = Date.now(); +/* 1268 */ Sk.execPaused = 0 +/* 1269 */ } +/* 1270 */ if (typeof Sk.lastYield === 'undefined') { +/* 1271 */ Sk.lastYield = Date.now() +/* 1272 */ } +/* 1273 */ if ($scope210.$wakingSuspension !== undefined) { +/* 1274 */ $wakeFromSuspension(); +/* 1275 */ } else {} +/* 1276 */ while (true) { +/* 1277 */ try { +/* 1278 */ var $dateNow = Date.now(); +/* 1279 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 1280 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 1281 */ } +/* 1282 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 1283 */ var $susp = $saveSuspension({ +/* 1284 */ data: { +/* 1285 */ type: 'Sk.yield' +/* 1286 */ }, +/* 1287 */ resume: function() {} +/* 1288 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 1289 */ $susp.$blk = $blk; +/* 1290 */ $susp.optional = true; +/* 1291 */ return $susp; +/* 1292 */ } +/* 1293 */ switch ($blk) { +/* 1294 */ case 0: +/* 1295 */ /* --- codeobj entry --- */ if (extracted_list === undefined) { +/* 1296 */ throw new Sk.builtin.UnboundLocalError('local variable \'extracted_list\' referenced before assignment'); +/* 1297 */ } +/* 1298 */ if (file === undefined) { +/* 1299 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 1300 */ } +/* 1301 */ +/* 1302 */ // +/* 1303 */ // line 32: +/* 1304 */ // if file is None: +/* 1305 */ // ^ +/* 1306 */ // +/* 1307 */ +/* 1308 */ $currLineNo = Sk.currLineNo = 32; +/* 1309 */ $currColNo = Sk.currColNo = 4; +/* 1310 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1311 */ if (file === undefined) { +/* 1312 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 1313 */ } +/* 1314 */ var $compareres212 = null; +/* 1315 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(file, Sk.builtin.none.none$, 'Is', true)); +/* 1316 */ $blk = 3; /* allowing case fallthrough */ +/* 1317 */ case 3: +/* 1318 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1319 */ return $saveSuspension($ret, 'src/lib/traceback.py', 32, 7); +/* 1320 */ } +/* 1321 */ $compareres212 = $ret; +/* 1322 */ var $jfalse213 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 1323 */ if ($jfalse213) { +/* 1324 */ /*test failed */ +/* 1325 */ $blk = 2; +/* 1326 */ continue; +/* 1327 */ } +/* 1328 */ $blk = 2; /* allowing case fallthrough */ +/* 1329 */ case 2: +/* 1330 */ /* --- done --- */ var $jfalse214 = ($compareres212 === false || !Sk.misceval.isTrue($compareres212)); +/* 1331 */ if ($jfalse214) { +/* 1332 */ /*test failed */ +/* 1333 */ $blk = 1; +/* 1334 */ continue; +/* 1335 */ } +/* 1336 */ // +/* 1337 */ // line 33: +/* 1338 */ // file = sys.stderr +/* 1339 */ // ^ +/* 1340 */ // +/* 1341 */ +/* 1342 */ $currLineNo = Sk.currLineNo = 33; +/* 1343 */ $currColNo = Sk.currColNo = 8; +/* 1344 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1345 */ var $loadgbl215 = Sk.misceval.loadname('sys', $gbl); +/* 1346 */ $ret = Sk.abstr.gattr($loadgbl215, $scope210.$const216, true); +/* 1347 */ $blk = 4; /* allowing case fallthrough */ +/* 1348 */ case 4: +/* 1349 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1350 */ return $saveSuspension($ret, 'src/lib/traceback.py', 33, 15); +/* 1351 */ } +/* 1352 */ var $lattr217 = $ret; +/* 1353 */ file = $lattr217; +/* 1354 */ $blk = 1; /* allowing case fallthrough */ +/* 1355 */ case 1: +/* 1356 */ /* --- end of if --- */ +/* 1357 */ // +/* 1358 */ // line 34: +/* 1359 */ // for item in StackSummary.from_list(extracted_list).format(): +/* 1360 */ // ^ +/* 1361 */ // +/* 1362 */ +/* 1363 */ $currLineNo = Sk.currLineNo = 34; +/* 1364 */ $currColNo = Sk.currColNo = 4; +/* 1365 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1366 */ var $loadgbl218 = Sk.misceval.loadname('StackSummary', $gbl); +/* 1367 */ $ret = Sk.abstr.gattr($loadgbl218, $scope210.$const219, true); +/* 1368 */ $blk = 8; /* allowing case fallthrough */ +/* 1369 */ case 8: +/* 1370 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1371 */ return $saveSuspension($ret, 'src/lib/traceback.py', 34, 16); +/* 1372 */ } +/* 1373 */ var $lattr220 = $ret; +/* 1374 */ if (extracted_list === undefined) { +/* 1375 */ throw new Sk.builtin.UnboundLocalError('local variable \'extracted_list\' referenced before assignment'); +/* 1376 */ } +/* 1377 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr220, [extracted_list]); +/* 1378 */ $blk = 9; /* allowing case fallthrough */ +/* 1379 */ case 9: +/* 1380 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1381 */ return $saveSuspension($ret, 'src/lib/traceback.py', 34, 16); +/* 1382 */ } +/* 1383 */ var $call221 = $ret; +/* 1384 */ // +/* 1385 */ // line 34: +/* 1386 */ // for item in StackSummary.from_list(extracted_list).format(): +/* 1387 */ // ^ +/* 1388 */ // +/* 1389 */ +/* 1390 */ $currLineNo = Sk.currLineNo = 34; +/* 1391 */ $currColNo = Sk.currColNo = 16; +/* 1392 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1393 */ $ret = Sk.abstr.gattr($call221, $scope210.$const222, true); +/* 1394 */ $blk = 10; /* allowing case fallthrough */ +/* 1395 */ case 10: +/* 1396 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1397 */ return $saveSuspension($ret, 'src/lib/traceback.py', 34, 16); +/* 1398 */ } +/* 1399 */ var $lattr223 = $ret; +/* 1400 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr223); +/* 1401 */ $blk = 11; /* allowing case fallthrough */ +/* 1402 */ case 11: +/* 1403 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1404 */ return $saveSuspension($ret, 'src/lib/traceback.py', 34, 16); +/* 1405 */ } +/* 1406 */ var $call224 = $ret; +/* 1407 */ // +/* 1408 */ // line 34: +/* 1409 */ // for item in StackSummary.from_list(extracted_list).format(): +/* 1410 */ // ^ +/* 1411 */ // +/* 1412 */ +/* 1413 */ $currLineNo = Sk.currLineNo = 34; +/* 1414 */ $currColNo = Sk.currColNo = 16; +/* 1415 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1416 */ var $iter225 = Sk.abstr.iter($call224); +/* 1417 */ $blk = 5; /* allowing case fallthrough */ +/* 1418 */ case 5: +/* 1419 */ /* --- for start --- */ $ret = Sk.abstr.iternext($iter225, true); +/* 1420 */ $blk = 12; /* allowing case fallthrough */ +/* 1421 */ case 12: +/* 1422 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1423 */ return $saveSuspension($ret, 'src/lib/traceback.py', 34, 4); +/* 1424 */ } +/* 1425 */ var $next226 = $ret; +/* 1426 */ if ($next226 === undefined) { +/* 1427 */ $blk = 6; +/* 1428 */ continue; +/* 1429 */ } +/* 1430 */ item = $next226; +/* 1431 */ // +/* 1432 */ // line 35: +/* 1433 */ // print(item, file=file, end="") +/* 1434 */ // ^ +/* 1435 */ // +/* 1436 */ +/* 1437 */ $currLineNo = Sk.currLineNo = 35; +/* 1438 */ $currColNo = Sk.currColNo = 8; +/* 1439 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1440 */ var $loadgbl227 = Sk.misceval.loadname('print', $gbl); +/* 1441 */ if (item === undefined) { +/* 1442 */ throw new Sk.builtin.UnboundLocalError('local variable \'item\' referenced before assignment'); +/* 1443 */ } +/* 1444 */ if (file === undefined) { +/* 1445 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 1446 */ } +/* 1447 */ $ret = Sk.misceval.applyOrSuspend($loadgbl227, undefined, undefined, ['file', file, 'end', $scope210.$const228], [item]); +/* 1448 */ $blk = 13; /* allowing case fallthrough */ +/* 1449 */ case 13: +/* 1450 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1451 */ return $saveSuspension($ret, 'src/lib/traceback.py', 35, 8); +/* 1452 */ } +/* 1453 */ var $call229 = $ret; +/* 1454 */ // +/* 1455 */ // line 35: +/* 1456 */ // print(item, file=file, end="") +/* 1457 */ // ^ +/* 1458 */ // +/* 1459 */ +/* 1460 */ $currLineNo = Sk.currLineNo = 35; +/* 1461 */ $currColNo = Sk.currColNo = 8; +/* 1462 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1463 */ $blk = 5; /* jump */ +/* 1464 */ continue; +/* 1465 */ case 6: +/* 1466 */ /* --- for cleanup --- */ $blk = 7; /* allowing case fallthrough */ +/* 1467 */ case 7: +/* 1468 */ /* --- for end --- */ return Sk.builtin.none.none$; +/* 1469 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 1470 */ } +/* 1471 */ } catch (err) { +/* 1472 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 1473 */ Sk.execStart = Date.now(); +/* 1474 */ Sk.execPaused = 0 +/* 1475 */ } +/* 1476 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 1477 */ err = new Sk.builtin.ExternalError(err); +/* 1478 */ } +/* 1479 */ Sk.err = err; +/* 1480 */ err.traceback.push({ +/* 1481 */ lineno: $currLineNo, +/* 1482 */ colno: $currColNo, +/* 1483 */ filename: 'src/lib/traceback.py', +/* 1484 */ scope: 'print_list' +/* 1485 */ }); +/* 1486 */ if ($exc.length > 0) { +/* 1487 */ $err = err; +/* 1488 */ $blk = $exc.pop(); +/* 1489 */ continue +/* 1490 */ } else { +/* 1491 */ throw err; +/* 1492 */ } +/* 1493 */ } +/* 1494 */ } +/* 1495 */ }); +/* 1496 */ $scope210.$const216 = new Sk.builtin.str('stderr'); +/* 1497 */ $scope210.$const219 = new Sk.builtin.str('from_list'); +/* 1498 */ $scope210.$const222 = new Sk.builtin.str('format'); +/* 1499 */ $scope210.$const228 = new Sk.builtin.str(''); +/* 1500 */ var $scope231 = (function $format_list232$(extracted_list) { +/* 1501 */ var extracted_list, extracted_list, $loadgbl233, $loadgbl233, $lattr235, $loadgbl233, $lattr235, $call236, $loadgbl233, $lattr235, $call236, $lattr238; +/* 1502 */ var $wakeFromSuspension = function() { +/* 1503 */ var susp = $scope231.$wakingSuspension; +/* 1504 */ $scope231.$wakingSuspension = undefined; +/* 1505 */ $blk = susp.$blk; +/* 1506 */ $loc = susp.$loc; +/* 1507 */ $gbl = susp.$gbl; +/* 1508 */ $exc = susp.$exc; +/* 1509 */ $err = susp.$err; +/* 1510 */ $postfinally = susp.$postfinally; +/* 1511 */ $currLineNo = susp.$lineno; +/* 1512 */ $currColNo = susp.$colno; +/* 1513 */ Sk.lastYield = Date.now(); +/* 1514 */ extracted_list = susp.$tmps.extracted_list; +/* 1515 */ $loadgbl233 = susp.$tmps.$loadgbl233; +/* 1516 */ $lattr235 = susp.$tmps.$lattr235; +/* 1517 */ $call236 = susp.$tmps.$call236; +/* 1518 */ $lattr238 = susp.$tmps.$lattr238; +/* 1519 */ try { +/* 1520 */ $ret = susp.child.resume(); +/* 1521 */ } catch (err) { +/* 1522 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 1523 */ Sk.execStart = Date.now(); +/* 1524 */ Sk.execPaused = 0 +/* 1525 */ } +/* 1526 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 1527 */ err = new Sk.builtin.ExternalError(err); +/* 1528 */ } +/* 1529 */ Sk.err = err; +/* 1530 */ err.traceback.push({ +/* 1531 */ lineno: $currLineNo, +/* 1532 */ colno: $currColNo, +/* 1533 */ filename: 'src/lib/traceback.py', +/* 1534 */ scope: '$scope231' +/* 1535 */ }); +/* 1536 */ if ($exc.length > 0) { +/* 1537 */ $err = err; +/* 1538 */ $blk = $exc.pop(); +/* 1539 */ } else { +/* 1540 */ throw err; +/* 1541 */ } +/* 1542 */ } +/* 1543 */ }; +/* 1544 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 1545 */ var susp = new Sk.misceval.Suspension(); +/* 1546 */ susp.child = $child; +/* 1547 */ susp.resume = function() { +/* 1548 */ $scope231.$wakingSuspension = susp; +/* 1549 */ return $scope231(); +/* 1550 */ }; +/* 1551 */ susp.data = susp.child.data; +/* 1552 */ susp.$blk = $blk; +/* 1553 */ susp.$loc = $loc; +/* 1554 */ susp.$gbl = $gbl; +/* 1555 */ susp.$exc = $exc; +/* 1556 */ susp.$err = $err; +/* 1557 */ susp.$postfinally = $postfinally; +/* 1558 */ susp.$filename = $filename; +/* 1559 */ susp.$lineno = $lineno; +/* 1560 */ susp.$colno = $colno; +/* 1561 */ susp.optional = susp.child.optional; +/* 1562 */ susp.$tmps = { +/* 1563 */ "extracted_list": extracted_list, +/* 1564 */ "$loadgbl233": $loadgbl233, +/* 1565 */ "$lattr235": $lattr235, +/* 1566 */ "$call236": $call236, +/* 1567 */ "$lattr238": $lattr238 +/* 1568 */ }; +/* 1569 */ return susp; +/* 1570 */ }; +/* 1571 */ var $blk = 0, +/* 1572 */ $exc = [], +/* 1573 */ $loc = {}, +/* 1574 */ $cell = {}, +/* 1575 */ $gbl = this, +/* 1576 */ $err = undefined, +/* 1577 */ $ret = undefined, +/* 1578 */ $postfinally = undefined, +/* 1579 */ $currLineNo = undefined, +/* 1580 */ $currColNo = undefined; +/* 1581 */ if (typeof Sk.execStart === 'undefined') { +/* 1582 */ Sk.execStart = Date.now(); +/* 1583 */ Sk.execPaused = 0 +/* 1584 */ } +/* 1585 */ if (typeof Sk.lastYield === 'undefined') { +/* 1586 */ Sk.lastYield = Date.now() +/* 1587 */ } +/* 1588 */ if ($scope231.$wakingSuspension !== undefined) { +/* 1589 */ $wakeFromSuspension(); +/* 1590 */ } else {} +/* 1591 */ while (true) { +/* 1592 */ try { +/* 1593 */ var $dateNow = Date.now(); +/* 1594 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 1595 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 1596 */ } +/* 1597 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 1598 */ var $susp = $saveSuspension({ +/* 1599 */ data: { +/* 1600 */ type: 'Sk.yield' +/* 1601 */ }, +/* 1602 */ resume: function() {} +/* 1603 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 1604 */ $susp.$blk = $blk; +/* 1605 */ $susp.optional = true; +/* 1606 */ return $susp; +/* 1607 */ } +/* 1608 */ switch ($blk) { +/* 1609 */ case 0: +/* 1610 */ /* --- codeobj entry --- */ if (extracted_list === undefined) { +/* 1611 */ throw new Sk.builtin.UnboundLocalError('local variable \'extracted_list\' referenced before assignment'); +/* 1612 */ } +/* 1613 */ +/* 1614 */ // +/* 1615 */ // line 39: +/* 1616 */ // return StackSummary.from_list(extracted_list).format() +/* 1617 */ // ^ +/* 1618 */ // +/* 1619 */ +/* 1620 */ $currLineNo = Sk.currLineNo = 39; +/* 1621 */ $currColNo = Sk.currColNo = 4; +/* 1622 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1623 */ var $loadgbl233 = Sk.misceval.loadname('StackSummary', $gbl); +/* 1624 */ $ret = Sk.abstr.gattr($loadgbl233, $scope231.$const234, true); +/* 1625 */ $blk = 1; /* allowing case fallthrough */ +/* 1626 */ case 1: +/* 1627 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1628 */ return $saveSuspension($ret, 'src/lib/traceback.py', 39, 11); +/* 1629 */ } +/* 1630 */ var $lattr235 = $ret; +/* 1631 */ if (extracted_list === undefined) { +/* 1632 */ throw new Sk.builtin.UnboundLocalError('local variable \'extracted_list\' referenced before assignment'); +/* 1633 */ } +/* 1634 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr235, [extracted_list]); +/* 1635 */ $blk = 2; /* allowing case fallthrough */ +/* 1636 */ case 2: +/* 1637 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1638 */ return $saveSuspension($ret, 'src/lib/traceback.py', 39, 11); +/* 1639 */ } +/* 1640 */ var $call236 = $ret; +/* 1641 */ // +/* 1642 */ // line 39: +/* 1643 */ // return StackSummary.from_list(extracted_list).format() +/* 1644 */ // ^ +/* 1645 */ // +/* 1646 */ +/* 1647 */ $currLineNo = Sk.currLineNo = 39; +/* 1648 */ $currColNo = Sk.currColNo = 11; +/* 1649 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1650 */ $ret = Sk.abstr.gattr($call236, $scope231.$const237, true); +/* 1651 */ $blk = 3; /* allowing case fallthrough */ +/* 1652 */ case 3: +/* 1653 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1654 */ return $saveSuspension($ret, 'src/lib/traceback.py', 39, 11); +/* 1655 */ } +/* 1656 */ var $lattr238 = $ret; +/* 1657 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr238); +/* 1658 */ $blk = 4; /* allowing case fallthrough */ +/* 1659 */ case 4: +/* 1660 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1661 */ return $saveSuspension($ret, 'src/lib/traceback.py', 39, 11); +/* 1662 */ } +/* 1663 */ var $call239 = $ret; +/* 1664 */ // +/* 1665 */ // line 39: +/* 1666 */ // return StackSummary.from_list(extracted_list).format() +/* 1667 */ // ^ +/* 1668 */ // +/* 1669 */ +/* 1670 */ $currLineNo = Sk.currLineNo = 39; +/* 1671 */ $currColNo = Sk.currColNo = 11; +/* 1672 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1673 */ return $call239; +/* 1674 */ return Sk.builtin.none.none$; +/* 1675 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 1676 */ } +/* 1677 */ } catch (err) { +/* 1678 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 1679 */ Sk.execStart = Date.now(); +/* 1680 */ Sk.execPaused = 0 +/* 1681 */ } +/* 1682 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 1683 */ err = new Sk.builtin.ExternalError(err); +/* 1684 */ } +/* 1685 */ Sk.err = err; +/* 1686 */ err.traceback.push({ +/* 1687 */ lineno: $currLineNo, +/* 1688 */ colno: $currColNo, +/* 1689 */ filename: 'src/lib/traceback.py', +/* 1690 */ scope: 'format_list' +/* 1691 */ }); +/* 1692 */ if ($exc.length > 0) { +/* 1693 */ $err = err; +/* 1694 */ $blk = $exc.pop(); +/* 1695 */ continue +/* 1696 */ } else { +/* 1697 */ throw err; +/* 1698 */ } +/* 1699 */ } +/* 1700 */ } +/* 1701 */ }); +/* 1702 */ $scope231.$const234 = new Sk.builtin.str('from_list'); +/* 1703 */ $scope231.$const237 = new Sk.builtin.str('format'); +/* 1704 */ var $scope241 = (function $print_tb242$(tb, limit, file) { +/* 1705 */ var file, file, limit, limit, tb, tb, $loadgbl243, $loadgbl244, $loadgbl243, $loadgbl244, $call245; +/* 1706 */ var $wakeFromSuspension = function() { +/* 1707 */ var susp = $scope241.$wakingSuspension; +/* 1708 */ $scope241.$wakingSuspension = undefined; +/* 1709 */ $blk = susp.$blk; +/* 1710 */ $loc = susp.$loc; +/* 1711 */ $gbl = susp.$gbl; +/* 1712 */ $exc = susp.$exc; +/* 1713 */ $err = susp.$err; +/* 1714 */ $postfinally = susp.$postfinally; +/* 1715 */ $currLineNo = susp.$lineno; +/* 1716 */ $currColNo = susp.$colno; +/* 1717 */ Sk.lastYield = Date.now(); +/* 1718 */ file = susp.$tmps.file; +/* 1719 */ limit = susp.$tmps.limit; +/* 1720 */ tb = susp.$tmps.tb; +/* 1721 */ $loadgbl243 = susp.$tmps.$loadgbl243; +/* 1722 */ $loadgbl244 = susp.$tmps.$loadgbl244; +/* 1723 */ $call245 = susp.$tmps.$call245; +/* 1724 */ try { +/* 1725 */ $ret = susp.child.resume(); +/* 1726 */ } catch (err) { +/* 1727 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 1728 */ Sk.execStart = Date.now(); +/* 1729 */ Sk.execPaused = 0 +/* 1730 */ } +/* 1731 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 1732 */ err = new Sk.builtin.ExternalError(err); +/* 1733 */ } +/* 1734 */ Sk.err = err; +/* 1735 */ err.traceback.push({ +/* 1736 */ lineno: $currLineNo, +/* 1737 */ colno: $currColNo, +/* 1738 */ filename: 'src/lib/traceback.py', +/* 1739 */ scope: '$scope241' +/* 1740 */ }); +/* 1741 */ if ($exc.length > 0) { +/* 1742 */ $err = err; +/* 1743 */ $blk = $exc.pop(); +/* 1744 */ } else { +/* 1745 */ throw err; +/* 1746 */ } +/* 1747 */ } +/* 1748 */ }; +/* 1749 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 1750 */ var susp = new Sk.misceval.Suspension(); +/* 1751 */ susp.child = $child; +/* 1752 */ susp.resume = function() { +/* 1753 */ $scope241.$wakingSuspension = susp; +/* 1754 */ return $scope241(); +/* 1755 */ }; +/* 1756 */ susp.data = susp.child.data; +/* 1757 */ susp.$blk = $blk; +/* 1758 */ susp.$loc = $loc; +/* 1759 */ susp.$gbl = $gbl; +/* 1760 */ susp.$exc = $exc; +/* 1761 */ susp.$err = $err; +/* 1762 */ susp.$postfinally = $postfinally; +/* 1763 */ susp.$filename = $filename; +/* 1764 */ susp.$lineno = $lineno; +/* 1765 */ susp.$colno = $colno; +/* 1766 */ susp.optional = susp.child.optional; +/* 1767 */ susp.$tmps = { +/* 1768 */ "file": file, +/* 1769 */ "limit": limit, +/* 1770 */ "tb": tb, +/* 1771 */ "$loadgbl243": $loadgbl243, +/* 1772 */ "$loadgbl244": $loadgbl244, +/* 1773 */ "$call245": $call245 +/* 1774 */ }; +/* 1775 */ return susp; +/* 1776 */ }; +/* 1777 */ var $blk = 0, +/* 1778 */ $exc = [], +/* 1779 */ $loc = {}, +/* 1780 */ $cell = {}, +/* 1781 */ $gbl = this, +/* 1782 */ $err = undefined, +/* 1783 */ $ret = undefined, +/* 1784 */ $postfinally = undefined, +/* 1785 */ $currLineNo = undefined, +/* 1786 */ $currColNo = undefined; +/* 1787 */ if (typeof Sk.execStart === 'undefined') { +/* 1788 */ Sk.execStart = Date.now(); +/* 1789 */ Sk.execPaused = 0 +/* 1790 */ } +/* 1791 */ if (typeof Sk.lastYield === 'undefined') { +/* 1792 */ Sk.lastYield = Date.now() +/* 1793 */ } +/* 1794 */ if ($scope241.$wakingSuspension !== undefined) { +/* 1795 */ $wakeFromSuspension(); +/* 1796 */ } else {} +/* 1797 */ while (true) { +/* 1798 */ try { +/* 1799 */ var $dateNow = Date.now(); +/* 1800 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 1801 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 1802 */ } +/* 1803 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 1804 */ var $susp = $saveSuspension({ +/* 1805 */ data: { +/* 1806 */ type: 'Sk.yield' +/* 1807 */ }, +/* 1808 */ resume: function() {} +/* 1809 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 1810 */ $susp.$blk = $blk; +/* 1811 */ $susp.optional = true; +/* 1812 */ return $susp; +/* 1813 */ } +/* 1814 */ switch ($blk) { +/* 1815 */ case 0: +/* 1816 */ /* --- codeobj entry --- */ if (tb === undefined) { +/* 1817 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 1818 */ } +/* 1819 */ if (limit === undefined) { +/* 1820 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 1821 */ } +/* 1822 */ if (file === undefined) { +/* 1823 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 1824 */ } +/* 1825 */ +/* 1826 */ // +/* 1827 */ // line 47: +/* 1828 */ // print_list(extract_tb(tb, limit=limit), file=file) +/* 1829 */ // ^ +/* 1830 */ // +/* 1831 */ +/* 1832 */ $currLineNo = Sk.currLineNo = 47; +/* 1833 */ $currColNo = Sk.currColNo = 4; +/* 1834 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1835 */ var $loadgbl243 = Sk.misceval.loadname('print_list', $gbl); +/* 1836 */ var $loadgbl244 = Sk.misceval.loadname('extract_tb', $gbl); +/* 1837 */ if (tb === undefined) { +/* 1838 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 1839 */ } +/* 1840 */ if (limit === undefined) { +/* 1841 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 1842 */ } +/* 1843 */ $ret = Sk.misceval.applyOrSuspend($loadgbl244, undefined, undefined, ['limit', limit], [tb]); +/* 1844 */ $blk = 1; /* allowing case fallthrough */ +/* 1845 */ case 1: +/* 1846 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1847 */ return $saveSuspension($ret, 'src/lib/traceback.py', 47, 15); +/* 1848 */ } +/* 1849 */ var $call245 = $ret; +/* 1850 */ // +/* 1851 */ // line 47: +/* 1852 */ // print_list(extract_tb(tb, limit=limit), file=file) +/* 1853 */ // ^ +/* 1854 */ // +/* 1855 */ +/* 1856 */ $currLineNo = Sk.currLineNo = 47; +/* 1857 */ $currColNo = Sk.currColNo = 15; +/* 1858 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1859 */ if (file === undefined) { +/* 1860 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 1861 */ } +/* 1862 */ $ret = Sk.misceval.applyOrSuspend($loadgbl243, undefined, undefined, ['file', file], [$call245]); +/* 1863 */ $blk = 2; /* allowing case fallthrough */ +/* 1864 */ case 2: +/* 1865 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 1866 */ return $saveSuspension($ret, 'src/lib/traceback.py', 47, 4); +/* 1867 */ } +/* 1868 */ var $call246 = $ret; +/* 1869 */ // +/* 1870 */ // line 47: +/* 1871 */ // print_list(extract_tb(tb, limit=limit), file=file) +/* 1872 */ // ^ +/* 1873 */ // +/* 1874 */ +/* 1875 */ $currLineNo = Sk.currLineNo = 47; +/* 1876 */ $currColNo = Sk.currColNo = 4; +/* 1877 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 1878 */ return Sk.builtin.none.none$; +/* 1879 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 1880 */ } +/* 1881 */ } catch (err) { +/* 1882 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 1883 */ Sk.execStart = Date.now(); +/* 1884 */ Sk.execPaused = 0 +/* 1885 */ } +/* 1886 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 1887 */ err = new Sk.builtin.ExternalError(err); +/* 1888 */ } +/* 1889 */ Sk.err = err; +/* 1890 */ err.traceback.push({ +/* 1891 */ lineno: $currLineNo, +/* 1892 */ colno: $currColNo, +/* 1893 */ filename: 'src/lib/traceback.py', +/* 1894 */ scope: 'print_tb' +/* 1895 */ }); +/* 1896 */ if ($exc.length > 0) { +/* 1897 */ $err = err; +/* 1898 */ $blk = $exc.pop(); +/* 1899 */ continue +/* 1900 */ } else { +/* 1901 */ throw err; +/* 1902 */ } +/* 1903 */ } +/* 1904 */ } +/* 1905 */ }); +/* 1906 */ var $scope248 = (function $format_tb249$(tb, limit) { +/* 1907 */ var limit, limit, tb, tb, $loadgbl250, $loadgbl250, $call251, $loadgbl250, $call251, $lattr253; +/* 1908 */ var $wakeFromSuspension = function() { +/* 1909 */ var susp = $scope248.$wakingSuspension; +/* 1910 */ $scope248.$wakingSuspension = undefined; +/* 1911 */ $blk = susp.$blk; +/* 1912 */ $loc = susp.$loc; +/* 1913 */ $gbl = susp.$gbl; +/* 1914 */ $exc = susp.$exc; +/* 1915 */ $err = susp.$err; +/* 1916 */ $postfinally = susp.$postfinally; +/* 1917 */ $currLineNo = susp.$lineno; +/* 1918 */ $currColNo = susp.$colno; +/* 1919 */ Sk.lastYield = Date.now(); +/* 1920 */ limit = susp.$tmps.limit; +/* 1921 */ tb = susp.$tmps.tb; +/* 1922 */ $loadgbl250 = susp.$tmps.$loadgbl250; +/* 1923 */ $call251 = susp.$tmps.$call251; +/* 1924 */ $lattr253 = susp.$tmps.$lattr253; +/* 1925 */ try { +/* 1926 */ $ret = susp.child.resume(); +/* 1927 */ } catch (err) { +/* 1928 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 1929 */ Sk.execStart = Date.now(); +/* 1930 */ Sk.execPaused = 0 +/* 1931 */ } +/* 1932 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 1933 */ err = new Sk.builtin.ExternalError(err); +/* 1934 */ } +/* 1935 */ Sk.err = err; +/* 1936 */ err.traceback.push({ +/* 1937 */ lineno: $currLineNo, +/* 1938 */ colno: $currColNo, +/* 1939 */ filename: 'src/lib/traceback.py', +/* 1940 */ scope: '$scope248' +/* 1941 */ }); +/* 1942 */ if ($exc.length > 0) { +/* 1943 */ $err = err; +/* 1944 */ $blk = $exc.pop(); +/* 1945 */ } else { +/* 1946 */ throw err; +/* 1947 */ } +/* 1948 */ } +/* 1949 */ }; +/* 1950 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 1951 */ var susp = new Sk.misceval.Suspension(); +/* 1952 */ susp.child = $child; +/* 1953 */ susp.resume = function() { +/* 1954 */ $scope248.$wakingSuspension = susp; +/* 1955 */ return $scope248(); +/* 1956 */ }; +/* 1957 */ susp.data = susp.child.data; +/* 1958 */ susp.$blk = $blk; +/* 1959 */ susp.$loc = $loc; +/* 1960 */ susp.$gbl = $gbl; +/* 1961 */ susp.$exc = $exc; +/* 1962 */ susp.$err = $err; +/* 1963 */ susp.$postfinally = $postfinally; +/* 1964 */ susp.$filename = $filename; +/* 1965 */ susp.$lineno = $lineno; +/* 1966 */ susp.$colno = $colno; +/* 1967 */ susp.optional = susp.child.optional; +/* 1968 */ susp.$tmps = { +/* 1969 */ "limit": limit, +/* 1970 */ "tb": tb, +/* 1971 */ "$loadgbl250": $loadgbl250, +/* 1972 */ "$call251": $call251, +/* 1973 */ "$lattr253": $lattr253 +/* 1974 */ }; +/* 1975 */ return susp; +/* 1976 */ }; +/* 1977 */ var $blk = 0, +/* 1978 */ $exc = [], +/* 1979 */ $loc = {}, +/* 1980 */ $cell = {}, +/* 1981 */ $gbl = this, +/* 1982 */ $err = undefined, +/* 1983 */ $ret = undefined, +/* 1984 */ $postfinally = undefined, +/* 1985 */ $currLineNo = undefined, +/* 1986 */ $currColNo = undefined; +/* 1987 */ if (typeof Sk.execStart === 'undefined') { +/* 1988 */ Sk.execStart = Date.now(); +/* 1989 */ Sk.execPaused = 0 +/* 1990 */ } +/* 1991 */ if (typeof Sk.lastYield === 'undefined') { +/* 1992 */ Sk.lastYield = Date.now() +/* 1993 */ } +/* 1994 */ if ($scope248.$wakingSuspension !== undefined) { +/* 1995 */ $wakeFromSuspension(); +/* 1996 */ } else {} +/* 1997 */ while (true) { +/* 1998 */ try { +/* 1999 */ var $dateNow = Date.now(); +/* 2000 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 2001 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 2002 */ } +/* 2003 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 2004 */ var $susp = $saveSuspension({ +/* 2005 */ data: { +/* 2006 */ type: 'Sk.yield' +/* 2007 */ }, +/* 2008 */ resume: function() {} +/* 2009 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 2010 */ $susp.$blk = $blk; +/* 2011 */ $susp.optional = true; +/* 2012 */ return $susp; +/* 2013 */ } +/* 2014 */ switch ($blk) { +/* 2015 */ case 0: +/* 2016 */ /* --- codeobj entry --- */ if (tb === undefined) { +/* 2017 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 2018 */ } +/* 2019 */ if (limit === undefined) { +/* 2020 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 2021 */ } +/* 2022 */ +/* 2023 */ // +/* 2024 */ // line 51: +/* 2025 */ // return extract_tb(tb, limit=limit).format() +/* 2026 */ // ^ +/* 2027 */ // +/* 2028 */ +/* 2029 */ $currLineNo = Sk.currLineNo = 51; +/* 2030 */ $currColNo = Sk.currColNo = 4; +/* 2031 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2032 */ var $loadgbl250 = Sk.misceval.loadname('extract_tb', $gbl); +/* 2033 */ if (tb === undefined) { +/* 2034 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 2035 */ } +/* 2036 */ if (limit === undefined) { +/* 2037 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 2038 */ } +/* 2039 */ $ret = Sk.misceval.applyOrSuspend($loadgbl250, undefined, undefined, ['limit', limit], [tb]); +/* 2040 */ $blk = 1; /* allowing case fallthrough */ +/* 2041 */ case 1: +/* 2042 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2043 */ return $saveSuspension($ret, 'src/lib/traceback.py', 51, 11); +/* 2044 */ } +/* 2045 */ var $call251 = $ret; +/* 2046 */ // +/* 2047 */ // line 51: +/* 2048 */ // return extract_tb(tb, limit=limit).format() +/* 2049 */ // ^ +/* 2050 */ // +/* 2051 */ +/* 2052 */ $currLineNo = Sk.currLineNo = 51; +/* 2053 */ $currColNo = Sk.currColNo = 11; +/* 2054 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2055 */ $ret = Sk.abstr.gattr($call251, $scope248.$const252, true); +/* 2056 */ $blk = 2; /* allowing case fallthrough */ +/* 2057 */ case 2: +/* 2058 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2059 */ return $saveSuspension($ret, 'src/lib/traceback.py', 51, 11); +/* 2060 */ } +/* 2061 */ var $lattr253 = $ret; +/* 2062 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr253); +/* 2063 */ $blk = 3; /* allowing case fallthrough */ +/* 2064 */ case 3: +/* 2065 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2066 */ return $saveSuspension($ret, 'src/lib/traceback.py', 51, 11); +/* 2067 */ } +/* 2068 */ var $call254 = $ret; +/* 2069 */ // +/* 2070 */ // line 51: +/* 2071 */ // return extract_tb(tb, limit=limit).format() +/* 2072 */ // ^ +/* 2073 */ // +/* 2074 */ +/* 2075 */ $currLineNo = Sk.currLineNo = 51; +/* 2076 */ $currColNo = Sk.currColNo = 11; +/* 2077 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2078 */ return $call254; +/* 2079 */ return Sk.builtin.none.none$; +/* 2080 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 2081 */ } +/* 2082 */ } catch (err) { +/* 2083 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 2084 */ Sk.execStart = Date.now(); +/* 2085 */ Sk.execPaused = 0 +/* 2086 */ } +/* 2087 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 2088 */ err = new Sk.builtin.ExternalError(err); +/* 2089 */ } +/* 2090 */ Sk.err = err; +/* 2091 */ err.traceback.push({ +/* 2092 */ lineno: $currLineNo, +/* 2093 */ colno: $currColNo, +/* 2094 */ filename: 'src/lib/traceback.py', +/* 2095 */ scope: 'format_tb' +/* 2096 */ }); +/* 2097 */ if ($exc.length > 0) { +/* 2098 */ $err = err; +/* 2099 */ $blk = $exc.pop(); +/* 2100 */ continue +/* 2101 */ } else { +/* 2102 */ throw err; +/* 2103 */ } +/* 2104 */ } +/* 2105 */ } +/* 2106 */ }); +/* 2107 */ $scope248.$const252 = new Sk.builtin.str('format'); +/* 2108 */ var $scope256 = (function $extract_tb257$(tb, limit) { +/* 2109 */ var limit, limit, tb, tb, $loadgbl258, $loadgbl259, $loadgbl258, $loadgbl259, $call260; +/* 2110 */ var $wakeFromSuspension = function() { +/* 2111 */ var susp = $scope256.$wakingSuspension; +/* 2112 */ $scope256.$wakingSuspension = undefined; +/* 2113 */ $blk = susp.$blk; +/* 2114 */ $loc = susp.$loc; +/* 2115 */ $gbl = susp.$gbl; +/* 2116 */ $exc = susp.$exc; +/* 2117 */ $err = susp.$err; +/* 2118 */ $postfinally = susp.$postfinally; +/* 2119 */ $currLineNo = susp.$lineno; +/* 2120 */ $currColNo = susp.$colno; +/* 2121 */ Sk.lastYield = Date.now(); +/* 2122 */ limit = susp.$tmps.limit; +/* 2123 */ tb = susp.$tmps.tb; +/* 2124 */ $loadgbl258 = susp.$tmps.$loadgbl258; +/* 2125 */ $loadgbl259 = susp.$tmps.$loadgbl259; +/* 2126 */ $call260 = susp.$tmps.$call260; +/* 2127 */ try { +/* 2128 */ $ret = susp.child.resume(); +/* 2129 */ } catch (err) { +/* 2130 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 2131 */ Sk.execStart = Date.now(); +/* 2132 */ Sk.execPaused = 0 +/* 2133 */ } +/* 2134 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 2135 */ err = new Sk.builtin.ExternalError(err); +/* 2136 */ } +/* 2137 */ Sk.err = err; +/* 2138 */ err.traceback.push({ +/* 2139 */ lineno: $currLineNo, +/* 2140 */ colno: $currColNo, +/* 2141 */ filename: 'src/lib/traceback.py', +/* 2142 */ scope: '$scope256' +/* 2143 */ }); +/* 2144 */ if ($exc.length > 0) { +/* 2145 */ $err = err; +/* 2146 */ $blk = $exc.pop(); +/* 2147 */ } else { +/* 2148 */ throw err; +/* 2149 */ } +/* 2150 */ } +/* 2151 */ }; +/* 2152 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 2153 */ var susp = new Sk.misceval.Suspension(); +/* 2154 */ susp.child = $child; +/* 2155 */ susp.resume = function() { +/* 2156 */ $scope256.$wakingSuspension = susp; +/* 2157 */ return $scope256(); +/* 2158 */ }; +/* 2159 */ susp.data = susp.child.data; +/* 2160 */ susp.$blk = $blk; +/* 2161 */ susp.$loc = $loc; +/* 2162 */ susp.$gbl = $gbl; +/* 2163 */ susp.$exc = $exc; +/* 2164 */ susp.$err = $err; +/* 2165 */ susp.$postfinally = $postfinally; +/* 2166 */ susp.$filename = $filename; +/* 2167 */ susp.$lineno = $lineno; +/* 2168 */ susp.$colno = $colno; +/* 2169 */ susp.optional = susp.child.optional; +/* 2170 */ susp.$tmps = { +/* 2171 */ "limit": limit, +/* 2172 */ "tb": tb, +/* 2173 */ "$loadgbl258": $loadgbl258, +/* 2174 */ "$loadgbl259": $loadgbl259, +/* 2175 */ "$call260": $call260 +/* 2176 */ }; +/* 2177 */ return susp; +/* 2178 */ }; +/* 2179 */ var $blk = 0, +/* 2180 */ $exc = [], +/* 2181 */ $loc = {}, +/* 2182 */ $cell = {}, +/* 2183 */ $gbl = this, +/* 2184 */ $err = undefined, +/* 2185 */ $ret = undefined, +/* 2186 */ $postfinally = undefined, +/* 2187 */ $currLineNo = undefined, +/* 2188 */ $currColNo = undefined; +/* 2189 */ if (typeof Sk.execStart === 'undefined') { +/* 2190 */ Sk.execStart = Date.now(); +/* 2191 */ Sk.execPaused = 0 +/* 2192 */ } +/* 2193 */ if (typeof Sk.lastYield === 'undefined') { +/* 2194 */ Sk.lastYield = Date.now() +/* 2195 */ } +/* 2196 */ if ($scope256.$wakingSuspension !== undefined) { +/* 2197 */ $wakeFromSuspension(); +/* 2198 */ } else {} +/* 2199 */ while (true) { +/* 2200 */ try { +/* 2201 */ var $dateNow = Date.now(); +/* 2202 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 2203 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 2204 */ } +/* 2205 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 2206 */ var $susp = $saveSuspension({ +/* 2207 */ data: { +/* 2208 */ type: 'Sk.yield' +/* 2209 */ }, +/* 2210 */ resume: function() {} +/* 2211 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 2212 */ $susp.$blk = $blk; +/* 2213 */ $susp.optional = true; +/* 2214 */ return $susp; +/* 2215 */ } +/* 2216 */ switch ($blk) { +/* 2217 */ case 0: +/* 2218 */ /* --- codeobj entry --- */ if (tb === undefined) { +/* 2219 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 2220 */ } +/* 2221 */ if (limit === undefined) { +/* 2222 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 2223 */ } +/* 2224 */ +/* 2225 */ // +/* 2226 */ // line 55: +/* 2227 */ // return extract(walk_tb(tb), limit=limit) +/* 2228 */ // ^ +/* 2229 */ // +/* 2230 */ +/* 2231 */ $currLineNo = Sk.currLineNo = 55; +/* 2232 */ $currColNo = Sk.currColNo = 4; +/* 2233 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2234 */ var $loadgbl258 = Sk.misceval.loadname('extract', $gbl); +/* 2235 */ var $loadgbl259 = Sk.misceval.loadname('walk_tb', $gbl); +/* 2236 */ if (tb === undefined) { +/* 2237 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 2238 */ } +/* 2239 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl259, [tb]); +/* 2240 */ $blk = 1; /* allowing case fallthrough */ +/* 2241 */ case 1: +/* 2242 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2243 */ return $saveSuspension($ret, 'src/lib/traceback.py', 55, 19); +/* 2244 */ } +/* 2245 */ var $call260 = $ret; +/* 2246 */ // +/* 2247 */ // line 55: +/* 2248 */ // return extract(walk_tb(tb), limit=limit) +/* 2249 */ // ^ +/* 2250 */ // +/* 2251 */ +/* 2252 */ $currLineNo = Sk.currLineNo = 55; +/* 2253 */ $currColNo = Sk.currColNo = 19; +/* 2254 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2255 */ if (limit === undefined) { +/* 2256 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 2257 */ } +/* 2258 */ $ret = Sk.misceval.applyOrSuspend($loadgbl258, undefined, undefined, ['limit', limit], [$call260]); +/* 2259 */ $blk = 2; /* allowing case fallthrough */ +/* 2260 */ case 2: +/* 2261 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2262 */ return $saveSuspension($ret, 'src/lib/traceback.py', 55, 11); +/* 2263 */ } +/* 2264 */ var $call261 = $ret; +/* 2265 */ // +/* 2266 */ // line 55: +/* 2267 */ // return extract(walk_tb(tb), limit=limit) +/* 2268 */ // ^ +/* 2269 */ // +/* 2270 */ +/* 2271 */ $currLineNo = Sk.currLineNo = 55; +/* 2272 */ $currColNo = Sk.currColNo = 11; +/* 2273 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2274 */ return $call261; +/* 2275 */ return Sk.builtin.none.none$; +/* 2276 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 2277 */ } +/* 2278 */ } catch (err) { +/* 2279 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 2280 */ Sk.execStart = Date.now(); +/* 2281 */ Sk.execPaused = 0 +/* 2282 */ } +/* 2283 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 2284 */ err = new Sk.builtin.ExternalError(err); +/* 2285 */ } +/* 2286 */ Sk.err = err; +/* 2287 */ err.traceback.push({ +/* 2288 */ lineno: $currLineNo, +/* 2289 */ colno: $currColNo, +/* 2290 */ filename: 'src/lib/traceback.py', +/* 2291 */ scope: 'extract_tb' +/* 2292 */ }); +/* 2293 */ if ($exc.length > 0) { +/* 2294 */ $err = err; +/* 2295 */ $blk = $exc.pop(); +/* 2296 */ continue +/* 2297 */ } else { +/* 2298 */ throw err; +/* 2299 */ } +/* 2300 */ } +/* 2301 */ } +/* 2302 */ }); +/* 2303 */ var $scope265 = (function $print_exception266$(etype, value, tb, limit, file, chain) { +/* 2304 */ var line; /* locals */ +/* 2305 */ var chain, chain, etype, file, file, file, file, limit, limit, line, line, tb, tb, value, value, value, $compareres267, $loadgbl270, $loadgbl273, $loadgbl274, $loadgbl273, $loadgbl274, $call275, $loadgbl273, $loadgbl274, $call275, $call276, $loadgbl273, $loadgbl274, $call275, $call276, $lattr278, $iter280, $loadgbl273, $loadgbl274, $call275, $call276, $lattr278, $call279, $iter280, $loadgbl282; +/* 2306 */ var $wakeFromSuspension = function() { +/* 2307 */ var susp = $scope265.$wakingSuspension; +/* 2308 */ $scope265.$wakingSuspension = undefined; +/* 2309 */ $blk = susp.$blk; +/* 2310 */ $loc = susp.$loc; +/* 2311 */ $gbl = susp.$gbl; +/* 2312 */ $exc = susp.$exc; +/* 2313 */ $err = susp.$err; +/* 2314 */ $postfinally = susp.$postfinally; +/* 2315 */ $currLineNo = susp.$lineno; +/* 2316 */ $currColNo = susp.$colno; +/* 2317 */ Sk.lastYield = Date.now(); +/* 2318 */ chain = susp.$tmps.chain; +/* 2319 */ etype = susp.$tmps.etype; +/* 2320 */ file = susp.$tmps.file; +/* 2321 */ limit = susp.$tmps.limit; +/* 2322 */ line = susp.$tmps.line; +/* 2323 */ tb = susp.$tmps.tb; +/* 2324 */ value = susp.$tmps.value; +/* 2325 */ $compareres267 = susp.$tmps.$compareres267; +/* 2326 */ $loadgbl270 = susp.$tmps.$loadgbl270; +/* 2327 */ $loadgbl273 = susp.$tmps.$loadgbl273; +/* 2328 */ $loadgbl274 = susp.$tmps.$loadgbl274; +/* 2329 */ $call275 = susp.$tmps.$call275; +/* 2330 */ $call276 = susp.$tmps.$call276; +/* 2331 */ $lattr278 = susp.$tmps.$lattr278; +/* 2332 */ $iter280 = susp.$tmps.$iter280; +/* 2333 */ $call279 = susp.$tmps.$call279; +/* 2334 */ $loadgbl282 = susp.$tmps.$loadgbl282; +/* 2335 */ try { +/* 2336 */ $ret = susp.child.resume(); +/* 2337 */ } catch (err) { +/* 2338 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 2339 */ Sk.execStart = Date.now(); +/* 2340 */ Sk.execPaused = 0 +/* 2341 */ } +/* 2342 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 2343 */ err = new Sk.builtin.ExternalError(err); +/* 2344 */ } +/* 2345 */ Sk.err = err; +/* 2346 */ err.traceback.push({ +/* 2347 */ lineno: $currLineNo, +/* 2348 */ colno: $currColNo, +/* 2349 */ filename: 'src/lib/traceback.py', +/* 2350 */ scope: '$scope265' +/* 2351 */ }); +/* 2352 */ if ($exc.length > 0) { +/* 2353 */ $err = err; +/* 2354 */ $blk = $exc.pop(); +/* 2355 */ } else { +/* 2356 */ throw err; +/* 2357 */ } +/* 2358 */ } +/* 2359 */ }; +/* 2360 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 2361 */ var susp = new Sk.misceval.Suspension(); +/* 2362 */ susp.child = $child; +/* 2363 */ susp.resume = function() { +/* 2364 */ $scope265.$wakingSuspension = susp; +/* 2365 */ return $scope265(); +/* 2366 */ }; +/* 2367 */ susp.data = susp.child.data; +/* 2368 */ susp.$blk = $blk; +/* 2369 */ susp.$loc = $loc; +/* 2370 */ susp.$gbl = $gbl; +/* 2371 */ susp.$exc = $exc; +/* 2372 */ susp.$err = $err; +/* 2373 */ susp.$postfinally = $postfinally; +/* 2374 */ susp.$filename = $filename; +/* 2375 */ susp.$lineno = $lineno; +/* 2376 */ susp.$colno = $colno; +/* 2377 */ susp.optional = susp.child.optional; +/* 2378 */ susp.$tmps = { +/* 2379 */ "chain": chain, +/* 2380 */ "etype": etype, +/* 2381 */ "file": file, +/* 2382 */ "limit": limit, +/* 2383 */ "line": line, +/* 2384 */ "tb": tb, +/* 2385 */ "value": value, +/* 2386 */ "$compareres267": $compareres267, +/* 2387 */ "$loadgbl270": $loadgbl270, +/* 2388 */ "$loadgbl273": $loadgbl273, +/* 2389 */ "$loadgbl274": $loadgbl274, +/* 2390 */ "$call275": $call275, +/* 2391 */ "$call276": $call276, +/* 2392 */ "$lattr278": $lattr278, +/* 2393 */ "$iter280": $iter280, +/* 2394 */ "$call279": $call279, +/* 2395 */ "$loadgbl282": $loadgbl282 +/* 2396 */ }; +/* 2397 */ return susp; +/* 2398 */ }; +/* 2399 */ var $blk = 0, +/* 2400 */ $exc = [], +/* 2401 */ $loc = {}, +/* 2402 */ $cell = {}, +/* 2403 */ $gbl = this, +/* 2404 */ $err = undefined, +/* 2405 */ $ret = undefined, +/* 2406 */ $postfinally = undefined, +/* 2407 */ $currLineNo = undefined, +/* 2408 */ $currColNo = undefined; +/* 2409 */ if (typeof Sk.execStart === 'undefined') { +/* 2410 */ Sk.execStart = Date.now(); +/* 2411 */ Sk.execPaused = 0 +/* 2412 */ } +/* 2413 */ if (typeof Sk.lastYield === 'undefined') { +/* 2414 */ Sk.lastYield = Date.now() +/* 2415 */ } +/* 2416 */ if ($scope265.$wakingSuspension !== undefined) { +/* 2417 */ $wakeFromSuspension(); +/* 2418 */ } else {} +/* 2419 */ while (true) { +/* 2420 */ try { +/* 2421 */ var $dateNow = Date.now(); +/* 2422 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 2423 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 2424 */ } +/* 2425 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 2426 */ var $susp = $saveSuspension({ +/* 2427 */ data: { +/* 2428 */ type: 'Sk.yield' +/* 2429 */ }, +/* 2430 */ resume: function() {} +/* 2431 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 2432 */ $susp.$blk = $blk; +/* 2433 */ $susp.optional = true; +/* 2434 */ return $susp; +/* 2435 */ } +/* 2436 */ switch ($blk) { +/* 2437 */ case 0: +/* 2438 */ /* --- codeobj entry --- */ if (etype === undefined) { +/* 2439 */ throw new Sk.builtin.UnboundLocalError('local variable \'etype\' referenced before assignment'); +/* 2440 */ } +/* 2441 */ if (value === undefined) { +/* 2442 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 2443 */ } +/* 2444 */ if (tb === undefined) { +/* 2445 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 2446 */ } +/* 2447 */ if (limit === undefined) { +/* 2448 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 2449 */ } +/* 2450 */ if (file === undefined) { +/* 2451 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 2452 */ } +/* 2453 */ if (chain === undefined) { +/* 2454 */ throw new Sk.builtin.UnboundLocalError('local variable \'chain\' referenced before assignment'); +/* 2455 */ } +/* 2456 */ +/* 2457 */ // +/* 2458 */ // line 75: +/* 2459 */ // if file is None: +/* 2460 */ // ^ +/* 2461 */ // +/* 2462 */ +/* 2463 */ $currLineNo = Sk.currLineNo = 75; +/* 2464 */ $currColNo = Sk.currColNo = 4; +/* 2465 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2466 */ if (file === undefined) { +/* 2467 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 2468 */ } +/* 2469 */ var $compareres267 = null; +/* 2470 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(file, Sk.builtin.none.none$, 'Is', true)); +/* 2471 */ $blk = 3; /* allowing case fallthrough */ +/* 2472 */ case 3: +/* 2473 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2474 */ return $saveSuspension($ret, 'src/lib/traceback.py', 75, 7); +/* 2475 */ } +/* 2476 */ $compareres267 = $ret; +/* 2477 */ var $jfalse268 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 2478 */ if ($jfalse268) { +/* 2479 */ /*test failed */ +/* 2480 */ $blk = 2; +/* 2481 */ continue; +/* 2482 */ } +/* 2483 */ $blk = 2; /* allowing case fallthrough */ +/* 2484 */ case 2: +/* 2485 */ /* --- done --- */ var $jfalse269 = ($compareres267 === false || !Sk.misceval.isTrue($compareres267)); +/* 2486 */ if ($jfalse269) { +/* 2487 */ /*test failed */ +/* 2488 */ $blk = 1; +/* 2489 */ continue; +/* 2490 */ } +/* 2491 */ // +/* 2492 */ // line 76: +/* 2493 */ // file = sys.stderr +/* 2494 */ // ^ +/* 2495 */ // +/* 2496 */ +/* 2497 */ $currLineNo = Sk.currLineNo = 76; +/* 2498 */ $currColNo = Sk.currColNo = 8; +/* 2499 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2500 */ var $loadgbl270 = Sk.misceval.loadname('sys', $gbl); +/* 2501 */ $ret = Sk.abstr.gattr($loadgbl270, $scope265.$const271, true); +/* 2502 */ $blk = 4; /* allowing case fallthrough */ +/* 2503 */ case 4: +/* 2504 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2505 */ return $saveSuspension($ret, 'src/lib/traceback.py', 76, 15); +/* 2506 */ } +/* 2507 */ var $lattr272 = $ret; +/* 2508 */ file = $lattr272; +/* 2509 */ $blk = 1; /* allowing case fallthrough */ +/* 2510 */ case 1: +/* 2511 */ /* --- end of if --- */ +/* 2512 */ // +/* 2513 */ // line 77: +/* 2514 */ // for line in TracebackException( +/* 2515 */ // ^ +/* 2516 */ // +/* 2517 */ +/* 2518 */ $currLineNo = Sk.currLineNo = 77; +/* 2519 */ $currColNo = Sk.currColNo = 4; +/* 2520 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2521 */ var $loadgbl273 = Sk.misceval.loadname('TracebackException', $gbl); +/* 2522 */ var $loadgbl274 = Sk.misceval.loadname('type', $gbl); +/* 2523 */ if (value === undefined) { +/* 2524 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 2525 */ } +/* 2526 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl274, [value]); +/* 2527 */ $blk = 8; /* allowing case fallthrough */ +/* 2528 */ case 8: +/* 2529 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2530 */ return $saveSuspension($ret, 'src/lib/traceback.py', 78, 12); +/* 2531 */ } +/* 2532 */ var $call275 = $ret; +/* 2533 */ // +/* 2534 */ // line 78: +/* 2535 */ // type(value), value, tb, limit=limit).format(chain=chain): +/* 2536 */ // ^ +/* 2537 */ // +/* 2538 */ +/* 2539 */ $currLineNo = Sk.currLineNo = 78; +/* 2540 */ $currColNo = Sk.currColNo = 12; +/* 2541 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2542 */ if (value === undefined) { +/* 2543 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 2544 */ } +/* 2545 */ if (tb === undefined) { +/* 2546 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 2547 */ } +/* 2548 */ if (limit === undefined) { +/* 2549 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 2550 */ } +/* 2551 */ $ret = Sk.misceval.applyOrSuspend($loadgbl273, undefined, undefined, ['limit', limit], [$call275, value, tb]); +/* 2552 */ $blk = 9; /* allowing case fallthrough */ +/* 2553 */ case 9: +/* 2554 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2555 */ return $saveSuspension($ret, 'src/lib/traceback.py', 77, 16); +/* 2556 */ } +/* 2557 */ var $call276 = $ret; +/* 2558 */ // +/* 2559 */ // line 77: +/* 2560 */ // for line in TracebackException( +/* 2561 */ // ^ +/* 2562 */ // +/* 2563 */ +/* 2564 */ $currLineNo = Sk.currLineNo = 77; +/* 2565 */ $currColNo = Sk.currColNo = 16; +/* 2566 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2567 */ $ret = Sk.abstr.gattr($call276, $scope265.$const277, true); +/* 2568 */ $blk = 10; /* allowing case fallthrough */ +/* 2569 */ case 10: +/* 2570 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2571 */ return $saveSuspension($ret, 'src/lib/traceback.py', 77, 16); +/* 2572 */ } +/* 2573 */ var $lattr278 = $ret; +/* 2574 */ if (chain === undefined) { +/* 2575 */ throw new Sk.builtin.UnboundLocalError('local variable \'chain\' referenced before assignment'); +/* 2576 */ } +/* 2577 */ $ret = Sk.misceval.applyOrSuspend($lattr278, undefined, undefined, ['chain', chain], []); +/* 2578 */ $blk = 11; /* allowing case fallthrough */ +/* 2579 */ case 11: +/* 2580 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2581 */ return $saveSuspension($ret, 'src/lib/traceback.py', 77, 16); +/* 2582 */ } +/* 2583 */ var $call279 = $ret; +/* 2584 */ // +/* 2585 */ // line 77: +/* 2586 */ // for line in TracebackException( +/* 2587 */ // ^ +/* 2588 */ // +/* 2589 */ +/* 2590 */ $currLineNo = Sk.currLineNo = 77; +/* 2591 */ $currColNo = Sk.currColNo = 16; +/* 2592 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2593 */ var $iter280 = Sk.abstr.iter($call279); +/* 2594 */ $blk = 5; /* allowing case fallthrough */ +/* 2595 */ case 5: +/* 2596 */ /* --- for start --- */ $ret = Sk.abstr.iternext($iter280, true); +/* 2597 */ $blk = 12; /* allowing case fallthrough */ +/* 2598 */ case 12: +/* 2599 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2600 */ return $saveSuspension($ret, 'src/lib/traceback.py', 77, 4); +/* 2601 */ } +/* 2602 */ var $next281 = $ret; +/* 2603 */ if ($next281 === undefined) { +/* 2604 */ $blk = 6; +/* 2605 */ continue; +/* 2606 */ } +/* 2607 */ line = $next281; +/* 2608 */ // +/* 2609 */ // line 79: +/* 2610 */ // print(line, file=file, end="") +/* 2611 */ // ^ +/* 2612 */ // +/* 2613 */ +/* 2614 */ $currLineNo = Sk.currLineNo = 79; +/* 2615 */ $currColNo = Sk.currColNo = 8; +/* 2616 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2617 */ var $loadgbl282 = Sk.misceval.loadname('print', $gbl); +/* 2618 */ if (line === undefined) { +/* 2619 */ throw new Sk.builtin.UnboundLocalError('local variable \'line\' referenced before assignment'); +/* 2620 */ } +/* 2621 */ if (file === undefined) { +/* 2622 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 2623 */ } +/* 2624 */ $ret = Sk.misceval.applyOrSuspend($loadgbl282, undefined, undefined, ['file', file, 'end', $scope265.$const283], [line]); +/* 2625 */ $blk = 13; /* allowing case fallthrough */ +/* 2626 */ case 13: +/* 2627 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2628 */ return $saveSuspension($ret, 'src/lib/traceback.py', 79, 8); +/* 2629 */ } +/* 2630 */ var $call284 = $ret; +/* 2631 */ // +/* 2632 */ // line 79: +/* 2633 */ // print(line, file=file, end="") +/* 2634 */ // ^ +/* 2635 */ // +/* 2636 */ +/* 2637 */ $currLineNo = Sk.currLineNo = 79; +/* 2638 */ $currColNo = Sk.currColNo = 8; +/* 2639 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2640 */ $blk = 5; /* jump */ +/* 2641 */ continue; +/* 2642 */ case 6: +/* 2643 */ /* --- for cleanup --- */ $blk = 7; /* allowing case fallthrough */ +/* 2644 */ case 7: +/* 2645 */ /* --- for end --- */ return Sk.builtin.none.none$; +/* 2646 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 2647 */ } +/* 2648 */ } catch (err) { +/* 2649 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 2650 */ Sk.execStart = Date.now(); +/* 2651 */ Sk.execPaused = 0 +/* 2652 */ } +/* 2653 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 2654 */ err = new Sk.builtin.ExternalError(err); +/* 2655 */ } +/* 2656 */ Sk.err = err; +/* 2657 */ err.traceback.push({ +/* 2658 */ lineno: $currLineNo, +/* 2659 */ colno: $currColNo, +/* 2660 */ filename: 'src/lib/traceback.py', +/* 2661 */ scope: 'print_exception' +/* 2662 */ }); +/* 2663 */ if ($exc.length > 0) { +/* 2664 */ $err = err; +/* 2665 */ $blk = $exc.pop(); +/* 2666 */ continue +/* 2667 */ } else { +/* 2668 */ throw err; +/* 2669 */ } +/* 2670 */ } +/* 2671 */ } +/* 2672 */ }); +/* 2673 */ $scope265.$const271 = new Sk.builtin.str('stderr'); +/* 2674 */ $scope265.$const277 = new Sk.builtin.str('format'); +/* 2675 */ $scope265.$const283 = new Sk.builtin.str(''); +/* 2676 */ var $scope286 = (function $format_exception287$(etype, value, tb, limit, chain) { +/* 2677 */ var chain, chain, etype, limit, limit, tb, tb, value, value, value, $loadgbl288, $loadgbl289, $loadgbl290, $loadgbl288, $loadgbl289, $loadgbl290, $call291, $loadgbl288, $loadgbl289, $loadgbl290, $call291, $call292, $loadgbl288, $loadgbl289, $loadgbl290, $call291, $call292, $lattr294, $loadgbl288, $loadgbl289, $loadgbl290, $call291, $call292, $lattr294, $call295; +/* 2678 */ var $wakeFromSuspension = function() { +/* 2679 */ var susp = $scope286.$wakingSuspension; +/* 2680 */ $scope286.$wakingSuspension = undefined; +/* 2681 */ $blk = susp.$blk; +/* 2682 */ $loc = susp.$loc; +/* 2683 */ $gbl = susp.$gbl; +/* 2684 */ $exc = susp.$exc; +/* 2685 */ $err = susp.$err; +/* 2686 */ $postfinally = susp.$postfinally; +/* 2687 */ $currLineNo = susp.$lineno; +/* 2688 */ $currColNo = susp.$colno; +/* 2689 */ Sk.lastYield = Date.now(); +/* 2690 */ chain = susp.$tmps.chain; +/* 2691 */ etype = susp.$tmps.etype; +/* 2692 */ limit = susp.$tmps.limit; +/* 2693 */ tb = susp.$tmps.tb; +/* 2694 */ value = susp.$tmps.value; +/* 2695 */ $loadgbl288 = susp.$tmps.$loadgbl288; +/* 2696 */ $loadgbl289 = susp.$tmps.$loadgbl289; +/* 2697 */ $loadgbl290 = susp.$tmps.$loadgbl290; +/* 2698 */ $call291 = susp.$tmps.$call291; +/* 2699 */ $call292 = susp.$tmps.$call292; +/* 2700 */ $lattr294 = susp.$tmps.$lattr294; +/* 2701 */ $call295 = susp.$tmps.$call295; +/* 2702 */ try { +/* 2703 */ $ret = susp.child.resume(); +/* 2704 */ } catch (err) { +/* 2705 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 2706 */ Sk.execStart = Date.now(); +/* 2707 */ Sk.execPaused = 0 +/* 2708 */ } +/* 2709 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 2710 */ err = new Sk.builtin.ExternalError(err); +/* 2711 */ } +/* 2712 */ Sk.err = err; +/* 2713 */ err.traceback.push({ +/* 2714 */ lineno: $currLineNo, +/* 2715 */ colno: $currColNo, +/* 2716 */ filename: 'src/lib/traceback.py', +/* 2717 */ scope: '$scope286' +/* 2718 */ }); +/* 2719 */ if ($exc.length > 0) { +/* 2720 */ $err = err; +/* 2721 */ $blk = $exc.pop(); +/* 2722 */ } else { +/* 2723 */ throw err; +/* 2724 */ } +/* 2725 */ } +/* 2726 */ }; +/* 2727 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 2728 */ var susp = new Sk.misceval.Suspension(); +/* 2729 */ susp.child = $child; +/* 2730 */ susp.resume = function() { +/* 2731 */ $scope286.$wakingSuspension = susp; +/* 2732 */ return $scope286(); +/* 2733 */ }; +/* 2734 */ susp.data = susp.child.data; +/* 2735 */ susp.$blk = $blk; +/* 2736 */ susp.$loc = $loc; +/* 2737 */ susp.$gbl = $gbl; +/* 2738 */ susp.$exc = $exc; +/* 2739 */ susp.$err = $err; +/* 2740 */ susp.$postfinally = $postfinally; +/* 2741 */ susp.$filename = $filename; +/* 2742 */ susp.$lineno = $lineno; +/* 2743 */ susp.$colno = $colno; +/* 2744 */ susp.optional = susp.child.optional; +/* 2745 */ susp.$tmps = { +/* 2746 */ "chain": chain, +/* 2747 */ "etype": etype, +/* 2748 */ "limit": limit, +/* 2749 */ "tb": tb, +/* 2750 */ "value": value, +/* 2751 */ "$loadgbl288": $loadgbl288, +/* 2752 */ "$loadgbl289": $loadgbl289, +/* 2753 */ "$loadgbl290": $loadgbl290, +/* 2754 */ "$call291": $call291, +/* 2755 */ "$call292": $call292, +/* 2756 */ "$lattr294": $lattr294, +/* 2757 */ "$call295": $call295 +/* 2758 */ }; +/* 2759 */ return susp; +/* 2760 */ }; +/* 2761 */ var $blk = 0, +/* 2762 */ $exc = [], +/* 2763 */ $loc = {}, +/* 2764 */ $cell = {}, +/* 2765 */ $gbl = this, +/* 2766 */ $err = undefined, +/* 2767 */ $ret = undefined, +/* 2768 */ $postfinally = undefined, +/* 2769 */ $currLineNo = undefined, +/* 2770 */ $currColNo = undefined; +/* 2771 */ if (typeof Sk.execStart === 'undefined') { +/* 2772 */ Sk.execStart = Date.now(); +/* 2773 */ Sk.execPaused = 0 +/* 2774 */ } +/* 2775 */ if (typeof Sk.lastYield === 'undefined') { +/* 2776 */ Sk.lastYield = Date.now() +/* 2777 */ } +/* 2778 */ if ($scope286.$wakingSuspension !== undefined) { +/* 2779 */ $wakeFromSuspension(); +/* 2780 */ } else {} +/* 2781 */ while (true) { +/* 2782 */ try { +/* 2783 */ var $dateNow = Date.now(); +/* 2784 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 2785 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 2786 */ } +/* 2787 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 2788 */ var $susp = $saveSuspension({ +/* 2789 */ data: { +/* 2790 */ type: 'Sk.yield' +/* 2791 */ }, +/* 2792 */ resume: function() {} +/* 2793 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 2794 */ $susp.$blk = $blk; +/* 2795 */ $susp.optional = true; +/* 2796 */ return $susp; +/* 2797 */ } +/* 2798 */ switch ($blk) { +/* 2799 */ case 0: +/* 2800 */ /* --- codeobj entry --- */ if (etype === undefined) { +/* 2801 */ throw new Sk.builtin.UnboundLocalError('local variable \'etype\' referenced before assignment'); +/* 2802 */ } +/* 2803 */ if (value === undefined) { +/* 2804 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 2805 */ } +/* 2806 */ if (tb === undefined) { +/* 2807 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 2808 */ } +/* 2809 */ if (limit === undefined) { +/* 2810 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 2811 */ } +/* 2812 */ if (chain === undefined) { +/* 2813 */ throw new Sk.builtin.UnboundLocalError('local variable \'chain\' referenced before assignment'); +/* 2814 */ } +/* 2815 */ +/* 2816 */ // +/* 2817 */ // line 87: +/* 2818 */ // return list(TracebackException( +/* 2819 */ // ^ +/* 2820 */ // +/* 2821 */ +/* 2822 */ $currLineNo = Sk.currLineNo = 87; +/* 2823 */ $currColNo = Sk.currColNo = 4; +/* 2824 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2825 */ var $loadgbl288 = Sk.misceval.loadname('list', $gbl); +/* 2826 */ var $loadgbl289 = Sk.misceval.loadname('TracebackException', $gbl); +/* 2827 */ var $loadgbl290 = Sk.misceval.loadname('type', $gbl); +/* 2828 */ if (value === undefined) { +/* 2829 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 2830 */ } +/* 2831 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl290, [value]); +/* 2832 */ $blk = 1; /* allowing case fallthrough */ +/* 2833 */ case 1: +/* 2834 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2835 */ return $saveSuspension($ret, 'src/lib/traceback.py', 88, 8); +/* 2836 */ } +/* 2837 */ var $call291 = $ret; +/* 2838 */ // +/* 2839 */ // line 88: +/* 2840 */ // type(value), value, tb, limit=limit).format(chain=chain)) +/* 2841 */ // ^ +/* 2842 */ // +/* 2843 */ +/* 2844 */ $currLineNo = Sk.currLineNo = 88; +/* 2845 */ $currColNo = Sk.currColNo = 8; +/* 2846 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2847 */ if (value === undefined) { +/* 2848 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 2849 */ } +/* 2850 */ if (tb === undefined) { +/* 2851 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 2852 */ } +/* 2853 */ if (limit === undefined) { +/* 2854 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 2855 */ } +/* 2856 */ $ret = Sk.misceval.applyOrSuspend($loadgbl289, undefined, undefined, ['limit', limit], [$call291, value, tb]); +/* 2857 */ $blk = 2; /* allowing case fallthrough */ +/* 2858 */ case 2: +/* 2859 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2860 */ return $saveSuspension($ret, 'src/lib/traceback.py', 87, 16); +/* 2861 */ } +/* 2862 */ var $call292 = $ret; +/* 2863 */ // +/* 2864 */ // line 87: +/* 2865 */ // return list(TracebackException( +/* 2866 */ // ^ +/* 2867 */ // +/* 2868 */ +/* 2869 */ $currLineNo = Sk.currLineNo = 87; +/* 2870 */ $currColNo = Sk.currColNo = 16; +/* 2871 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2872 */ $ret = Sk.abstr.gattr($call292, $scope286.$const293, true); +/* 2873 */ $blk = 3; /* allowing case fallthrough */ +/* 2874 */ case 3: +/* 2875 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2876 */ return $saveSuspension($ret, 'src/lib/traceback.py', 87, 16); +/* 2877 */ } +/* 2878 */ var $lattr294 = $ret; +/* 2879 */ if (chain === undefined) { +/* 2880 */ throw new Sk.builtin.UnboundLocalError('local variable \'chain\' referenced before assignment'); +/* 2881 */ } +/* 2882 */ $ret = Sk.misceval.applyOrSuspend($lattr294, undefined, undefined, ['chain', chain], []); +/* 2883 */ $blk = 4; /* allowing case fallthrough */ +/* 2884 */ case 4: +/* 2885 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2886 */ return $saveSuspension($ret, 'src/lib/traceback.py', 87, 16); +/* 2887 */ } +/* 2888 */ var $call295 = $ret; +/* 2889 */ // +/* 2890 */ // line 87: +/* 2891 */ // return list(TracebackException( +/* 2892 */ // ^ +/* 2893 */ // +/* 2894 */ +/* 2895 */ $currLineNo = Sk.currLineNo = 87; +/* 2896 */ $currColNo = Sk.currColNo = 16; +/* 2897 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2898 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl288, [$call295]); +/* 2899 */ $blk = 5; /* allowing case fallthrough */ +/* 2900 */ case 5: +/* 2901 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 2902 */ return $saveSuspension($ret, 'src/lib/traceback.py', 87, 11); +/* 2903 */ } +/* 2904 */ var $call296 = $ret; +/* 2905 */ // +/* 2906 */ // line 87: +/* 2907 */ // return list(TracebackException( +/* 2908 */ // ^ +/* 2909 */ // +/* 2910 */ +/* 2911 */ $currLineNo = Sk.currLineNo = 87; +/* 2912 */ $currColNo = Sk.currColNo = 11; +/* 2913 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 2914 */ return $call296; +/* 2915 */ return Sk.builtin.none.none$; +/* 2916 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 2917 */ } +/* 2918 */ } catch (err) { +/* 2919 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 2920 */ Sk.execStart = Date.now(); +/* 2921 */ Sk.execPaused = 0 +/* 2922 */ } +/* 2923 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 2924 */ err = new Sk.builtin.ExternalError(err); +/* 2925 */ } +/* 2926 */ Sk.err = err; +/* 2927 */ err.traceback.push({ +/* 2928 */ lineno: $currLineNo, +/* 2929 */ colno: $currColNo, +/* 2930 */ filename: 'src/lib/traceback.py', +/* 2931 */ scope: 'format_exception' +/* 2932 */ }); +/* 2933 */ if ($exc.length > 0) { +/* 2934 */ $err = err; +/* 2935 */ $blk = $exc.pop(); +/* 2936 */ continue +/* 2937 */ } else { +/* 2938 */ throw err; +/* 2939 */ } +/* 2940 */ } +/* 2941 */ } +/* 2942 */ }); +/* 2943 */ $scope286.$const293 = new Sk.builtin.str('format'); +/* 2944 */ var $scope298 = (function $format_exception_only299$(etype, value) { +/* 2945 */ var etype, etype, value, value, $loadgbl300, $loadgbl301, $loadgbl300, $loadgbl301, $call302, $loadgbl300, $loadgbl301, $call302, $lattr304, $loadgbl300, $loadgbl301, $call302, $lattr304, $call305; +/* 2946 */ var $wakeFromSuspension = function() { +/* 2947 */ var susp = $scope298.$wakingSuspension; +/* 2948 */ $scope298.$wakingSuspension = undefined; +/* 2949 */ $blk = susp.$blk; +/* 2950 */ $loc = susp.$loc; +/* 2951 */ $gbl = susp.$gbl; +/* 2952 */ $exc = susp.$exc; +/* 2953 */ $err = susp.$err; +/* 2954 */ $postfinally = susp.$postfinally; +/* 2955 */ $currLineNo = susp.$lineno; +/* 2956 */ $currColNo = susp.$colno; +/* 2957 */ Sk.lastYield = Date.now(); +/* 2958 */ etype = susp.$tmps.etype; +/* 2959 */ value = susp.$tmps.value; +/* 2960 */ $loadgbl300 = susp.$tmps.$loadgbl300; +/* 2961 */ $loadgbl301 = susp.$tmps.$loadgbl301; +/* 2962 */ $call302 = susp.$tmps.$call302; +/* 2963 */ $lattr304 = susp.$tmps.$lattr304; +/* 2964 */ $call305 = susp.$tmps.$call305; +/* 2965 */ try { +/* 2966 */ $ret = susp.child.resume(); +/* 2967 */ } catch (err) { +/* 2968 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 2969 */ Sk.execStart = Date.now(); +/* 2970 */ Sk.execPaused = 0 +/* 2971 */ } +/* 2972 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 2973 */ err = new Sk.builtin.ExternalError(err); +/* 2974 */ } +/* 2975 */ Sk.err = err; +/* 2976 */ err.traceback.push({ +/* 2977 */ lineno: $currLineNo, +/* 2978 */ colno: $currColNo, +/* 2979 */ filename: 'src/lib/traceback.py', +/* 2980 */ scope: '$scope298' +/* 2981 */ }); +/* 2982 */ if ($exc.length > 0) { +/* 2983 */ $err = err; +/* 2984 */ $blk = $exc.pop(); +/* 2985 */ } else { +/* 2986 */ throw err; +/* 2987 */ } +/* 2988 */ } +/* 2989 */ }; +/* 2990 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 2991 */ var susp = new Sk.misceval.Suspension(); +/* 2992 */ susp.child = $child; +/* 2993 */ susp.resume = function() { +/* 2994 */ $scope298.$wakingSuspension = susp; +/* 2995 */ return $scope298(); +/* 2996 */ }; +/* 2997 */ susp.data = susp.child.data; +/* 2998 */ susp.$blk = $blk; +/* 2999 */ susp.$loc = $loc; +/* 3000 */ susp.$gbl = $gbl; +/* 3001 */ susp.$exc = $exc; +/* 3002 */ susp.$err = $err; +/* 3003 */ susp.$postfinally = $postfinally; +/* 3004 */ susp.$filename = $filename; +/* 3005 */ susp.$lineno = $lineno; +/* 3006 */ susp.$colno = $colno; +/* 3007 */ susp.optional = susp.child.optional; +/* 3008 */ susp.$tmps = { +/* 3009 */ "etype": etype, +/* 3010 */ "value": value, +/* 3011 */ "$loadgbl300": $loadgbl300, +/* 3012 */ "$loadgbl301": $loadgbl301, +/* 3013 */ "$call302": $call302, +/* 3014 */ "$lattr304": $lattr304, +/* 3015 */ "$call305": $call305 +/* 3016 */ }; +/* 3017 */ return susp; +/* 3018 */ }; +/* 3019 */ var $blk = 0, +/* 3020 */ $exc = [], +/* 3021 */ $loc = {}, +/* 3022 */ $cell = {}, +/* 3023 */ $gbl = this, +/* 3024 */ $err = undefined, +/* 3025 */ $ret = undefined, +/* 3026 */ $postfinally = undefined, +/* 3027 */ $currLineNo = undefined, +/* 3028 */ $currColNo = undefined; +/* 3029 */ if (typeof Sk.execStart === 'undefined') { +/* 3030 */ Sk.execStart = Date.now(); +/* 3031 */ Sk.execPaused = 0 +/* 3032 */ } +/* 3033 */ if (typeof Sk.lastYield === 'undefined') { +/* 3034 */ Sk.lastYield = Date.now() +/* 3035 */ } +/* 3036 */ if ($scope298.$wakingSuspension !== undefined) { +/* 3037 */ $wakeFromSuspension(); +/* 3038 */ } else {} +/* 3039 */ while (true) { +/* 3040 */ try { +/* 3041 */ var $dateNow = Date.now(); +/* 3042 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 3043 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 3044 */ } +/* 3045 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 3046 */ var $susp = $saveSuspension({ +/* 3047 */ data: { +/* 3048 */ type: 'Sk.yield' +/* 3049 */ }, +/* 3050 */ resume: function() {} +/* 3051 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 3052 */ $susp.$blk = $blk; +/* 3053 */ $susp.optional = true; +/* 3054 */ return $susp; +/* 3055 */ } +/* 3056 */ switch ($blk) { +/* 3057 */ case 0: +/* 3058 */ /* --- codeobj entry --- */ if (etype === undefined) { +/* 3059 */ throw new Sk.builtin.UnboundLocalError('local variable \'etype\' referenced before assignment'); +/* 3060 */ } +/* 3061 */ if (value === undefined) { +/* 3062 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 3063 */ } +/* 3064 */ +/* 3065 */ // +/* 3066 */ // line 93: +/* 3067 */ // return list(TracebackException(etype, value, None).format_exception_only()) +/* 3068 */ // ^ +/* 3069 */ // +/* 3070 */ +/* 3071 */ $currLineNo = Sk.currLineNo = 93; +/* 3072 */ $currColNo = Sk.currColNo = 4; +/* 3073 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3074 */ var $loadgbl300 = Sk.misceval.loadname('list', $gbl); +/* 3075 */ var $loadgbl301 = Sk.misceval.loadname('TracebackException', $gbl); +/* 3076 */ if (etype === undefined) { +/* 3077 */ throw new Sk.builtin.UnboundLocalError('local variable \'etype\' referenced before assignment'); +/* 3078 */ } +/* 3079 */ if (value === undefined) { +/* 3080 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 3081 */ } +/* 3082 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl301, [etype, value, Sk.builtin.none.none$]); +/* 3083 */ $blk = 1; /* allowing case fallthrough */ +/* 3084 */ case 1: +/* 3085 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3086 */ return $saveSuspension($ret, 'src/lib/traceback.py', 93, 16); +/* 3087 */ } +/* 3088 */ var $call302 = $ret; +/* 3089 */ // +/* 3090 */ // line 93: +/* 3091 */ // return list(TracebackException(etype, value, None).format_exception_only()) +/* 3092 */ // ^ +/* 3093 */ // +/* 3094 */ +/* 3095 */ $currLineNo = Sk.currLineNo = 93; +/* 3096 */ $currColNo = Sk.currColNo = 16; +/* 3097 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3098 */ $ret = Sk.abstr.gattr($call302, $scope298.$const303, true); +/* 3099 */ $blk = 2; /* allowing case fallthrough */ +/* 3100 */ case 2: +/* 3101 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3102 */ return $saveSuspension($ret, 'src/lib/traceback.py', 93, 16); +/* 3103 */ } +/* 3104 */ var $lattr304 = $ret; +/* 3105 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr304); +/* 3106 */ $blk = 3; /* allowing case fallthrough */ +/* 3107 */ case 3: +/* 3108 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3109 */ return $saveSuspension($ret, 'src/lib/traceback.py', 93, 16); +/* 3110 */ } +/* 3111 */ var $call305 = $ret; +/* 3112 */ // +/* 3113 */ // line 93: +/* 3114 */ // return list(TracebackException(etype, value, None).format_exception_only()) +/* 3115 */ // ^ +/* 3116 */ // +/* 3117 */ +/* 3118 */ $currLineNo = Sk.currLineNo = 93; +/* 3119 */ $currColNo = Sk.currColNo = 16; +/* 3120 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3121 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl300, [$call305]); +/* 3122 */ $blk = 4; /* allowing case fallthrough */ +/* 3123 */ case 4: +/* 3124 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3125 */ return $saveSuspension($ret, 'src/lib/traceback.py', 93, 11); +/* 3126 */ } +/* 3127 */ var $call306 = $ret; +/* 3128 */ // +/* 3129 */ // line 93: +/* 3130 */ // return list(TracebackException(etype, value, None).format_exception_only()) +/* 3131 */ // ^ +/* 3132 */ // +/* 3133 */ +/* 3134 */ $currLineNo = Sk.currLineNo = 93; +/* 3135 */ $currColNo = Sk.currColNo = 11; +/* 3136 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3137 */ return $call306; +/* 3138 */ return Sk.builtin.none.none$; +/* 3139 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 3140 */ } +/* 3141 */ } catch (err) { +/* 3142 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 3143 */ Sk.execStart = Date.now(); +/* 3144 */ Sk.execPaused = 0 +/* 3145 */ } +/* 3146 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 3147 */ err = new Sk.builtin.ExternalError(err); +/* 3148 */ } +/* 3149 */ Sk.err = err; +/* 3150 */ err.traceback.push({ +/* 3151 */ lineno: $currLineNo, +/* 3152 */ colno: $currColNo, +/* 3153 */ filename: 'src/lib/traceback.py', +/* 3154 */ scope: 'format_exception_only' +/* 3155 */ }); +/* 3156 */ if ($exc.length > 0) { +/* 3157 */ $err = err; +/* 3158 */ $blk = $exc.pop(); +/* 3159 */ continue +/* 3160 */ } else { +/* 3161 */ throw err; +/* 3162 */ } +/* 3163 */ } +/* 3164 */ } +/* 3165 */ }); +/* 3166 */ $scope298.$const303 = new Sk.builtin.str('format_exception_only'); +/* 3167 */ var $scope308 = (function $_format_final_exc_line309$(etype, value) { +/* 3168 */ var line, valuestr; /* locals */ +/* 3169 */ var etype, etype, etype, line, line, line, value, value, value, valuestr, valuestr, valuestr, $loadgbl310, $compareres312; +/* 3170 */ var $wakeFromSuspension = function() { +/* 3171 */ var susp = $scope308.$wakingSuspension; +/* 3172 */ $scope308.$wakingSuspension = undefined; +/* 3173 */ $blk = susp.$blk; +/* 3174 */ $loc = susp.$loc; +/* 3175 */ $gbl = susp.$gbl; +/* 3176 */ $exc = susp.$exc; +/* 3177 */ $err = susp.$err; +/* 3178 */ $postfinally = susp.$postfinally; +/* 3179 */ $currLineNo = susp.$lineno; +/* 3180 */ $currColNo = susp.$colno; +/* 3181 */ Sk.lastYield = Date.now(); +/* 3182 */ etype = susp.$tmps.etype; +/* 3183 */ line = susp.$tmps.line; +/* 3184 */ value = susp.$tmps.value; +/* 3185 */ valuestr = susp.$tmps.valuestr; +/* 3186 */ $loadgbl310 = susp.$tmps.$loadgbl310; +/* 3187 */ $compareres312 = susp.$tmps.$compareres312; +/* 3188 */ try { +/* 3189 */ $ret = susp.child.resume(); +/* 3190 */ } catch (err) { +/* 3191 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 3192 */ Sk.execStart = Date.now(); +/* 3193 */ Sk.execPaused = 0 +/* 3194 */ } +/* 3195 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 3196 */ err = new Sk.builtin.ExternalError(err); +/* 3197 */ } +/* 3198 */ Sk.err = err; +/* 3199 */ err.traceback.push({ +/* 3200 */ lineno: $currLineNo, +/* 3201 */ colno: $currColNo, +/* 3202 */ filename: 'src/lib/traceback.py', +/* 3203 */ scope: '$scope308' +/* 3204 */ }); +/* 3205 */ if ($exc.length > 0) { +/* 3206 */ $err = err; +/* 3207 */ $blk = $exc.pop(); +/* 3208 */ } else { +/* 3209 */ throw err; +/* 3210 */ } +/* 3211 */ } +/* 3212 */ }; +/* 3213 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 3214 */ var susp = new Sk.misceval.Suspension(); +/* 3215 */ susp.child = $child; +/* 3216 */ susp.resume = function() { +/* 3217 */ $scope308.$wakingSuspension = susp; +/* 3218 */ return $scope308(); +/* 3219 */ }; +/* 3220 */ susp.data = susp.child.data; +/* 3221 */ susp.$blk = $blk; +/* 3222 */ susp.$loc = $loc; +/* 3223 */ susp.$gbl = $gbl; +/* 3224 */ susp.$exc = $exc; +/* 3225 */ susp.$err = $err; +/* 3226 */ susp.$postfinally = $postfinally; +/* 3227 */ susp.$filename = $filename; +/* 3228 */ susp.$lineno = $lineno; +/* 3229 */ susp.$colno = $colno; +/* 3230 */ susp.optional = susp.child.optional; +/* 3231 */ susp.$tmps = { +/* 3232 */ "etype": etype, +/* 3233 */ "line": line, +/* 3234 */ "value": value, +/* 3235 */ "valuestr": valuestr, +/* 3236 */ "$loadgbl310": $loadgbl310, +/* 3237 */ "$compareres312": $compareres312 +/* 3238 */ }; +/* 3239 */ return susp; +/* 3240 */ }; +/* 3241 */ var $blk = 0, +/* 3242 */ $exc = [], +/* 3243 */ $loc = {}, +/* 3244 */ $cell = {}, +/* 3245 */ $gbl = this, +/* 3246 */ $err = undefined, +/* 3247 */ $ret = undefined, +/* 3248 */ $postfinally = undefined, +/* 3249 */ $currLineNo = undefined, +/* 3250 */ $currColNo = undefined; +/* 3251 */ if (typeof Sk.execStart === 'undefined') { +/* 3252 */ Sk.execStart = Date.now(); +/* 3253 */ Sk.execPaused = 0 +/* 3254 */ } +/* 3255 */ if (typeof Sk.lastYield === 'undefined') { +/* 3256 */ Sk.lastYield = Date.now() +/* 3257 */ } +/* 3258 */ if ($scope308.$wakingSuspension !== undefined) { +/* 3259 */ $wakeFromSuspension(); +/* 3260 */ } else {} +/* 3261 */ while (true) { +/* 3262 */ try { +/* 3263 */ var $dateNow = Date.now(); +/* 3264 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 3265 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 3266 */ } +/* 3267 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 3268 */ var $susp = $saveSuspension({ +/* 3269 */ data: { +/* 3270 */ type: 'Sk.yield' +/* 3271 */ }, +/* 3272 */ resume: function() {} +/* 3273 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 3274 */ $susp.$blk = $blk; +/* 3275 */ $susp.optional = true; +/* 3276 */ return $susp; +/* 3277 */ } +/* 3278 */ switch ($blk) { +/* 3279 */ case 0: +/* 3280 */ /* --- codeobj entry --- */ if (etype === undefined) { +/* 3281 */ throw new Sk.builtin.UnboundLocalError('local variable \'etype\' referenced before assignment'); +/* 3282 */ } +/* 3283 */ if (value === undefined) { +/* 3284 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 3285 */ } +/* 3286 */ +/* 3287 */ // +/* 3288 */ // line 99: +/* 3289 */ // valuestr = _some_str(value) +/* 3290 */ // ^ +/* 3291 */ // +/* 3292 */ +/* 3293 */ $currLineNo = Sk.currLineNo = 99; +/* 3294 */ $currColNo = Sk.currColNo = 4; +/* 3295 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3296 */ var $loadgbl310 = Sk.misceval.loadname('_some_str', $gbl); +/* 3297 */ if (value === undefined) { +/* 3298 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 3299 */ } +/* 3300 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl310, [value]); +/* 3301 */ $blk = 1; /* allowing case fallthrough */ +/* 3302 */ case 1: +/* 3303 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3304 */ return $saveSuspension($ret, 'src/lib/traceback.py', 99, 15); +/* 3305 */ } +/* 3306 */ var $call311 = $ret; +/* 3307 */ // +/* 3308 */ // line 99: +/* 3309 */ // valuestr = _some_str(value) +/* 3310 */ // ^ +/* 3311 */ // +/* 3312 */ +/* 3313 */ $currLineNo = Sk.currLineNo = 99; +/* 3314 */ $currColNo = Sk.currColNo = 15; +/* 3315 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3316 */ valuestr = $call311; +/* 3317 */ // +/* 3318 */ // line 100: +/* 3319 */ // if value is None or not valuestr: +/* 3320 */ // ^ +/* 3321 */ // +/* 3322 */ +/* 3323 */ $currLineNo = Sk.currLineNo = 100; +/* 3324 */ $currColNo = Sk.currColNo = 4; +/* 3325 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3326 */ if (value === undefined) { +/* 3327 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 3328 */ } +/* 3329 */ var $compareres312 = null; +/* 3330 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(value, Sk.builtin.none.none$, 'Is', true)); +/* 3331 */ $blk = 6; /* allowing case fallthrough */ +/* 3332 */ case 6: +/* 3333 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3334 */ return $saveSuspension($ret, 'src/lib/traceback.py', 100, 7); +/* 3335 */ } +/* 3336 */ $compareres312 = $ret; +/* 3337 */ var $jfalse313 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 3338 */ if ($jfalse313) { +/* 3339 */ /*test failed */ +/* 3340 */ $blk = 5; +/* 3341 */ continue; +/* 3342 */ } +/* 3343 */ $blk = 5; /* allowing case fallthrough */ +/* 3344 */ case 5: +/* 3345 */ /* --- done --- */ var $boolopsucc314 = $compareres312; +/* 3346 */ $boolopsucc314 = $compareres312; +/* 3347 */ var $jtrue315 = ($compareres312 === true || Sk.misceval.isTrue($compareres312)); +/* 3348 */ if ($jtrue315) { +/* 3349 */ /*test passed */ +/* 3350 */ $blk = 4; +/* 3351 */ continue; +/* 3352 */ } +/* 3353 */ if (valuestr === undefined) { +/* 3354 */ throw new Sk.builtin.UnboundLocalError('local variable \'valuestr\' referenced before assignment'); +/* 3355 */ } +/* 3356 */ var $unaryop316 = Sk.abstr.numberUnaryOp(valuestr, 'Not'); +/* 3357 */ $boolopsucc314 = $unaryop316; +/* 3358 */ var $jtrue317 = ($unaryop316 === true || Sk.misceval.isTrue($unaryop316)); +/* 3359 */ if ($jtrue317) { +/* 3360 */ /*test passed */ +/* 3361 */ $blk = 4; +/* 3362 */ continue; +/* 3363 */ } +/* 3364 */ $blk = 4; /* allowing case fallthrough */ +/* 3365 */ case 4: +/* 3366 */ /* --- end of boolop --- */ var $jfalse318 = ($boolopsucc314 === false || !Sk.misceval.isTrue($boolopsucc314)); +/* 3367 */ if ($jfalse318) { +/* 3368 */ /*test failed */ +/* 3369 */ $blk = 3; +/* 3370 */ continue; +/* 3371 */ } +/* 3372 */ // +/* 3373 */ // line 101: +/* 3374 */ // line = "%s\n" % etype +/* 3375 */ // ^ +/* 3376 */ // +/* 3377 */ +/* 3378 */ $currLineNo = Sk.currLineNo = 101; +/* 3379 */ $currColNo = Sk.currColNo = 8; +/* 3380 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3381 */ if (etype === undefined) { +/* 3382 */ throw new Sk.builtin.UnboundLocalError('local variable \'etype\' referenced before assignment'); +/* 3383 */ } +/* 3384 */ var $binop320 = Sk.abstr.numberBinOp($scope308.$const319, etype, 'Mod'); +/* 3385 */ line = $binop320; +/* 3386 */ $blk = 2; /* allowing case fallthrough */ +/* 3387 */ case 2: +/* 3388 */ /* --- end of if --- */ +/* 3389 */ // +/* 3390 */ // line 104: +/* 3391 */ // return line +/* 3392 */ // ^ +/* 3393 */ // +/* 3394 */ +/* 3395 */ $currLineNo = Sk.currLineNo = 104; +/* 3396 */ $currColNo = Sk.currColNo = 4; +/* 3397 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3398 */ if (line === undefined) { +/* 3399 */ throw new Sk.builtin.UnboundLocalError('local variable \'line\' referenced before assignment'); +/* 3400 */ } +/* 3401 */ return line; +/* 3402 */ return Sk.builtin.none.none$; +/* 3403 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 3404 */ case 3: +/* 3405 */ /* --- next branch of if --- */ +/* 3406 */ // +/* 3407 */ // line 103: +/* 3408 */ // line = "%s: %s\n" % (etype, valuestr) +/* 3409 */ // ^ +/* 3410 */ // +/* 3411 */ +/* 3412 */ $currLineNo = Sk.currLineNo = 103; +/* 3413 */ $currColNo = Sk.currColNo = 8; +/* 3414 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3415 */ if (etype === undefined) { +/* 3416 */ throw new Sk.builtin.UnboundLocalError('local variable \'etype\' referenced before assignment'); +/* 3417 */ } +/* 3418 */ if (valuestr === undefined) { +/* 3419 */ throw new Sk.builtin.UnboundLocalError('local variable \'valuestr\' referenced before assignment'); +/* 3420 */ } +/* 3421 */ var $elem322 = etype; +/* 3422 */ var $elem323 = valuestr; +/* 3423 */ var $loadtuple324 = new Sk.builtins['tuple']([$elem322, $elem323]); +/* 3424 */ var $binop325 = Sk.abstr.numberBinOp($scope308.$const321, $loadtuple324, 'Mod'); +/* 3425 */ line = $binop325; +/* 3426 */ $blk = 2; /* jump */ +/* 3427 */ continue; +/* 3428 */ } +/* 3429 */ } catch (err) { +/* 3430 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 3431 */ Sk.execStart = Date.now(); +/* 3432 */ Sk.execPaused = 0 +/* 3433 */ } +/* 3434 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 3435 */ err = new Sk.builtin.ExternalError(err); +/* 3436 */ } +/* 3437 */ Sk.err = err; +/* 3438 */ err.traceback.push({ +/* 3439 */ lineno: $currLineNo, +/* 3440 */ colno: $currColNo, +/* 3441 */ filename: 'src/lib/traceback.py', +/* 3442 */ scope: '_format_final_exc_line' +/* 3443 */ }); +/* 3444 */ if ($exc.length > 0) { +/* 3445 */ $err = err; +/* 3446 */ $blk = $exc.pop(); +/* 3447 */ continue +/* 3448 */ } else { +/* 3449 */ throw err; +/* 3450 */ } +/* 3451 */ } +/* 3452 */ } +/* 3453 */ }); +/* 3454 */ $scope308.$const319 = new Sk.builtin.str('%s\n'); +/* 3455 */ $scope308.$const321 = new Sk.builtin.str('%s: %s\n'); +/* 3456 */ var $scope327 = (function $_some_str328$(value) { +/* 3457 */ var value, value, value, $loadgbl329, $loadgbl332, $loadgbl332, $call333; +/* 3458 */ var $wakeFromSuspension = function() { +/* 3459 */ var susp = $scope327.$wakingSuspension; +/* 3460 */ $scope327.$wakingSuspension = undefined; +/* 3461 */ $blk = susp.$blk; +/* 3462 */ $loc = susp.$loc; +/* 3463 */ $gbl = susp.$gbl; +/* 3464 */ $exc = susp.$exc; +/* 3465 */ $err = susp.$err; +/* 3466 */ $postfinally = susp.$postfinally; +/* 3467 */ $currLineNo = susp.$lineno; +/* 3468 */ $currColNo = susp.$colno; +/* 3469 */ Sk.lastYield = Date.now(); +/* 3470 */ value = susp.$tmps.value; +/* 3471 */ $loadgbl329 = susp.$tmps.$loadgbl329; +/* 3472 */ $loadgbl332 = susp.$tmps.$loadgbl332; +/* 3473 */ $call333 = susp.$tmps.$call333; +/* 3474 */ try { +/* 3475 */ $ret = susp.child.resume(); +/* 3476 */ } catch (err) { +/* 3477 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 3478 */ Sk.execStart = Date.now(); +/* 3479 */ Sk.execPaused = 0 +/* 3480 */ } +/* 3481 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 3482 */ err = new Sk.builtin.ExternalError(err); +/* 3483 */ } +/* 3484 */ Sk.err = err; +/* 3485 */ err.traceback.push({ +/* 3486 */ lineno: $currLineNo, +/* 3487 */ colno: $currColNo, +/* 3488 */ filename: 'src/lib/traceback.py', +/* 3489 */ scope: '$scope327' +/* 3490 */ }); +/* 3491 */ if ($exc.length > 0) { +/* 3492 */ $err = err; +/* 3493 */ $blk = $exc.pop(); +/* 3494 */ } else { +/* 3495 */ throw err; +/* 3496 */ } +/* 3497 */ } +/* 3498 */ }; +/* 3499 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 3500 */ var susp = new Sk.misceval.Suspension(); +/* 3501 */ susp.child = $child; +/* 3502 */ susp.resume = function() { +/* 3503 */ $scope327.$wakingSuspension = susp; +/* 3504 */ return $scope327(); +/* 3505 */ }; +/* 3506 */ susp.data = susp.child.data; +/* 3507 */ susp.$blk = $blk; +/* 3508 */ susp.$loc = $loc; +/* 3509 */ susp.$gbl = $gbl; +/* 3510 */ susp.$exc = $exc; +/* 3511 */ susp.$err = $err; +/* 3512 */ susp.$postfinally = $postfinally; +/* 3513 */ susp.$filename = $filename; +/* 3514 */ susp.$lineno = $lineno; +/* 3515 */ susp.$colno = $colno; +/* 3516 */ susp.optional = susp.child.optional; +/* 3517 */ susp.$tmps = { +/* 3518 */ "value": value, +/* 3519 */ "$loadgbl329": $loadgbl329, +/* 3520 */ "$loadgbl332": $loadgbl332, +/* 3521 */ "$call333": $call333 +/* 3522 */ }; +/* 3523 */ return susp; +/* 3524 */ }; +/* 3525 */ var $blk = 0, +/* 3526 */ $exc = [], +/* 3527 */ $loc = {}, +/* 3528 */ $cell = {}, +/* 3529 */ $gbl = this, +/* 3530 */ $err = undefined, +/* 3531 */ $ret = undefined, +/* 3532 */ $postfinally = undefined, +/* 3533 */ $currLineNo = undefined, +/* 3534 */ $currColNo = undefined; +/* 3535 */ if (typeof Sk.execStart === 'undefined') { +/* 3536 */ Sk.execStart = Date.now(); +/* 3537 */ Sk.execPaused = 0 +/* 3538 */ } +/* 3539 */ if (typeof Sk.lastYield === 'undefined') { +/* 3540 */ Sk.lastYield = Date.now() +/* 3541 */ } +/* 3542 */ if ($scope327.$wakingSuspension !== undefined) { +/* 3543 */ $wakeFromSuspension(); +/* 3544 */ } else {} +/* 3545 */ while (true) { +/* 3546 */ try { +/* 3547 */ var $dateNow = Date.now(); +/* 3548 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 3549 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 3550 */ } +/* 3551 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 3552 */ var $susp = $saveSuspension({ +/* 3553 */ data: { +/* 3554 */ type: 'Sk.yield' +/* 3555 */ }, +/* 3556 */ resume: function() {} +/* 3557 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 3558 */ $susp.$blk = $blk; +/* 3559 */ $susp.optional = true; +/* 3560 */ return $susp; +/* 3561 */ } +/* 3562 */ switch ($blk) { +/* 3563 */ case 0: +/* 3564 */ /* --- codeobj entry --- */ if (value === undefined) { +/* 3565 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 3566 */ } +/* 3567 */ +/* 3568 */ // +/* 3569 */ // line 107: +/* 3570 */ // try: +/* 3571 */ // ^ +/* 3572 */ // +/* 3573 */ +/* 3574 */ $currLineNo = Sk.currLineNo = 107; +/* 3575 */ $currColNo = Sk.currColNo = 4; +/* 3576 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3577 */ $exc.push(1); +/* 3578 */ // +/* 3579 */ // line 108: +/* 3580 */ // return str(value) +/* 3581 */ // ^ +/* 3582 */ // +/* 3583 */ +/* 3584 */ $currLineNo = Sk.currLineNo = 108; +/* 3585 */ $currColNo = Sk.currColNo = 8; +/* 3586 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3587 */ var $loadgbl329 = Sk.misceval.loadname('str', $gbl); +/* 3588 */ if (value === undefined) { +/* 3589 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 3590 */ } +/* 3591 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl329, [value]); +/* 3592 */ $blk = 5; /* allowing case fallthrough */ +/* 3593 */ case 5: +/* 3594 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3595 */ return $saveSuspension($ret, 'src/lib/traceback.py', 108, 15); +/* 3596 */ } +/* 3597 */ var $call330 = $ret; +/* 3598 */ // +/* 3599 */ // line 108: +/* 3600 */ // return str(value) +/* 3601 */ // ^ +/* 3602 */ // +/* 3603 */ +/* 3604 */ $currLineNo = Sk.currLineNo = 108; +/* 3605 */ $currColNo = Sk.currColNo = 15; +/* 3606 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3607 */ return $call330; +/* 3608 */ $exc.pop(); +/* 3609 */ $blk = 3; /* allowing case fallthrough */ +/* 3610 */ case 3: +/* 3611 */ /* --- orelse --- */ $blk = 4; /* allowing case fallthrough */ +/* 3612 */ case 4: +/* 3613 */ /* --- end --- */ return Sk.builtin.none.none$; +/* 3614 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 3615 */ case 1: +/* 3616 */ /* --- except_0_ --- */ +/* 3617 */ // +/* 3618 */ // line 110: +/* 3619 */ // return '' % type(value).__name__ +/* 3620 */ // ^ +/* 3621 */ // +/* 3622 */ +/* 3623 */ $currLineNo = Sk.currLineNo = 110; +/* 3624 */ $currColNo = Sk.currColNo = 8; +/* 3625 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3626 */ var $loadgbl332 = Sk.misceval.loadname('type', $gbl); +/* 3627 */ if (value === undefined) { +/* 3628 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 3629 */ } +/* 3630 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl332, [value]); +/* 3631 */ $blk = 6; /* allowing case fallthrough */ +/* 3632 */ case 6: +/* 3633 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3634 */ return $saveSuspension($ret, 'src/lib/traceback.py', 110, 43); +/* 3635 */ } +/* 3636 */ var $call333 = $ret; +/* 3637 */ // +/* 3638 */ // line 110: +/* 3639 */ // return '' % type(value).__name__ +/* 3640 */ // ^ +/* 3641 */ // +/* 3642 */ +/* 3643 */ $currLineNo = Sk.currLineNo = 110; +/* 3644 */ $currColNo = Sk.currColNo = 43; +/* 3645 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3646 */ $ret = Sk.abstr.gattr($call333, $scope327.$const334, true); +/* 3647 */ $blk = 7; /* allowing case fallthrough */ +/* 3648 */ case 7: +/* 3649 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3650 */ return $saveSuspension($ret, 'src/lib/traceback.py', 110, 43); +/* 3651 */ } +/* 3652 */ var $lattr335 = $ret; +/* 3653 */ var $binop336 = Sk.abstr.numberBinOp($scope327.$const331, $lattr335, 'Mod'); +/* 3654 */ return $binop336; +/* 3655 */ $blk = 4; /* jump */ +/* 3656 */ continue; +/* 3657 */ case 2: +/* 3658 */ /* --- unhandled --- */ throw $err; +/* 3659 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 3660 */ } +/* 3661 */ } catch (err) { +/* 3662 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 3663 */ Sk.execStart = Date.now(); +/* 3664 */ Sk.execPaused = 0 +/* 3665 */ } +/* 3666 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 3667 */ err = new Sk.builtin.ExternalError(err); +/* 3668 */ } +/* 3669 */ Sk.err = err; +/* 3670 */ err.traceback.push({ +/* 3671 */ lineno: $currLineNo, +/* 3672 */ colno: $currColNo, +/* 3673 */ filename: 'src/lib/traceback.py', +/* 3674 */ scope: '_some_str' +/* 3675 */ }); +/* 3676 */ if ($exc.length > 0) { +/* 3677 */ $err = err; +/* 3678 */ $blk = $exc.pop(); +/* 3679 */ continue +/* 3680 */ } else { +/* 3681 */ throw err; +/* 3682 */ } +/* 3683 */ } +/* 3684 */ } +/* 3685 */ }); +/* 3686 */ $scope327.$const331 = new Sk.builtin.str(''); +/* 3687 */ $scope327.$const334 = new Sk.builtin.str('__name__'); +/* 3688 */ var $scope338 = (function $print_exc339$(limit, file, chain) { +/* 3689 */ var chain, chain, file, file, limit, limit, $loadgbl340, $unpack341, $loadgbl342, $loadgbl340, $unpack341, $loadgbl342, $lattr344, $loadgbl340, $unpack341, $loadgbl342, $lattr344, $call345, $loadgbl340, $unpack341, $loadgbl342, $lattr344, $call345; +/* 3690 */ var $wakeFromSuspension = function() { +/* 3691 */ var susp = $scope338.$wakingSuspension; +/* 3692 */ $scope338.$wakingSuspension = undefined; +/* 3693 */ $blk = susp.$blk; +/* 3694 */ $loc = susp.$loc; +/* 3695 */ $gbl = susp.$gbl; +/* 3696 */ $exc = susp.$exc; +/* 3697 */ $err = susp.$err; +/* 3698 */ $postfinally = susp.$postfinally; +/* 3699 */ $currLineNo = susp.$lineno; +/* 3700 */ $currColNo = susp.$colno; +/* 3701 */ Sk.lastYield = Date.now(); +/* 3702 */ chain = susp.$tmps.chain; +/* 3703 */ file = susp.$tmps.file; +/* 3704 */ limit = susp.$tmps.limit; +/* 3705 */ $loadgbl340 = susp.$tmps.$loadgbl340; +/* 3706 */ $unpack341 = susp.$tmps.$unpack341; +/* 3707 */ $loadgbl342 = susp.$tmps.$loadgbl342; +/* 3708 */ $lattr344 = susp.$tmps.$lattr344; +/* 3709 */ $call345 = susp.$tmps.$call345; +/* 3710 */ try { +/* 3711 */ $ret = susp.child.resume(); +/* 3712 */ } catch (err) { +/* 3713 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 3714 */ Sk.execStart = Date.now(); +/* 3715 */ Sk.execPaused = 0 +/* 3716 */ } +/* 3717 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 3718 */ err = new Sk.builtin.ExternalError(err); +/* 3719 */ } +/* 3720 */ Sk.err = err; +/* 3721 */ err.traceback.push({ +/* 3722 */ lineno: $currLineNo, +/* 3723 */ colno: $currColNo, +/* 3724 */ filename: 'src/lib/traceback.py', +/* 3725 */ scope: '$scope338' +/* 3726 */ }); +/* 3727 */ if ($exc.length > 0) { +/* 3728 */ $err = err; +/* 3729 */ $blk = $exc.pop(); +/* 3730 */ } else { +/* 3731 */ throw err; +/* 3732 */ } +/* 3733 */ } +/* 3734 */ }; +/* 3735 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 3736 */ var susp = new Sk.misceval.Suspension(); +/* 3737 */ susp.child = $child; +/* 3738 */ susp.resume = function() { +/* 3739 */ $scope338.$wakingSuspension = susp; +/* 3740 */ return $scope338(); +/* 3741 */ }; +/* 3742 */ susp.data = susp.child.data; +/* 3743 */ susp.$blk = $blk; +/* 3744 */ susp.$loc = $loc; +/* 3745 */ susp.$gbl = $gbl; +/* 3746 */ susp.$exc = $exc; +/* 3747 */ susp.$err = $err; +/* 3748 */ susp.$postfinally = $postfinally; +/* 3749 */ susp.$filename = $filename; +/* 3750 */ susp.$lineno = $lineno; +/* 3751 */ susp.$colno = $colno; +/* 3752 */ susp.optional = susp.child.optional; +/* 3753 */ susp.$tmps = { +/* 3754 */ "chain": chain, +/* 3755 */ "file": file, +/* 3756 */ "limit": limit, +/* 3757 */ "$loadgbl340": $loadgbl340, +/* 3758 */ "$unpack341": $unpack341, +/* 3759 */ "$loadgbl342": $loadgbl342, +/* 3760 */ "$lattr344": $lattr344, +/* 3761 */ "$call345": $call345 +/* 3762 */ }; +/* 3763 */ return susp; +/* 3764 */ }; +/* 3765 */ var $blk = 0, +/* 3766 */ $exc = [], +/* 3767 */ $loc = {}, +/* 3768 */ $cell = {}, +/* 3769 */ $gbl = this, +/* 3770 */ $err = undefined, +/* 3771 */ $ret = undefined, +/* 3772 */ $postfinally = undefined, +/* 3773 */ $currLineNo = undefined, +/* 3774 */ $currColNo = undefined; +/* 3775 */ if (typeof Sk.execStart === 'undefined') { +/* 3776 */ Sk.execStart = Date.now(); +/* 3777 */ Sk.execPaused = 0 +/* 3778 */ } +/* 3779 */ if (typeof Sk.lastYield === 'undefined') { +/* 3780 */ Sk.lastYield = Date.now() +/* 3781 */ } +/* 3782 */ if ($scope338.$wakingSuspension !== undefined) { +/* 3783 */ $wakeFromSuspension(); +/* 3784 */ } else {} +/* 3785 */ while (true) { +/* 3786 */ try { +/* 3787 */ var $dateNow = Date.now(); +/* 3788 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 3789 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 3790 */ } +/* 3791 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 3792 */ var $susp = $saveSuspension({ +/* 3793 */ data: { +/* 3794 */ type: 'Sk.yield' +/* 3795 */ }, +/* 3796 */ resume: function() {} +/* 3797 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 3798 */ $susp.$blk = $blk; +/* 3799 */ $susp.optional = true; +/* 3800 */ return $susp; +/* 3801 */ } +/* 3802 */ switch ($blk) { +/* 3803 */ case 0: +/* 3804 */ /* --- codeobj entry --- */ if (limit === undefined) { +/* 3805 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 3806 */ } +/* 3807 */ if (file === undefined) { +/* 3808 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 3809 */ } +/* 3810 */ if (chain === undefined) { +/* 3811 */ throw new Sk.builtin.UnboundLocalError('local variable \'chain\' referenced before assignment'); +/* 3812 */ } +/* 3813 */ +/* 3814 */ // +/* 3815 */ // line 116: +/* 3816 */ // print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain) +/* 3817 */ // ^ +/* 3818 */ // +/* 3819 */ +/* 3820 */ $currLineNo = Sk.currLineNo = 116; +/* 3821 */ $currColNo = Sk.currColNo = 4; +/* 3822 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3823 */ var $loadgbl340 = Sk.misceval.loadname('print_exception', $gbl); +/* 3824 */ var $unpack341 = []; +/* 3825 */ var $loadgbl342 = Sk.misceval.loadname('sys', $gbl); +/* 3826 */ $ret = Sk.abstr.gattr($loadgbl342, $scope338.$const343, true); +/* 3827 */ $blk = 1; /* allowing case fallthrough */ +/* 3828 */ case 1: +/* 3829 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3830 */ return $saveSuspension($ret, 'src/lib/traceback.py', 116, 21); +/* 3831 */ } +/* 3832 */ var $lattr344 = $ret; +/* 3833 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr344); +/* 3834 */ $blk = 2; /* allowing case fallthrough */ +/* 3835 */ case 2: +/* 3836 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3837 */ return $saveSuspension($ret, 'src/lib/traceback.py', 116, 21); +/* 3838 */ } +/* 3839 */ var $call345 = $ret; +/* 3840 */ // +/* 3841 */ // line 116: +/* 3842 */ // print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain) +/* 3843 */ // ^ +/* 3844 */ // +/* 3845 */ +/* 3846 */ $currLineNo = Sk.currLineNo = 116; +/* 3847 */ $currColNo = Sk.currColNo = 21; +/* 3848 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3849 */ $ret = Sk.misceval.iterFor(Sk.abstr.iter($call345), function(e) { +/* 3850 */ $unpack341.push(e); +/* 3851 */ }); +/* 3852 */ $blk = 3; /* allowing case fallthrough */ +/* 3853 */ case 3: +/* 3854 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3855 */ return $saveSuspension($ret, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 3856 */ } +/* 3857 */ if (limit === undefined) { +/* 3858 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 3859 */ } +/* 3860 */ if (file === undefined) { +/* 3861 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 3862 */ } +/* 3863 */ if (chain === undefined) { +/* 3864 */ throw new Sk.builtin.UnboundLocalError('local variable \'chain\' referenced before assignment'); +/* 3865 */ } +/* 3866 */ $ret = Sk.misceval.applyOrSuspend($loadgbl340, undefined, undefined, ['limit', limit, 'file', file, 'chain', chain], $unpack341); +/* 3867 */ $blk = 4; /* allowing case fallthrough */ +/* 3868 */ case 4: +/* 3869 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 3870 */ return $saveSuspension($ret, 'src/lib/traceback.py', 116, 4); +/* 3871 */ } +/* 3872 */ var $call346 = $ret; +/* 3873 */ // +/* 3874 */ // line 116: +/* 3875 */ // print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain) +/* 3876 */ // ^ +/* 3877 */ // +/* 3878 */ +/* 3879 */ $currLineNo = Sk.currLineNo = 116; +/* 3880 */ $currColNo = Sk.currColNo = 4; +/* 3881 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 3882 */ return Sk.builtin.none.none$; +/* 3883 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 3884 */ } +/* 3885 */ } catch (err) { +/* 3886 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 3887 */ Sk.execStart = Date.now(); +/* 3888 */ Sk.execPaused = 0 +/* 3889 */ } +/* 3890 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 3891 */ err = new Sk.builtin.ExternalError(err); +/* 3892 */ } +/* 3893 */ Sk.err = err; +/* 3894 */ err.traceback.push({ +/* 3895 */ lineno: $currLineNo, +/* 3896 */ colno: $currColNo, +/* 3897 */ filename: 'src/lib/traceback.py', +/* 3898 */ scope: 'print_exc' +/* 3899 */ }); +/* 3900 */ if ($exc.length > 0) { +/* 3901 */ $err = err; +/* 3902 */ $blk = $exc.pop(); +/* 3903 */ continue +/* 3904 */ } else { +/* 3905 */ throw err; +/* 3906 */ } +/* 3907 */ } +/* 3908 */ } +/* 3909 */ }); +/* 3910 */ $scope338.$const343 = new Sk.builtin.str('exc_info'); +/* 3911 */ var $scope348 = (function $format_exc349$(limit, chain) { +/* 3912 */ var chain, chain, limit, limit, $lattr352, $loadgbl353, $unpack354, $loadgbl355, $lattr352, $loadgbl353, $unpack354, $loadgbl355, $lattr357, $lattr352, $loadgbl353, $unpack354, $loadgbl355, $lattr357, $call358, $lattr352, $loadgbl353, $unpack354, $loadgbl355, $lattr357, $call358, $lattr352, $loadgbl353, $unpack354, $loadgbl355, $lattr357, $call358, $call359; +/* 3913 */ var $wakeFromSuspension = function() { +/* 3914 */ var susp = $scope348.$wakingSuspension; +/* 3915 */ $scope348.$wakingSuspension = undefined; +/* 3916 */ $blk = susp.$blk; +/* 3917 */ $loc = susp.$loc; +/* 3918 */ $gbl = susp.$gbl; +/* 3919 */ $exc = susp.$exc; +/* 3920 */ $err = susp.$err; +/* 3921 */ $postfinally = susp.$postfinally; +/* 3922 */ $currLineNo = susp.$lineno; +/* 3923 */ $currColNo = susp.$colno; +/* 3924 */ Sk.lastYield = Date.now(); +/* 3925 */ chain = susp.$tmps.chain; +/* 3926 */ limit = susp.$tmps.limit; +/* 3927 */ $lattr352 = susp.$tmps.$lattr352; +/* 3928 */ $loadgbl353 = susp.$tmps.$loadgbl353; +/* 3929 */ $unpack354 = susp.$tmps.$unpack354; +/* 3930 */ $loadgbl355 = susp.$tmps.$loadgbl355; +/* 3931 */ $lattr357 = susp.$tmps.$lattr357; +/* 3932 */ $call358 = susp.$tmps.$call358; +/* 3933 */ $call359 = susp.$tmps.$call359; +/* 3934 */ try { +/* 3935 */ $ret = susp.child.resume(); +/* 3936 */ } catch (err) { +/* 3937 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 3938 */ Sk.execStart = Date.now(); +/* 3939 */ Sk.execPaused = 0 +/* 3940 */ } +/* 3941 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 3942 */ err = new Sk.builtin.ExternalError(err); +/* 3943 */ } +/* 3944 */ Sk.err = err; +/* 3945 */ err.traceback.push({ +/* 3946 */ lineno: $currLineNo, +/* 3947 */ colno: $currColNo, +/* 3948 */ filename: 'src/lib/traceback.py', +/* 3949 */ scope: '$scope348' +/* 3950 */ }); +/* 3951 */ if ($exc.length > 0) { +/* 3952 */ $err = err; +/* 3953 */ $blk = $exc.pop(); +/* 3954 */ } else { +/* 3955 */ throw err; +/* 3956 */ } +/* 3957 */ } +/* 3958 */ }; +/* 3959 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 3960 */ var susp = new Sk.misceval.Suspension(); +/* 3961 */ susp.child = $child; +/* 3962 */ susp.resume = function() { +/* 3963 */ $scope348.$wakingSuspension = susp; +/* 3964 */ return $scope348(); +/* 3965 */ }; +/* 3966 */ susp.data = susp.child.data; +/* 3967 */ susp.$blk = $blk; +/* 3968 */ susp.$loc = $loc; +/* 3969 */ susp.$gbl = $gbl; +/* 3970 */ susp.$exc = $exc; +/* 3971 */ susp.$err = $err; +/* 3972 */ susp.$postfinally = $postfinally; +/* 3973 */ susp.$filename = $filename; +/* 3974 */ susp.$lineno = $lineno; +/* 3975 */ susp.$colno = $colno; +/* 3976 */ susp.optional = susp.child.optional; +/* 3977 */ susp.$tmps = { +/* 3978 */ "chain": chain, +/* 3979 */ "limit": limit, +/* 3980 */ "$lattr352": $lattr352, +/* 3981 */ "$loadgbl353": $loadgbl353, +/* 3982 */ "$unpack354": $unpack354, +/* 3983 */ "$loadgbl355": $loadgbl355, +/* 3984 */ "$lattr357": $lattr357, +/* 3985 */ "$call358": $call358, +/* 3986 */ "$call359": $call359 +/* 3987 */ }; +/* 3988 */ return susp; +/* 3989 */ }; +/* 3990 */ var $blk = 0, +/* 3991 */ $exc = [], +/* 3992 */ $loc = {}, +/* 3993 */ $cell = {}, +/* 3994 */ $gbl = this, +/* 3995 */ $err = undefined, +/* 3996 */ $ret = undefined, +/* 3997 */ $postfinally = undefined, +/* 3998 */ $currLineNo = undefined, +/* 3999 */ $currColNo = undefined; +/* 4000 */ if (typeof Sk.execStart === 'undefined') { +/* 4001 */ Sk.execStart = Date.now(); +/* 4002 */ Sk.execPaused = 0 +/* 4003 */ } +/* 4004 */ if (typeof Sk.lastYield === 'undefined') { +/* 4005 */ Sk.lastYield = Date.now() +/* 4006 */ } +/* 4007 */ if ($scope348.$wakingSuspension !== undefined) { +/* 4008 */ $wakeFromSuspension(); +/* 4009 */ } else {} +/* 4010 */ while (true) { +/* 4011 */ try { +/* 4012 */ var $dateNow = Date.now(); +/* 4013 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 4014 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 4015 */ } +/* 4016 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 4017 */ var $susp = $saveSuspension({ +/* 4018 */ data: { +/* 4019 */ type: 'Sk.yield' +/* 4020 */ }, +/* 4021 */ resume: function() {} +/* 4022 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 4023 */ $susp.$blk = $blk; +/* 4024 */ $susp.optional = true; +/* 4025 */ return $susp; +/* 4026 */ } +/* 4027 */ switch ($blk) { +/* 4028 */ case 0: +/* 4029 */ /* --- codeobj entry --- */ if (limit === undefined) { +/* 4030 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 4031 */ } +/* 4032 */ if (chain === undefined) { +/* 4033 */ throw new Sk.builtin.UnboundLocalError('local variable \'chain\' referenced before assignment'); +/* 4034 */ } +/* 4035 */ +/* 4036 */ // +/* 4037 */ // line 120: +/* 4038 */ // return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain)) +/* 4039 */ // ^ +/* 4040 */ // +/* 4041 */ +/* 4042 */ $currLineNo = Sk.currLineNo = 120; +/* 4043 */ $currColNo = Sk.currColNo = 4; +/* 4044 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4045 */ $ret = Sk.abstr.gattr($scope348.$const350, $scope348.$const351, true); +/* 4046 */ $blk = 1; /* allowing case fallthrough */ +/* 4047 */ case 1: +/* 4048 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4049 */ return $saveSuspension($ret, 'src/lib/traceback.py', 120, 11); +/* 4050 */ } +/* 4051 */ var $lattr352 = $ret; +/* 4052 */ var $loadgbl353 = Sk.misceval.loadname('format_exception', $gbl); +/* 4053 */ var $unpack354 = []; +/* 4054 */ var $loadgbl355 = Sk.misceval.loadname('sys', $gbl); +/* 4055 */ $ret = Sk.abstr.gattr($loadgbl355, $scope348.$const356, true); +/* 4056 */ $blk = 2; /* allowing case fallthrough */ +/* 4057 */ case 2: +/* 4058 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4059 */ return $saveSuspension($ret, 'src/lib/traceback.py', 120, 37); +/* 4060 */ } +/* 4061 */ var $lattr357 = $ret; +/* 4062 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr357); +/* 4063 */ $blk = 3; /* allowing case fallthrough */ +/* 4064 */ case 3: +/* 4065 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4066 */ return $saveSuspension($ret, 'src/lib/traceback.py', 120, 37); +/* 4067 */ } +/* 4068 */ var $call358 = $ret; +/* 4069 */ // +/* 4070 */ // line 120: +/* 4071 */ // return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain)) +/* 4072 */ // ^ +/* 4073 */ // +/* 4074 */ +/* 4075 */ $currLineNo = Sk.currLineNo = 120; +/* 4076 */ $currColNo = Sk.currColNo = 37; +/* 4077 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4078 */ $ret = Sk.misceval.iterFor(Sk.abstr.iter($call358), function(e) { +/* 4079 */ $unpack354.push(e); +/* 4080 */ }); +/* 4081 */ $blk = 4; /* allowing case fallthrough */ +/* 4082 */ case 4: +/* 4083 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4084 */ return $saveSuspension($ret, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 4085 */ } +/* 4086 */ if (limit === undefined) { +/* 4087 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 4088 */ } +/* 4089 */ if (chain === undefined) { +/* 4090 */ throw new Sk.builtin.UnboundLocalError('local variable \'chain\' referenced before assignment'); +/* 4091 */ } +/* 4092 */ $ret = Sk.misceval.applyOrSuspend($loadgbl353, undefined, undefined, ['limit', limit, 'chain', chain], $unpack354); +/* 4093 */ $blk = 5; /* allowing case fallthrough */ +/* 4094 */ case 5: +/* 4095 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4096 */ return $saveSuspension($ret, 'src/lib/traceback.py', 120, 19); +/* 4097 */ } +/* 4098 */ var $call359 = $ret; +/* 4099 */ // +/* 4100 */ // line 120: +/* 4101 */ // return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain)) +/* 4102 */ // ^ +/* 4103 */ // +/* 4104 */ +/* 4105 */ $currLineNo = Sk.currLineNo = 120; +/* 4106 */ $currColNo = Sk.currColNo = 19; +/* 4107 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4108 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr352, [$call359]); +/* 4109 */ $blk = 6; /* allowing case fallthrough */ +/* 4110 */ case 6: +/* 4111 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4112 */ return $saveSuspension($ret, 'src/lib/traceback.py', 120, 11); +/* 4113 */ } +/* 4114 */ var $call360 = $ret; +/* 4115 */ // +/* 4116 */ // line 120: +/* 4117 */ // return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain)) +/* 4118 */ // ^ +/* 4119 */ // +/* 4120 */ +/* 4121 */ $currLineNo = Sk.currLineNo = 120; +/* 4122 */ $currColNo = Sk.currColNo = 11; +/* 4123 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4124 */ return $call360; +/* 4125 */ return Sk.builtin.none.none$; +/* 4126 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 4127 */ } +/* 4128 */ } catch (err) { +/* 4129 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 4130 */ Sk.execStart = Date.now(); +/* 4131 */ Sk.execPaused = 0 +/* 4132 */ } +/* 4133 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 4134 */ err = new Sk.builtin.ExternalError(err); +/* 4135 */ } +/* 4136 */ Sk.err = err; +/* 4137 */ err.traceback.push({ +/* 4138 */ lineno: $currLineNo, +/* 4139 */ colno: $currColNo, +/* 4140 */ filename: 'src/lib/traceback.py', +/* 4141 */ scope: 'format_exc' +/* 4142 */ }); +/* 4143 */ if ($exc.length > 0) { +/* 4144 */ $err = err; +/* 4145 */ $blk = $exc.pop(); +/* 4146 */ continue +/* 4147 */ } else { +/* 4148 */ throw err; +/* 4149 */ } +/* 4150 */ } +/* 4151 */ } +/* 4152 */ }); +/* 4153 */ $scope348.$const350 = new Sk.builtin.str(''); +/* 4154 */ $scope348.$const351 = new Sk.builtin.str('join'); +/* 4155 */ $scope348.$const356 = new Sk.builtin.str('exc_info'); +/* 4156 */ var $scope362 = (function $print_last363$(limit, file, chain) { +/* 4157 */ var chain, chain, file, file, limit, limit, $loadgbl364, $loadgbl365, $loadgbl370, $loadgbl370, $call372, $exc373, $isclass374, $jfalse375, $loadgbl376, $loadgbl377, $loadgbl376, $loadgbl377, $lattr378, $loadgbl379, $loadgbl376, $loadgbl377, $lattr378, $loadgbl379, $lattr381, $loadgbl382, $loadgbl376, $loadgbl377, $lattr378, $loadgbl379, $lattr381, $loadgbl382, $lattr384; +/* 4158 */ var $wakeFromSuspension = function() { +/* 4159 */ var susp = $scope362.$wakingSuspension; +/* 4160 */ $scope362.$wakingSuspension = undefined; +/* 4161 */ $blk = susp.$blk; +/* 4162 */ $loc = susp.$loc; +/* 4163 */ $gbl = susp.$gbl; +/* 4164 */ $exc = susp.$exc; +/* 4165 */ $err = susp.$err; +/* 4166 */ $postfinally = susp.$postfinally; +/* 4167 */ $currLineNo = susp.$lineno; +/* 4168 */ $currColNo = susp.$colno; +/* 4169 */ Sk.lastYield = Date.now(); +/* 4170 */ chain = susp.$tmps.chain; +/* 4171 */ file = susp.$tmps.file; +/* 4172 */ limit = susp.$tmps.limit; +/* 4173 */ $loadgbl364 = susp.$tmps.$loadgbl364; +/* 4174 */ $loadgbl365 = susp.$tmps.$loadgbl365; +/* 4175 */ $loadgbl370 = susp.$tmps.$loadgbl370; +/* 4176 */ $call372 = susp.$tmps.$call372; +/* 4177 */ $exc373 = susp.$tmps.$exc373; +/* 4178 */ $isclass374 = susp.$tmps.$isclass374; +/* 4179 */ $jfalse375 = susp.$tmps.$jfalse375; +/* 4180 */ $loadgbl376 = susp.$tmps.$loadgbl376; +/* 4181 */ $loadgbl377 = susp.$tmps.$loadgbl377; +/* 4182 */ $lattr378 = susp.$tmps.$lattr378; +/* 4183 */ $loadgbl379 = susp.$tmps.$loadgbl379; +/* 4184 */ $lattr381 = susp.$tmps.$lattr381; +/* 4185 */ $loadgbl382 = susp.$tmps.$loadgbl382; +/* 4186 */ $lattr384 = susp.$tmps.$lattr384; +/* 4187 */ try { +/* 4188 */ $ret = susp.child.resume(); +/* 4189 */ } catch (err) { +/* 4190 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 4191 */ Sk.execStart = Date.now(); +/* 4192 */ Sk.execPaused = 0 +/* 4193 */ } +/* 4194 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 4195 */ err = new Sk.builtin.ExternalError(err); +/* 4196 */ } +/* 4197 */ Sk.err = err; +/* 4198 */ err.traceback.push({ +/* 4199 */ lineno: $currLineNo, +/* 4200 */ colno: $currColNo, +/* 4201 */ filename: 'src/lib/traceback.py', +/* 4202 */ scope: '$scope362' +/* 4203 */ }); +/* 4204 */ if ($exc.length > 0) { +/* 4205 */ $err = err; +/* 4206 */ $blk = $exc.pop(); +/* 4207 */ } else { +/* 4208 */ throw err; +/* 4209 */ } +/* 4210 */ } +/* 4211 */ }; +/* 4212 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 4213 */ var susp = new Sk.misceval.Suspension(); +/* 4214 */ susp.child = $child; +/* 4215 */ susp.resume = function() { +/* 4216 */ $scope362.$wakingSuspension = susp; +/* 4217 */ return $scope362(); +/* 4218 */ }; +/* 4219 */ susp.data = susp.child.data; +/* 4220 */ susp.$blk = $blk; +/* 4221 */ susp.$loc = $loc; +/* 4222 */ susp.$gbl = $gbl; +/* 4223 */ susp.$exc = $exc; +/* 4224 */ susp.$err = $err; +/* 4225 */ susp.$postfinally = $postfinally; +/* 4226 */ susp.$filename = $filename; +/* 4227 */ susp.$lineno = $lineno; +/* 4228 */ susp.$colno = $colno; +/* 4229 */ susp.optional = susp.child.optional; +/* 4230 */ susp.$tmps = { +/* 4231 */ "chain": chain, +/* 4232 */ "file": file, +/* 4233 */ "limit": limit, +/* 4234 */ "$loadgbl364": $loadgbl364, +/* 4235 */ "$loadgbl365": $loadgbl365, +/* 4236 */ "$loadgbl370": $loadgbl370, +/* 4237 */ "$call372": $call372, +/* 4238 */ "$exc373": $exc373, +/* 4239 */ "$isclass374": $isclass374, +/* 4240 */ "$jfalse375": $jfalse375, +/* 4241 */ "$loadgbl376": $loadgbl376, +/* 4242 */ "$loadgbl377": $loadgbl377, +/* 4243 */ "$lattr378": $lattr378, +/* 4244 */ "$loadgbl379": $loadgbl379, +/* 4245 */ "$lattr381": $lattr381, +/* 4246 */ "$loadgbl382": $loadgbl382, +/* 4247 */ "$lattr384": $lattr384 +/* 4248 */ }; +/* 4249 */ return susp; +/* 4250 */ }; +/* 4251 */ var $blk = 0, +/* 4252 */ $exc = [], +/* 4253 */ $loc = {}, +/* 4254 */ $cell = {}, +/* 4255 */ $gbl = this, +/* 4256 */ $err = undefined, +/* 4257 */ $ret = undefined, +/* 4258 */ $postfinally = undefined, +/* 4259 */ $currLineNo = undefined, +/* 4260 */ $currColNo = undefined; +/* 4261 */ if (typeof Sk.execStart === 'undefined') { +/* 4262 */ Sk.execStart = Date.now(); +/* 4263 */ Sk.execPaused = 0 +/* 4264 */ } +/* 4265 */ if (typeof Sk.lastYield === 'undefined') { +/* 4266 */ Sk.lastYield = Date.now() +/* 4267 */ } +/* 4268 */ if ($scope362.$wakingSuspension !== undefined) { +/* 4269 */ $wakeFromSuspension(); +/* 4270 */ } else {} +/* 4271 */ while (true) { +/* 4272 */ try { +/* 4273 */ var $dateNow = Date.now(); +/* 4274 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 4275 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 4276 */ } +/* 4277 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 4278 */ var $susp = $saveSuspension({ +/* 4279 */ data: { +/* 4280 */ type: 'Sk.yield' +/* 4281 */ }, +/* 4282 */ resume: function() {} +/* 4283 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 4284 */ $susp.$blk = $blk; +/* 4285 */ $susp.optional = true; +/* 4286 */ return $susp; +/* 4287 */ } +/* 4288 */ switch ($blk) { +/* 4289 */ case 0: +/* 4290 */ /* --- codeobj entry --- */ if (limit === undefined) { +/* 4291 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 4292 */ } +/* 4293 */ if (file === undefined) { +/* 4294 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 4295 */ } +/* 4296 */ if (chain === undefined) { +/* 4297 */ throw new Sk.builtin.UnboundLocalError('local variable \'chain\' referenced before assignment'); +/* 4298 */ } +/* 4299 */ +/* 4300 */ // +/* 4301 */ // line 124: +/* 4302 */ // if not hasattr(sys, "last_type"): +/* 4303 */ // ^ +/* 4304 */ // +/* 4305 */ +/* 4306 */ $currLineNo = Sk.currLineNo = 124; +/* 4307 */ $currColNo = Sk.currColNo = 4; +/* 4308 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4309 */ var $loadgbl364 = Sk.misceval.loadname('hasattr', $gbl); +/* 4310 */ var $loadgbl365 = Sk.misceval.loadname('sys', $gbl); +/* 4311 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl364, [$loadgbl365, $scope362.$const366]); +/* 4312 */ $blk = 2; /* allowing case fallthrough */ +/* 4313 */ case 2: +/* 4314 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4315 */ return $saveSuspension($ret, 'src/lib/traceback.py', 124, 11); +/* 4316 */ } +/* 4317 */ var $call367 = $ret; +/* 4318 */ // +/* 4319 */ // line 124: +/* 4320 */ // if not hasattr(sys, "last_type"): +/* 4321 */ // ^ +/* 4322 */ // +/* 4323 */ +/* 4324 */ $currLineNo = Sk.currLineNo = 124; +/* 4325 */ $currColNo = Sk.currColNo = 11; +/* 4326 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4327 */ var $unaryop368 = Sk.abstr.numberUnaryOp($call367, 'Not'); +/* 4328 */ var $jfalse369 = ($unaryop368 === false || !Sk.misceval.isTrue($unaryop368)); +/* 4329 */ if ($jfalse369) { +/* 4330 */ /*test failed */ +/* 4331 */ $blk = 1; +/* 4332 */ continue; +/* 4333 */ } +/* 4334 */ // +/* 4335 */ // line 125: +/* 4336 */ // raise ValueError("no last exception") +/* 4337 */ // ^ +/* 4338 */ // +/* 4339 */ +/* 4340 */ $currLineNo = Sk.currLineNo = 125; +/* 4341 */ $currColNo = Sk.currColNo = 8; +/* 4342 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4343 */ var $loadgbl370 = Sk.misceval.loadname('ValueError', $gbl); +/* 4344 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl370, [$scope362.$const371]); +/* 4345 */ $blk = 3; /* allowing case fallthrough */ +/* 4346 */ case 3: +/* 4347 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4348 */ return $saveSuspension($ret, 'src/lib/traceback.py', 125, 14); +/* 4349 */ } +/* 4350 */ var $call372 = $ret; +/* 4351 */ // +/* 4352 */ // line 125: +/* 4353 */ // raise ValueError("no last exception") +/* 4354 */ // ^ +/* 4355 */ // +/* 4356 */ +/* 4357 */ $currLineNo = Sk.currLineNo = 125; +/* 4358 */ $currColNo = Sk.currColNo = 14; +/* 4359 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4360 */ var $exc373 = $call372; +/* 4361 */ var $isclass374 = $exc373 instanceof Sk.builtin.type || $exc373.prototype instanceof Sk.builtin.BaseException; +/* 4362 */ var $jfalse375 = ($isclass374 === false || !Sk.misceval.isTrue($isclass374)); +/* 4363 */ if ($jfalse375) { +/* 4364 */ /*test failed */ +/* 4365 */ $blk = 4; +/* 4366 */ continue; +/* 4367 */ } +/* 4368 */ $ret = Sk.misceval.callsimOrSuspend($exc373); +/* 4369 */ $blk = 5; /* allowing case fallthrough */ +/* 4370 */ case 5: +/* 4371 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4372 */ return $saveSuspension($ret, 'src/lib/traceback.py', 125, 8); +/* 4373 */ } +/* 4374 */ $exc373 = $ret; +/* 4375 */ $blk = 4; /* allowing case fallthrough */ +/* 4376 */ case 4: +/* 4377 */ /* --- exception now instantiated --- */ throw $exc373; +/* 4378 */ $blk = 1; /* allowing case fallthrough */ +/* 4379 */ case 1: +/* 4380 */ /* --- end of if --- */ +/* 4381 */ // +/* 4382 */ // line 126: +/* 4383 */ // print_exception(sys.last_type, sys.last_value, sys.last_traceback, +/* 4384 */ // ^ +/* 4385 */ // +/* 4386 */ +/* 4387 */ $currLineNo = Sk.currLineNo = 126; +/* 4388 */ $currColNo = Sk.currColNo = 4; +/* 4389 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4390 */ var $loadgbl376 = Sk.misceval.loadname('print_exception', $gbl); +/* 4391 */ var $loadgbl377 = Sk.misceval.loadname('sys', $gbl); +/* 4392 */ $ret = Sk.abstr.gattr($loadgbl377, $scope362.$const366, true); +/* 4393 */ $blk = 6; /* allowing case fallthrough */ +/* 4394 */ case 6: +/* 4395 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4396 */ return $saveSuspension($ret, 'src/lib/traceback.py', 126, 20); +/* 4397 */ } +/* 4398 */ var $lattr378 = $ret; +/* 4399 */ var $loadgbl379 = Sk.misceval.loadname('sys', $gbl); +/* 4400 */ $ret = Sk.abstr.gattr($loadgbl379, $scope362.$const380, true); +/* 4401 */ $blk = 7; /* allowing case fallthrough */ +/* 4402 */ case 7: +/* 4403 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4404 */ return $saveSuspension($ret, 'src/lib/traceback.py', 126, 35); +/* 4405 */ } +/* 4406 */ var $lattr381 = $ret; +/* 4407 */ var $loadgbl382 = Sk.misceval.loadname('sys', $gbl); +/* 4408 */ $ret = Sk.abstr.gattr($loadgbl382, $scope362.$const383, true); +/* 4409 */ $blk = 8; /* allowing case fallthrough */ +/* 4410 */ case 8: +/* 4411 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4412 */ return $saveSuspension($ret, 'src/lib/traceback.py', 126, 51); +/* 4413 */ } +/* 4414 */ var $lattr384 = $ret; +/* 4415 */ if (limit === undefined) { +/* 4416 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 4417 */ } +/* 4418 */ if (file === undefined) { +/* 4419 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 4420 */ } +/* 4421 */ if (chain === undefined) { +/* 4422 */ throw new Sk.builtin.UnboundLocalError('local variable \'chain\' referenced before assignment'); +/* 4423 */ } +/* 4424 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl376, [$lattr378, $lattr381, $lattr384, limit, file, chain]); +/* 4425 */ $blk = 9; /* allowing case fallthrough */ +/* 4426 */ case 9: +/* 4427 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4428 */ return $saveSuspension($ret, 'src/lib/traceback.py', 126, 4); +/* 4429 */ } +/* 4430 */ var $call385 = $ret; +/* 4431 */ // +/* 4432 */ // line 126: +/* 4433 */ // print_exception(sys.last_type, sys.last_value, sys.last_traceback, +/* 4434 */ // ^ +/* 4435 */ // +/* 4436 */ +/* 4437 */ $currLineNo = Sk.currLineNo = 126; +/* 4438 */ $currColNo = Sk.currColNo = 4; +/* 4439 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4440 */ return Sk.builtin.none.none$; +/* 4441 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 4442 */ } +/* 4443 */ } catch (err) { +/* 4444 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 4445 */ Sk.execStart = Date.now(); +/* 4446 */ Sk.execPaused = 0 +/* 4447 */ } +/* 4448 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 4449 */ err = new Sk.builtin.ExternalError(err); +/* 4450 */ } +/* 4451 */ Sk.err = err; +/* 4452 */ err.traceback.push({ +/* 4453 */ lineno: $currLineNo, +/* 4454 */ colno: $currColNo, +/* 4455 */ filename: 'src/lib/traceback.py', +/* 4456 */ scope: 'print_last' +/* 4457 */ }); +/* 4458 */ if ($exc.length > 0) { +/* 4459 */ $err = err; +/* 4460 */ $blk = $exc.pop(); +/* 4461 */ continue +/* 4462 */ } else { +/* 4463 */ throw err; +/* 4464 */ } +/* 4465 */ } +/* 4466 */ } +/* 4467 */ }); +/* 4468 */ $scope362.$const366 = new Sk.builtin.str('last_type'); +/* 4469 */ $scope362.$const371 = new Sk.builtin.str('no last exception'); +/* 4470 */ $scope362.$const380 = new Sk.builtin.str('last_value'); +/* 4471 */ $scope362.$const383 = new Sk.builtin.str('last_traceback'); +/* 4472 */ var $scope387 = (function $print_stack388$(f, limit, file) { +/* 4473 */ var f, f, f, f, file, file, limit, limit, $compareres389, $loadgbl392, $loadgbl392, $lattr394, $loadgbl392, $lattr394, $call395, $loadgbl398, $loadgbl399, $loadgbl398, $loadgbl399, $call400; +/* 4474 */ var $wakeFromSuspension = function() { +/* 4475 */ var susp = $scope387.$wakingSuspension; +/* 4476 */ $scope387.$wakingSuspension = undefined; +/* 4477 */ $blk = susp.$blk; +/* 4478 */ $loc = susp.$loc; +/* 4479 */ $gbl = susp.$gbl; +/* 4480 */ $exc = susp.$exc; +/* 4481 */ $err = susp.$err; +/* 4482 */ $postfinally = susp.$postfinally; +/* 4483 */ $currLineNo = susp.$lineno; +/* 4484 */ $currColNo = susp.$colno; +/* 4485 */ Sk.lastYield = Date.now(); +/* 4486 */ f = susp.$tmps.f; +/* 4487 */ file = susp.$tmps.file; +/* 4488 */ limit = susp.$tmps.limit; +/* 4489 */ $compareres389 = susp.$tmps.$compareres389; +/* 4490 */ $loadgbl392 = susp.$tmps.$loadgbl392; +/* 4491 */ $lattr394 = susp.$tmps.$lattr394; +/* 4492 */ $call395 = susp.$tmps.$call395; +/* 4493 */ $loadgbl398 = susp.$tmps.$loadgbl398; +/* 4494 */ $loadgbl399 = susp.$tmps.$loadgbl399; +/* 4495 */ $call400 = susp.$tmps.$call400; +/* 4496 */ try { +/* 4497 */ $ret = susp.child.resume(); +/* 4498 */ } catch (err) { +/* 4499 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 4500 */ Sk.execStart = Date.now(); +/* 4501 */ Sk.execPaused = 0 +/* 4502 */ } +/* 4503 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 4504 */ err = new Sk.builtin.ExternalError(err); +/* 4505 */ } +/* 4506 */ Sk.err = err; +/* 4507 */ err.traceback.push({ +/* 4508 */ lineno: $currLineNo, +/* 4509 */ colno: $currColNo, +/* 4510 */ filename: 'src/lib/traceback.py', +/* 4511 */ scope: '$scope387' +/* 4512 */ }); +/* 4513 */ if ($exc.length > 0) { +/* 4514 */ $err = err; +/* 4515 */ $blk = $exc.pop(); +/* 4516 */ } else { +/* 4517 */ throw err; +/* 4518 */ } +/* 4519 */ } +/* 4520 */ }; +/* 4521 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 4522 */ var susp = new Sk.misceval.Suspension(); +/* 4523 */ susp.child = $child; +/* 4524 */ susp.resume = function() { +/* 4525 */ $scope387.$wakingSuspension = susp; +/* 4526 */ return $scope387(); +/* 4527 */ }; +/* 4528 */ susp.data = susp.child.data; +/* 4529 */ susp.$blk = $blk; +/* 4530 */ susp.$loc = $loc; +/* 4531 */ susp.$gbl = $gbl; +/* 4532 */ susp.$exc = $exc; +/* 4533 */ susp.$err = $err; +/* 4534 */ susp.$postfinally = $postfinally; +/* 4535 */ susp.$filename = $filename; +/* 4536 */ susp.$lineno = $lineno; +/* 4537 */ susp.$colno = $colno; +/* 4538 */ susp.optional = susp.child.optional; +/* 4539 */ susp.$tmps = { +/* 4540 */ "f": f, +/* 4541 */ "file": file, +/* 4542 */ "limit": limit, +/* 4543 */ "$compareres389": $compareres389, +/* 4544 */ "$loadgbl392": $loadgbl392, +/* 4545 */ "$lattr394": $lattr394, +/* 4546 */ "$call395": $call395, +/* 4547 */ "$loadgbl398": $loadgbl398, +/* 4548 */ "$loadgbl399": $loadgbl399, +/* 4549 */ "$call400": $call400 +/* 4550 */ }; +/* 4551 */ return susp; +/* 4552 */ }; +/* 4553 */ var $blk = 0, +/* 4554 */ $exc = [], +/* 4555 */ $loc = {}, +/* 4556 */ $cell = {}, +/* 4557 */ $gbl = this, +/* 4558 */ $err = undefined, +/* 4559 */ $ret = undefined, +/* 4560 */ $postfinally = undefined, +/* 4561 */ $currLineNo = undefined, +/* 4562 */ $currColNo = undefined; +/* 4563 */ if (typeof Sk.execStart === 'undefined') { +/* 4564 */ Sk.execStart = Date.now(); +/* 4565 */ Sk.execPaused = 0 +/* 4566 */ } +/* 4567 */ if (typeof Sk.lastYield === 'undefined') { +/* 4568 */ Sk.lastYield = Date.now() +/* 4569 */ } +/* 4570 */ if ($scope387.$wakingSuspension !== undefined) { +/* 4571 */ $wakeFromSuspension(); +/* 4572 */ } else {} +/* 4573 */ while (true) { +/* 4574 */ try { +/* 4575 */ var $dateNow = Date.now(); +/* 4576 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 4577 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 4578 */ } +/* 4579 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 4580 */ var $susp = $saveSuspension({ +/* 4581 */ data: { +/* 4582 */ type: 'Sk.yield' +/* 4583 */ }, +/* 4584 */ resume: function() {} +/* 4585 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 4586 */ $susp.$blk = $blk; +/* 4587 */ $susp.optional = true; +/* 4588 */ return $susp; +/* 4589 */ } +/* 4590 */ switch ($blk) { +/* 4591 */ case 0: +/* 4592 */ /* --- codeobj entry --- */ if (f === undefined) { +/* 4593 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 4594 */ } +/* 4595 */ if (limit === undefined) { +/* 4596 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 4597 */ } +/* 4598 */ if (file === undefined) { +/* 4599 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 4600 */ } +/* 4601 */ +/* 4602 */ // +/* 4603 */ // line 135: +/* 4604 */ // if f is None: +/* 4605 */ // ^ +/* 4606 */ // +/* 4607 */ +/* 4608 */ $currLineNo = Sk.currLineNo = 135; +/* 4609 */ $currColNo = Sk.currColNo = 4; +/* 4610 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4611 */ if (f === undefined) { +/* 4612 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 4613 */ } +/* 4614 */ var $compareres389 = null; +/* 4615 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(f, Sk.builtin.none.none$, 'Is', true)); +/* 4616 */ $blk = 3; /* allowing case fallthrough */ +/* 4617 */ case 3: +/* 4618 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4619 */ return $saveSuspension($ret, 'src/lib/traceback.py', 135, 7); +/* 4620 */ } +/* 4621 */ $compareres389 = $ret; +/* 4622 */ var $jfalse390 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 4623 */ if ($jfalse390) { +/* 4624 */ /*test failed */ +/* 4625 */ $blk = 2; +/* 4626 */ continue; +/* 4627 */ } +/* 4628 */ $blk = 2; /* allowing case fallthrough */ +/* 4629 */ case 2: +/* 4630 */ /* --- done --- */ var $jfalse391 = ($compareres389 === false || !Sk.misceval.isTrue($compareres389)); +/* 4631 */ if ($jfalse391) { +/* 4632 */ /*test failed */ +/* 4633 */ $blk = 1; +/* 4634 */ continue; +/* 4635 */ } +/* 4636 */ // +/* 4637 */ // line 136: +/* 4638 */ // f = sys._getframe().f_back +/* 4639 */ // ^ +/* 4640 */ // +/* 4641 */ +/* 4642 */ $currLineNo = Sk.currLineNo = 136; +/* 4643 */ $currColNo = Sk.currColNo = 8; +/* 4644 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4645 */ var $loadgbl392 = Sk.misceval.loadname('sys', $gbl); +/* 4646 */ $ret = Sk.abstr.gattr($loadgbl392, $scope387.$const393, true); +/* 4647 */ $blk = 4; /* allowing case fallthrough */ +/* 4648 */ case 4: +/* 4649 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4650 */ return $saveSuspension($ret, 'src/lib/traceback.py', 136, 12); +/* 4651 */ } +/* 4652 */ var $lattr394 = $ret; +/* 4653 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr394); +/* 4654 */ $blk = 5; /* allowing case fallthrough */ +/* 4655 */ case 5: +/* 4656 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4657 */ return $saveSuspension($ret, 'src/lib/traceback.py', 136, 12); +/* 4658 */ } +/* 4659 */ var $call395 = $ret; +/* 4660 */ // +/* 4661 */ // line 136: +/* 4662 */ // f = sys._getframe().f_back +/* 4663 */ // ^ +/* 4664 */ // +/* 4665 */ +/* 4666 */ $currLineNo = Sk.currLineNo = 136; +/* 4667 */ $currColNo = Sk.currColNo = 12; +/* 4668 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4669 */ $ret = Sk.abstr.gattr($call395, $scope387.$const396, true); +/* 4670 */ $blk = 6; /* allowing case fallthrough */ +/* 4671 */ case 6: +/* 4672 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4673 */ return $saveSuspension($ret, 'src/lib/traceback.py', 136, 12); +/* 4674 */ } +/* 4675 */ var $lattr397 = $ret; +/* 4676 */ f = $lattr397; +/* 4677 */ $blk = 1; /* allowing case fallthrough */ +/* 4678 */ case 1: +/* 4679 */ /* --- end of if --- */ +/* 4680 */ // +/* 4681 */ // line 137: +/* 4682 */ // print_list(extract_stack(f, limit=limit), file=file) +/* 4683 */ // ^ +/* 4684 */ // +/* 4685 */ +/* 4686 */ $currLineNo = Sk.currLineNo = 137; +/* 4687 */ $currColNo = Sk.currColNo = 4; +/* 4688 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4689 */ var $loadgbl398 = Sk.misceval.loadname('print_list', $gbl); +/* 4690 */ var $loadgbl399 = Sk.misceval.loadname('extract_stack', $gbl); +/* 4691 */ if (f === undefined) { +/* 4692 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 4693 */ } +/* 4694 */ if (limit === undefined) { +/* 4695 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 4696 */ } +/* 4697 */ $ret = Sk.misceval.applyOrSuspend($loadgbl399, undefined, undefined, ['limit', limit], [f]); +/* 4698 */ $blk = 7; /* allowing case fallthrough */ +/* 4699 */ case 7: +/* 4700 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4701 */ return $saveSuspension($ret, 'src/lib/traceback.py', 137, 15); +/* 4702 */ } +/* 4703 */ var $call400 = $ret; +/* 4704 */ // +/* 4705 */ // line 137: +/* 4706 */ // print_list(extract_stack(f, limit=limit), file=file) +/* 4707 */ // ^ +/* 4708 */ // +/* 4709 */ +/* 4710 */ $currLineNo = Sk.currLineNo = 137; +/* 4711 */ $currColNo = Sk.currColNo = 15; +/* 4712 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4713 */ if (file === undefined) { +/* 4714 */ throw new Sk.builtin.UnboundLocalError('local variable \'file\' referenced before assignment'); +/* 4715 */ } +/* 4716 */ $ret = Sk.misceval.applyOrSuspend($loadgbl398, undefined, undefined, ['file', file], [$call400]); +/* 4717 */ $blk = 8; /* allowing case fallthrough */ +/* 4718 */ case 8: +/* 4719 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4720 */ return $saveSuspension($ret, 'src/lib/traceback.py', 137, 4); +/* 4721 */ } +/* 4722 */ var $call401 = $ret; +/* 4723 */ // +/* 4724 */ // line 137: +/* 4725 */ // print_list(extract_stack(f, limit=limit), file=file) +/* 4726 */ // ^ +/* 4727 */ // +/* 4728 */ +/* 4729 */ $currLineNo = Sk.currLineNo = 137; +/* 4730 */ $currColNo = Sk.currColNo = 4; +/* 4731 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4732 */ return Sk.builtin.none.none$; +/* 4733 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 4734 */ } +/* 4735 */ } catch (err) { +/* 4736 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 4737 */ Sk.execStart = Date.now(); +/* 4738 */ Sk.execPaused = 0 +/* 4739 */ } +/* 4740 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 4741 */ err = new Sk.builtin.ExternalError(err); +/* 4742 */ } +/* 4743 */ Sk.err = err; +/* 4744 */ err.traceback.push({ +/* 4745 */ lineno: $currLineNo, +/* 4746 */ colno: $currColNo, +/* 4747 */ filename: 'src/lib/traceback.py', +/* 4748 */ scope: 'print_stack' +/* 4749 */ }); +/* 4750 */ if ($exc.length > 0) { +/* 4751 */ $err = err; +/* 4752 */ $blk = $exc.pop(); +/* 4753 */ continue +/* 4754 */ } else { +/* 4755 */ throw err; +/* 4756 */ } +/* 4757 */ } +/* 4758 */ } +/* 4759 */ }); +/* 4760 */ $scope387.$const393 = new Sk.builtin.str('_getframe'); +/* 4761 */ $scope387.$const396 = new Sk.builtin.str('f_back'); +/* 4762 */ var $scope403 = (function $format_stack404$(f, limit) { +/* 4763 */ var f, f, f, f, limit, limit, $compareres405, $loadgbl408, $loadgbl408, $lattr410, $loadgbl408, $lattr410, $call411, $loadgbl414, $loadgbl415, $loadgbl414, $loadgbl415, $call416; +/* 4764 */ var $wakeFromSuspension = function() { +/* 4765 */ var susp = $scope403.$wakingSuspension; +/* 4766 */ $scope403.$wakingSuspension = undefined; +/* 4767 */ $blk = susp.$blk; +/* 4768 */ $loc = susp.$loc; +/* 4769 */ $gbl = susp.$gbl; +/* 4770 */ $exc = susp.$exc; +/* 4771 */ $err = susp.$err; +/* 4772 */ $postfinally = susp.$postfinally; +/* 4773 */ $currLineNo = susp.$lineno; +/* 4774 */ $currColNo = susp.$colno; +/* 4775 */ Sk.lastYield = Date.now(); +/* 4776 */ f = susp.$tmps.f; +/* 4777 */ limit = susp.$tmps.limit; +/* 4778 */ $compareres405 = susp.$tmps.$compareres405; +/* 4779 */ $loadgbl408 = susp.$tmps.$loadgbl408; +/* 4780 */ $lattr410 = susp.$tmps.$lattr410; +/* 4781 */ $call411 = susp.$tmps.$call411; +/* 4782 */ $loadgbl414 = susp.$tmps.$loadgbl414; +/* 4783 */ $loadgbl415 = susp.$tmps.$loadgbl415; +/* 4784 */ $call416 = susp.$tmps.$call416; +/* 4785 */ try { +/* 4786 */ $ret = susp.child.resume(); +/* 4787 */ } catch (err) { +/* 4788 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 4789 */ Sk.execStart = Date.now(); +/* 4790 */ Sk.execPaused = 0 +/* 4791 */ } +/* 4792 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 4793 */ err = new Sk.builtin.ExternalError(err); +/* 4794 */ } +/* 4795 */ Sk.err = err; +/* 4796 */ err.traceback.push({ +/* 4797 */ lineno: $currLineNo, +/* 4798 */ colno: $currColNo, +/* 4799 */ filename: 'src/lib/traceback.py', +/* 4800 */ scope: '$scope403' +/* 4801 */ }); +/* 4802 */ if ($exc.length > 0) { +/* 4803 */ $err = err; +/* 4804 */ $blk = $exc.pop(); +/* 4805 */ } else { +/* 4806 */ throw err; +/* 4807 */ } +/* 4808 */ } +/* 4809 */ }; +/* 4810 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 4811 */ var susp = new Sk.misceval.Suspension(); +/* 4812 */ susp.child = $child; +/* 4813 */ susp.resume = function() { +/* 4814 */ $scope403.$wakingSuspension = susp; +/* 4815 */ return $scope403(); +/* 4816 */ }; +/* 4817 */ susp.data = susp.child.data; +/* 4818 */ susp.$blk = $blk; +/* 4819 */ susp.$loc = $loc; +/* 4820 */ susp.$gbl = $gbl; +/* 4821 */ susp.$exc = $exc; +/* 4822 */ susp.$err = $err; +/* 4823 */ susp.$postfinally = $postfinally; +/* 4824 */ susp.$filename = $filename; +/* 4825 */ susp.$lineno = $lineno; +/* 4826 */ susp.$colno = $colno; +/* 4827 */ susp.optional = susp.child.optional; +/* 4828 */ susp.$tmps = { +/* 4829 */ "f": f, +/* 4830 */ "limit": limit, +/* 4831 */ "$compareres405": $compareres405, +/* 4832 */ "$loadgbl408": $loadgbl408, +/* 4833 */ "$lattr410": $lattr410, +/* 4834 */ "$call411": $call411, +/* 4835 */ "$loadgbl414": $loadgbl414, +/* 4836 */ "$loadgbl415": $loadgbl415, +/* 4837 */ "$call416": $call416 +/* 4838 */ }; +/* 4839 */ return susp; +/* 4840 */ }; +/* 4841 */ var $blk = 0, +/* 4842 */ $exc = [], +/* 4843 */ $loc = {}, +/* 4844 */ $cell = {}, +/* 4845 */ $gbl = this, +/* 4846 */ $err = undefined, +/* 4847 */ $ret = undefined, +/* 4848 */ $postfinally = undefined, +/* 4849 */ $currLineNo = undefined, +/* 4850 */ $currColNo = undefined; +/* 4851 */ if (typeof Sk.execStart === 'undefined') { +/* 4852 */ Sk.execStart = Date.now(); +/* 4853 */ Sk.execPaused = 0 +/* 4854 */ } +/* 4855 */ if (typeof Sk.lastYield === 'undefined') { +/* 4856 */ Sk.lastYield = Date.now() +/* 4857 */ } +/* 4858 */ if ($scope403.$wakingSuspension !== undefined) { +/* 4859 */ $wakeFromSuspension(); +/* 4860 */ } else {} +/* 4861 */ while (true) { +/* 4862 */ try { +/* 4863 */ var $dateNow = Date.now(); +/* 4864 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 4865 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 4866 */ } +/* 4867 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 4868 */ var $susp = $saveSuspension({ +/* 4869 */ data: { +/* 4870 */ type: 'Sk.yield' +/* 4871 */ }, +/* 4872 */ resume: function() {} +/* 4873 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 4874 */ $susp.$blk = $blk; +/* 4875 */ $susp.optional = true; +/* 4876 */ return $susp; +/* 4877 */ } +/* 4878 */ switch ($blk) { +/* 4879 */ case 0: +/* 4880 */ /* --- codeobj entry --- */ if (f === undefined) { +/* 4881 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 4882 */ } +/* 4883 */ if (limit === undefined) { +/* 4884 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 4885 */ } +/* 4886 */ +/* 4887 */ // +/* 4888 */ // line 142: +/* 4889 */ // if f is None: +/* 4890 */ // ^ +/* 4891 */ // +/* 4892 */ +/* 4893 */ $currLineNo = Sk.currLineNo = 142; +/* 4894 */ $currColNo = Sk.currColNo = 4; +/* 4895 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4896 */ if (f === undefined) { +/* 4897 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 4898 */ } +/* 4899 */ var $compareres405 = null; +/* 4900 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(f, Sk.builtin.none.none$, 'Is', true)); +/* 4901 */ $blk = 3; /* allowing case fallthrough */ +/* 4902 */ case 3: +/* 4903 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4904 */ return $saveSuspension($ret, 'src/lib/traceback.py', 142, 7); +/* 4905 */ } +/* 4906 */ $compareres405 = $ret; +/* 4907 */ var $jfalse406 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 4908 */ if ($jfalse406) { +/* 4909 */ /*test failed */ +/* 4910 */ $blk = 2; +/* 4911 */ continue; +/* 4912 */ } +/* 4913 */ $blk = 2; /* allowing case fallthrough */ +/* 4914 */ case 2: +/* 4915 */ /* --- done --- */ var $jfalse407 = ($compareres405 === false || !Sk.misceval.isTrue($compareres405)); +/* 4916 */ if ($jfalse407) { +/* 4917 */ /*test failed */ +/* 4918 */ $blk = 1; +/* 4919 */ continue; +/* 4920 */ } +/* 4921 */ // +/* 4922 */ // line 143: +/* 4923 */ // f = sys._getframe().f_back +/* 4924 */ // ^ +/* 4925 */ // +/* 4926 */ +/* 4927 */ $currLineNo = Sk.currLineNo = 143; +/* 4928 */ $currColNo = Sk.currColNo = 8; +/* 4929 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4930 */ var $loadgbl408 = Sk.misceval.loadname('sys', $gbl); +/* 4931 */ $ret = Sk.abstr.gattr($loadgbl408, $scope403.$const409, true); +/* 4932 */ $blk = 4; /* allowing case fallthrough */ +/* 4933 */ case 4: +/* 4934 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4935 */ return $saveSuspension($ret, 'src/lib/traceback.py', 143, 12); +/* 4936 */ } +/* 4937 */ var $lattr410 = $ret; +/* 4938 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr410); +/* 4939 */ $blk = 5; /* allowing case fallthrough */ +/* 4940 */ case 5: +/* 4941 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4942 */ return $saveSuspension($ret, 'src/lib/traceback.py', 143, 12); +/* 4943 */ } +/* 4944 */ var $call411 = $ret; +/* 4945 */ // +/* 4946 */ // line 143: +/* 4947 */ // f = sys._getframe().f_back +/* 4948 */ // ^ +/* 4949 */ // +/* 4950 */ +/* 4951 */ $currLineNo = Sk.currLineNo = 143; +/* 4952 */ $currColNo = Sk.currColNo = 12; +/* 4953 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4954 */ $ret = Sk.abstr.gattr($call411, $scope403.$const412, true); +/* 4955 */ $blk = 6; /* allowing case fallthrough */ +/* 4956 */ case 6: +/* 4957 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4958 */ return $saveSuspension($ret, 'src/lib/traceback.py', 143, 12); +/* 4959 */ } +/* 4960 */ var $lattr413 = $ret; +/* 4961 */ f = $lattr413; +/* 4962 */ $blk = 1; /* allowing case fallthrough */ +/* 4963 */ case 1: +/* 4964 */ /* --- end of if --- */ +/* 4965 */ // +/* 4966 */ // line 144: +/* 4967 */ // return format_list(extract_stack(f, limit=limit)) +/* 4968 */ // ^ +/* 4969 */ // +/* 4970 */ +/* 4971 */ $currLineNo = Sk.currLineNo = 144; +/* 4972 */ $currColNo = Sk.currColNo = 4; +/* 4973 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4974 */ var $loadgbl414 = Sk.misceval.loadname('format_list', $gbl); +/* 4975 */ var $loadgbl415 = Sk.misceval.loadname('extract_stack', $gbl); +/* 4976 */ if (f === undefined) { +/* 4977 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 4978 */ } +/* 4979 */ if (limit === undefined) { +/* 4980 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 4981 */ } +/* 4982 */ $ret = Sk.misceval.applyOrSuspend($loadgbl415, undefined, undefined, ['limit', limit], [f]); +/* 4983 */ $blk = 7; /* allowing case fallthrough */ +/* 4984 */ case 7: +/* 4985 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 4986 */ return $saveSuspension($ret, 'src/lib/traceback.py', 144, 23); +/* 4987 */ } +/* 4988 */ var $call416 = $ret; +/* 4989 */ // +/* 4990 */ // line 144: +/* 4991 */ // return format_list(extract_stack(f, limit=limit)) +/* 4992 */ // ^ +/* 4993 */ // +/* 4994 */ +/* 4995 */ $currLineNo = Sk.currLineNo = 144; +/* 4996 */ $currColNo = Sk.currColNo = 23; +/* 4997 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 4998 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl414, [$call416]); +/* 4999 */ $blk = 8; /* allowing case fallthrough */ +/* 5000 */ case 8: +/* 5001 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5002 */ return $saveSuspension($ret, 'src/lib/traceback.py', 144, 11); +/* 5003 */ } +/* 5004 */ var $call417 = $ret; +/* 5005 */ // +/* 5006 */ // line 144: +/* 5007 */ // return format_list(extract_stack(f, limit=limit)) +/* 5008 */ // ^ +/* 5009 */ // +/* 5010 */ +/* 5011 */ $currLineNo = Sk.currLineNo = 144; +/* 5012 */ $currColNo = Sk.currColNo = 11; +/* 5013 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5014 */ return $call417; +/* 5015 */ return Sk.builtin.none.none$; +/* 5016 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 5017 */ } +/* 5018 */ } catch (err) { +/* 5019 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 5020 */ Sk.execStart = Date.now(); +/* 5021 */ Sk.execPaused = 0 +/* 5022 */ } +/* 5023 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 5024 */ err = new Sk.builtin.ExternalError(err); +/* 5025 */ } +/* 5026 */ Sk.err = err; +/* 5027 */ err.traceback.push({ +/* 5028 */ lineno: $currLineNo, +/* 5029 */ colno: $currColNo, +/* 5030 */ filename: 'src/lib/traceback.py', +/* 5031 */ scope: 'format_stack' +/* 5032 */ }); +/* 5033 */ if ($exc.length > 0) { +/* 5034 */ $err = err; +/* 5035 */ $blk = $exc.pop(); +/* 5036 */ continue +/* 5037 */ } else { +/* 5038 */ throw err; +/* 5039 */ } +/* 5040 */ } +/* 5041 */ } +/* 5042 */ }); +/* 5043 */ $scope403.$const409 = new Sk.builtin.str('_getframe'); +/* 5044 */ $scope403.$const412 = new Sk.builtin.str('f_back'); +/* 5045 */ var $scope419 = (function $extract_stack420$(f, limit) { +/* 5046 */ var stack; /* locals */ +/* 5047 */ var f, f, f, f, limit, limit, stack, stack, stack, $compareres421, $loadgbl424, $loadgbl424, $lattr426, $loadgbl424, $lattr426, $call427, $loadgbl430, $loadgbl430, $lattr432, $loadgbl433, $loadgbl430, $lattr432, $loadgbl433, $call434, $lattr437; +/* 5048 */ var $wakeFromSuspension = function() { +/* 5049 */ var susp = $scope419.$wakingSuspension; +/* 5050 */ $scope419.$wakingSuspension = undefined; +/* 5051 */ $blk = susp.$blk; +/* 5052 */ $loc = susp.$loc; +/* 5053 */ $gbl = susp.$gbl; +/* 5054 */ $exc = susp.$exc; +/* 5055 */ $err = susp.$err; +/* 5056 */ $postfinally = susp.$postfinally; +/* 5057 */ $currLineNo = susp.$lineno; +/* 5058 */ $currColNo = susp.$colno; +/* 5059 */ Sk.lastYield = Date.now(); +/* 5060 */ f = susp.$tmps.f; +/* 5061 */ limit = susp.$tmps.limit; +/* 5062 */ stack = susp.$tmps.stack; +/* 5063 */ $compareres421 = susp.$tmps.$compareres421; +/* 5064 */ $loadgbl424 = susp.$tmps.$loadgbl424; +/* 5065 */ $lattr426 = susp.$tmps.$lattr426; +/* 5066 */ $call427 = susp.$tmps.$call427; +/* 5067 */ $loadgbl430 = susp.$tmps.$loadgbl430; +/* 5068 */ $lattr432 = susp.$tmps.$lattr432; +/* 5069 */ $loadgbl433 = susp.$tmps.$loadgbl433; +/* 5070 */ $call434 = susp.$tmps.$call434; +/* 5071 */ $lattr437 = susp.$tmps.$lattr437; +/* 5072 */ try { +/* 5073 */ $ret = susp.child.resume(); +/* 5074 */ } catch (err) { +/* 5075 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 5076 */ Sk.execStart = Date.now(); +/* 5077 */ Sk.execPaused = 0 +/* 5078 */ } +/* 5079 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 5080 */ err = new Sk.builtin.ExternalError(err); +/* 5081 */ } +/* 5082 */ Sk.err = err; +/* 5083 */ err.traceback.push({ +/* 5084 */ lineno: $currLineNo, +/* 5085 */ colno: $currColNo, +/* 5086 */ filename: 'src/lib/traceback.py', +/* 5087 */ scope: '$scope419' +/* 5088 */ }); +/* 5089 */ if ($exc.length > 0) { +/* 5090 */ $err = err; +/* 5091 */ $blk = $exc.pop(); +/* 5092 */ } else { +/* 5093 */ throw err; +/* 5094 */ } +/* 5095 */ } +/* 5096 */ }; +/* 5097 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 5098 */ var susp = new Sk.misceval.Suspension(); +/* 5099 */ susp.child = $child; +/* 5100 */ susp.resume = function() { +/* 5101 */ $scope419.$wakingSuspension = susp; +/* 5102 */ return $scope419(); +/* 5103 */ }; +/* 5104 */ susp.data = susp.child.data; +/* 5105 */ susp.$blk = $blk; +/* 5106 */ susp.$loc = $loc; +/* 5107 */ susp.$gbl = $gbl; +/* 5108 */ susp.$exc = $exc; +/* 5109 */ susp.$err = $err; +/* 5110 */ susp.$postfinally = $postfinally; +/* 5111 */ susp.$filename = $filename; +/* 5112 */ susp.$lineno = $lineno; +/* 5113 */ susp.$colno = $colno; +/* 5114 */ susp.optional = susp.child.optional; +/* 5115 */ susp.$tmps = { +/* 5116 */ "f": f, +/* 5117 */ "limit": limit, +/* 5118 */ "stack": stack, +/* 5119 */ "$compareres421": $compareres421, +/* 5120 */ "$loadgbl424": $loadgbl424, +/* 5121 */ "$lattr426": $lattr426, +/* 5122 */ "$call427": $call427, +/* 5123 */ "$loadgbl430": $loadgbl430, +/* 5124 */ "$lattr432": $lattr432, +/* 5125 */ "$loadgbl433": $loadgbl433, +/* 5126 */ "$call434": $call434, +/* 5127 */ "$lattr437": $lattr437 +/* 5128 */ }; +/* 5129 */ return susp; +/* 5130 */ }; +/* 5131 */ var $blk = 0, +/* 5132 */ $exc = [], +/* 5133 */ $loc = {}, +/* 5134 */ $cell = {}, +/* 5135 */ $gbl = this, +/* 5136 */ $err = undefined, +/* 5137 */ $ret = undefined, +/* 5138 */ $postfinally = undefined, +/* 5139 */ $currLineNo = undefined, +/* 5140 */ $currColNo = undefined; +/* 5141 */ if (typeof Sk.execStart === 'undefined') { +/* 5142 */ Sk.execStart = Date.now(); +/* 5143 */ Sk.execPaused = 0 +/* 5144 */ } +/* 5145 */ if (typeof Sk.lastYield === 'undefined') { +/* 5146 */ Sk.lastYield = Date.now() +/* 5147 */ } +/* 5148 */ if ($scope419.$wakingSuspension !== undefined) { +/* 5149 */ $wakeFromSuspension(); +/* 5150 */ } else {} +/* 5151 */ while (true) { +/* 5152 */ try { +/* 5153 */ var $dateNow = Date.now(); +/* 5154 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 5155 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 5156 */ } +/* 5157 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 5158 */ var $susp = $saveSuspension({ +/* 5159 */ data: { +/* 5160 */ type: 'Sk.yield' +/* 5161 */ }, +/* 5162 */ resume: function() {} +/* 5163 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 5164 */ $susp.$blk = $blk; +/* 5165 */ $susp.optional = true; +/* 5166 */ return $susp; +/* 5167 */ } +/* 5168 */ switch ($blk) { +/* 5169 */ case 0: +/* 5170 */ /* --- codeobj entry --- */ if (f === undefined) { +/* 5171 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 5172 */ } +/* 5173 */ if (limit === undefined) { +/* 5174 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 5175 */ } +/* 5176 */ +/* 5177 */ // +/* 5178 */ // line 149: +/* 5179 */ // if f is None: +/* 5180 */ // ^ +/* 5181 */ // +/* 5182 */ +/* 5183 */ $currLineNo = Sk.currLineNo = 149; +/* 5184 */ $currColNo = Sk.currColNo = 4; +/* 5185 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5186 */ if (f === undefined) { +/* 5187 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 5188 */ } +/* 5189 */ var $compareres421 = null; +/* 5190 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(f, Sk.builtin.none.none$, 'Is', true)); +/* 5191 */ $blk = 3; /* allowing case fallthrough */ +/* 5192 */ case 3: +/* 5193 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5194 */ return $saveSuspension($ret, 'src/lib/traceback.py', 149, 7); +/* 5195 */ } +/* 5196 */ $compareres421 = $ret; +/* 5197 */ var $jfalse422 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 5198 */ if ($jfalse422) { +/* 5199 */ /*test failed */ +/* 5200 */ $blk = 2; +/* 5201 */ continue; +/* 5202 */ } +/* 5203 */ $blk = 2; /* allowing case fallthrough */ +/* 5204 */ case 2: +/* 5205 */ /* --- done --- */ var $jfalse423 = ($compareres421 === false || !Sk.misceval.isTrue($compareres421)); +/* 5206 */ if ($jfalse423) { +/* 5207 */ /*test failed */ +/* 5208 */ $blk = 1; +/* 5209 */ continue; +/* 5210 */ } +/* 5211 */ // +/* 5212 */ // line 150: +/* 5213 */ // f = sys._getframe().f_back +/* 5214 */ // ^ +/* 5215 */ // +/* 5216 */ +/* 5217 */ $currLineNo = Sk.currLineNo = 150; +/* 5218 */ $currColNo = Sk.currColNo = 8; +/* 5219 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5220 */ var $loadgbl424 = Sk.misceval.loadname('sys', $gbl); +/* 5221 */ $ret = Sk.abstr.gattr($loadgbl424, $scope419.$const425, true); +/* 5222 */ $blk = 4; /* allowing case fallthrough */ +/* 5223 */ case 4: +/* 5224 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5225 */ return $saveSuspension($ret, 'src/lib/traceback.py', 150, 12); +/* 5226 */ } +/* 5227 */ var $lattr426 = $ret; +/* 5228 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr426); +/* 5229 */ $blk = 5; /* allowing case fallthrough */ +/* 5230 */ case 5: +/* 5231 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5232 */ return $saveSuspension($ret, 'src/lib/traceback.py', 150, 12); +/* 5233 */ } +/* 5234 */ var $call427 = $ret; +/* 5235 */ // +/* 5236 */ // line 150: +/* 5237 */ // f = sys._getframe().f_back +/* 5238 */ // ^ +/* 5239 */ // +/* 5240 */ +/* 5241 */ $currLineNo = Sk.currLineNo = 150; +/* 5242 */ $currColNo = Sk.currColNo = 12; +/* 5243 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5244 */ $ret = Sk.abstr.gattr($call427, $scope419.$const428, true); +/* 5245 */ $blk = 6; /* allowing case fallthrough */ +/* 5246 */ case 6: +/* 5247 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5248 */ return $saveSuspension($ret, 'src/lib/traceback.py', 150, 12); +/* 5249 */ } +/* 5250 */ var $lattr429 = $ret; +/* 5251 */ f = $lattr429; +/* 5252 */ $blk = 1; /* allowing case fallthrough */ +/* 5253 */ case 1: +/* 5254 */ /* --- end of if --- */ +/* 5255 */ // +/* 5256 */ // line 151: +/* 5257 */ // stack = StackSummary.extract(walk_stack(f), limit=limit) +/* 5258 */ // ^ +/* 5259 */ // +/* 5260 */ +/* 5261 */ $currLineNo = Sk.currLineNo = 151; +/* 5262 */ $currColNo = Sk.currColNo = 4; +/* 5263 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5264 */ var $loadgbl430 = Sk.misceval.loadname('StackSummary', $gbl); +/* 5265 */ $ret = Sk.abstr.gattr($loadgbl430, $scope419.$const431, true); +/* 5266 */ $blk = 7; /* allowing case fallthrough */ +/* 5267 */ case 7: +/* 5268 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5269 */ return $saveSuspension($ret, 'src/lib/traceback.py', 151, 12); +/* 5270 */ } +/* 5271 */ var $lattr432 = $ret; +/* 5272 */ var $loadgbl433 = Sk.misceval.loadname('walk_stack', $gbl); +/* 5273 */ if (f === undefined) { +/* 5274 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 5275 */ } +/* 5276 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl433, [f]); +/* 5277 */ $blk = 8; /* allowing case fallthrough */ +/* 5278 */ case 8: +/* 5279 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5280 */ return $saveSuspension($ret, 'src/lib/traceback.py', 151, 33); +/* 5281 */ } +/* 5282 */ var $call434 = $ret; +/* 5283 */ // +/* 5284 */ // line 151: +/* 5285 */ // stack = StackSummary.extract(walk_stack(f), limit=limit) +/* 5286 */ // ^ +/* 5287 */ // +/* 5288 */ +/* 5289 */ $currLineNo = Sk.currLineNo = 151; +/* 5290 */ $currColNo = Sk.currColNo = 33; +/* 5291 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5292 */ if (limit === undefined) { +/* 5293 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 5294 */ } +/* 5295 */ $ret = Sk.misceval.applyOrSuspend($lattr432, undefined, undefined, ['limit', limit], [$call434]); +/* 5296 */ $blk = 9; /* allowing case fallthrough */ +/* 5297 */ case 9: +/* 5298 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5299 */ return $saveSuspension($ret, 'src/lib/traceback.py', 151, 12); +/* 5300 */ } +/* 5301 */ var $call435 = $ret; +/* 5302 */ // +/* 5303 */ // line 151: +/* 5304 */ // stack = StackSummary.extract(walk_stack(f), limit=limit) +/* 5305 */ // ^ +/* 5306 */ // +/* 5307 */ +/* 5308 */ $currLineNo = Sk.currLineNo = 151; +/* 5309 */ $currColNo = Sk.currColNo = 12; +/* 5310 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5311 */ stack = $call435; +/* 5312 */ // +/* 5313 */ // line 152: +/* 5314 */ // stack.reverse() +/* 5315 */ // ^ +/* 5316 */ // +/* 5317 */ +/* 5318 */ $currLineNo = Sk.currLineNo = 152; +/* 5319 */ $currColNo = Sk.currColNo = 4; +/* 5320 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5321 */ if (stack === undefined) { +/* 5322 */ throw new Sk.builtin.UnboundLocalError('local variable \'stack\' referenced before assignment'); +/* 5323 */ } +/* 5324 */ $ret = Sk.abstr.gattr(stack, $scope419.$const436, true); +/* 5325 */ $blk = 10; /* allowing case fallthrough */ +/* 5326 */ case 10: +/* 5327 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5328 */ return $saveSuspension($ret, 'src/lib/traceback.py', 152, 4); +/* 5329 */ } +/* 5330 */ var $lattr437 = $ret; +/* 5331 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr437); +/* 5332 */ $blk = 11; /* allowing case fallthrough */ +/* 5333 */ case 11: +/* 5334 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5335 */ return $saveSuspension($ret, 'src/lib/traceback.py', 152, 4); +/* 5336 */ } +/* 5337 */ var $call438 = $ret; +/* 5338 */ // +/* 5339 */ // line 152: +/* 5340 */ // stack.reverse() +/* 5341 */ // ^ +/* 5342 */ // +/* 5343 */ +/* 5344 */ $currLineNo = Sk.currLineNo = 152; +/* 5345 */ $currColNo = Sk.currColNo = 4; +/* 5346 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5347 */ // +/* 5348 */ // line 153: +/* 5349 */ // return stack +/* 5350 */ // ^ +/* 5351 */ // +/* 5352 */ +/* 5353 */ $currLineNo = Sk.currLineNo = 153; +/* 5354 */ $currColNo = Sk.currColNo = 4; +/* 5355 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5356 */ if (stack === undefined) { +/* 5357 */ throw new Sk.builtin.UnboundLocalError('local variable \'stack\' referenced before assignment'); +/* 5358 */ } +/* 5359 */ return stack; +/* 5360 */ return Sk.builtin.none.none$; +/* 5361 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 5362 */ } +/* 5363 */ } catch (err) { +/* 5364 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 5365 */ Sk.execStart = Date.now(); +/* 5366 */ Sk.execPaused = 0 +/* 5367 */ } +/* 5368 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 5369 */ err = new Sk.builtin.ExternalError(err); +/* 5370 */ } +/* 5371 */ Sk.err = err; +/* 5372 */ err.traceback.push({ +/* 5373 */ lineno: $currLineNo, +/* 5374 */ colno: $currColNo, +/* 5375 */ filename: 'src/lib/traceback.py', +/* 5376 */ scope: 'extract_stack' +/* 5377 */ }); +/* 5378 */ if ($exc.length > 0) { +/* 5379 */ $err = err; +/* 5380 */ $blk = $exc.pop(); +/* 5381 */ continue +/* 5382 */ } else { +/* 5383 */ throw err; +/* 5384 */ } +/* 5385 */ } +/* 5386 */ } +/* 5387 */ }); +/* 5388 */ $scope419.$const425 = new Sk.builtin.str('_getframe'); +/* 5389 */ $scope419.$const428 = new Sk.builtin.str('f_back'); +/* 5390 */ $scope419.$const431 = new Sk.builtin.str('extract'); +/* 5391 */ $scope419.$const436 = new Sk.builtin.str('reverse'); +/* 5392 */ var $scope440 = (function $clear_frames441$(tb) { +/* 5393 */ var tb, tb, tb, tb, tb, $compareres443, $lattr447, $lattr447, $lattr449; +/* 5394 */ var $wakeFromSuspension = function() { +/* 5395 */ var susp = $scope440.$wakingSuspension; +/* 5396 */ $scope440.$wakingSuspension = undefined; +/* 5397 */ $blk = susp.$blk; +/* 5398 */ $loc = susp.$loc; +/* 5399 */ $gbl = susp.$gbl; +/* 5400 */ $exc = susp.$exc; +/* 5401 */ $err = susp.$err; +/* 5402 */ $postfinally = susp.$postfinally; +/* 5403 */ $currLineNo = susp.$lineno; +/* 5404 */ $currColNo = susp.$colno; +/* 5405 */ Sk.lastYield = Date.now(); +/* 5406 */ tb = susp.$tmps.tb; +/* 5407 */ $compareres443 = susp.$tmps.$compareres443; +/* 5408 */ $lattr447 = susp.$tmps.$lattr447; +/* 5409 */ $lattr449 = susp.$tmps.$lattr449; +/* 5410 */ try { +/* 5411 */ $ret = susp.child.resume(); +/* 5412 */ } catch (err) { +/* 5413 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 5414 */ Sk.execStart = Date.now(); +/* 5415 */ Sk.execPaused = 0 +/* 5416 */ } +/* 5417 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 5418 */ err = new Sk.builtin.ExternalError(err); +/* 5419 */ } +/* 5420 */ Sk.err = err; +/* 5421 */ err.traceback.push({ +/* 5422 */ lineno: $currLineNo, +/* 5423 */ colno: $currColNo, +/* 5424 */ filename: 'src/lib/traceback.py', +/* 5425 */ scope: '$scope440' +/* 5426 */ }); +/* 5427 */ if ($exc.length > 0) { +/* 5428 */ $err = err; +/* 5429 */ $blk = $exc.pop(); +/* 5430 */ } else { +/* 5431 */ throw err; +/* 5432 */ } +/* 5433 */ } +/* 5434 */ }; +/* 5435 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 5436 */ var susp = new Sk.misceval.Suspension(); +/* 5437 */ susp.child = $child; +/* 5438 */ susp.resume = function() { +/* 5439 */ $scope440.$wakingSuspension = susp; +/* 5440 */ return $scope440(); +/* 5441 */ }; +/* 5442 */ susp.data = susp.child.data; +/* 5443 */ susp.$blk = $blk; +/* 5444 */ susp.$loc = $loc; +/* 5445 */ susp.$gbl = $gbl; +/* 5446 */ susp.$exc = $exc; +/* 5447 */ susp.$err = $err; +/* 5448 */ susp.$postfinally = $postfinally; +/* 5449 */ susp.$filename = $filename; +/* 5450 */ susp.$lineno = $lineno; +/* 5451 */ susp.$colno = $colno; +/* 5452 */ susp.optional = susp.child.optional; +/* 5453 */ susp.$tmps = { +/* 5454 */ "tb": tb, +/* 5455 */ "$compareres443": $compareres443, +/* 5456 */ "$lattr447": $lattr447, +/* 5457 */ "$lattr449": $lattr449 +/* 5458 */ }; +/* 5459 */ return susp; +/* 5460 */ }; +/* 5461 */ var $blk = 0, +/* 5462 */ $exc = [], +/* 5463 */ $loc = {}, +/* 5464 */ $cell = {}, +/* 5465 */ $gbl = this, +/* 5466 */ $err = undefined, +/* 5467 */ $ret = undefined, +/* 5468 */ $postfinally = undefined, +/* 5469 */ $currLineNo = undefined, +/* 5470 */ $currColNo = undefined; +/* 5471 */ if (typeof Sk.execStart === 'undefined') { +/* 5472 */ Sk.execStart = Date.now(); +/* 5473 */ Sk.execPaused = 0 +/* 5474 */ } +/* 5475 */ if (typeof Sk.lastYield === 'undefined') { +/* 5476 */ Sk.lastYield = Date.now() +/* 5477 */ } +/* 5478 */ if ($scope440.$wakingSuspension !== undefined) { +/* 5479 */ $wakeFromSuspension(); +/* 5480 */ } else {} +/* 5481 */ while (true) { +/* 5482 */ try { +/* 5483 */ var $dateNow = Date.now(); +/* 5484 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 5485 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 5486 */ } +/* 5487 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 5488 */ var $susp = $saveSuspension({ +/* 5489 */ data: { +/* 5490 */ type: 'Sk.yield' +/* 5491 */ }, +/* 5492 */ resume: function() {} +/* 5493 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 5494 */ $susp.$blk = $blk; +/* 5495 */ $susp.optional = true; +/* 5496 */ return $susp; +/* 5497 */ } +/* 5498 */ switch ($blk) { +/* 5499 */ case 0: +/* 5500 */ /* --- codeobj entry --- */ if (tb === undefined) { +/* 5501 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 5502 */ } +/* 5503 */ +/* 5504 */ // +/* 5505 */ // line 157: +/* 5506 */ // "Clear all references to local variables in the frames of a traceback." +/* 5507 */ // ^ +/* 5508 */ // +/* 5509 */ +/* 5510 */ $currLineNo = Sk.currLineNo = 157; +/* 5511 */ $currColNo = Sk.currColNo = 4; +/* 5512 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5513 */ // +/* 5514 */ // line 158: +/* 5515 */ // while tb is not None: +/* 5516 */ // ^ +/* 5517 */ // +/* 5518 */ +/* 5519 */ $currLineNo = Sk.currLineNo = 158; +/* 5520 */ $currColNo = Sk.currColNo = 4; +/* 5521 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5522 */ $blk = 1; /* allowing case fallthrough */ +/* 5523 */ case 1: +/* 5524 */ /* --- while test --- */ +/* 5525 */ // +/* 5526 */ // line 158: +/* 5527 */ // while tb is not None: +/* 5528 */ // ^ +/* 5529 */ // +/* 5530 */ +/* 5531 */ $currLineNo = Sk.currLineNo = 158; +/* 5532 */ $currColNo = Sk.currColNo = 4; +/* 5533 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5534 */ if (tb === undefined) { +/* 5535 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 5536 */ } +/* 5537 */ var $compareres443 = null; +/* 5538 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(tb, Sk.builtin.none.none$, 'IsNot', true)); +/* 5539 */ $blk = 5; /* allowing case fallthrough */ +/* 5540 */ case 5: +/* 5541 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5542 */ return $saveSuspension($ret, 'src/lib/traceback.py', 158, 10); +/* 5543 */ } +/* 5544 */ $compareres443 = $ret; +/* 5545 */ var $jfalse444 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 5546 */ if ($jfalse444) { +/* 5547 */ /*test failed */ +/* 5548 */ $blk = 4; +/* 5549 */ continue; +/* 5550 */ } +/* 5551 */ $blk = 4; /* allowing case fallthrough */ +/* 5552 */ case 4: +/* 5553 */ /* --- done --- */ var $jfalse445 = ($compareres443 === false || !Sk.misceval.isTrue($compareres443)); +/* 5554 */ if ($jfalse445) { +/* 5555 */ /*test failed */ +/* 5556 */ $blk = 2; +/* 5557 */ continue; +/* 5558 */ } +/* 5559 */ $blk = 3; /* allowing case fallthrough */ +/* 5560 */ case 3: +/* 5561 */ /* --- while body --- */ +/* 5562 */ // +/* 5563 */ // line 159: +/* 5564 */ // try: +/* 5565 */ // ^ +/* 5566 */ // +/* 5567 */ +/* 5568 */ $currLineNo = Sk.currLineNo = 159; +/* 5569 */ $currColNo = Sk.currColNo = 8; +/* 5570 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5571 */ $exc.push(6); +/* 5572 */ // +/* 5573 */ // line 160: +/* 5574 */ // tb.tb_frame.clear() +/* 5575 */ // ^ +/* 5576 */ // +/* 5577 */ +/* 5578 */ $currLineNo = Sk.currLineNo = 160; +/* 5579 */ $currColNo = Sk.currColNo = 12; +/* 5580 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5581 */ if (tb === undefined) { +/* 5582 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 5583 */ } +/* 5584 */ $ret = Sk.abstr.gattr(tb, $scope440.$const446, true); +/* 5585 */ $blk = 10; /* allowing case fallthrough */ +/* 5586 */ case 10: +/* 5587 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5588 */ return $saveSuspension($ret, 'src/lib/traceback.py', 160, 12); +/* 5589 */ } +/* 5590 */ var $lattr447 = $ret; +/* 5591 */ $ret = Sk.abstr.gattr($lattr447, $scope440.$const448, true); +/* 5592 */ $blk = 11; /* allowing case fallthrough */ +/* 5593 */ case 11: +/* 5594 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5595 */ return $saveSuspension($ret, 'src/lib/traceback.py', 160, 12); +/* 5596 */ } +/* 5597 */ var $lattr449 = $ret; +/* 5598 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr449); +/* 5599 */ $blk = 12; /* allowing case fallthrough */ +/* 5600 */ case 12: +/* 5601 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5602 */ return $saveSuspension($ret, 'src/lib/traceback.py', 160, 12); +/* 5603 */ } +/* 5604 */ var $call450 = $ret; +/* 5605 */ // +/* 5606 */ // line 160: +/* 5607 */ // tb.tb_frame.clear() +/* 5608 */ // ^ +/* 5609 */ // +/* 5610 */ +/* 5611 */ $currLineNo = Sk.currLineNo = 160; +/* 5612 */ $currColNo = Sk.currColNo = 12; +/* 5613 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5614 */ $exc.pop(); +/* 5615 */ $blk = 8; /* allowing case fallthrough */ +/* 5616 */ case 8: +/* 5617 */ /* --- orelse --- */ $blk = 9; /* allowing case fallthrough */ +/* 5618 */ case 9: +/* 5619 */ /* --- end --- */ +/* 5620 */ // +/* 5621 */ // line 164: +/* 5622 */ // tb = tb.tb_next +/* 5623 */ // ^ +/* 5624 */ // +/* 5625 */ +/* 5626 */ $currLineNo = Sk.currLineNo = 164; +/* 5627 */ $currColNo = Sk.currColNo = 8; +/* 5628 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5629 */ if (tb === undefined) { +/* 5630 */ throw new Sk.builtin.UnboundLocalError('local variable \'tb\' referenced before assignment'); +/* 5631 */ } +/* 5632 */ $ret = Sk.abstr.gattr(tb, $scope440.$const454, true); +/* 5633 */ $blk = 13; /* allowing case fallthrough */ +/* 5634 */ case 13: +/* 5635 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 5636 */ return $saveSuspension($ret, 'src/lib/traceback.py', 164, 13); +/* 5637 */ } +/* 5638 */ var $lattr455 = $ret; +/* 5639 */ tb = $lattr455; +/* 5640 */ $blk = 1; /* jump */ +/* 5641 */ continue; +/* 5642 */ case 2: +/* 5643 */ /* --- after while --- */ return Sk.builtin.none.none$; +/* 5644 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 5645 */ case 6: +/* 5646 */ /* --- except_0_ --- */ var $loadgbl451 = Sk.misceval.loadname('RuntimeError', $gbl); +/* 5647 */ var $instance452 = Sk.misceval.isTrue(Sk.builtin.isinstance($err, $loadgbl451)); +/* 5648 */ var $jfalse453 = ($instance452 === false || !Sk.misceval.isTrue($instance452)); +/* 5649 */ if ($jfalse453) { +/* 5650 */ /*test failed */ +/* 5651 */ $blk = 7; +/* 5652 */ continue; +/* 5653 */ } +/* 5654 */ // +/* 5655 */ // line 163: +/* 5656 */ // pass +/* 5657 */ // ^ +/* 5658 */ // +/* 5659 */ +/* 5660 */ $currLineNo = Sk.currLineNo = 163; +/* 5661 */ $currColNo = Sk.currColNo = 12; +/* 5662 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5663 */ $blk = 9; /* jump */ +/* 5664 */ continue; +/* 5665 */ case 7: +/* 5666 */ /* --- unhandled --- */ throw $err; +/* 5667 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 5668 */ } +/* 5669 */ } catch (err) { +/* 5670 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 5671 */ Sk.execStart = Date.now(); +/* 5672 */ Sk.execPaused = 0 +/* 5673 */ } +/* 5674 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 5675 */ err = new Sk.builtin.ExternalError(err); +/* 5676 */ } +/* 5677 */ Sk.err = err; +/* 5678 */ err.traceback.push({ +/* 5679 */ lineno: $currLineNo, +/* 5680 */ colno: $currColNo, +/* 5681 */ filename: 'src/lib/traceback.py', +/* 5682 */ scope: 'clear_frames' +/* 5683 */ }); +/* 5684 */ if ($exc.length > 0) { +/* 5685 */ $err = err; +/* 5686 */ $blk = $exc.pop(); +/* 5687 */ continue +/* 5688 */ } else { +/* 5689 */ throw err; +/* 5690 */ } +/* 5691 */ } +/* 5692 */ } +/* 5693 */ }); +/* 5694 */ $scope440.$const442 = new Sk.builtin.str('Clear all references to local variables in the frames of a traceback.'); +/* 5695 */ $scope440.$const446 = new Sk.builtin.str('tb_frame'); +/* 5696 */ $scope440.$const448 = new Sk.builtin.str('clear'); +/* 5697 */ $scope440.$const454 = new Sk.builtin.str('tb_next'); +/* 5698 */ var $scope457 = (function $FrameSummary$class_outer($globals, $locals, $cell) { +/* 5699 */ var $gbl = $globals, +/* 5700 */ $loc = $locals; +/* 5701 */ $free = $globals; +/* 5702 */ (function $FrameSummary$_closure($cell) { +/* 5703 */ var $blk = 0, +/* 5704 */ $exc = [], +/* 5705 */ $ret = undefined, +/* 5706 */ $postfinally = undefined, +/* 5707 */ $currLineNo = undefined, +/* 5708 */ $currColNo = undefined; +/* 5709 */ if (typeof Sk.execStart === 'undefined') { +/* 5710 */ Sk.execStart = Date.now(); +/* 5711 */ Sk.execPaused = 0 +/* 5712 */ } +/* 5713 */ while (true) { +/* 5714 */ try { +/* 5715 */ var $dateNow = Date.now(); +/* 5716 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 5717 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 5718 */ } +/* 5719 */ switch ($blk) { +/* 5720 */ case 0: +/* 5721 */ /* --- class entry --- */ +/* 5722 */ // +/* 5723 */ // line 170: +/* 5724 */ // __slots__ = ('filename', 'lineno', 'name', '_line', 'locals') +/* 5725 */ // ^ +/* 5726 */ // +/* 5727 */ +/* 5728 */ $currLineNo = Sk.currLineNo = 170; +/* 5729 */ $currColNo = Sk.currColNo = 4; +/* 5730 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5731 */ $loc.__slots__ = $scope457.$const463; +/* 5732 */ // +/* 5733 */ // line 172: +/* 5734 */ // def __init__(self, filename, lineno, name, *, lookup_line=True, +/* 5735 */ // ^ +/* 5736 */ // +/* 5737 */ +/* 5738 */ $currLineNo = Sk.currLineNo = 172; +/* 5739 */ $currColNo = Sk.currColNo = 4; +/* 5740 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5741 */ $scope464.co_name = new Sk.builtins['str']('__init__'); +/* 5742 */ $scope464.co_argcount = 4; +/* 5743 */ $scope464.co_kwonlyargcount = 3; +/* 5744 */ $scope464.$kwdefs = [Sk.builtin.bool.true$, Sk.builtin.none.none$, Sk.builtin.none.none$]; +/* 5745 */ $scope464.co_varnames = ['self', 'filename', 'lineno', 'name', 'lookup_line', 'locals', 'line']; +/* 5746 */ var $funcobj485 = new Sk.builtins['function']($scope464, $gbl); +/* 5747 */ $loc.__init__ = $funcobj485; +/* 5748 */ // +/* 5749 */ // line 183: +/* 5750 */ // def __eq__(self, other): +/* 5751 */ // ^ +/* 5752 */ // +/* 5753 */ +/* 5754 */ $currLineNo = Sk.currLineNo = 183; +/* 5755 */ $currColNo = Sk.currColNo = 4; +/* 5756 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5757 */ $scope486.co_name = new Sk.builtins['str']('__eq__'); +/* 5758 */ $scope486.co_varnames = ['self', 'other']; +/* 5759 */ var $funcobj533 = new Sk.builtins['function']($scope486, $gbl); +/* 5760 */ $loc.__eq__ = $funcobj533; +/* 5761 */ // +/* 5762 */ // line 193: +/* 5763 */ // def __getitem__(self, pos): +/* 5764 */ // ^ +/* 5765 */ // +/* 5766 */ +/* 5767 */ $currLineNo = Sk.currLineNo = 193; +/* 5768 */ $currColNo = Sk.currColNo = 4; +/* 5769 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5770 */ $scope534.co_name = new Sk.builtins['str']('__getitem__'); +/* 5771 */ $scope534.co_varnames = ['self', 'pos']; +/* 5772 */ var $funcobj550 = new Sk.builtins['function']($scope534, $gbl); +/* 5773 */ $loc.__getitem__ = $funcobj550; +/* 5774 */ // +/* 5775 */ // line 196: +/* 5776 */ // def __iter__(self): +/* 5777 */ // ^ +/* 5778 */ // +/* 5779 */ +/* 5780 */ $currLineNo = Sk.currLineNo = 196; +/* 5781 */ $currColNo = Sk.currColNo = 4; +/* 5782 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5783 */ $scope551.co_name = new Sk.builtins['str']('__iter__'); +/* 5784 */ $scope551.co_varnames = ['self']; +/* 5785 */ var $funcobj568 = new Sk.builtins['function']($scope551, $gbl); +/* 5786 */ $loc.__iter__ = $funcobj568; +/* 5787 */ // +/* 5788 */ // line 199: +/* 5789 */ // def __repr__(self): +/* 5790 */ // ^ +/* 5791 */ // +/* 5792 */ +/* 5793 */ $currLineNo = Sk.currLineNo = 199; +/* 5794 */ $currColNo = Sk.currColNo = 4; +/* 5795 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5796 */ $scope569.co_name = new Sk.builtins['str']('__repr__'); +/* 5797 */ $scope569.co_varnames = ['self']; +/* 5798 */ var $funcobj581 = new Sk.builtins['function']($scope569, $gbl); +/* 5799 */ $loc.__repr__ = $funcobj581; +/* 5800 */ // +/* 5801 */ // line 203: +/* 5802 */ // def __len__(self): +/* 5803 */ // ^ +/* 5804 */ // +/* 5805 */ +/* 5806 */ $currLineNo = Sk.currLineNo = 203; +/* 5807 */ $currColNo = Sk.currColNo = 4; +/* 5808 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5809 */ $scope582.co_name = new Sk.builtins['str']('__len__'); +/* 5810 */ $scope582.co_varnames = ['self']; +/* 5811 */ var $funcobj585 = new Sk.builtins['function']($scope582, $gbl); +/* 5812 */ $loc.__len__ = $funcobj585; +/* 5813 */ // +/* 5814 */ // line 206: +/* 5815 */ // @property +/* 5816 */ // ^ +/* 5817 */ // +/* 5818 */ +/* 5819 */ $currLineNo = Sk.currLineNo = 206; +/* 5820 */ $currColNo = Sk.currColNo = 4; +/* 5821 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 5822 */ var $loadname586 = $loc.property !== undefined ? $loc.property : Sk.misceval.loadname('property', $gbl);; +/* 5823 */ $scope587.co_name = new Sk.builtins['str']('line'); +/* 5824 */ $scope587.$decorators = [$loadname586]; +/* 5825 */ $scope587.co_varnames = ['self']; +/* 5826 */ $ret = Sk.misceval.callsimOrSuspendArray($scope587.$decorators[0], [new Sk.builtins['function']($scope587, $gbl)]); +/* 5827 */ if ($ret && $ret.$isSuspension) { +/* 5828 */ $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); +/* 5829 */ } +/* 5830 */ var $funcobj606 = $ret; +/* 5831 */ $loc.line = $funcobj606; +/* 5832 */ return; +/* 5833 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 5834 */ } +/* 5835 */ } catch (err) { +/* 5836 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 5837 */ Sk.execStart = Date.now(); +/* 5838 */ Sk.execPaused = 0 +/* 5839 */ } +/* 5840 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 5841 */ err = new Sk.builtin.ExternalError(err); +/* 5842 */ } +/* 5843 */ Sk.err = err; +/* 5844 */ err.traceback.push({ +/* 5845 */ lineno: $currLineNo, +/* 5846 */ colno: $currColNo, +/* 5847 */ filename: 'src/lib/traceback.py', +/* 5848 */ scope: 'FrameSummary' +/* 5849 */ }); +/* 5850 */ if ($exc.length > 0) { +/* 5851 */ $err = err; +/* 5852 */ $blk = $exc.pop(); +/* 5853 */ continue +/* 5854 */ } else { +/* 5855 */ throw err; +/* 5856 */ } +/* 5857 */ } +/* 5858 */ } +/* 5859 */ }).call(null, $cell); +/* 5860 */ }); +/* 5861 */ $scope457.$const458 = new Sk.builtin.str('filename'); +/* 5862 */ $scope457.$const459 = new Sk.builtin.str('lineno'); +/* 5863 */ $scope457.$const460 = new Sk.builtin.str('name'); +/* 5864 */ $scope457.$const461 = new Sk.builtin.str('_line'); +/* 5865 */ $scope457.$const462 = new Sk.builtin.str('locals'); +/* 5866 */ $scope457.$const463 = new Sk.builtin.tuple([$scope457.$const458, $scope457.$const459, $scope457.$const460, $scope457.$const461, $scope457.$const462]); +/* 5867 */ var $scope464 = (function $__init__465$(self, filename, lineno, name_$rn$, lookup_line, locals, line) { +/* 5868 */ var k, name_$rn$, v; /* locals */ +/* 5869 */ var filename, filename, k, k, line, line, lineno, lineno, locals, locals, locals, lookup_line, lookup_line, name_$rn$, name_$rn$, self, self, self, self, self, self, self, v, v, $res473, $jfalse474, $_dcompr475, $res473, $jfalse474, $_dcompr475, $lattr477, $res473, $jfalse474, $_dcompr475, $lattr477, $call478, $iter479, $res473, $jfalse474, $_dcompr475, $lattr477, $call478, $iter479, $next480, $items481, $loadgbl482, $res473, $jfalse474, $_dcompr475, $lattr477, $call478, $iter479, $next480, $items481, $loadgbl482, $call483; +/* 5870 */ var $wakeFromSuspension = function() { +/* 5871 */ var susp = $scope464.$wakingSuspension; +/* 5872 */ $scope464.$wakingSuspension = undefined; +/* 5873 */ $blk = susp.$blk; +/* 5874 */ $loc = susp.$loc; +/* 5875 */ $gbl = susp.$gbl; +/* 5876 */ $exc = susp.$exc; +/* 5877 */ $err = susp.$err; +/* 5878 */ $postfinally = susp.$postfinally; +/* 5879 */ $currLineNo = susp.$lineno; +/* 5880 */ $currColNo = susp.$colno; +/* 5881 */ Sk.lastYield = Date.now(); +/* 5882 */ filename = susp.$tmps.filename; +/* 5883 */ k = susp.$tmps.k; +/* 5884 */ line = susp.$tmps.line; +/* 5885 */ lineno = susp.$tmps.lineno; +/* 5886 */ locals = susp.$tmps.locals; +/* 5887 */ lookup_line = susp.$tmps.lookup_line; +/* 5888 */ name_$rn$ = susp.$tmps.name_$rn$; +/* 5889 */ self = susp.$tmps.self; +/* 5890 */ v = susp.$tmps.v; +/* 5891 */ $res473 = susp.$tmps.$res473; +/* 5892 */ $jfalse474 = susp.$tmps.$jfalse474; +/* 5893 */ $_dcompr475 = susp.$tmps.$_dcompr475; +/* 5894 */ $lattr477 = susp.$tmps.$lattr477; +/* 5895 */ $call478 = susp.$tmps.$call478; +/* 5896 */ $iter479 = susp.$tmps.$iter479; +/* 5897 */ $next480 = susp.$tmps.$next480; +/* 5898 */ $items481 = susp.$tmps.$items481; +/* 5899 */ $loadgbl482 = susp.$tmps.$loadgbl482; +/* 5900 */ $call483 = susp.$tmps.$call483; +/* 5901 */ try { +/* 5902 */ $ret = susp.child.resume(); +/* 5903 */ } catch (err) { +/* 5904 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 5905 */ Sk.execStart = Date.now(); +/* 5906 */ Sk.execPaused = 0 +/* 5907 */ } +/* 5908 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 5909 */ err = new Sk.builtin.ExternalError(err); +/* 5910 */ } +/* 5911 */ Sk.err = err; +/* 5912 */ err.traceback.push({ +/* 5913 */ lineno: $currLineNo, +/* 5914 */ colno: $currColNo, +/* 5915 */ filename: 'src/lib/traceback.py', +/* 5916 */ scope: '$scope464' +/* 5917 */ }); +/* 5918 */ if ($exc.length > 0) { +/* 5919 */ $err = err; +/* 5920 */ $blk = $exc.pop(); +/* 5921 */ } else { +/* 5922 */ throw err; +/* 5923 */ } +/* 5924 */ } +/* 5925 */ }; +/* 5926 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 5927 */ var susp = new Sk.misceval.Suspension(); +/* 5928 */ susp.child = $child; +/* 5929 */ susp.resume = function() { +/* 5930 */ $scope464.$wakingSuspension = susp; +/* 5931 */ return $scope464(); +/* 5932 */ }; +/* 5933 */ susp.data = susp.child.data; +/* 5934 */ susp.$blk = $blk; +/* 5935 */ susp.$loc = $loc; +/* 5936 */ susp.$gbl = $gbl; +/* 5937 */ susp.$exc = $exc; +/* 5938 */ susp.$err = $err; +/* 5939 */ susp.$postfinally = $postfinally; +/* 5940 */ susp.$filename = $filename; +/* 5941 */ susp.$lineno = $lineno; +/* 5942 */ susp.$colno = $colno; +/* 5943 */ susp.optional = susp.child.optional; +/* 5944 */ susp.$tmps = { +/* 5945 */ "filename": filename, +/* 5946 */ "k": k, +/* 5947 */ "line": line, +/* 5948 */ "lineno": lineno, +/* 5949 */ "locals": locals, +/* 5950 */ "lookup_line": lookup_line, +/* 5951 */ "name_$rn$": name_$rn$, +/* 5952 */ "self": self, +/* 5953 */ "v": v, +/* 5954 */ "$res473": $res473, +/* 5955 */ "$jfalse474": $jfalse474, +/* 5956 */ "$_dcompr475": $_dcompr475, +/* 5957 */ "$lattr477": $lattr477, +/* 5958 */ "$call478": $call478, +/* 5959 */ "$iter479": $iter479, +/* 5960 */ "$next480": $next480, +/* 5961 */ "$items481": $items481, +/* 5962 */ "$loadgbl482": $loadgbl482, +/* 5963 */ "$call483": $call483 +/* 5964 */ }; +/* 5965 */ return susp; +/* 5966 */ }; +/* 5967 */ var $blk = 0, +/* 5968 */ $exc = [], +/* 5969 */ $loc = {}, +/* 5970 */ $cell = {}, +/* 5971 */ $gbl = this, +/* 5972 */ $err = undefined, +/* 5973 */ $ret = undefined, +/* 5974 */ $postfinally = undefined, +/* 5975 */ $currLineNo = undefined, +/* 5976 */ $currColNo = undefined; +/* 5977 */ if (typeof Sk.execStart === 'undefined') { +/* 5978 */ Sk.execStart = Date.now(); +/* 5979 */ Sk.execPaused = 0 +/* 5980 */ } +/* 5981 */ if (typeof Sk.lastYield === 'undefined') { +/* 5982 */ Sk.lastYield = Date.now() +/* 5983 */ } +/* 5984 */ if ($scope464.$wakingSuspension !== undefined) { +/* 5985 */ $wakeFromSuspension(); +/* 5986 */ } else {} +/* 5987 */ $gbl.__class__ = this.FrameSummary; +/* 5988 */ while (true) { +/* 5989 */ try { +/* 5990 */ var $dateNow = Date.now(); +/* 5991 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 5992 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 5993 */ } +/* 5994 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 5995 */ var $susp = $saveSuspension({ +/* 5996 */ data: { +/* 5997 */ type: 'Sk.yield' +/* 5998 */ }, +/* 5999 */ resume: function() {} +/* 6000 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 6001 */ $susp.$blk = $blk; +/* 6002 */ $susp.optional = true; +/* 6003 */ return $susp; +/* 6004 */ } +/* 6005 */ switch ($blk) { +/* 6006 */ case 0: +/* 6007 */ /* --- codeobj entry --- */ if (self === undefined) { +/* 6008 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6009 */ } +/* 6010 */ if (filename === undefined) { +/* 6011 */ throw new Sk.builtin.UnboundLocalError('local variable \'filename\' referenced before assignment'); +/* 6012 */ } +/* 6013 */ if (lineno === undefined) { +/* 6014 */ throw new Sk.builtin.UnboundLocalError('local variable \'lineno\' referenced before assignment'); +/* 6015 */ } +/* 6016 */ if (name_$rn$ === undefined) { +/* 6017 */ throw new Sk.builtin.UnboundLocalError('local variable \'name_$rn$\' referenced before assignment'); +/* 6018 */ } +/* 6019 */ if (lookup_line === undefined) { +/* 6020 */ throw new Sk.builtin.UnboundLocalError('local variable \'lookup_line\' referenced before assignment'); +/* 6021 */ } +/* 6022 */ if (locals === undefined) { +/* 6023 */ throw new Sk.builtin.UnboundLocalError('local variable \'locals\' referenced before assignment'); +/* 6024 */ } +/* 6025 */ if (line === undefined) { +/* 6026 */ throw new Sk.builtin.UnboundLocalError('local variable \'line\' referenced before assignment'); +/* 6027 */ } +/* 6028 */ +/* 6029 */ // +/* 6030 */ // line 175: +/* 6031 */ // self.filename = filename +/* 6032 */ // ^ +/* 6033 */ // +/* 6034 */ +/* 6035 */ $currLineNo = Sk.currLineNo = 175; +/* 6036 */ $currColNo = Sk.currColNo = 8; +/* 6037 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6038 */ if (filename === undefined) { +/* 6039 */ throw new Sk.builtin.UnboundLocalError('local variable \'filename\' referenced before assignment'); +/* 6040 */ } +/* 6041 */ if (self === undefined) { +/* 6042 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6043 */ } +/* 6044 */ $ret = Sk.abstr.sattr(self, $scope464.$const466, filename, true); +/* 6045 */ $blk = 1; /* allowing case fallthrough */ +/* 6046 */ case 1: +/* 6047 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6048 */ return $saveSuspension($ret, 'src/lib/traceback.py', 175, 8); +/* 6049 */ } +/* 6050 */ // +/* 6051 */ // line 176: +/* 6052 */ // self.lineno = lineno +/* 6053 */ // ^ +/* 6054 */ // +/* 6055 */ +/* 6056 */ $currLineNo = Sk.currLineNo = 176; +/* 6057 */ $currColNo = Sk.currColNo = 8; +/* 6058 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6059 */ if (lineno === undefined) { +/* 6060 */ throw new Sk.builtin.UnboundLocalError('local variable \'lineno\' referenced before assignment'); +/* 6061 */ } +/* 6062 */ if (self === undefined) { +/* 6063 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6064 */ } +/* 6065 */ $ret = Sk.abstr.sattr(self, $scope464.$const467, lineno, true); +/* 6066 */ $blk = 2; /* allowing case fallthrough */ +/* 6067 */ case 2: +/* 6068 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6069 */ return $saveSuspension($ret, 'src/lib/traceback.py', 176, 8); +/* 6070 */ } +/* 6071 */ // +/* 6072 */ // line 177: +/* 6073 */ // self.name = name +/* 6074 */ // ^ +/* 6075 */ // +/* 6076 */ +/* 6077 */ $currLineNo = Sk.currLineNo = 177; +/* 6078 */ $currColNo = Sk.currColNo = 8; +/* 6079 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6080 */ if (name_$rn$ === undefined) { +/* 6081 */ throw new Sk.builtin.UnboundLocalError('local variable \'name_$rn$\' referenced before assignment'); +/* 6082 */ } +/* 6083 */ if (self === undefined) { +/* 6084 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6085 */ } +/* 6086 */ $ret = Sk.abstr.sattr(self, $scope464.$const468, name_$rn$, true); +/* 6087 */ $blk = 3; /* allowing case fallthrough */ +/* 6088 */ case 3: +/* 6089 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6090 */ return $saveSuspension($ret, 'src/lib/traceback.py', 177, 8); +/* 6091 */ } +/* 6092 */ // +/* 6093 */ // line 178: +/* 6094 */ // self._line = line +/* 6095 */ // ^ +/* 6096 */ // +/* 6097 */ +/* 6098 */ $currLineNo = Sk.currLineNo = 178; +/* 6099 */ $currColNo = Sk.currColNo = 8; +/* 6100 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6101 */ if (line === undefined) { +/* 6102 */ throw new Sk.builtin.UnboundLocalError('local variable \'line\' referenced before assignment'); +/* 6103 */ } +/* 6104 */ if (self === undefined) { +/* 6105 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6106 */ } +/* 6107 */ $ret = Sk.abstr.sattr(self, $scope464.$const469, line, true); +/* 6108 */ $blk = 4; /* allowing case fallthrough */ +/* 6109 */ case 4: +/* 6110 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6111 */ return $saveSuspension($ret, 'src/lib/traceback.py', 178, 8); +/* 6112 */ } +/* 6113 */ // +/* 6114 */ // line 179: +/* 6115 */ // if lookup_line: +/* 6116 */ // ^ +/* 6117 */ // +/* 6118 */ +/* 6119 */ $currLineNo = Sk.currLineNo = 179; +/* 6120 */ $currColNo = Sk.currColNo = 8; +/* 6121 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6122 */ if (lookup_line === undefined) { +/* 6123 */ throw new Sk.builtin.UnboundLocalError('local variable \'lookup_line\' referenced before assignment'); +/* 6124 */ } +/* 6125 */ var $jfalse470 = (lookup_line === false || !Sk.misceval.isTrue(lookup_line)); +/* 6126 */ if ($jfalse470) { +/* 6127 */ /*test failed */ +/* 6128 */ $blk = 5; +/* 6129 */ continue; +/* 6130 */ } +/* 6131 */ // +/* 6132 */ // line 180: +/* 6133 */ // self.line +/* 6134 */ // ^ +/* 6135 */ // +/* 6136 */ +/* 6137 */ $currLineNo = Sk.currLineNo = 180; +/* 6138 */ $currColNo = Sk.currColNo = 12; +/* 6139 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6140 */ if (self === undefined) { +/* 6141 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6142 */ } +/* 6143 */ $ret = Sk.abstr.gattr(self, $scope464.$const471, true); +/* 6144 */ $blk = 6; /* allowing case fallthrough */ +/* 6145 */ case 6: +/* 6146 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6147 */ return $saveSuspension($ret, 'src/lib/traceback.py', 180, 12); +/* 6148 */ } +/* 6149 */ var $lattr472 = $ret; +/* 6150 */ $blk = 5; /* allowing case fallthrough */ +/* 6151 */ case 5: +/* 6152 */ /* --- end of if --- */ +/* 6153 */ // +/* 6154 */ // line 181: +/* 6155 */ // self.locals = {k: repr(v) for k, v in locals.items()} if locals else None +/* 6156 */ // ^ +/* 6157 */ // +/* 6158 */ +/* 6159 */ $currLineNo = Sk.currLineNo = 181; +/* 6160 */ $currColNo = Sk.currColNo = 8; +/* 6161 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6162 */ var $res473 = null; +/* 6163 */ if (locals === undefined) { +/* 6164 */ throw new Sk.builtin.UnboundLocalError('local variable \'locals\' referenced before assignment'); +/* 6165 */ } +/* 6166 */ var $jfalse474 = (locals === false || !Sk.misceval.isTrue(locals)); +/* 6167 */ if ($jfalse474) { +/* 6168 */ /*test failed */ +/* 6169 */ $blk = 7; +/* 6170 */ continue; +/* 6171 */ } +/* 6172 */ var $_dcompr475 = new Sk.builtins.dict([]); +/* 6173 */ if (locals === undefined) { +/* 6174 */ throw new Sk.builtin.UnboundLocalError('local variable \'locals\' referenced before assignment'); +/* 6175 */ } +/* 6176 */ $ret = Sk.abstr.gattr(locals, $scope464.$const476, true); +/* 6177 */ $blk = 12; /* allowing case fallthrough */ +/* 6178 */ case 12: +/* 6179 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6180 */ return $saveSuspension($ret, 'src/lib/traceback.py', 181, 46); +/* 6181 */ } +/* 6182 */ var $lattr477 = $ret; +/* 6183 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr477); +/* 6184 */ $blk = 13; /* allowing case fallthrough */ +/* 6185 */ case 13: +/* 6186 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6187 */ return $saveSuspension($ret, 'src/lib/traceback.py', 181, 46); +/* 6188 */ } +/* 6189 */ var $call478 = $ret; +/* 6190 */ // +/* 6191 */ // line 181: +/* 6192 */ // self.locals = {k: repr(v) for k, v in locals.items()} if locals else None +/* 6193 */ // ^ +/* 6194 */ // +/* 6195 */ +/* 6196 */ $currLineNo = Sk.currLineNo = 181; +/* 6197 */ $currColNo = Sk.currColNo = 46; +/* 6198 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6199 */ var $iter479 = Sk.abstr.iter($call478); +/* 6200 */ $blk = 9; /* allowing case fallthrough */ +/* 6201 */ case 9: +/* 6202 */ /* --- dict comp start --- */ $ret = Sk.abstr.iternext($iter479, true); +/* 6203 */ $blk = 14; /* allowing case fallthrough */ +/* 6204 */ case 14: +/* 6205 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6206 */ return $saveSuspension($ret, 'src/lib/traceback.py', 181, 22); +/* 6207 */ } +/* 6208 */ var $next480 = $ret; +/* 6209 */ if ($next480 === undefined) { +/* 6210 */ $blk = 11; +/* 6211 */ continue; +/* 6212 */ } +/* 6213 */ var $items481 = Sk.abstr.sequenceUnpack($next480, 2); +/* 6214 */ k = $items481[0]; +/* 6215 */ v = $items481[1]; +/* 6216 */ var $loadgbl482 = Sk.misceval.loadname('repr', $gbl); +/* 6217 */ if (v === undefined) { +/* 6218 */ throw new Sk.builtin.UnboundLocalError('local variable \'v\' referenced before assignment'); +/* 6219 */ } +/* 6220 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl482, [v]); +/* 6221 */ $blk = 15; /* allowing case fallthrough */ +/* 6222 */ case 15: +/* 6223 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6224 */ return $saveSuspension($ret, 'src/lib/traceback.py', 181, 26); +/* 6225 */ } +/* 6226 */ var $call483 = $ret; +/* 6227 */ // +/* 6228 */ // line 181: +/* 6229 */ // self.locals = {k: repr(v) for k, v in locals.items()} if locals else None +/* 6230 */ // ^ +/* 6231 */ // +/* 6232 */ +/* 6233 */ $currLineNo = Sk.currLineNo = 181; +/* 6234 */ $currColNo = Sk.currColNo = 26; +/* 6235 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6236 */ if (k === undefined) { +/* 6237 */ throw new Sk.builtin.UnboundLocalError('local variable \'k\' referenced before assignment'); +/* 6238 */ } +/* 6239 */ $_dcompr475.mp$ass_subscript(k, $call483); +/* 6240 */ $blk = 10; /* allowing case fallthrough */ +/* 6241 */ case 10: +/* 6242 */ /* --- dict comp skip --- */ $blk = 9; /* jump */ +/* 6243 */ continue; +/* 6244 */ case 7: +/* 6245 */ /* --- next of ifexp --- */ $res473 = Sk.builtin.none.none$; +/* 6246 */ $blk = 8; /* allowing case fallthrough */ +/* 6247 */ case 8: +/* 6248 */ /* --- end of ifexp --- */ if (self === undefined) { +/* 6249 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6250 */ } +/* 6251 */ $ret = Sk.abstr.sattr(self, $scope464.$const484, $res473, true); +/* 6252 */ $blk = 16; /* allowing case fallthrough */ +/* 6253 */ case 16: +/* 6254 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6255 */ return $saveSuspension($ret, 'src/lib/traceback.py', 181, 8); +/* 6256 */ } +/* 6257 */ return Sk.builtin.none.none$; +/* 6258 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 6259 */ case 11: +/* 6260 */ /* --- dict comp anchor --- */ $res473 = $_dcompr475; +/* 6261 */ $blk = 8; /* jump */ +/* 6262 */ continue; +/* 6263 */ } +/* 6264 */ } catch (err) { +/* 6265 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 6266 */ Sk.execStart = Date.now(); +/* 6267 */ Sk.execPaused = 0 +/* 6268 */ } +/* 6269 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 6270 */ err = new Sk.builtin.ExternalError(err); +/* 6271 */ } +/* 6272 */ Sk.err = err; +/* 6273 */ err.traceback.push({ +/* 6274 */ lineno: $currLineNo, +/* 6275 */ colno: $currColNo, +/* 6276 */ filename: 'src/lib/traceback.py', +/* 6277 */ scope: '__init__' +/* 6278 */ }); +/* 6279 */ if ($exc.length > 0) { +/* 6280 */ $err = err; +/* 6281 */ $blk = $exc.pop(); +/* 6282 */ continue +/* 6283 */ } else { +/* 6284 */ throw err; +/* 6285 */ } +/* 6286 */ } +/* 6287 */ } +/* 6288 */ }); +/* 6289 */ $scope464.$const466 = new Sk.builtin.str('filename'); +/* 6290 */ $scope464.$const467 = new Sk.builtin.str('lineno'); +/* 6291 */ $scope464.$const468 = new Sk.builtin.str('name_$rn$'); +/* 6292 */ $scope464.$const469 = new Sk.builtin.str('_line'); +/* 6293 */ $scope464.$const471 = new Sk.builtin.str('line'); +/* 6294 */ $scope464.$const476 = new Sk.builtin.str('items'); +/* 6295 */ $scope464.$const484 = new Sk.builtin.str('locals'); +/* 6296 */ var $scope486 = (function $__eq__487$(self, other) { +/* 6297 */ var other, other, other, other, other, other, other, other, self, self, self, self, self, self, self, self, self, $loadgbl488, $loadgbl489, $lattr493, $compareres494, $lattr493, $compareres494, $lattr495, $lattr493, $compareres494, $lattr495, $jfalse496, $boolopsucc497, $jfalse498, $lattr493, $compareres494, $lattr495, $jfalse496, $boolopsucc497, $jfalse498, $lattr500, $compareres501, $lattr493, $compareres494, $lattr495, $jfalse496, $boolopsucc497, $jfalse498, $lattr500, $compareres501, $lattr502, $lattr493, $compareres494, $lattr495, $jfalse496, $boolopsucc497, $jfalse498, $lattr500, $compareres501, $lattr502, $jfalse503, $jfalse504, $lattr493, $compareres494, $lattr495, $jfalse496, $boolopsucc497, $jfalse498, $lattr500, $compareres501, $lattr502, $jfalse503, $jfalse504, $lattr506, $compareres507, $lattr493, $compareres494, $lattr495, $jfalse496, $boolopsucc497, $jfalse498, $lattr500, $compareres501, $lattr502, $jfalse503, $jfalse504, $lattr506, $compareres507, $lattr508, $lattr493, $compareres494, $lattr495, $jfalse496, $boolopsucc497, $jfalse498, $lattr500, $compareres501, $lattr502, $jfalse503, $jfalse504, $lattr506, $compareres507, $lattr508, $jfalse509, $jfalse510, $lattr493, $compareres494, $lattr495, $jfalse496, $boolopsucc497, $jfalse498, $lattr500, $compareres501, $lattr502, $jfalse503, $jfalse504, $lattr506, $compareres507, $lattr508, $jfalse509, $jfalse510, $lattr512, $compareres513, $lattr493, $compareres494, $lattr495, $jfalse496, $boolopsucc497, $jfalse498, $lattr500, $compareres501, $lattr502, $jfalse503, $jfalse504, $lattr506, $compareres507, $lattr508, $jfalse509, $jfalse510, $lattr512, $compareres513, $lattr514, $loadgbl517, $loadgbl518, $lattr521, $lattr521, $lattr522, $lattr521, $lattr522, $lattr523, $lattr521, $lattr522, $lattr523, $lattr525, $elem526, $elem527, $elem528, $elem529, $loadtuple530, $compareres531; +/* 6298 */ var $wakeFromSuspension = function() { +/* 6299 */ var susp = $scope486.$wakingSuspension; +/* 6300 */ $scope486.$wakingSuspension = undefined; +/* 6301 */ $blk = susp.$blk; +/* 6302 */ $loc = susp.$loc; +/* 6303 */ $gbl = susp.$gbl; +/* 6304 */ $exc = susp.$exc; +/* 6305 */ $err = susp.$err; +/* 6306 */ $postfinally = susp.$postfinally; +/* 6307 */ $currLineNo = susp.$lineno; +/* 6308 */ $currColNo = susp.$colno; +/* 6309 */ Sk.lastYield = Date.now(); +/* 6310 */ other = susp.$tmps.other; +/* 6311 */ self = susp.$tmps.self; +/* 6312 */ $loadgbl488 = susp.$tmps.$loadgbl488; +/* 6313 */ $loadgbl489 = susp.$tmps.$loadgbl489; +/* 6314 */ $lattr493 = susp.$tmps.$lattr493; +/* 6315 */ $compareres494 = susp.$tmps.$compareres494; +/* 6316 */ $lattr495 = susp.$tmps.$lattr495; +/* 6317 */ $jfalse496 = susp.$tmps.$jfalse496; +/* 6318 */ $boolopsucc497 = susp.$tmps.$boolopsucc497; +/* 6319 */ $jfalse498 = susp.$tmps.$jfalse498; +/* 6320 */ $lattr500 = susp.$tmps.$lattr500; +/* 6321 */ $compareres501 = susp.$tmps.$compareres501; +/* 6322 */ $lattr502 = susp.$tmps.$lattr502; +/* 6323 */ $jfalse503 = susp.$tmps.$jfalse503; +/* 6324 */ $jfalse504 = susp.$tmps.$jfalse504; +/* 6325 */ $lattr506 = susp.$tmps.$lattr506; +/* 6326 */ $compareres507 = susp.$tmps.$compareres507; +/* 6327 */ $lattr508 = susp.$tmps.$lattr508; +/* 6328 */ $jfalse509 = susp.$tmps.$jfalse509; +/* 6329 */ $jfalse510 = susp.$tmps.$jfalse510; +/* 6330 */ $lattr512 = susp.$tmps.$lattr512; +/* 6331 */ $compareres513 = susp.$tmps.$compareres513; +/* 6332 */ $lattr514 = susp.$tmps.$lattr514; +/* 6333 */ $loadgbl517 = susp.$tmps.$loadgbl517; +/* 6334 */ $loadgbl518 = susp.$tmps.$loadgbl518; +/* 6335 */ $lattr521 = susp.$tmps.$lattr521; +/* 6336 */ $lattr522 = susp.$tmps.$lattr522; +/* 6337 */ $lattr523 = susp.$tmps.$lattr523; +/* 6338 */ $lattr525 = susp.$tmps.$lattr525; +/* 6339 */ $elem526 = susp.$tmps.$elem526; +/* 6340 */ $elem527 = susp.$tmps.$elem527; +/* 6341 */ $elem528 = susp.$tmps.$elem528; +/* 6342 */ $elem529 = susp.$tmps.$elem529; +/* 6343 */ $loadtuple530 = susp.$tmps.$loadtuple530; +/* 6344 */ $compareres531 = susp.$tmps.$compareres531; +/* 6345 */ try { +/* 6346 */ $ret = susp.child.resume(); +/* 6347 */ } catch (err) { +/* 6348 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 6349 */ Sk.execStart = Date.now(); +/* 6350 */ Sk.execPaused = 0 +/* 6351 */ } +/* 6352 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 6353 */ err = new Sk.builtin.ExternalError(err); +/* 6354 */ } +/* 6355 */ Sk.err = err; +/* 6356 */ err.traceback.push({ +/* 6357 */ lineno: $currLineNo, +/* 6358 */ colno: $currColNo, +/* 6359 */ filename: 'src/lib/traceback.py', +/* 6360 */ scope: '$scope486' +/* 6361 */ }); +/* 6362 */ if ($exc.length > 0) { +/* 6363 */ $err = err; +/* 6364 */ $blk = $exc.pop(); +/* 6365 */ } else { +/* 6366 */ throw err; +/* 6367 */ } +/* 6368 */ } +/* 6369 */ }; +/* 6370 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 6371 */ var susp = new Sk.misceval.Suspension(); +/* 6372 */ susp.child = $child; +/* 6373 */ susp.resume = function() { +/* 6374 */ $scope486.$wakingSuspension = susp; +/* 6375 */ return $scope486(); +/* 6376 */ }; +/* 6377 */ susp.data = susp.child.data; +/* 6378 */ susp.$blk = $blk; +/* 6379 */ susp.$loc = $loc; +/* 6380 */ susp.$gbl = $gbl; +/* 6381 */ susp.$exc = $exc; +/* 6382 */ susp.$err = $err; +/* 6383 */ susp.$postfinally = $postfinally; +/* 6384 */ susp.$filename = $filename; +/* 6385 */ susp.$lineno = $lineno; +/* 6386 */ susp.$colno = $colno; +/* 6387 */ susp.optional = susp.child.optional; +/* 6388 */ susp.$tmps = { +/* 6389 */ "other": other, +/* 6390 */ "self": self, +/* 6391 */ "$loadgbl488": $loadgbl488, +/* 6392 */ "$loadgbl489": $loadgbl489, +/* 6393 */ "$lattr493": $lattr493, +/* 6394 */ "$compareres494": $compareres494, +/* 6395 */ "$lattr495": $lattr495, +/* 6396 */ "$jfalse496": $jfalse496, +/* 6397 */ "$boolopsucc497": $boolopsucc497, +/* 6398 */ "$jfalse498": $jfalse498, +/* 6399 */ "$lattr500": $lattr500, +/* 6400 */ "$compareres501": $compareres501, +/* 6401 */ "$lattr502": $lattr502, +/* 6402 */ "$jfalse503": $jfalse503, +/* 6403 */ "$jfalse504": $jfalse504, +/* 6404 */ "$lattr506": $lattr506, +/* 6405 */ "$compareres507": $compareres507, +/* 6406 */ "$lattr508": $lattr508, +/* 6407 */ "$jfalse509": $jfalse509, +/* 6408 */ "$jfalse510": $jfalse510, +/* 6409 */ "$lattr512": $lattr512, +/* 6410 */ "$compareres513": $compareres513, +/* 6411 */ "$lattr514": $lattr514, +/* 6412 */ "$loadgbl517": $loadgbl517, +/* 6413 */ "$loadgbl518": $loadgbl518, +/* 6414 */ "$lattr521": $lattr521, +/* 6415 */ "$lattr522": $lattr522, +/* 6416 */ "$lattr523": $lattr523, +/* 6417 */ "$lattr525": $lattr525, +/* 6418 */ "$elem526": $elem526, +/* 6419 */ "$elem527": $elem527, +/* 6420 */ "$elem528": $elem528, +/* 6421 */ "$elem529": $elem529, +/* 6422 */ "$loadtuple530": $loadtuple530, +/* 6423 */ "$compareres531": $compareres531 +/* 6424 */ }; +/* 6425 */ return susp; +/* 6426 */ }; +/* 6427 */ var $blk = 0, +/* 6428 */ $exc = [], +/* 6429 */ $loc = {}, +/* 6430 */ $cell = {}, +/* 6431 */ $gbl = this, +/* 6432 */ $err = undefined, +/* 6433 */ $ret = undefined, +/* 6434 */ $postfinally = undefined, +/* 6435 */ $currLineNo = undefined, +/* 6436 */ $currColNo = undefined; +/* 6437 */ if (typeof Sk.execStart === 'undefined') { +/* 6438 */ Sk.execStart = Date.now(); +/* 6439 */ Sk.execPaused = 0 +/* 6440 */ } +/* 6441 */ if (typeof Sk.lastYield === 'undefined') { +/* 6442 */ Sk.lastYield = Date.now() +/* 6443 */ } +/* 6444 */ if ($scope486.$wakingSuspension !== undefined) { +/* 6445 */ $wakeFromSuspension(); +/* 6446 */ } else {} +/* 6447 */ $gbl.__class__ = this.FrameSummary; +/* 6448 */ while (true) { +/* 6449 */ try { +/* 6450 */ var $dateNow = Date.now(); +/* 6451 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 6452 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 6453 */ } +/* 6454 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 6455 */ var $susp = $saveSuspension({ +/* 6456 */ data: { +/* 6457 */ type: 'Sk.yield' +/* 6458 */ }, +/* 6459 */ resume: function() {} +/* 6460 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 6461 */ $susp.$blk = $blk; +/* 6462 */ $susp.optional = true; +/* 6463 */ return $susp; +/* 6464 */ } +/* 6465 */ switch ($blk) { +/* 6466 */ case 0: +/* 6467 */ /* --- codeobj entry --- */ if (self === undefined) { +/* 6468 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6469 */ } +/* 6470 */ if (other === undefined) { +/* 6471 */ throw new Sk.builtin.UnboundLocalError('local variable \'other\' referenced before assignment'); +/* 6472 */ } +/* 6473 */ +/* 6474 */ // +/* 6475 */ // line 184: +/* 6476 */ // if isinstance(other, FrameSummary): +/* 6477 */ // ^ +/* 6478 */ // +/* 6479 */ +/* 6480 */ $currLineNo = Sk.currLineNo = 184; +/* 6481 */ $currColNo = Sk.currColNo = 8; +/* 6482 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6483 */ var $loadgbl488 = Sk.misceval.loadname('isinstance', $gbl); +/* 6484 */ if (other === undefined) { +/* 6485 */ throw new Sk.builtin.UnboundLocalError('local variable \'other\' referenced before assignment'); +/* 6486 */ } +/* 6487 */ var $loadgbl489 = Sk.misceval.loadname('FrameSummary', $gbl); +/* 6488 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl488, [other, $loadgbl489]); +/* 6489 */ $blk = 2; /* allowing case fallthrough */ +/* 6490 */ case 2: +/* 6491 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6492 */ return $saveSuspension($ret, 'src/lib/traceback.py', 184, 11); +/* 6493 */ } +/* 6494 */ var $call490 = $ret; +/* 6495 */ // +/* 6496 */ // line 184: +/* 6497 */ // if isinstance(other, FrameSummary): +/* 6498 */ // ^ +/* 6499 */ // +/* 6500 */ +/* 6501 */ $currLineNo = Sk.currLineNo = 184; +/* 6502 */ $currColNo = Sk.currColNo = 11; +/* 6503 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6504 */ var $jfalse491 = ($call490 === false || !Sk.misceval.isTrue($call490)); +/* 6505 */ if ($jfalse491) { +/* 6506 */ /*test failed */ +/* 6507 */ $blk = 1; +/* 6508 */ continue; +/* 6509 */ } +/* 6510 */ // +/* 6511 */ // line 185: +/* 6512 */ // return (self.filename == other.filename and +/* 6513 */ // ^ +/* 6514 */ // +/* 6515 */ +/* 6516 */ $currLineNo = Sk.currLineNo = 185; +/* 6517 */ $currColNo = Sk.currColNo = 12; +/* 6518 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6519 */ if (self === undefined) { +/* 6520 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6521 */ } +/* 6522 */ $ret = Sk.abstr.gattr(self, $scope486.$const492, true); +/* 6523 */ $blk = 4; /* allowing case fallthrough */ +/* 6524 */ case 4: +/* 6525 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6526 */ return $saveSuspension($ret, 'src/lib/traceback.py', 185, 20); +/* 6527 */ } +/* 6528 */ var $lattr493 = $ret; +/* 6529 */ var $compareres494 = null; +/* 6530 */ if (other === undefined) { +/* 6531 */ throw new Sk.builtin.UnboundLocalError('local variable \'other\' referenced before assignment'); +/* 6532 */ } +/* 6533 */ $ret = Sk.abstr.gattr(other, $scope486.$const492, true); +/* 6534 */ $blk = 6; /* allowing case fallthrough */ +/* 6535 */ case 6: +/* 6536 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6537 */ return $saveSuspension($ret, 'src/lib/traceback.py', 185, 37); +/* 6538 */ } +/* 6539 */ var $lattr495 = $ret; +/* 6540 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($lattr493, $lattr495, 'Eq', true)); +/* 6541 */ $blk = 7; /* allowing case fallthrough */ +/* 6542 */ case 7: +/* 6543 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6544 */ return $saveSuspension($ret, 'src/lib/traceback.py', 185, 20); +/* 6545 */ } +/* 6546 */ $compareres494 = $ret; +/* 6547 */ var $jfalse496 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 6548 */ if ($jfalse496) { +/* 6549 */ /*test failed */ +/* 6550 */ $blk = 5; +/* 6551 */ continue; +/* 6552 */ } +/* 6553 */ $blk = 5; /* allowing case fallthrough */ +/* 6554 */ case 5: +/* 6555 */ /* --- done --- */ var $boolopsucc497 = $compareres494; +/* 6556 */ $boolopsucc497 = $compareres494; +/* 6557 */ var $jfalse498 = ($compareres494 === false || !Sk.misceval.isTrue($compareres494)); +/* 6558 */ if ($jfalse498) { +/* 6559 */ /*test failed */ +/* 6560 */ $blk = 3; +/* 6561 */ continue; +/* 6562 */ } +/* 6563 */ if (self === undefined) { +/* 6564 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6565 */ } +/* 6566 */ $ret = Sk.abstr.gattr(self, $scope486.$const499, true); +/* 6567 */ $blk = 8; /* allowing case fallthrough */ +/* 6568 */ case 8: +/* 6569 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6570 */ return $saveSuspension($ret, 'src/lib/traceback.py', 186, 20); +/* 6571 */ } +/* 6572 */ var $lattr500 = $ret; +/* 6573 */ var $compareres501 = null; +/* 6574 */ if (other === undefined) { +/* 6575 */ throw new Sk.builtin.UnboundLocalError('local variable \'other\' referenced before assignment'); +/* 6576 */ } +/* 6577 */ $ret = Sk.abstr.gattr(other, $scope486.$const499, true); +/* 6578 */ $blk = 10; /* allowing case fallthrough */ +/* 6579 */ case 10: +/* 6580 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6581 */ return $saveSuspension($ret, 'src/lib/traceback.py', 186, 35); +/* 6582 */ } +/* 6583 */ var $lattr502 = $ret; +/* 6584 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($lattr500, $lattr502, 'Eq', true)); +/* 6585 */ $blk = 11; /* allowing case fallthrough */ +/* 6586 */ case 11: +/* 6587 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6588 */ return $saveSuspension($ret, 'src/lib/traceback.py', 186, 20); +/* 6589 */ } +/* 6590 */ $compareres501 = $ret; +/* 6591 */ var $jfalse503 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 6592 */ if ($jfalse503) { +/* 6593 */ /*test failed */ +/* 6594 */ $blk = 9; +/* 6595 */ continue; +/* 6596 */ } +/* 6597 */ $blk = 9; /* allowing case fallthrough */ +/* 6598 */ case 9: +/* 6599 */ /* --- done --- */ $boolopsucc497 = $compareres501; +/* 6600 */ var $jfalse504 = ($compareres501 === false || !Sk.misceval.isTrue($compareres501)); +/* 6601 */ if ($jfalse504) { +/* 6602 */ /*test failed */ +/* 6603 */ $blk = 3; +/* 6604 */ continue; +/* 6605 */ } +/* 6606 */ if (self === undefined) { +/* 6607 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6608 */ } +/* 6609 */ $ret = Sk.abstr.gattr(self, $scope486.$const505, true); +/* 6610 */ $blk = 12; /* allowing case fallthrough */ +/* 6611 */ case 12: +/* 6612 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6613 */ return $saveSuspension($ret, 'src/lib/traceback.py', 187, 20); +/* 6614 */ } +/* 6615 */ var $lattr506 = $ret; +/* 6616 */ var $compareres507 = null; +/* 6617 */ if (other === undefined) { +/* 6618 */ throw new Sk.builtin.UnboundLocalError('local variable \'other\' referenced before assignment'); +/* 6619 */ } +/* 6620 */ $ret = Sk.abstr.gattr(other, $scope486.$const505, true); +/* 6621 */ $blk = 14; /* allowing case fallthrough */ +/* 6622 */ case 14: +/* 6623 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6624 */ return $saveSuspension($ret, 'src/lib/traceback.py', 187, 33); +/* 6625 */ } +/* 6626 */ var $lattr508 = $ret; +/* 6627 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($lattr506, $lattr508, 'Eq', true)); +/* 6628 */ $blk = 15; /* allowing case fallthrough */ +/* 6629 */ case 15: +/* 6630 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6631 */ return $saveSuspension($ret, 'src/lib/traceback.py', 187, 20); +/* 6632 */ } +/* 6633 */ $compareres507 = $ret; +/* 6634 */ var $jfalse509 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 6635 */ if ($jfalse509) { +/* 6636 */ /*test failed */ +/* 6637 */ $blk = 13; +/* 6638 */ continue; +/* 6639 */ } +/* 6640 */ $blk = 13; /* allowing case fallthrough */ +/* 6641 */ case 13: +/* 6642 */ /* --- done --- */ $boolopsucc497 = $compareres507; +/* 6643 */ var $jfalse510 = ($compareres507 === false || !Sk.misceval.isTrue($compareres507)); +/* 6644 */ if ($jfalse510) { +/* 6645 */ /*test failed */ +/* 6646 */ $blk = 3; +/* 6647 */ continue; +/* 6648 */ } +/* 6649 */ if (self === undefined) { +/* 6650 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6651 */ } +/* 6652 */ $ret = Sk.abstr.gattr(self, $scope486.$const511, true); +/* 6653 */ $blk = 16; /* allowing case fallthrough */ +/* 6654 */ case 16: +/* 6655 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6656 */ return $saveSuspension($ret, 'src/lib/traceback.py', 188, 20); +/* 6657 */ } +/* 6658 */ var $lattr512 = $ret; +/* 6659 */ var $compareres513 = null; +/* 6660 */ if (other === undefined) { +/* 6661 */ throw new Sk.builtin.UnboundLocalError('local variable \'other\' referenced before assignment'); +/* 6662 */ } +/* 6663 */ $ret = Sk.abstr.gattr(other, $scope486.$const511, true); +/* 6664 */ $blk = 18; /* allowing case fallthrough */ +/* 6665 */ case 18: +/* 6666 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6667 */ return $saveSuspension($ret, 'src/lib/traceback.py', 188, 35); +/* 6668 */ } +/* 6669 */ var $lattr514 = $ret; +/* 6670 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($lattr512, $lattr514, 'Eq', true)); +/* 6671 */ $blk = 19; /* allowing case fallthrough */ +/* 6672 */ case 19: +/* 6673 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6674 */ return $saveSuspension($ret, 'src/lib/traceback.py', 188, 20); +/* 6675 */ } +/* 6676 */ $compareres513 = $ret; +/* 6677 */ var $jfalse515 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 6678 */ if ($jfalse515) { +/* 6679 */ /*test failed */ +/* 6680 */ $blk = 17; +/* 6681 */ continue; +/* 6682 */ } +/* 6683 */ $blk = 17; /* allowing case fallthrough */ +/* 6684 */ case 17: +/* 6685 */ /* --- done --- */ $boolopsucc497 = $compareres513; +/* 6686 */ var $jfalse516 = ($compareres513 === false || !Sk.misceval.isTrue($compareres513)); +/* 6687 */ if ($jfalse516) { +/* 6688 */ /*test failed */ +/* 6689 */ $blk = 3; +/* 6690 */ continue; +/* 6691 */ } +/* 6692 */ $blk = 3; /* allowing case fallthrough */ +/* 6693 */ case 3: +/* 6694 */ /* --- end of boolop --- */ return $boolopsucc497; +/* 6695 */ $blk = 1; /* allowing case fallthrough */ +/* 6696 */ case 1: +/* 6697 */ /* --- end of if --- */ +/* 6698 */ // +/* 6699 */ // line 189: +/* 6700 */ // if isinstance(other, tuple): +/* 6701 */ // ^ +/* 6702 */ // +/* 6703 */ +/* 6704 */ $currLineNo = Sk.currLineNo = 189; +/* 6705 */ $currColNo = Sk.currColNo = 8; +/* 6706 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6707 */ var $loadgbl517 = Sk.misceval.loadname('isinstance', $gbl); +/* 6708 */ if (other === undefined) { +/* 6709 */ throw new Sk.builtin.UnboundLocalError('local variable \'other\' referenced before assignment'); +/* 6710 */ } +/* 6711 */ var $loadgbl518 = Sk.misceval.loadname('tuple', $gbl); +/* 6712 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl517, [other, $loadgbl518]); +/* 6713 */ $blk = 21; /* allowing case fallthrough */ +/* 6714 */ case 21: +/* 6715 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6716 */ return $saveSuspension($ret, 'src/lib/traceback.py', 189, 11); +/* 6717 */ } +/* 6718 */ var $call519 = $ret; +/* 6719 */ // +/* 6720 */ // line 189: +/* 6721 */ // if isinstance(other, tuple): +/* 6722 */ // ^ +/* 6723 */ // +/* 6724 */ +/* 6725 */ $currLineNo = Sk.currLineNo = 189; +/* 6726 */ $currColNo = Sk.currColNo = 11; +/* 6727 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6728 */ var $jfalse520 = ($call519 === false || !Sk.misceval.isTrue($call519)); +/* 6729 */ if ($jfalse520) { +/* 6730 */ /*test failed */ +/* 6731 */ $blk = 20; +/* 6732 */ continue; +/* 6733 */ } +/* 6734 */ // +/* 6735 */ // line 190: +/* 6736 */ // return (self.filename, self.lineno, self.name, self.line) == other +/* 6737 */ // ^ +/* 6738 */ // +/* 6739 */ +/* 6740 */ $currLineNo = Sk.currLineNo = 190; +/* 6741 */ $currColNo = Sk.currColNo = 12; +/* 6742 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6743 */ if (self === undefined) { +/* 6744 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6745 */ } +/* 6746 */ $ret = Sk.abstr.gattr(self, $scope486.$const492, true); +/* 6747 */ $blk = 22; /* allowing case fallthrough */ +/* 6748 */ case 22: +/* 6749 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6750 */ return $saveSuspension($ret, 'src/lib/traceback.py', 190, 20); +/* 6751 */ } +/* 6752 */ var $lattr521 = $ret; +/* 6753 */ if (self === undefined) { +/* 6754 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6755 */ } +/* 6756 */ $ret = Sk.abstr.gattr(self, $scope486.$const499, true); +/* 6757 */ $blk = 23; /* allowing case fallthrough */ +/* 6758 */ case 23: +/* 6759 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6760 */ return $saveSuspension($ret, 'src/lib/traceback.py', 190, 35); +/* 6761 */ } +/* 6762 */ var $lattr522 = $ret; +/* 6763 */ if (self === undefined) { +/* 6764 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6765 */ } +/* 6766 */ $ret = Sk.abstr.gattr(self, $scope486.$const505, true); +/* 6767 */ $blk = 24; /* allowing case fallthrough */ +/* 6768 */ case 24: +/* 6769 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6770 */ return $saveSuspension($ret, 'src/lib/traceback.py', 190, 48); +/* 6771 */ } +/* 6772 */ var $lattr523 = $ret; +/* 6773 */ if (self === undefined) { +/* 6774 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6775 */ } +/* 6776 */ $ret = Sk.abstr.gattr(self, $scope486.$const524, true); +/* 6777 */ $blk = 25; /* allowing case fallthrough */ +/* 6778 */ case 25: +/* 6779 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6780 */ return $saveSuspension($ret, 'src/lib/traceback.py', 190, 59); +/* 6781 */ } +/* 6782 */ var $lattr525 = $ret; +/* 6783 */ var $elem526 = $lattr521; +/* 6784 */ var $elem527 = $lattr522; +/* 6785 */ var $elem528 = $lattr523; +/* 6786 */ var $elem529 = $lattr525; +/* 6787 */ var $loadtuple530 = new Sk.builtins['tuple']([$elem526, $elem527, $elem528, $elem529]); +/* 6788 */ var $compareres531 = null; +/* 6789 */ if (other === undefined) { +/* 6790 */ throw new Sk.builtin.UnboundLocalError('local variable \'other\' referenced before assignment'); +/* 6791 */ } +/* 6792 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($loadtuple530, other, 'Eq', true)); +/* 6793 */ $blk = 27; /* allowing case fallthrough */ +/* 6794 */ case 27: +/* 6795 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 6796 */ return $saveSuspension($ret, 'src/lib/traceback.py', 190, 19); +/* 6797 */ } +/* 6798 */ $compareres531 = $ret; +/* 6799 */ var $jfalse532 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 6800 */ if ($jfalse532) { +/* 6801 */ /*test failed */ +/* 6802 */ $blk = 26; +/* 6803 */ continue; +/* 6804 */ } +/* 6805 */ $blk = 26; /* allowing case fallthrough */ +/* 6806 */ case 26: +/* 6807 */ /* --- done --- */ return $compareres531; +/* 6808 */ $blk = 20; /* allowing case fallthrough */ +/* 6809 */ case 20: +/* 6810 */ /* --- end of if --- */ +/* 6811 */ // +/* 6812 */ // line 191: +/* 6813 */ // return NotImplemented +/* 6814 */ // ^ +/* 6815 */ // +/* 6816 */ +/* 6817 */ $currLineNo = Sk.currLineNo = 191; +/* 6818 */ $currColNo = Sk.currColNo = 8; +/* 6819 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6820 */ return Sk.builtin.NotImplemented.NotImplemented$; +/* 6821 */ return Sk.builtin.none.none$; +/* 6822 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 6823 */ } +/* 6824 */ } catch (err) { +/* 6825 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 6826 */ Sk.execStart = Date.now(); +/* 6827 */ Sk.execPaused = 0 +/* 6828 */ } +/* 6829 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 6830 */ err = new Sk.builtin.ExternalError(err); +/* 6831 */ } +/* 6832 */ Sk.err = err; +/* 6833 */ err.traceback.push({ +/* 6834 */ lineno: $currLineNo, +/* 6835 */ colno: $currColNo, +/* 6836 */ filename: 'src/lib/traceback.py', +/* 6837 */ scope: '__eq__' +/* 6838 */ }); +/* 6839 */ if ($exc.length > 0) { +/* 6840 */ $err = err; +/* 6841 */ $blk = $exc.pop(); +/* 6842 */ continue +/* 6843 */ } else { +/* 6844 */ throw err; +/* 6845 */ } +/* 6846 */ } +/* 6847 */ } +/* 6848 */ }); +/* 6849 */ $scope486.$const492 = new Sk.builtin.str('filename'); +/* 6850 */ $scope486.$const499 = new Sk.builtin.str('lineno'); +/* 6851 */ $scope486.$const505 = new Sk.builtin.str('name_$rn$'); +/* 6852 */ $scope486.$const511 = new Sk.builtin.str('locals'); +/* 6853 */ $scope486.$const524 = new Sk.builtin.str('line'); +/* 6854 */ var $scope534 = (function $__getitem__535$(self, pos) { +/* 6855 */ var pos, pos, self, self, self, self, self, $lattr537, $lattr537, $lattr539, $lattr537, $lattr539, $lattr541, $lattr537, $lattr539, $lattr541, $lattr543, $elem544, $elem545, $elem546, $elem547, $loadtuple548; +/* 6856 */ var $wakeFromSuspension = function() { +/* 6857 */ var susp = $scope534.$wakingSuspension; +/* 6858 */ $scope534.$wakingSuspension = undefined; +/* 6859 */ $blk = susp.$blk; +/* 6860 */ $loc = susp.$loc; +/* 6861 */ $gbl = susp.$gbl; +/* 6862 */ $exc = susp.$exc; +/* 6863 */ $err = susp.$err; +/* 6864 */ $postfinally = susp.$postfinally; +/* 6865 */ $currLineNo = susp.$lineno; +/* 6866 */ $currColNo = susp.$colno; +/* 6867 */ Sk.lastYield = Date.now(); +/* 6868 */ pos = susp.$tmps.pos; +/* 6869 */ self = susp.$tmps.self; +/* 6870 */ $lattr537 = susp.$tmps.$lattr537; +/* 6871 */ $lattr539 = susp.$tmps.$lattr539; +/* 6872 */ $lattr541 = susp.$tmps.$lattr541; +/* 6873 */ $lattr543 = susp.$tmps.$lattr543; +/* 6874 */ $elem544 = susp.$tmps.$elem544; +/* 6875 */ $elem545 = susp.$tmps.$elem545; +/* 6876 */ $elem546 = susp.$tmps.$elem546; +/* 6877 */ $elem547 = susp.$tmps.$elem547; +/* 6878 */ $loadtuple548 = susp.$tmps.$loadtuple548; +/* 6879 */ try { +/* 6880 */ $ret = susp.child.resume(); +/* 6881 */ } catch (err) { +/* 6882 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 6883 */ Sk.execStart = Date.now(); +/* 6884 */ Sk.execPaused = 0 +/* 6885 */ } +/* 6886 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 6887 */ err = new Sk.builtin.ExternalError(err); +/* 6888 */ } +/* 6889 */ Sk.err = err; +/* 6890 */ err.traceback.push({ +/* 6891 */ lineno: $currLineNo, +/* 6892 */ colno: $currColNo, +/* 6893 */ filename: 'src/lib/traceback.py', +/* 6894 */ scope: '$scope534' +/* 6895 */ }); +/* 6896 */ if ($exc.length > 0) { +/* 6897 */ $err = err; +/* 6898 */ $blk = $exc.pop(); +/* 6899 */ } else { +/* 6900 */ throw err; +/* 6901 */ } +/* 6902 */ } +/* 6903 */ }; +/* 6904 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 6905 */ var susp = new Sk.misceval.Suspension(); +/* 6906 */ susp.child = $child; +/* 6907 */ susp.resume = function() { +/* 6908 */ $scope534.$wakingSuspension = susp; +/* 6909 */ return $scope534(); +/* 6910 */ }; +/* 6911 */ susp.data = susp.child.data; +/* 6912 */ susp.$blk = $blk; +/* 6913 */ susp.$loc = $loc; +/* 6914 */ susp.$gbl = $gbl; +/* 6915 */ susp.$exc = $exc; +/* 6916 */ susp.$err = $err; +/* 6917 */ susp.$postfinally = $postfinally; +/* 6918 */ susp.$filename = $filename; +/* 6919 */ susp.$lineno = $lineno; +/* 6920 */ susp.$colno = $colno; +/* 6921 */ susp.optional = susp.child.optional; +/* 6922 */ susp.$tmps = { +/* 6923 */ "pos": pos, +/* 6924 */ "self": self, +/* 6925 */ "$lattr537": $lattr537, +/* 6926 */ "$lattr539": $lattr539, +/* 6927 */ "$lattr541": $lattr541, +/* 6928 */ "$lattr543": $lattr543, +/* 6929 */ "$elem544": $elem544, +/* 6930 */ "$elem545": $elem545, +/* 6931 */ "$elem546": $elem546, +/* 6932 */ "$elem547": $elem547, +/* 6933 */ "$loadtuple548": $loadtuple548 +/* 6934 */ }; +/* 6935 */ return susp; +/* 6936 */ }; +/* 6937 */ var $blk = 0, +/* 6938 */ $exc = [], +/* 6939 */ $loc = {}, +/* 6940 */ $cell = {}, +/* 6941 */ $gbl = this, +/* 6942 */ $err = undefined, +/* 6943 */ $ret = undefined, +/* 6944 */ $postfinally = undefined, +/* 6945 */ $currLineNo = undefined, +/* 6946 */ $currColNo = undefined; +/* 6947 */ if (typeof Sk.execStart === 'undefined') { +/* 6948 */ Sk.execStart = Date.now(); +/* 6949 */ Sk.execPaused = 0 +/* 6950 */ } +/* 6951 */ if (typeof Sk.lastYield === 'undefined') { +/* 6952 */ Sk.lastYield = Date.now() +/* 6953 */ } +/* 6954 */ if ($scope534.$wakingSuspension !== undefined) { +/* 6955 */ $wakeFromSuspension(); +/* 6956 */ } else {} +/* 6957 */ $gbl.__class__ = this.FrameSummary; +/* 6958 */ while (true) { +/* 6959 */ try { +/* 6960 */ var $dateNow = Date.now(); +/* 6961 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 6962 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 6963 */ } +/* 6964 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 6965 */ var $susp = $saveSuspension({ +/* 6966 */ data: { +/* 6967 */ type: 'Sk.yield' +/* 6968 */ }, +/* 6969 */ resume: function() {} +/* 6970 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 6971 */ $susp.$blk = $blk; +/* 6972 */ $susp.optional = true; +/* 6973 */ return $susp; +/* 6974 */ } +/* 6975 */ switch ($blk) { +/* 6976 */ case 0: +/* 6977 */ /* --- codeobj entry --- */ if (self === undefined) { +/* 6978 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6979 */ } +/* 6980 */ if (pos === undefined) { +/* 6981 */ throw new Sk.builtin.UnboundLocalError('local variable \'pos\' referenced before assignment'); +/* 6982 */ } +/* 6983 */ +/* 6984 */ // +/* 6985 */ // line 194: +/* 6986 */ // return (self.filename, self.lineno, self.name, self.line)[pos] +/* 6987 */ // ^ +/* 6988 */ // +/* 6989 */ +/* 6990 */ $currLineNo = Sk.currLineNo = 194; +/* 6991 */ $currColNo = Sk.currColNo = 8; +/* 6992 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 6993 */ if (self === undefined) { +/* 6994 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 6995 */ } +/* 6996 */ $ret = Sk.abstr.gattr(self, $scope534.$const536, true); +/* 6997 */ $blk = 1; /* allowing case fallthrough */ +/* 6998 */ case 1: +/* 6999 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7000 */ return $saveSuspension($ret, 'src/lib/traceback.py', 194, 16); +/* 7001 */ } +/* 7002 */ var $lattr537 = $ret; +/* 7003 */ if (self === undefined) { +/* 7004 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7005 */ } +/* 7006 */ $ret = Sk.abstr.gattr(self, $scope534.$const538, true); +/* 7007 */ $blk = 2; /* allowing case fallthrough */ +/* 7008 */ case 2: +/* 7009 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7010 */ return $saveSuspension($ret, 'src/lib/traceback.py', 194, 31); +/* 7011 */ } +/* 7012 */ var $lattr539 = $ret; +/* 7013 */ if (self === undefined) { +/* 7014 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7015 */ } +/* 7016 */ $ret = Sk.abstr.gattr(self, $scope534.$const540, true); +/* 7017 */ $blk = 3; /* allowing case fallthrough */ +/* 7018 */ case 3: +/* 7019 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7020 */ return $saveSuspension($ret, 'src/lib/traceback.py', 194, 44); +/* 7021 */ } +/* 7022 */ var $lattr541 = $ret; +/* 7023 */ if (self === undefined) { +/* 7024 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7025 */ } +/* 7026 */ $ret = Sk.abstr.gattr(self, $scope534.$const542, true); +/* 7027 */ $blk = 4; /* allowing case fallthrough */ +/* 7028 */ case 4: +/* 7029 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7030 */ return $saveSuspension($ret, 'src/lib/traceback.py', 194, 55); +/* 7031 */ } +/* 7032 */ var $lattr543 = $ret; +/* 7033 */ var $elem544 = $lattr537; +/* 7034 */ var $elem545 = $lattr539; +/* 7035 */ var $elem546 = $lattr541; +/* 7036 */ var $elem547 = $lattr543; +/* 7037 */ var $loadtuple548 = new Sk.builtins['tuple']([$elem544, $elem545, $elem546, $elem547]); +/* 7038 */ if (pos === undefined) { +/* 7039 */ throw new Sk.builtin.UnboundLocalError('local variable \'pos\' referenced before assignment'); +/* 7040 */ } +/* 7041 */ $ret = Sk.abstr.objectGetItem($loadtuple548, pos, true); +/* 7042 */ $blk = 5; /* allowing case fallthrough */ +/* 7043 */ case 5: +/* 7044 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7045 */ return $saveSuspension($ret, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 7046 */ } +/* 7047 */ var $lsubscr549 = $ret; +/* 7048 */ return $lsubscr549; +/* 7049 */ return Sk.builtin.none.none$; +/* 7050 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 7051 */ } +/* 7052 */ } catch (err) { +/* 7053 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 7054 */ Sk.execStart = Date.now(); +/* 7055 */ Sk.execPaused = 0 +/* 7056 */ } +/* 7057 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 7058 */ err = new Sk.builtin.ExternalError(err); +/* 7059 */ } +/* 7060 */ Sk.err = err; +/* 7061 */ err.traceback.push({ +/* 7062 */ lineno: $currLineNo, +/* 7063 */ colno: $currColNo, +/* 7064 */ filename: 'src/lib/traceback.py', +/* 7065 */ scope: '__getitem__' +/* 7066 */ }); +/* 7067 */ if ($exc.length > 0) { +/* 7068 */ $err = err; +/* 7069 */ $blk = $exc.pop(); +/* 7070 */ continue +/* 7071 */ } else { +/* 7072 */ throw err; +/* 7073 */ } +/* 7074 */ } +/* 7075 */ } +/* 7076 */ }); +/* 7077 */ $scope534.$const536 = new Sk.builtin.str('filename'); +/* 7078 */ $scope534.$const538 = new Sk.builtin.str('lineno'); +/* 7079 */ $scope534.$const540 = new Sk.builtin.str('name_$rn$'); +/* 7080 */ $scope534.$const542 = new Sk.builtin.str('line'); +/* 7081 */ var $scope551 = (function $__iter__552$(self) { +/* 7082 */ var self, self, self, self, self, $loadgbl553, $loadgbl553, $lattr555, $elem556, $loadgbl553, $lattr555, $elem556, $lattr558, $elem559, $loadgbl553, $lattr555, $elem556, $lattr558, $elem559, $lattr561, $elem562, $loadgbl553, $lattr555, $elem556, $lattr558, $elem559, $lattr561, $elem562, $lattr564, $elem565, $loadlist566; +/* 7083 */ var $wakeFromSuspension = function() { +/* 7084 */ var susp = $scope551.$wakingSuspension; +/* 7085 */ $scope551.$wakingSuspension = undefined; +/* 7086 */ $blk = susp.$blk; +/* 7087 */ $loc = susp.$loc; +/* 7088 */ $gbl = susp.$gbl; +/* 7089 */ $exc = susp.$exc; +/* 7090 */ $err = susp.$err; +/* 7091 */ $postfinally = susp.$postfinally; +/* 7092 */ $currLineNo = susp.$lineno; +/* 7093 */ $currColNo = susp.$colno; +/* 7094 */ Sk.lastYield = Date.now(); +/* 7095 */ self = susp.$tmps.self; +/* 7096 */ $loadgbl553 = susp.$tmps.$loadgbl553; +/* 7097 */ $lattr555 = susp.$tmps.$lattr555; +/* 7098 */ $elem556 = susp.$tmps.$elem556; +/* 7099 */ $lattr558 = susp.$tmps.$lattr558; +/* 7100 */ $elem559 = susp.$tmps.$elem559; +/* 7101 */ $lattr561 = susp.$tmps.$lattr561; +/* 7102 */ $elem562 = susp.$tmps.$elem562; +/* 7103 */ $lattr564 = susp.$tmps.$lattr564; +/* 7104 */ $elem565 = susp.$tmps.$elem565; +/* 7105 */ $loadlist566 = susp.$tmps.$loadlist566; +/* 7106 */ try { +/* 7107 */ $ret = susp.child.resume(); +/* 7108 */ } catch (err) { +/* 7109 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 7110 */ Sk.execStart = Date.now(); +/* 7111 */ Sk.execPaused = 0 +/* 7112 */ } +/* 7113 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 7114 */ err = new Sk.builtin.ExternalError(err); +/* 7115 */ } +/* 7116 */ Sk.err = err; +/* 7117 */ err.traceback.push({ +/* 7118 */ lineno: $currLineNo, +/* 7119 */ colno: $currColNo, +/* 7120 */ filename: 'src/lib/traceback.py', +/* 7121 */ scope: '$scope551' +/* 7122 */ }); +/* 7123 */ if ($exc.length > 0) { +/* 7124 */ $err = err; +/* 7125 */ $blk = $exc.pop(); +/* 7126 */ } else { +/* 7127 */ throw err; +/* 7128 */ } +/* 7129 */ } +/* 7130 */ }; +/* 7131 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 7132 */ var susp = new Sk.misceval.Suspension(); +/* 7133 */ susp.child = $child; +/* 7134 */ susp.resume = function() { +/* 7135 */ $scope551.$wakingSuspension = susp; +/* 7136 */ return $scope551(); +/* 7137 */ }; +/* 7138 */ susp.data = susp.child.data; +/* 7139 */ susp.$blk = $blk; +/* 7140 */ susp.$loc = $loc; +/* 7141 */ susp.$gbl = $gbl; +/* 7142 */ susp.$exc = $exc; +/* 7143 */ susp.$err = $err; +/* 7144 */ susp.$postfinally = $postfinally; +/* 7145 */ susp.$filename = $filename; +/* 7146 */ susp.$lineno = $lineno; +/* 7147 */ susp.$colno = $colno; +/* 7148 */ susp.optional = susp.child.optional; +/* 7149 */ susp.$tmps = { +/* 7150 */ "self": self, +/* 7151 */ "$loadgbl553": $loadgbl553, +/* 7152 */ "$lattr555": $lattr555, +/* 7153 */ "$elem556": $elem556, +/* 7154 */ "$lattr558": $lattr558, +/* 7155 */ "$elem559": $elem559, +/* 7156 */ "$lattr561": $lattr561, +/* 7157 */ "$elem562": $elem562, +/* 7158 */ "$lattr564": $lattr564, +/* 7159 */ "$elem565": $elem565, +/* 7160 */ "$loadlist566": $loadlist566 +/* 7161 */ }; +/* 7162 */ return susp; +/* 7163 */ }; +/* 7164 */ var $blk = 0, +/* 7165 */ $exc = [], +/* 7166 */ $loc = {}, +/* 7167 */ $cell = {}, +/* 7168 */ $gbl = this, +/* 7169 */ $err = undefined, +/* 7170 */ $ret = undefined, +/* 7171 */ $postfinally = undefined, +/* 7172 */ $currLineNo = undefined, +/* 7173 */ $currColNo = undefined; +/* 7174 */ if (typeof Sk.execStart === 'undefined') { +/* 7175 */ Sk.execStart = Date.now(); +/* 7176 */ Sk.execPaused = 0 +/* 7177 */ } +/* 7178 */ if (typeof Sk.lastYield === 'undefined') { +/* 7179 */ Sk.lastYield = Date.now() +/* 7180 */ } +/* 7181 */ if ($scope551.$wakingSuspension !== undefined) { +/* 7182 */ $wakeFromSuspension(); +/* 7183 */ } else {} +/* 7184 */ $gbl.__class__ = this.FrameSummary; +/* 7185 */ while (true) { +/* 7186 */ try { +/* 7187 */ var $dateNow = Date.now(); +/* 7188 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 7189 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 7190 */ } +/* 7191 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 7192 */ var $susp = $saveSuspension({ +/* 7193 */ data: { +/* 7194 */ type: 'Sk.yield' +/* 7195 */ }, +/* 7196 */ resume: function() {} +/* 7197 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 7198 */ $susp.$blk = $blk; +/* 7199 */ $susp.optional = true; +/* 7200 */ return $susp; +/* 7201 */ } +/* 7202 */ switch ($blk) { +/* 7203 */ case 0: +/* 7204 */ /* --- codeobj entry --- */ if (self === undefined) { +/* 7205 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7206 */ } +/* 7207 */ +/* 7208 */ // +/* 7209 */ // line 197: +/* 7210 */ // return iter([self.filename, self.lineno, self.name, self.line]) +/* 7211 */ // ^ +/* 7212 */ // +/* 7213 */ +/* 7214 */ $currLineNo = Sk.currLineNo = 197; +/* 7215 */ $currColNo = Sk.currColNo = 8; +/* 7216 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 7217 */ var $loadgbl553 = Sk.misceval.loadname('iter', $gbl); +/* 7218 */ if (self === undefined) { +/* 7219 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7220 */ } +/* 7221 */ $ret = Sk.abstr.gattr(self, $scope551.$const554, true); +/* 7222 */ $blk = 1; /* allowing case fallthrough */ +/* 7223 */ case 1: +/* 7224 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7225 */ return $saveSuspension($ret, 'src/lib/traceback.py', 197, 21); +/* 7226 */ } +/* 7227 */ var $lattr555 = $ret; +/* 7228 */ var $elem556 = $lattr555; +/* 7229 */ if (self === undefined) { +/* 7230 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7231 */ } +/* 7232 */ $ret = Sk.abstr.gattr(self, $scope551.$const557, true); +/* 7233 */ $blk = 2; /* allowing case fallthrough */ +/* 7234 */ case 2: +/* 7235 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7236 */ return $saveSuspension($ret, 'src/lib/traceback.py', 197, 36); +/* 7237 */ } +/* 7238 */ var $lattr558 = $ret; +/* 7239 */ var $elem559 = $lattr558; +/* 7240 */ if (self === undefined) { +/* 7241 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7242 */ } +/* 7243 */ $ret = Sk.abstr.gattr(self, $scope551.$const560, true); +/* 7244 */ $blk = 3; /* allowing case fallthrough */ +/* 7245 */ case 3: +/* 7246 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7247 */ return $saveSuspension($ret, 'src/lib/traceback.py', 197, 49); +/* 7248 */ } +/* 7249 */ var $lattr561 = $ret; +/* 7250 */ var $elem562 = $lattr561; +/* 7251 */ if (self === undefined) { +/* 7252 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7253 */ } +/* 7254 */ $ret = Sk.abstr.gattr(self, $scope551.$const563, true); +/* 7255 */ $blk = 4; /* allowing case fallthrough */ +/* 7256 */ case 4: +/* 7257 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7258 */ return $saveSuspension($ret, 'src/lib/traceback.py', 197, 60); +/* 7259 */ } +/* 7260 */ var $lattr564 = $ret; +/* 7261 */ var $elem565 = $lattr564; +/* 7262 */ var $loadlist566 = new Sk.builtins['list']([$elem556, $elem559, $elem562, $elem565]); +/* 7263 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl553, [$loadlist566]); +/* 7264 */ $blk = 5; /* allowing case fallthrough */ +/* 7265 */ case 5: +/* 7266 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7267 */ return $saveSuspension($ret, 'src/lib/traceback.py', 197, 15); +/* 7268 */ } +/* 7269 */ var $call567 = $ret; +/* 7270 */ // +/* 7271 */ // line 197: +/* 7272 */ // return iter([self.filename, self.lineno, self.name, self.line]) +/* 7273 */ // ^ +/* 7274 */ // +/* 7275 */ +/* 7276 */ $currLineNo = Sk.currLineNo = 197; +/* 7277 */ $currColNo = Sk.currColNo = 15; +/* 7278 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 7279 */ return $call567; +/* 7280 */ return Sk.builtin.none.none$; +/* 7281 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 7282 */ } +/* 7283 */ } catch (err) { +/* 7284 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 7285 */ Sk.execStart = Date.now(); +/* 7286 */ Sk.execPaused = 0 +/* 7287 */ } +/* 7288 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 7289 */ err = new Sk.builtin.ExternalError(err); +/* 7290 */ } +/* 7291 */ Sk.err = err; +/* 7292 */ err.traceback.push({ +/* 7293 */ lineno: $currLineNo, +/* 7294 */ colno: $currColNo, +/* 7295 */ filename: 'src/lib/traceback.py', +/* 7296 */ scope: '__iter__' +/* 7297 */ }); +/* 7298 */ if ($exc.length > 0) { +/* 7299 */ $err = err; +/* 7300 */ $blk = $exc.pop(); +/* 7301 */ continue +/* 7302 */ } else { +/* 7303 */ throw err; +/* 7304 */ } +/* 7305 */ } +/* 7306 */ } +/* 7307 */ }); +/* 7308 */ $scope551.$const554 = new Sk.builtin.str('filename'); +/* 7309 */ $scope551.$const557 = new Sk.builtin.str('lineno'); +/* 7310 */ $scope551.$const560 = new Sk.builtin.str('name_$rn$'); +/* 7311 */ $scope551.$const563 = new Sk.builtin.str('line'); +/* 7312 */ var $scope569 = (function $__repr__570$(self) { +/* 7313 */ var self, self, self, self, $lattr573, $lattr573, $lattr575, $lattr573, $lattr575, $lattr577, $lattr573, $lattr575, $lattr577, $lattr579; +/* 7314 */ var $wakeFromSuspension = function() { +/* 7315 */ var susp = $scope569.$wakingSuspension; +/* 7316 */ $scope569.$wakingSuspension = undefined; +/* 7317 */ $blk = susp.$blk; +/* 7318 */ $loc = susp.$loc; +/* 7319 */ $gbl = susp.$gbl; +/* 7320 */ $exc = susp.$exc; +/* 7321 */ $err = susp.$err; +/* 7322 */ $postfinally = susp.$postfinally; +/* 7323 */ $currLineNo = susp.$lineno; +/* 7324 */ $currColNo = susp.$colno; +/* 7325 */ Sk.lastYield = Date.now(); +/* 7326 */ self = susp.$tmps.self; +/* 7327 */ $lattr573 = susp.$tmps.$lattr573; +/* 7328 */ $lattr575 = susp.$tmps.$lattr575; +/* 7329 */ $lattr577 = susp.$tmps.$lattr577; +/* 7330 */ $lattr579 = susp.$tmps.$lattr579; +/* 7331 */ try { +/* 7332 */ $ret = susp.child.resume(); +/* 7333 */ } catch (err) { +/* 7334 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 7335 */ Sk.execStart = Date.now(); +/* 7336 */ Sk.execPaused = 0 +/* 7337 */ } +/* 7338 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 7339 */ err = new Sk.builtin.ExternalError(err); +/* 7340 */ } +/* 7341 */ Sk.err = err; +/* 7342 */ err.traceback.push({ +/* 7343 */ lineno: $currLineNo, +/* 7344 */ colno: $currColNo, +/* 7345 */ filename: 'src/lib/traceback.py', +/* 7346 */ scope: '$scope569' +/* 7347 */ }); +/* 7348 */ if ($exc.length > 0) { +/* 7349 */ $err = err; +/* 7350 */ $blk = $exc.pop(); +/* 7351 */ } else { +/* 7352 */ throw err; +/* 7353 */ } +/* 7354 */ } +/* 7355 */ }; +/* 7356 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 7357 */ var susp = new Sk.misceval.Suspension(); +/* 7358 */ susp.child = $child; +/* 7359 */ susp.resume = function() { +/* 7360 */ $scope569.$wakingSuspension = susp; +/* 7361 */ return $scope569(); +/* 7362 */ }; +/* 7363 */ susp.data = susp.child.data; +/* 7364 */ susp.$blk = $blk; +/* 7365 */ susp.$loc = $loc; +/* 7366 */ susp.$gbl = $gbl; +/* 7367 */ susp.$exc = $exc; +/* 7368 */ susp.$err = $err; +/* 7369 */ susp.$postfinally = $postfinally; +/* 7370 */ susp.$filename = $filename; +/* 7371 */ susp.$lineno = $lineno; +/* 7372 */ susp.$colno = $colno; +/* 7373 */ susp.optional = susp.child.optional; +/* 7374 */ susp.$tmps = { +/* 7375 */ "self": self, +/* 7376 */ "$lattr573": $lattr573, +/* 7377 */ "$lattr575": $lattr575, +/* 7378 */ "$lattr577": $lattr577, +/* 7379 */ "$lattr579": $lattr579 +/* 7380 */ }; +/* 7381 */ return susp; +/* 7382 */ }; +/* 7383 */ var $blk = 0, +/* 7384 */ $exc = [], +/* 7385 */ $loc = {}, +/* 7386 */ $cell = {}, +/* 7387 */ $gbl = this, +/* 7388 */ $err = undefined, +/* 7389 */ $ret = undefined, +/* 7390 */ $postfinally = undefined, +/* 7391 */ $currLineNo = undefined, +/* 7392 */ $currColNo = undefined; +/* 7393 */ if (typeof Sk.execStart === 'undefined') { +/* 7394 */ Sk.execStart = Date.now(); +/* 7395 */ Sk.execPaused = 0 +/* 7396 */ } +/* 7397 */ if (typeof Sk.lastYield === 'undefined') { +/* 7398 */ Sk.lastYield = Date.now() +/* 7399 */ } +/* 7400 */ if ($scope569.$wakingSuspension !== undefined) { +/* 7401 */ $wakeFromSuspension(); +/* 7402 */ } else {} +/* 7403 */ $gbl.__class__ = this.FrameSummary; +/* 7404 */ while (true) { +/* 7405 */ try { +/* 7406 */ var $dateNow = Date.now(); +/* 7407 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 7408 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 7409 */ } +/* 7410 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 7411 */ var $susp = $saveSuspension({ +/* 7412 */ data: { +/* 7413 */ type: 'Sk.yield' +/* 7414 */ }, +/* 7415 */ resume: function() {} +/* 7416 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 7417 */ $susp.$blk = $blk; +/* 7418 */ $susp.optional = true; +/* 7419 */ return $susp; +/* 7420 */ } +/* 7421 */ switch ($blk) { +/* 7422 */ case 0: +/* 7423 */ /* --- codeobj entry --- */ if (self === undefined) { +/* 7424 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7425 */ } +/* 7426 */ +/* 7427 */ // +/* 7428 */ // line 200: +/* 7429 */ // return "".format( +/* 7430 */ // ^ +/* 7431 */ // +/* 7432 */ +/* 7433 */ $currLineNo = Sk.currLineNo = 200; +/* 7434 */ $currColNo = Sk.currColNo = 8; +/* 7435 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 7436 */ $ret = Sk.abstr.gattr($scope569.$const571, $scope569.$const572, true); +/* 7437 */ $blk = 1; /* allowing case fallthrough */ +/* 7438 */ case 1: +/* 7439 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7440 */ return $saveSuspension($ret, 'src/lib/traceback.py', 200, 15); +/* 7441 */ } +/* 7442 */ var $lattr573 = $ret; +/* 7443 */ if (self === undefined) { +/* 7444 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7445 */ } +/* 7446 */ $ret = Sk.abstr.gattr(self, $scope569.$const574, true); +/* 7447 */ $blk = 2; /* allowing case fallthrough */ +/* 7448 */ case 2: +/* 7449 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7450 */ return $saveSuspension($ret, 'src/lib/traceback.py', 201, 21); +/* 7451 */ } +/* 7452 */ var $lattr575 = $ret; +/* 7453 */ if (self === undefined) { +/* 7454 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7455 */ } +/* 7456 */ $ret = Sk.abstr.gattr(self, $scope569.$const576, true); +/* 7457 */ $blk = 3; /* allowing case fallthrough */ +/* 7458 */ case 3: +/* 7459 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7460 */ return $saveSuspension($ret, 'src/lib/traceback.py', 201, 43); +/* 7461 */ } +/* 7462 */ var $lattr577 = $ret; +/* 7463 */ if (self === undefined) { +/* 7464 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7465 */ } +/* 7466 */ $ret = Sk.abstr.gattr(self, $scope569.$const578, true); +/* 7467 */ $blk = 4; /* allowing case fallthrough */ +/* 7468 */ case 4: +/* 7469 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7470 */ return $saveSuspension($ret, 'src/lib/traceback.py', 201, 61); +/* 7471 */ } +/* 7472 */ var $lattr579 = $ret; +/* 7473 */ $ret = Sk.misceval.applyOrSuspend($lattr573, undefined, undefined, ['filename', $lattr575, 'lineno', $lattr577, 'name', $lattr579], []); +/* 7474 */ $blk = 5; /* allowing case fallthrough */ +/* 7475 */ case 5: +/* 7476 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7477 */ return $saveSuspension($ret, 'src/lib/traceback.py', 200, 15); +/* 7478 */ } +/* 7479 */ var $call580 = $ret; +/* 7480 */ // +/* 7481 */ // line 200: +/* 7482 */ // return "".format( +/* 7483 */ // ^ +/* 7484 */ // +/* 7485 */ +/* 7486 */ $currLineNo = Sk.currLineNo = 200; +/* 7487 */ $currColNo = Sk.currColNo = 15; +/* 7488 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 7489 */ return $call580; +/* 7490 */ return Sk.builtin.none.none$; +/* 7491 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 7492 */ } +/* 7493 */ } catch (err) { +/* 7494 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 7495 */ Sk.execStart = Date.now(); +/* 7496 */ Sk.execPaused = 0 +/* 7497 */ } +/* 7498 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 7499 */ err = new Sk.builtin.ExternalError(err); +/* 7500 */ } +/* 7501 */ Sk.err = err; +/* 7502 */ err.traceback.push({ +/* 7503 */ lineno: $currLineNo, +/* 7504 */ colno: $currColNo, +/* 7505 */ filename: 'src/lib/traceback.py', +/* 7506 */ scope: '__repr__' +/* 7507 */ }); +/* 7508 */ if ($exc.length > 0) { +/* 7509 */ $err = err; +/* 7510 */ $blk = $exc.pop(); +/* 7511 */ continue +/* 7512 */ } else { +/* 7513 */ throw err; +/* 7514 */ } +/* 7515 */ } +/* 7516 */ } +/* 7517 */ }); +/* 7518 */ $scope569.$const571 = new Sk.builtin.str(''); +/* 7519 */ $scope569.$const572 = new Sk.builtin.str('format'); +/* 7520 */ $scope569.$const574 = new Sk.builtin.str('filename'); +/* 7521 */ $scope569.$const576 = new Sk.builtin.str('lineno'); +/* 7522 */ $scope569.$const578 = new Sk.builtin.str('name_$rn$'); +/* 7523 */ var $scope582 = (function $__len__583$(self) { +/* 7524 */ var self; +/* 7525 */ var $wakeFromSuspension = function() { +/* 7526 */ var susp = $scope582.$wakingSuspension; +/* 7527 */ $scope582.$wakingSuspension = undefined; +/* 7528 */ $blk = susp.$blk; +/* 7529 */ $loc = susp.$loc; +/* 7530 */ $gbl = susp.$gbl; +/* 7531 */ $exc = susp.$exc; +/* 7532 */ $err = susp.$err; +/* 7533 */ $postfinally = susp.$postfinally; +/* 7534 */ $currLineNo = susp.$lineno; +/* 7535 */ $currColNo = susp.$colno; +/* 7536 */ Sk.lastYield = Date.now(); +/* 7537 */ self = susp.$tmps.self; +/* 7538 */ try { +/* 7539 */ $ret = susp.child.resume(); +/* 7540 */ } catch (err) { +/* 7541 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 7542 */ Sk.execStart = Date.now(); +/* 7543 */ Sk.execPaused = 0 +/* 7544 */ } +/* 7545 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 7546 */ err = new Sk.builtin.ExternalError(err); +/* 7547 */ } +/* 7548 */ Sk.err = err; +/* 7549 */ err.traceback.push({ +/* 7550 */ lineno: $currLineNo, +/* 7551 */ colno: $currColNo, +/* 7552 */ filename: 'src/lib/traceback.py', +/* 7553 */ scope: '$scope582' +/* 7554 */ }); +/* 7555 */ if ($exc.length > 0) { +/* 7556 */ $err = err; +/* 7557 */ $blk = $exc.pop(); +/* 7558 */ } else { +/* 7559 */ throw err; +/* 7560 */ } +/* 7561 */ } +/* 7562 */ }; +/* 7563 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 7564 */ var susp = new Sk.misceval.Suspension(); +/* 7565 */ susp.child = $child; +/* 7566 */ susp.resume = function() { +/* 7567 */ $scope582.$wakingSuspension = susp; +/* 7568 */ return $scope582(); +/* 7569 */ }; +/* 7570 */ susp.data = susp.child.data; +/* 7571 */ susp.$blk = $blk; +/* 7572 */ susp.$loc = $loc; +/* 7573 */ susp.$gbl = $gbl; +/* 7574 */ susp.$exc = $exc; +/* 7575 */ susp.$err = $err; +/* 7576 */ susp.$postfinally = $postfinally; +/* 7577 */ susp.$filename = $filename; +/* 7578 */ susp.$lineno = $lineno; +/* 7579 */ susp.$colno = $colno; +/* 7580 */ susp.optional = susp.child.optional; +/* 7581 */ susp.$tmps = { +/* 7582 */ "self": self +/* 7583 */ }; +/* 7584 */ return susp; +/* 7585 */ }; +/* 7586 */ var $blk = 0, +/* 7587 */ $exc = [], +/* 7588 */ $loc = {}, +/* 7589 */ $cell = {}, +/* 7590 */ $gbl = this, +/* 7591 */ $err = undefined, +/* 7592 */ $ret = undefined, +/* 7593 */ $postfinally = undefined, +/* 7594 */ $currLineNo = undefined, +/* 7595 */ $currColNo = undefined; +/* 7596 */ if (typeof Sk.execStart === 'undefined') { +/* 7597 */ Sk.execStart = Date.now(); +/* 7598 */ Sk.execPaused = 0 +/* 7599 */ } +/* 7600 */ if (typeof Sk.lastYield === 'undefined') { +/* 7601 */ Sk.lastYield = Date.now() +/* 7602 */ } +/* 7603 */ if ($scope582.$wakingSuspension !== undefined) { +/* 7604 */ $wakeFromSuspension(); +/* 7605 */ } else {} +/* 7606 */ $gbl.__class__ = this.FrameSummary; +/* 7607 */ while (true) { +/* 7608 */ try { +/* 7609 */ var $dateNow = Date.now(); +/* 7610 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 7611 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 7612 */ } +/* 7613 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 7614 */ var $susp = $saveSuspension({ +/* 7615 */ data: { +/* 7616 */ type: 'Sk.yield' +/* 7617 */ }, +/* 7618 */ resume: function() {} +/* 7619 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 7620 */ $susp.$blk = $blk; +/* 7621 */ $susp.optional = true; +/* 7622 */ return $susp; +/* 7623 */ } +/* 7624 */ switch ($blk) { +/* 7625 */ case 0: +/* 7626 */ /* --- codeobj entry --- */ if (self === undefined) { +/* 7627 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7628 */ } +/* 7629 */ +/* 7630 */ // +/* 7631 */ // line 204: +/* 7632 */ // return 4 +/* 7633 */ // ^ +/* 7634 */ // +/* 7635 */ +/* 7636 */ $currLineNo = Sk.currLineNo = 204; +/* 7637 */ $currColNo = Sk.currColNo = 8; +/* 7638 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 7639 */ return $scope582.$const584; +/* 7640 */ return Sk.builtin.none.none$; +/* 7641 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 7642 */ } +/* 7643 */ } catch (err) { +/* 7644 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 7645 */ Sk.execStart = Date.now(); +/* 7646 */ Sk.execPaused = 0 +/* 7647 */ } +/* 7648 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 7649 */ err = new Sk.builtin.ExternalError(err); +/* 7650 */ } +/* 7651 */ Sk.err = err; +/* 7652 */ err.traceback.push({ +/* 7653 */ lineno: $currLineNo, +/* 7654 */ colno: $currColNo, +/* 7655 */ filename: 'src/lib/traceback.py', +/* 7656 */ scope: '__len__' +/* 7657 */ }); +/* 7658 */ if ($exc.length > 0) { +/* 7659 */ $err = err; +/* 7660 */ $blk = $exc.pop(); +/* 7661 */ continue +/* 7662 */ } else { +/* 7663 */ throw err; +/* 7664 */ } +/* 7665 */ } +/* 7666 */ } +/* 7667 */ }); +/* 7668 */ $scope582.$const584 = new Sk.builtin.int_(4); +/* 7669 */ var $scope587 = (function $line588$(self) { +/* 7670 */ var self, self, self, self, self, self, $lattr590, $compareres591, $loadgbl594, $loadgbl594, $lattr596, $loadgbl594, $lattr596, $lattr598, $loadgbl594, $lattr596, $lattr598, $lattr600, $loadgbl594, $lattr596, $lattr598, $lattr600, $call601, $loadgbl594, $lattr596, $lattr598, $lattr600, $call601, $lattr603, $loadgbl594, $lattr596, $lattr598, $lattr600, $call601, $lattr603, $call604; +/* 7671 */ var $wakeFromSuspension = function() { +/* 7672 */ var susp = $scope587.$wakingSuspension; +/* 7673 */ $scope587.$wakingSuspension = undefined; +/* 7674 */ $blk = susp.$blk; +/* 7675 */ $loc = susp.$loc; +/* 7676 */ $gbl = susp.$gbl; +/* 7677 */ $exc = susp.$exc; +/* 7678 */ $err = susp.$err; +/* 7679 */ $postfinally = susp.$postfinally; +/* 7680 */ $currLineNo = susp.$lineno; +/* 7681 */ $currColNo = susp.$colno; +/* 7682 */ Sk.lastYield = Date.now(); +/* 7683 */ self = susp.$tmps.self; +/* 7684 */ $lattr590 = susp.$tmps.$lattr590; +/* 7685 */ $compareres591 = susp.$tmps.$compareres591; +/* 7686 */ $loadgbl594 = susp.$tmps.$loadgbl594; +/* 7687 */ $lattr596 = susp.$tmps.$lattr596; +/* 7688 */ $lattr598 = susp.$tmps.$lattr598; +/* 7689 */ $lattr600 = susp.$tmps.$lattr600; +/* 7690 */ $call601 = susp.$tmps.$call601; +/* 7691 */ $lattr603 = susp.$tmps.$lattr603; +/* 7692 */ $call604 = susp.$tmps.$call604; +/* 7693 */ try { +/* 7694 */ $ret = susp.child.resume(); +/* 7695 */ } catch (err) { +/* 7696 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 7697 */ Sk.execStart = Date.now(); +/* 7698 */ Sk.execPaused = 0 +/* 7699 */ } +/* 7700 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 7701 */ err = new Sk.builtin.ExternalError(err); +/* 7702 */ } +/* 7703 */ Sk.err = err; +/* 7704 */ err.traceback.push({ +/* 7705 */ lineno: $currLineNo, +/* 7706 */ colno: $currColNo, +/* 7707 */ filename: 'src/lib/traceback.py', +/* 7708 */ scope: '$scope587' +/* 7709 */ }); +/* 7710 */ if ($exc.length > 0) { +/* 7711 */ $err = err; +/* 7712 */ $blk = $exc.pop(); +/* 7713 */ } else { +/* 7714 */ throw err; +/* 7715 */ } +/* 7716 */ } +/* 7717 */ }; +/* 7718 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 7719 */ var susp = new Sk.misceval.Suspension(); +/* 7720 */ susp.child = $child; +/* 7721 */ susp.resume = function() { +/* 7722 */ $scope587.$wakingSuspension = susp; +/* 7723 */ return $scope587(); +/* 7724 */ }; +/* 7725 */ susp.data = susp.child.data; +/* 7726 */ susp.$blk = $blk; +/* 7727 */ susp.$loc = $loc; +/* 7728 */ susp.$gbl = $gbl; +/* 7729 */ susp.$exc = $exc; +/* 7730 */ susp.$err = $err; +/* 7731 */ susp.$postfinally = $postfinally; +/* 7732 */ susp.$filename = $filename; +/* 7733 */ susp.$lineno = $lineno; +/* 7734 */ susp.$colno = $colno; +/* 7735 */ susp.optional = susp.child.optional; +/* 7736 */ susp.$tmps = { +/* 7737 */ "self": self, +/* 7738 */ "$lattr590": $lattr590, +/* 7739 */ "$compareres591": $compareres591, +/* 7740 */ "$loadgbl594": $loadgbl594, +/* 7741 */ "$lattr596": $lattr596, +/* 7742 */ "$lattr598": $lattr598, +/* 7743 */ "$lattr600": $lattr600, +/* 7744 */ "$call601": $call601, +/* 7745 */ "$lattr603": $lattr603, +/* 7746 */ "$call604": $call604 +/* 7747 */ }; +/* 7748 */ return susp; +/* 7749 */ }; +/* 7750 */ var $blk = 0, +/* 7751 */ $exc = [], +/* 7752 */ $loc = {}, +/* 7753 */ $cell = {}, +/* 7754 */ $gbl = this, +/* 7755 */ $err = undefined, +/* 7756 */ $ret = undefined, +/* 7757 */ $postfinally = undefined, +/* 7758 */ $currLineNo = undefined, +/* 7759 */ $currColNo = undefined; +/* 7760 */ if (typeof Sk.execStart === 'undefined') { +/* 7761 */ Sk.execStart = Date.now(); +/* 7762 */ Sk.execPaused = 0 +/* 7763 */ } +/* 7764 */ if (typeof Sk.lastYield === 'undefined') { +/* 7765 */ Sk.lastYield = Date.now() +/* 7766 */ } +/* 7767 */ if ($scope587.$wakingSuspension !== undefined) { +/* 7768 */ $wakeFromSuspension(); +/* 7769 */ } else {} +/* 7770 */ $gbl.__class__ = this.FrameSummary; +/* 7771 */ while (true) { +/* 7772 */ try { +/* 7773 */ var $dateNow = Date.now(); +/* 7774 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 7775 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 7776 */ } +/* 7777 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 7778 */ var $susp = $saveSuspension({ +/* 7779 */ data: { +/* 7780 */ type: 'Sk.yield' +/* 7781 */ }, +/* 7782 */ resume: function() {} +/* 7783 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 7784 */ $susp.$blk = $blk; +/* 7785 */ $susp.optional = true; +/* 7786 */ return $susp; +/* 7787 */ } +/* 7788 */ switch ($blk) { +/* 7789 */ case 0: +/* 7790 */ /* --- codeobj entry --- */ if (self === undefined) { +/* 7791 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7792 */ } +/* 7793 */ +/* 7794 */ // +/* 7795 */ // line 208: +/* 7796 */ // if self._line is None: +/* 7797 */ // ^ +/* 7798 */ // +/* 7799 */ +/* 7800 */ $currLineNo = Sk.currLineNo = 208; +/* 7801 */ $currColNo = Sk.currColNo = 8; +/* 7802 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 7803 */ if (self === undefined) { +/* 7804 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7805 */ } +/* 7806 */ $ret = Sk.abstr.gattr(self, $scope587.$const589, true); +/* 7807 */ $blk = 2; /* allowing case fallthrough */ +/* 7808 */ case 2: +/* 7809 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7810 */ return $saveSuspension($ret, 'src/lib/traceback.py', 208, 11); +/* 7811 */ } +/* 7812 */ var $lattr590 = $ret; +/* 7813 */ var $compareres591 = null; +/* 7814 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($lattr590, Sk.builtin.none.none$, 'Is', true)); +/* 7815 */ $blk = 4; /* allowing case fallthrough */ +/* 7816 */ case 4: +/* 7817 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7818 */ return $saveSuspension($ret, 'src/lib/traceback.py', 208, 11); +/* 7819 */ } +/* 7820 */ $compareres591 = $ret; +/* 7821 */ var $jfalse592 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 7822 */ if ($jfalse592) { +/* 7823 */ /*test failed */ +/* 7824 */ $blk = 3; +/* 7825 */ continue; +/* 7826 */ } +/* 7827 */ $blk = 3; /* allowing case fallthrough */ +/* 7828 */ case 3: +/* 7829 */ /* --- done --- */ var $jfalse593 = ($compareres591 === false || !Sk.misceval.isTrue($compareres591)); +/* 7830 */ if ($jfalse593) { +/* 7831 */ /*test failed */ +/* 7832 */ $blk = 1; +/* 7833 */ continue; +/* 7834 */ } +/* 7835 */ // +/* 7836 */ // line 209: +/* 7837 */ // self._line = linecache.getline(self.filename, self.lineno).strip() +/* 7838 */ // ^ +/* 7839 */ // +/* 7840 */ +/* 7841 */ $currLineNo = Sk.currLineNo = 209; +/* 7842 */ $currColNo = Sk.currColNo = 12; +/* 7843 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 7844 */ var $loadgbl594 = Sk.misceval.loadname('linecache', $gbl); +/* 7845 */ $ret = Sk.abstr.gattr($loadgbl594, $scope587.$const595, true); +/* 7846 */ $blk = 5; /* allowing case fallthrough */ +/* 7847 */ case 5: +/* 7848 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7849 */ return $saveSuspension($ret, 'src/lib/traceback.py', 209, 25); +/* 7850 */ } +/* 7851 */ var $lattr596 = $ret; +/* 7852 */ if (self === undefined) { +/* 7853 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7854 */ } +/* 7855 */ $ret = Sk.abstr.gattr(self, $scope587.$const597, true); +/* 7856 */ $blk = 6; /* allowing case fallthrough */ +/* 7857 */ case 6: +/* 7858 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7859 */ return $saveSuspension($ret, 'src/lib/traceback.py', 209, 43); +/* 7860 */ } +/* 7861 */ var $lattr598 = $ret; +/* 7862 */ if (self === undefined) { +/* 7863 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7864 */ } +/* 7865 */ $ret = Sk.abstr.gattr(self, $scope587.$const599, true); +/* 7866 */ $blk = 7; /* allowing case fallthrough */ +/* 7867 */ case 7: +/* 7868 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7869 */ return $saveSuspension($ret, 'src/lib/traceback.py', 209, 58); +/* 7870 */ } +/* 7871 */ var $lattr600 = $ret; +/* 7872 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr596, [$lattr598, $lattr600]); +/* 7873 */ $blk = 8; /* allowing case fallthrough */ +/* 7874 */ case 8: +/* 7875 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7876 */ return $saveSuspension($ret, 'src/lib/traceback.py', 209, 25); +/* 7877 */ } +/* 7878 */ var $call601 = $ret; +/* 7879 */ // +/* 7880 */ // line 209: +/* 7881 */ // self._line = linecache.getline(self.filename, self.lineno).strip() +/* 7882 */ // ^ +/* 7883 */ // +/* 7884 */ +/* 7885 */ $currLineNo = Sk.currLineNo = 209; +/* 7886 */ $currColNo = Sk.currColNo = 25; +/* 7887 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 7888 */ $ret = Sk.abstr.gattr($call601, $scope587.$const602, true); +/* 7889 */ $blk = 9; /* allowing case fallthrough */ +/* 7890 */ case 9: +/* 7891 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7892 */ return $saveSuspension($ret, 'src/lib/traceback.py', 209, 25); +/* 7893 */ } +/* 7894 */ var $lattr603 = $ret; +/* 7895 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr603); +/* 7896 */ $blk = 10; /* allowing case fallthrough */ +/* 7897 */ case 10: +/* 7898 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7899 */ return $saveSuspension($ret, 'src/lib/traceback.py', 209, 25); +/* 7900 */ } +/* 7901 */ var $call604 = $ret; +/* 7902 */ // +/* 7903 */ // line 209: +/* 7904 */ // self._line = linecache.getline(self.filename, self.lineno).strip() +/* 7905 */ // ^ +/* 7906 */ // +/* 7907 */ +/* 7908 */ $currLineNo = Sk.currLineNo = 209; +/* 7909 */ $currColNo = Sk.currColNo = 25; +/* 7910 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 7911 */ if (self === undefined) { +/* 7912 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7913 */ } +/* 7914 */ $ret = Sk.abstr.sattr(self, $scope587.$const589, $call604, true); +/* 7915 */ $blk = 11; /* allowing case fallthrough */ +/* 7916 */ case 11: +/* 7917 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7918 */ return $saveSuspension($ret, 'src/lib/traceback.py', 209, 12); +/* 7919 */ } +/* 7920 */ $blk = 1; /* allowing case fallthrough */ +/* 7921 */ case 1: +/* 7922 */ /* --- end of if --- */ +/* 7923 */ // +/* 7924 */ // line 210: +/* 7925 */ // return self._line +/* 7926 */ // ^ +/* 7927 */ // +/* 7928 */ +/* 7929 */ $currLineNo = Sk.currLineNo = 210; +/* 7930 */ $currColNo = Sk.currColNo = 8; +/* 7931 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 7932 */ if (self === undefined) { +/* 7933 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 7934 */ } +/* 7935 */ $ret = Sk.abstr.gattr(self, $scope587.$const589, true); +/* 7936 */ $blk = 12; /* allowing case fallthrough */ +/* 7937 */ case 12: +/* 7938 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 7939 */ return $saveSuspension($ret, 'src/lib/traceback.py', 210, 15); +/* 7940 */ } +/* 7941 */ var $lattr605 = $ret; +/* 7942 */ return $lattr605; +/* 7943 */ return Sk.builtin.none.none$; +/* 7944 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 7945 */ } +/* 7946 */ } catch (err) { +/* 7947 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 7948 */ Sk.execStart = Date.now(); +/* 7949 */ Sk.execPaused = 0 +/* 7950 */ } +/* 7951 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 7952 */ err = new Sk.builtin.ExternalError(err); +/* 7953 */ } +/* 7954 */ Sk.err = err; +/* 7955 */ err.traceback.push({ +/* 7956 */ lineno: $currLineNo, +/* 7957 */ colno: $currColNo, +/* 7958 */ filename: 'src/lib/traceback.py', +/* 7959 */ scope: 'line' +/* 7960 */ }); +/* 7961 */ if ($exc.length > 0) { +/* 7962 */ $err = err; +/* 7963 */ $blk = $exc.pop(); +/* 7964 */ continue +/* 7965 */ } else { +/* 7966 */ throw err; +/* 7967 */ } +/* 7968 */ } +/* 7969 */ } +/* 7970 */ }); +/* 7971 */ $scope587.$const589 = new Sk.builtin.str('_line'); +/* 7972 */ $scope587.$const595 = new Sk.builtin.str('getline'); +/* 7973 */ $scope587.$const597 = new Sk.builtin.str('filename'); +/* 7974 */ $scope587.$const599 = new Sk.builtin.str('lineno'); +/* 7975 */ $scope587.$const602 = new Sk.builtin.str('strip'); +/* 7976 */ var $scope608 = (function $walk_stack609$($gen) { +/* 7977 */ // generator +/* 7978 */ var $loadname610, $compareres611, $loadgbl614, $loadgbl614, $lattr616, $loadgbl614, $lattr616, $call617, $loadgbl614, $lattr616, $call617, $lattr619, $loadname621, $compareres622, $loadname625, $loadname626, $loadname632; +/* 7979 */ var $wakeFromSuspension = function() { +/* 7980 */ var susp = $scope608.$wakingSuspension; +/* 7981 */ $scope608.$wakingSuspension = undefined; +/* 7982 */ $blk = susp.$blk; +/* 7983 */ $loc = susp.$loc; +/* 7984 */ $gbl = susp.$gbl; +/* 7985 */ $exc = susp.$exc; +/* 7986 */ $err = susp.$err; +/* 7987 */ $postfinally = susp.$postfinally; +/* 7988 */ $currLineNo = susp.$lineno; +/* 7989 */ $currColNo = susp.$colno; +/* 7990 */ Sk.lastYield = Date.now(); +/* 7991 */ $loadname610 = susp.$tmps.$loadname610; +/* 7992 */ $compareres611 = susp.$tmps.$compareres611; +/* 7993 */ $loadgbl614 = susp.$tmps.$loadgbl614; +/* 7994 */ $lattr616 = susp.$tmps.$lattr616; +/* 7995 */ $call617 = susp.$tmps.$call617; +/* 7996 */ $lattr619 = susp.$tmps.$lattr619; +/* 7997 */ $loadname621 = susp.$tmps.$loadname621; +/* 7998 */ $compareres622 = susp.$tmps.$compareres622; +/* 7999 */ $loadname625 = susp.$tmps.$loadname625; +/* 8000 */ $loadname626 = susp.$tmps.$loadname626; +/* 8001 */ $loadname632 = susp.$tmps.$loadname632; +/* 8002 */ try { +/* 8003 */ $ret = susp.child.resume(); +/* 8004 */ } catch (err) { +/* 8005 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 8006 */ Sk.execStart = Date.now(); +/* 8007 */ Sk.execPaused = 0 +/* 8008 */ } +/* 8009 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 8010 */ err = new Sk.builtin.ExternalError(err); +/* 8011 */ } +/* 8012 */ Sk.err = err; +/* 8013 */ err.traceback.push({ +/* 8014 */ lineno: $currLineNo, +/* 8015 */ colno: $currColNo, +/* 8016 */ filename: 'src/lib/traceback.py', +/* 8017 */ scope: '$scope608' +/* 8018 */ }); +/* 8019 */ if ($exc.length > 0) { +/* 8020 */ $err = err; +/* 8021 */ $blk = $exc.pop(); +/* 8022 */ } else { +/* 8023 */ throw err; +/* 8024 */ } +/* 8025 */ } +/* 8026 */ }; +/* 8027 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 8028 */ var susp = new Sk.misceval.Suspension(); +/* 8029 */ susp.child = $child; +/* 8030 */ susp.resume = function() { +/* 8031 */ $scope608.$wakingSuspension = susp; +/* 8032 */ return $scope608($gen); +/* 8033 */ }; +/* 8034 */ susp.data = susp.child.data; +/* 8035 */ susp.$blk = $blk; +/* 8036 */ susp.$loc = $loc; +/* 8037 */ susp.$gbl = $gbl; +/* 8038 */ susp.$exc = $exc; +/* 8039 */ susp.$err = $err; +/* 8040 */ susp.$postfinally = $postfinally; +/* 8041 */ susp.$filename = $filename; +/* 8042 */ susp.$lineno = $lineno; +/* 8043 */ susp.$colno = $colno; +/* 8044 */ susp.optional = susp.child.optional; +/* 8045 */ susp.$tmps = { +/* 8046 */ "$loadname610": $loadname610, +/* 8047 */ "$compareres611": $compareres611, +/* 8048 */ "$loadgbl614": $loadgbl614, +/* 8049 */ "$lattr616": $lattr616, +/* 8050 */ "$call617": $call617, +/* 8051 */ "$lattr619": $lattr619, +/* 8052 */ "$loadname621": $loadname621, +/* 8053 */ "$compareres622": $compareres622, +/* 8054 */ "$loadname625": $loadname625, +/* 8055 */ "$loadname626": $loadname626, +/* 8056 */ "$loadname632": $loadname632 +/* 8057 */ }; +/* 8058 */ return susp; +/* 8059 */ }; +/* 8060 */ var $blk = $gen.gi$resumeat, +/* 8061 */ $exc = [], +/* 8062 */ $loc = $gen.gi$locals, +/* 8063 */ $cell = {}, +/* 8064 */ $gbl = this, +/* 8065 */ $err = undefined, +/* 8066 */ $ret = undefined, +/* 8067 */ $postfinally = undefined, +/* 8068 */ $currLineNo = undefined, +/* 8069 */ $currColNo = undefined; +/* 8070 */ if (typeof Sk.execStart === 'undefined') { +/* 8071 */ Sk.execStart = Date.now(); +/* 8072 */ Sk.execPaused = 0 +/* 8073 */ } +/* 8074 */ if (typeof Sk.lastYield === 'undefined') { +/* 8075 */ Sk.lastYield = Date.now() +/* 8076 */ } +/* 8077 */ if ($scope608.$wakingSuspension !== undefined) { +/* 8078 */ $wakeFromSuspension(); +/* 8079 */ } else {} +/* 8080 */ while (true) { +/* 8081 */ try { +/* 8082 */ var $dateNow = Date.now(); +/* 8083 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 8084 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 8085 */ } +/* 8086 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 8087 */ var $susp = $saveSuspension({ +/* 8088 */ data: { +/* 8089 */ type: 'Sk.yield' +/* 8090 */ }, +/* 8091 */ resume: function() {} +/* 8092 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 8093 */ $susp.$blk = $blk; +/* 8094 */ $susp.optional = true; +/* 8095 */ return $susp; +/* 8096 */ } +/* 8097 */ switch ($blk) { +/* 8098 */ case 0: +/* 8099 */ /* --- codeobj entry --- */ +/* 8100 */ // +/* 8101 */ // line 215: +/* 8102 */ // if f is None: +/* 8103 */ // ^ +/* 8104 */ // +/* 8105 */ +/* 8106 */ $currLineNo = Sk.currLineNo = 215; +/* 8107 */ $currColNo = Sk.currColNo = 4; +/* 8108 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8109 */ var $loadname610 = $loc.f !== undefined ? $loc.f : Sk.misceval.loadname('f', $gbl);; +/* 8110 */ var $compareres611 = null; +/* 8111 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($loadname610, Sk.builtin.none.none$, 'Is', true)); +/* 8112 */ $blk = 3; /* allowing case fallthrough */ +/* 8113 */ case 3: +/* 8114 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8115 */ return $saveSuspension($ret, 'src/lib/traceback.py', 215, 7); +/* 8116 */ } +/* 8117 */ $compareres611 = $ret; +/* 8118 */ var $jfalse612 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 8119 */ if ($jfalse612) { +/* 8120 */ /*test failed */ +/* 8121 */ $blk = 2; +/* 8122 */ continue; +/* 8123 */ } +/* 8124 */ $blk = 2; /* allowing case fallthrough */ +/* 8125 */ case 2: +/* 8126 */ /* --- done --- */ var $jfalse613 = ($compareres611 === false || !Sk.misceval.isTrue($compareres611)); +/* 8127 */ if ($jfalse613) { +/* 8128 */ /*test failed */ +/* 8129 */ $blk = 1; +/* 8130 */ continue; +/* 8131 */ } +/* 8132 */ // +/* 8133 */ // line 216: +/* 8134 */ // f = sys._getframe().f_back.f_back +/* 8135 */ // ^ +/* 8136 */ // +/* 8137 */ +/* 8138 */ $currLineNo = Sk.currLineNo = 216; +/* 8139 */ $currColNo = Sk.currColNo = 8; +/* 8140 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8141 */ var $loadgbl614 = Sk.misceval.loadname('sys', $gbl); +/* 8142 */ $ret = Sk.abstr.gattr($loadgbl614, $scope608.$const615, true); +/* 8143 */ $blk = 4; /* allowing case fallthrough */ +/* 8144 */ case 4: +/* 8145 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8146 */ return $saveSuspension($ret, 'src/lib/traceback.py', 216, 12); +/* 8147 */ } +/* 8148 */ var $lattr616 = $ret; +/* 8149 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr616); +/* 8150 */ $blk = 5; /* allowing case fallthrough */ +/* 8151 */ case 5: +/* 8152 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8153 */ return $saveSuspension($ret, 'src/lib/traceback.py', 216, 12); +/* 8154 */ } +/* 8155 */ var $call617 = $ret; +/* 8156 */ // +/* 8157 */ // line 216: +/* 8158 */ // f = sys._getframe().f_back.f_back +/* 8159 */ // ^ +/* 8160 */ // +/* 8161 */ +/* 8162 */ $currLineNo = Sk.currLineNo = 216; +/* 8163 */ $currColNo = Sk.currColNo = 12; +/* 8164 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8165 */ $ret = Sk.abstr.gattr($call617, $scope608.$const618, true); +/* 8166 */ $blk = 6; /* allowing case fallthrough */ +/* 8167 */ case 6: +/* 8168 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8169 */ return $saveSuspension($ret, 'src/lib/traceback.py', 216, 12); +/* 8170 */ } +/* 8171 */ var $lattr619 = $ret; +/* 8172 */ $ret = Sk.abstr.gattr($lattr619, $scope608.$const618, true); +/* 8173 */ $blk = 7; /* allowing case fallthrough */ +/* 8174 */ case 7: +/* 8175 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8176 */ return $saveSuspension($ret, 'src/lib/traceback.py', 216, 12); +/* 8177 */ } +/* 8178 */ var $lattr620 = $ret; +/* 8179 */ $loc.f = $lattr620; +/* 8180 */ $blk = 1; /* allowing case fallthrough */ +/* 8181 */ case 1: +/* 8182 */ /* --- end of if --- */ +/* 8183 */ // +/* 8184 */ // line 217: +/* 8185 */ // while f is not None: +/* 8186 */ // ^ +/* 8187 */ // +/* 8188 */ +/* 8189 */ $currLineNo = Sk.currLineNo = 217; +/* 8190 */ $currColNo = Sk.currColNo = 4; +/* 8191 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8192 */ $blk = 8; /* allowing case fallthrough */ +/* 8193 */ case 8: +/* 8194 */ /* --- while test --- */ +/* 8195 */ // +/* 8196 */ // line 217: +/* 8197 */ // while f is not None: +/* 8198 */ // ^ +/* 8199 */ // +/* 8200 */ +/* 8201 */ $currLineNo = Sk.currLineNo = 217; +/* 8202 */ $currColNo = Sk.currColNo = 4; +/* 8203 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8204 */ var $loadname621 = $loc.f !== undefined ? $loc.f : Sk.misceval.loadname('f', $gbl);; +/* 8205 */ var $compareres622 = null; +/* 8206 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($loadname621, Sk.builtin.none.none$, 'IsNot', true)); +/* 8207 */ $blk = 12; /* allowing case fallthrough */ +/* 8208 */ case 12: +/* 8209 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8210 */ return $saveSuspension($ret, 'src/lib/traceback.py', 217, 10); +/* 8211 */ } +/* 8212 */ $compareres622 = $ret; +/* 8213 */ var $jfalse623 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 8214 */ if ($jfalse623) { +/* 8215 */ /*test failed */ +/* 8216 */ $blk = 11; +/* 8217 */ continue; +/* 8218 */ } +/* 8219 */ $blk = 11; /* allowing case fallthrough */ +/* 8220 */ case 11: +/* 8221 */ /* --- done --- */ var $jfalse624 = ($compareres622 === false || !Sk.misceval.isTrue($compareres622)); +/* 8222 */ if ($jfalse624) { +/* 8223 */ /*test failed */ +/* 8224 */ $blk = 9; +/* 8225 */ continue; +/* 8226 */ } +/* 8227 */ $blk = 10; /* allowing case fallthrough */ +/* 8228 */ case 10: +/* 8229 */ /* --- while body --- */ +/* 8230 */ // +/* 8231 */ // line 218: +/* 8232 */ // yield f, f.f_lineno +/* 8233 */ // ^ +/* 8234 */ // +/* 8235 */ +/* 8236 */ $currLineNo = Sk.currLineNo = 218; +/* 8237 */ $currColNo = Sk.currColNo = 8; +/* 8238 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8239 */ var $loadname625 = $loc.f !== undefined ? $loc.f : Sk.misceval.loadname('f', $gbl);; +/* 8240 */ var $loadname626 = $loc.f !== undefined ? $loc.f : Sk.misceval.loadname('f', $gbl);; +/* 8241 */ $ret = Sk.abstr.gattr($loadname626, $scope608.$const627, true); +/* 8242 */ $blk = 13; /* allowing case fallthrough */ +/* 8243 */ case 13: +/* 8244 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8245 */ return $saveSuspension($ret, 'src/lib/traceback.py', 218, 17); +/* 8246 */ } +/* 8247 */ var $lattr628 = $ret; +/* 8248 */ var $elem629 = $loadname625; +/* 8249 */ var $elem630 = $lattr628; +/* 8250 */ var $loadtuple631 = new Sk.builtins['tuple']([$elem629, $elem630]); +/* 8251 */ return [ /*resume*/ 14, /*ret*/ $loadtuple631]; +/* 8252 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 8253 */ case 9: +/* 8254 */ /* --- after while --- */ return Sk.builtin.none.none$; +/* 8255 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 8256 */ case 14: +/* 8257 */ /* --- after yield --- */ +/* 8258 */ // +/* 8259 */ // line 219: +/* 8260 */ // f = f.f_back +/* 8261 */ // ^ +/* 8262 */ // +/* 8263 */ +/* 8264 */ $currLineNo = Sk.currLineNo = 219; +/* 8265 */ $currColNo = Sk.currColNo = 8; +/* 8266 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8267 */ var $loadname632 = $loc.f !== undefined ? $loc.f : Sk.misceval.loadname('f', $gbl);; +/* 8268 */ $ret = Sk.abstr.gattr($loadname632, $scope608.$const618, true); +/* 8269 */ $blk = 15; /* allowing case fallthrough */ +/* 8270 */ case 15: +/* 8271 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8272 */ return $saveSuspension($ret, 'src/lib/traceback.py', 219, 12); +/* 8273 */ } +/* 8274 */ var $lattr633 = $ret; +/* 8275 */ $loc.f = $lattr633; +/* 8276 */ $blk = 8; /* jump */ +/* 8277 */ continue; +/* 8278 */ } +/* 8279 */ } catch (err) { +/* 8280 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 8281 */ Sk.execStart = Date.now(); +/* 8282 */ Sk.execPaused = 0 +/* 8283 */ } +/* 8284 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 8285 */ err = new Sk.builtin.ExternalError(err); +/* 8286 */ } +/* 8287 */ Sk.err = err; +/* 8288 */ err.traceback.push({ +/* 8289 */ lineno: $currLineNo, +/* 8290 */ colno: $currColNo, +/* 8291 */ filename: 'src/lib/traceback.py', +/* 8292 */ scope: 'walk_stack' +/* 8293 */ }); +/* 8294 */ if ($exc.length > 0) { +/* 8295 */ $err = err; +/* 8296 */ $blk = $exc.pop(); +/* 8297 */ continue +/* 8298 */ } else { +/* 8299 */ throw err; +/* 8300 */ } +/* 8301 */ } +/* 8302 */ } +/* 8303 */ }); +/* 8304 */ $scope608.$const615 = new Sk.builtin.str('_getframe'); +/* 8305 */ $scope608.$const618 = new Sk.builtin.str('f_back'); +/* 8306 */ $scope608.$const627 = new Sk.builtin.str('f_lineno'); +/* 8307 */ var $scope635 = (function $walk_tb636$($gen) { +/* 8308 */ // generator +/* 8309 */ var $loadname637, $compareres638, $loadname641, $loadname641, $lattr643, $loadname644, $loadname650; +/* 8310 */ var $wakeFromSuspension = function() { +/* 8311 */ var susp = $scope635.$wakingSuspension; +/* 8312 */ $scope635.$wakingSuspension = undefined; +/* 8313 */ $blk = susp.$blk; +/* 8314 */ $loc = susp.$loc; +/* 8315 */ $gbl = susp.$gbl; +/* 8316 */ $exc = susp.$exc; +/* 8317 */ $err = susp.$err; +/* 8318 */ $postfinally = susp.$postfinally; +/* 8319 */ $currLineNo = susp.$lineno; +/* 8320 */ $currColNo = susp.$colno; +/* 8321 */ Sk.lastYield = Date.now(); +/* 8322 */ $loadname637 = susp.$tmps.$loadname637; +/* 8323 */ $compareres638 = susp.$tmps.$compareres638; +/* 8324 */ $loadname641 = susp.$tmps.$loadname641; +/* 8325 */ $lattr643 = susp.$tmps.$lattr643; +/* 8326 */ $loadname644 = susp.$tmps.$loadname644; +/* 8327 */ $loadname650 = susp.$tmps.$loadname650; +/* 8328 */ try { +/* 8329 */ $ret = susp.child.resume(); +/* 8330 */ } catch (err) { +/* 8331 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 8332 */ Sk.execStart = Date.now(); +/* 8333 */ Sk.execPaused = 0 +/* 8334 */ } +/* 8335 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 8336 */ err = new Sk.builtin.ExternalError(err); +/* 8337 */ } +/* 8338 */ Sk.err = err; +/* 8339 */ err.traceback.push({ +/* 8340 */ lineno: $currLineNo, +/* 8341 */ colno: $currColNo, +/* 8342 */ filename: 'src/lib/traceback.py', +/* 8343 */ scope: '$scope635' +/* 8344 */ }); +/* 8345 */ if ($exc.length > 0) { +/* 8346 */ $err = err; +/* 8347 */ $blk = $exc.pop(); +/* 8348 */ } else { +/* 8349 */ throw err; +/* 8350 */ } +/* 8351 */ } +/* 8352 */ }; +/* 8353 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 8354 */ var susp = new Sk.misceval.Suspension(); +/* 8355 */ susp.child = $child; +/* 8356 */ susp.resume = function() { +/* 8357 */ $scope635.$wakingSuspension = susp; +/* 8358 */ return $scope635($gen); +/* 8359 */ }; +/* 8360 */ susp.data = susp.child.data; +/* 8361 */ susp.$blk = $blk; +/* 8362 */ susp.$loc = $loc; +/* 8363 */ susp.$gbl = $gbl; +/* 8364 */ susp.$exc = $exc; +/* 8365 */ susp.$err = $err; +/* 8366 */ susp.$postfinally = $postfinally; +/* 8367 */ susp.$filename = $filename; +/* 8368 */ susp.$lineno = $lineno; +/* 8369 */ susp.$colno = $colno; +/* 8370 */ susp.optional = susp.child.optional; +/* 8371 */ susp.$tmps = { +/* 8372 */ "$loadname637": $loadname637, +/* 8373 */ "$compareres638": $compareres638, +/* 8374 */ "$loadname641": $loadname641, +/* 8375 */ "$lattr643": $lattr643, +/* 8376 */ "$loadname644": $loadname644, +/* 8377 */ "$loadname650": $loadname650 +/* 8378 */ }; +/* 8379 */ return susp; +/* 8380 */ }; +/* 8381 */ var $blk = $gen.gi$resumeat, +/* 8382 */ $exc = [], +/* 8383 */ $loc = $gen.gi$locals, +/* 8384 */ $cell = {}, +/* 8385 */ $gbl = this, +/* 8386 */ $err = undefined, +/* 8387 */ $ret = undefined, +/* 8388 */ $postfinally = undefined, +/* 8389 */ $currLineNo = undefined, +/* 8390 */ $currColNo = undefined; +/* 8391 */ if (typeof Sk.execStart === 'undefined') { +/* 8392 */ Sk.execStart = Date.now(); +/* 8393 */ Sk.execPaused = 0 +/* 8394 */ } +/* 8395 */ if (typeof Sk.lastYield === 'undefined') { +/* 8396 */ Sk.lastYield = Date.now() +/* 8397 */ } +/* 8398 */ if ($scope635.$wakingSuspension !== undefined) { +/* 8399 */ $wakeFromSuspension(); +/* 8400 */ } else {} +/* 8401 */ while (true) { +/* 8402 */ try { +/* 8403 */ var $dateNow = Date.now(); +/* 8404 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 8405 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 8406 */ } +/* 8407 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 8408 */ var $susp = $saveSuspension({ +/* 8409 */ data: { +/* 8410 */ type: 'Sk.yield' +/* 8411 */ }, +/* 8412 */ resume: function() {} +/* 8413 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 8414 */ $susp.$blk = $blk; +/* 8415 */ $susp.optional = true; +/* 8416 */ return $susp; +/* 8417 */ } +/* 8418 */ switch ($blk) { +/* 8419 */ case 0: +/* 8420 */ /* --- codeobj entry --- */ +/* 8421 */ // +/* 8422 */ // line 224: +/* 8423 */ // while tb is not None: +/* 8424 */ // ^ +/* 8425 */ // +/* 8426 */ +/* 8427 */ $currLineNo = Sk.currLineNo = 224; +/* 8428 */ $currColNo = Sk.currColNo = 4; +/* 8429 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8430 */ $blk = 1; /* allowing case fallthrough */ +/* 8431 */ case 1: +/* 8432 */ /* --- while test --- */ +/* 8433 */ // +/* 8434 */ // line 224: +/* 8435 */ // while tb is not None: +/* 8436 */ // ^ +/* 8437 */ // +/* 8438 */ +/* 8439 */ $currLineNo = Sk.currLineNo = 224; +/* 8440 */ $currColNo = Sk.currColNo = 4; +/* 8441 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8442 */ var $loadname637 = $loc.tb !== undefined ? $loc.tb : Sk.misceval.loadname('tb', $gbl);; +/* 8443 */ var $compareres638 = null; +/* 8444 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($loadname637, Sk.builtin.none.none$, 'IsNot', true)); +/* 8445 */ $blk = 5; /* allowing case fallthrough */ +/* 8446 */ case 5: +/* 8447 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8448 */ return $saveSuspension($ret, 'src/lib/traceback.py', 224, 10); +/* 8449 */ } +/* 8450 */ $compareres638 = $ret; +/* 8451 */ var $jfalse639 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 8452 */ if ($jfalse639) { +/* 8453 */ /*test failed */ +/* 8454 */ $blk = 4; +/* 8455 */ continue; +/* 8456 */ } +/* 8457 */ $blk = 4; /* allowing case fallthrough */ +/* 8458 */ case 4: +/* 8459 */ /* --- done --- */ var $jfalse640 = ($compareres638 === false || !Sk.misceval.isTrue($compareres638)); +/* 8460 */ if ($jfalse640) { +/* 8461 */ /*test failed */ +/* 8462 */ $blk = 2; +/* 8463 */ continue; +/* 8464 */ } +/* 8465 */ $blk = 3; /* allowing case fallthrough */ +/* 8466 */ case 3: +/* 8467 */ /* --- while body --- */ +/* 8468 */ // +/* 8469 */ // line 225: +/* 8470 */ // yield tb.tb_frame, tb.tb_lineno +/* 8471 */ // ^ +/* 8472 */ // +/* 8473 */ +/* 8474 */ $currLineNo = Sk.currLineNo = 225; +/* 8475 */ $currColNo = Sk.currColNo = 8; +/* 8476 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8477 */ var $loadname641 = $loc.tb !== undefined ? $loc.tb : Sk.misceval.loadname('tb', $gbl);; +/* 8478 */ $ret = Sk.abstr.gattr($loadname641, $scope635.$const642, true); +/* 8479 */ $blk = 6; /* allowing case fallthrough */ +/* 8480 */ case 6: +/* 8481 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8482 */ return $saveSuspension($ret, 'src/lib/traceback.py', 225, 14); +/* 8483 */ } +/* 8484 */ var $lattr643 = $ret; +/* 8485 */ var $loadname644 = $loc.tb !== undefined ? $loc.tb : Sk.misceval.loadname('tb', $gbl);; +/* 8486 */ $ret = Sk.abstr.gattr($loadname644, $scope635.$const645, true); +/* 8487 */ $blk = 7; /* allowing case fallthrough */ +/* 8488 */ case 7: +/* 8489 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8490 */ return $saveSuspension($ret, 'src/lib/traceback.py', 225, 27); +/* 8491 */ } +/* 8492 */ var $lattr646 = $ret; +/* 8493 */ var $elem647 = $lattr643; +/* 8494 */ var $elem648 = $lattr646; +/* 8495 */ var $loadtuple649 = new Sk.builtins['tuple']([$elem647, $elem648]); +/* 8496 */ return [ /*resume*/ 8, /*ret*/ $loadtuple649]; +/* 8497 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 8498 */ case 2: +/* 8499 */ /* --- after while --- */ return Sk.builtin.none.none$; +/* 8500 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 8501 */ case 8: +/* 8502 */ /* --- after yield --- */ +/* 8503 */ // +/* 8504 */ // line 226: +/* 8505 */ // tb = tb.tb_next +/* 8506 */ // ^ +/* 8507 */ // +/* 8508 */ +/* 8509 */ $currLineNo = Sk.currLineNo = 226; +/* 8510 */ $currColNo = Sk.currColNo = 8; +/* 8511 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8512 */ var $loadname650 = $loc.tb !== undefined ? $loc.tb : Sk.misceval.loadname('tb', $gbl);; +/* 8513 */ $ret = Sk.abstr.gattr($loadname650, $scope635.$const651, true); +/* 8514 */ $blk = 9; /* allowing case fallthrough */ +/* 8515 */ case 9: +/* 8516 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8517 */ return $saveSuspension($ret, 'src/lib/traceback.py', 226, 13); +/* 8518 */ } +/* 8519 */ var $lattr652 = $ret; +/* 8520 */ $loc.tb = $lattr652; +/* 8521 */ $blk = 1; /* jump */ +/* 8522 */ continue; +/* 8523 */ } +/* 8524 */ } catch (err) { +/* 8525 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 8526 */ Sk.execStart = Date.now(); +/* 8527 */ Sk.execPaused = 0 +/* 8528 */ } +/* 8529 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 8530 */ err = new Sk.builtin.ExternalError(err); +/* 8531 */ } +/* 8532 */ Sk.err = err; +/* 8533 */ err.traceback.push({ +/* 8534 */ lineno: $currLineNo, +/* 8535 */ colno: $currColNo, +/* 8536 */ filename: 'src/lib/traceback.py', +/* 8537 */ scope: 'walk_tb' +/* 8538 */ }); +/* 8539 */ if ($exc.length > 0) { +/* 8540 */ $err = err; +/* 8541 */ $blk = $exc.pop(); +/* 8542 */ continue +/* 8543 */ } else { +/* 8544 */ throw err; +/* 8545 */ } +/* 8546 */ } +/* 8547 */ } +/* 8548 */ }); +/* 8549 */ $scope635.$const642 = new Sk.builtin.str('tb_frame'); +/* 8550 */ $scope635.$const645 = new Sk.builtin.str('tb_lineno'); +/* 8551 */ $scope635.$const651 = new Sk.builtin.str('tb_next'); +/* 8552 */ var $scope655 = (function $extract656$(frame_gen, limit, lookup_lines, capture_locals) { +/* 8553 */ var co, f, f_locals, filename, fnames, lineno, name_$rn$, result; /* locals */ +/* 8554 */ var capture_locals, capture_locals, co, f, f, f, f, f, f, f, f, f_locals, f_locals, f_locals, filename, filename, filename, filename, filename, filename, fnames, fnames, fnames, frame_gen, frame_gen, frame_gen, frame_gen, frame_gen, frame_gen, limit, limit, limit, limit, limit, limit, limit, limit, limit, limit, lineno, lineno, lookup_lines, lookup_lines, name_$rn$, name_$rn$, result, result, result, result, $compareres657, $loadgbl660, $loadgbl661, $compareres664, $compareres664, $jfalse665, $boolopsucc666, $jfalse667, $compareres668, $compareres673, $compareres676, $loadgbl679, $loadgbl681, $loadgbl681, $lattr683, $unaryop684, $loadgbl686, $loadgbl688, $iter690, $iter690, $lattr700, $loadgbl702, $loadgbl702, $lattr704, $loadgbl702, $lattr704, $lattr706, $lattr712, $loadgbl713, $lattr712, $loadgbl713, $call714, $iter716, $iter716, $loadgbl718, $loadgbl718, $lattr720, $iter723, $iter723; +/* 8555 */ var $wakeFromSuspension = function() { +/* 8556 */ var susp = $scope655.$wakingSuspension; +/* 8557 */ $scope655.$wakingSuspension = undefined; +/* 8558 */ $blk = susp.$blk; +/* 8559 */ $loc = susp.$loc; +/* 8560 */ $gbl = susp.$gbl; +/* 8561 */ $exc = susp.$exc; +/* 8562 */ $err = susp.$err; +/* 8563 */ $postfinally = susp.$postfinally; +/* 8564 */ $currLineNo = susp.$lineno; +/* 8565 */ $currColNo = susp.$colno; +/* 8566 */ Sk.lastYield = Date.now(); +/* 8567 */ capture_locals = susp.$tmps.capture_locals; +/* 8568 */ co = susp.$tmps.co; +/* 8569 */ f = susp.$tmps.f; +/* 8570 */ f_locals = susp.$tmps.f_locals; +/* 8571 */ filename = susp.$tmps.filename; +/* 8572 */ fnames = susp.$tmps.fnames; +/* 8573 */ frame_gen = susp.$tmps.frame_gen; +/* 8574 */ limit = susp.$tmps.limit; +/* 8575 */ lineno = susp.$tmps.lineno; +/* 8576 */ lookup_lines = susp.$tmps.lookup_lines; +/* 8577 */ name_$rn$ = susp.$tmps.name_$rn$; +/* 8578 */ result = susp.$tmps.result; +/* 8579 */ $compareres657 = susp.$tmps.$compareres657; +/* 8580 */ $loadgbl660 = susp.$tmps.$loadgbl660; +/* 8581 */ $loadgbl661 = susp.$tmps.$loadgbl661; +/* 8582 */ $compareres664 = susp.$tmps.$compareres664; +/* 8583 */ $jfalse665 = susp.$tmps.$jfalse665; +/* 8584 */ $boolopsucc666 = susp.$tmps.$boolopsucc666; +/* 8585 */ $jfalse667 = susp.$tmps.$jfalse667; +/* 8586 */ $compareres668 = susp.$tmps.$compareres668; +/* 8587 */ $compareres673 = susp.$tmps.$compareres673; +/* 8588 */ $compareres676 = susp.$tmps.$compareres676; +/* 8589 */ $loadgbl679 = susp.$tmps.$loadgbl679; +/* 8590 */ $loadgbl681 = susp.$tmps.$loadgbl681; +/* 8591 */ $lattr683 = susp.$tmps.$lattr683; +/* 8592 */ $unaryop684 = susp.$tmps.$unaryop684; +/* 8593 */ $loadgbl686 = susp.$tmps.$loadgbl686; +/* 8594 */ $loadgbl688 = susp.$tmps.$loadgbl688; +/* 8595 */ $iter690 = susp.$tmps.$iter690; +/* 8596 */ $lattr700 = susp.$tmps.$lattr700; +/* 8597 */ $loadgbl702 = susp.$tmps.$loadgbl702; +/* 8598 */ $lattr704 = susp.$tmps.$lattr704; +/* 8599 */ $lattr706 = susp.$tmps.$lattr706; +/* 8600 */ $lattr712 = susp.$tmps.$lattr712; +/* 8601 */ $loadgbl713 = susp.$tmps.$loadgbl713; +/* 8602 */ $call714 = susp.$tmps.$call714; +/* 8603 */ $iter716 = susp.$tmps.$iter716; +/* 8604 */ $loadgbl718 = susp.$tmps.$loadgbl718; +/* 8605 */ $lattr720 = susp.$tmps.$lattr720; +/* 8606 */ $iter723 = susp.$tmps.$iter723; +/* 8607 */ try { +/* 8608 */ $ret = susp.child.resume(); +/* 8609 */ } catch (err) { +/* 8610 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 8611 */ Sk.execStart = Date.now(); +/* 8612 */ Sk.execPaused = 0 +/* 8613 */ } +/* 8614 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 8615 */ err = new Sk.builtin.ExternalError(err); +/* 8616 */ } +/* 8617 */ Sk.err = err; +/* 8618 */ err.traceback.push({ +/* 8619 */ lineno: $currLineNo, +/* 8620 */ colno: $currColNo, +/* 8621 */ filename: 'src/lib/traceback.py', +/* 8622 */ scope: '$scope655' +/* 8623 */ }); +/* 8624 */ if ($exc.length > 0) { +/* 8625 */ $err = err; +/* 8626 */ $blk = $exc.pop(); +/* 8627 */ } else { +/* 8628 */ throw err; +/* 8629 */ } +/* 8630 */ } +/* 8631 */ }; +/* 8632 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 8633 */ var susp = new Sk.misceval.Suspension(); +/* 8634 */ susp.child = $child; +/* 8635 */ susp.resume = function() { +/* 8636 */ $scope655.$wakingSuspension = susp; +/* 8637 */ return $scope655(); +/* 8638 */ }; +/* 8639 */ susp.data = susp.child.data; +/* 8640 */ susp.$blk = $blk; +/* 8641 */ susp.$loc = $loc; +/* 8642 */ susp.$gbl = $gbl; +/* 8643 */ susp.$exc = $exc; +/* 8644 */ susp.$err = $err; +/* 8645 */ susp.$postfinally = $postfinally; +/* 8646 */ susp.$filename = $filename; +/* 8647 */ susp.$lineno = $lineno; +/* 8648 */ susp.$colno = $colno; +/* 8649 */ susp.optional = susp.child.optional; +/* 8650 */ susp.$tmps = { +/* 8651 */ "capture_locals": capture_locals, +/* 8652 */ "co": co, +/* 8653 */ "f": f, +/* 8654 */ "f_locals": f_locals, +/* 8655 */ "filename": filename, +/* 8656 */ "fnames": fnames, +/* 8657 */ "frame_gen": frame_gen, +/* 8658 */ "limit": limit, +/* 8659 */ "lineno": lineno, +/* 8660 */ "lookup_lines": lookup_lines, +/* 8661 */ "name_$rn$": name_$rn$, +/* 8662 */ "result": result, +/* 8663 */ "$compareres657": $compareres657, +/* 8664 */ "$loadgbl660": $loadgbl660, +/* 8665 */ "$loadgbl661": $loadgbl661, +/* 8666 */ "$compareres664": $compareres664, +/* 8667 */ "$jfalse665": $jfalse665, +/* 8668 */ "$boolopsucc666": $boolopsucc666, +/* 8669 */ "$jfalse667": $jfalse667, +/* 8670 */ "$compareres668": $compareres668, +/* 8671 */ "$compareres673": $compareres673, +/* 8672 */ "$compareres676": $compareres676, +/* 8673 */ "$loadgbl679": $loadgbl679, +/* 8674 */ "$loadgbl681": $loadgbl681, +/* 8675 */ "$lattr683": $lattr683, +/* 8676 */ "$unaryop684": $unaryop684, +/* 8677 */ "$loadgbl686": $loadgbl686, +/* 8678 */ "$loadgbl688": $loadgbl688, +/* 8679 */ "$iter690": $iter690, +/* 8680 */ "$lattr700": $lattr700, +/* 8681 */ "$loadgbl702": $loadgbl702, +/* 8682 */ "$lattr704": $lattr704, +/* 8683 */ "$lattr706": $lattr706, +/* 8684 */ "$lattr712": $lattr712, +/* 8685 */ "$loadgbl713": $loadgbl713, +/* 8686 */ "$call714": $call714, +/* 8687 */ "$iter716": $iter716, +/* 8688 */ "$loadgbl718": $loadgbl718, +/* 8689 */ "$lattr720": $lattr720, +/* 8690 */ "$iter723": $iter723 +/* 8691 */ }; +/* 8692 */ return susp; +/* 8693 */ }; +/* 8694 */ var $blk = 0, +/* 8695 */ $exc = [], +/* 8696 */ $loc = {}, +/* 8697 */ $cell = {}, +/* 8698 */ $gbl = this, +/* 8699 */ $err = undefined, +/* 8700 */ $ret = undefined, +/* 8701 */ $postfinally = undefined, +/* 8702 */ $currLineNo = undefined, +/* 8703 */ $currColNo = undefined; +/* 8704 */ if (typeof Sk.execStart === 'undefined') { +/* 8705 */ Sk.execStart = Date.now(); +/* 8706 */ Sk.execPaused = 0 +/* 8707 */ } +/* 8708 */ if (typeof Sk.lastYield === 'undefined') { +/* 8709 */ Sk.lastYield = Date.now() +/* 8710 */ } +/* 8711 */ if ($scope655.$wakingSuspension !== undefined) { +/* 8712 */ $wakeFromSuspension(); +/* 8713 */ } else {} +/* 8714 */ while (true) { +/* 8715 */ try { +/* 8716 */ var $dateNow = Date.now(); +/* 8717 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 8718 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 8719 */ } +/* 8720 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 8721 */ var $susp = $saveSuspension({ +/* 8722 */ data: { +/* 8723 */ type: 'Sk.yield' +/* 8724 */ }, +/* 8725 */ resume: function() {} +/* 8726 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 8727 */ $susp.$blk = $blk; +/* 8728 */ $susp.optional = true; +/* 8729 */ return $susp; +/* 8730 */ } +/* 8731 */ switch ($blk) { +/* 8732 */ case 0: +/* 8733 */ /* --- codeobj entry --- */ if (frame_gen === undefined) { +/* 8734 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame_gen\' referenced before assignment'); +/* 8735 */ } +/* 8736 */ if (limit === undefined) { +/* 8737 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 8738 */ } +/* 8739 */ if (lookup_lines === undefined) { +/* 8740 */ throw new Sk.builtin.UnboundLocalError('local variable \'lookup_lines\' referenced before assignment'); +/* 8741 */ } +/* 8742 */ if (capture_locals === undefined) { +/* 8743 */ throw new Sk.builtin.UnboundLocalError('local variable \'capture_locals\' referenced before assignment'); +/* 8744 */ } +/* 8745 */ +/* 8746 */ // +/* 8747 */ // line 234: +/* 8748 */ // if limit is None: +/* 8749 */ // ^ +/* 8750 */ // +/* 8751 */ +/* 8752 */ $currLineNo = Sk.currLineNo = 234; +/* 8753 */ $currColNo = Sk.currColNo = 4; +/* 8754 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8755 */ if (limit === undefined) { +/* 8756 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 8757 */ } +/* 8758 */ var $compareres657 = null; +/* 8759 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(limit, Sk.builtin.none.none$, 'Is', true)); +/* 8760 */ $blk = 3; /* allowing case fallthrough */ +/* 8761 */ case 3: +/* 8762 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8763 */ return $saveSuspension($ret, 'src/lib/traceback.py', 234, 7); +/* 8764 */ } +/* 8765 */ $compareres657 = $ret; +/* 8766 */ var $jfalse658 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 8767 */ if ($jfalse658) { +/* 8768 */ /*test failed */ +/* 8769 */ $blk = 2; +/* 8770 */ continue; +/* 8771 */ } +/* 8772 */ $blk = 2; /* allowing case fallthrough */ +/* 8773 */ case 2: +/* 8774 */ /* --- done --- */ var $jfalse659 = ($compareres657 === false || !Sk.misceval.isTrue($compareres657)); +/* 8775 */ if ($jfalse659) { +/* 8776 */ /*test failed */ +/* 8777 */ $blk = 1; +/* 8778 */ continue; +/* 8779 */ } +/* 8780 */ // +/* 8781 */ // line 235: +/* 8782 */ // limit = getattr(sys, 'tracebacklimit', None) +/* 8783 */ // ^ +/* 8784 */ // +/* 8785 */ +/* 8786 */ $currLineNo = Sk.currLineNo = 235; +/* 8787 */ $currColNo = Sk.currColNo = 8; +/* 8788 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8789 */ var $loadgbl660 = Sk.misceval.loadname('getattr', $gbl); +/* 8790 */ var $loadgbl661 = Sk.misceval.loadname('sys', $gbl); +/* 8791 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl660, [$loadgbl661, $scope655.$const662, Sk.builtin.none.none$]); +/* 8792 */ $blk = 4; /* allowing case fallthrough */ +/* 8793 */ case 4: +/* 8794 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8795 */ return $saveSuspension($ret, 'src/lib/traceback.py', 235, 16); +/* 8796 */ } +/* 8797 */ var $call663 = $ret; +/* 8798 */ // +/* 8799 */ // line 235: +/* 8800 */ // limit = getattr(sys, 'tracebacklimit', None) +/* 8801 */ // ^ +/* 8802 */ // +/* 8803 */ +/* 8804 */ $currLineNo = Sk.currLineNo = 235; +/* 8805 */ $currColNo = Sk.currColNo = 16; +/* 8806 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8807 */ limit = $call663; +/* 8808 */ // +/* 8809 */ // line 236: +/* 8810 */ // if limit is not None and limit < 0: +/* 8811 */ // ^ +/* 8812 */ // +/* 8813 */ +/* 8814 */ $currLineNo = Sk.currLineNo = 236; +/* 8815 */ $currColNo = Sk.currColNo = 8; +/* 8816 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8817 */ if (limit === undefined) { +/* 8818 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 8819 */ } +/* 8820 */ var $compareres664 = null; +/* 8821 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(limit, Sk.builtin.none.none$, 'IsNot', true)); +/* 8822 */ $blk = 8; /* allowing case fallthrough */ +/* 8823 */ case 8: +/* 8824 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8825 */ return $saveSuspension($ret, 'src/lib/traceback.py', 236, 11); +/* 8826 */ } +/* 8827 */ $compareres664 = $ret; +/* 8828 */ var $jfalse665 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 8829 */ if ($jfalse665) { +/* 8830 */ /*test failed */ +/* 8831 */ $blk = 7; +/* 8832 */ continue; +/* 8833 */ } +/* 8834 */ $blk = 7; /* allowing case fallthrough */ +/* 8835 */ case 7: +/* 8836 */ /* --- done --- */ var $boolopsucc666 = $compareres664; +/* 8837 */ $boolopsucc666 = $compareres664; +/* 8838 */ var $jfalse667 = ($compareres664 === false || !Sk.misceval.isTrue($compareres664)); +/* 8839 */ if ($jfalse667) { +/* 8840 */ /*test failed */ +/* 8841 */ $blk = 6; +/* 8842 */ continue; +/* 8843 */ } +/* 8844 */ if (limit === undefined) { +/* 8845 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 8846 */ } +/* 8847 */ var $compareres668 = null; +/* 8848 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(limit, $scope655.$const669, 'Lt', true)); +/* 8849 */ $blk = 10; /* allowing case fallthrough */ +/* 8850 */ case 10: +/* 8851 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8852 */ return $saveSuspension($ret, 'src/lib/traceback.py', 236, 33); +/* 8853 */ } +/* 8854 */ $compareres668 = $ret; +/* 8855 */ var $jfalse670 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 8856 */ if ($jfalse670) { +/* 8857 */ /*test failed */ +/* 8858 */ $blk = 9; +/* 8859 */ continue; +/* 8860 */ } +/* 8861 */ $blk = 9; /* allowing case fallthrough */ +/* 8862 */ case 9: +/* 8863 */ /* --- done --- */ $boolopsucc666 = $compareres668; +/* 8864 */ var $jfalse671 = ($compareres668 === false || !Sk.misceval.isTrue($compareres668)); +/* 8865 */ if ($jfalse671) { +/* 8866 */ /*test failed */ +/* 8867 */ $blk = 6; +/* 8868 */ continue; +/* 8869 */ } +/* 8870 */ $blk = 6; /* allowing case fallthrough */ +/* 8871 */ case 6: +/* 8872 */ /* --- end of boolop --- */ var $jfalse672 = ($boolopsucc666 === false || !Sk.misceval.isTrue($boolopsucc666)); +/* 8873 */ if ($jfalse672) { +/* 8874 */ /*test failed */ +/* 8875 */ $blk = 5; +/* 8876 */ continue; +/* 8877 */ } +/* 8878 */ // +/* 8879 */ // line 237: +/* 8880 */ // limit = 0 +/* 8881 */ // ^ +/* 8882 */ // +/* 8883 */ +/* 8884 */ $currLineNo = Sk.currLineNo = 237; +/* 8885 */ $currColNo = Sk.currColNo = 12; +/* 8886 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8887 */ limit = $scope655.$const669; +/* 8888 */ $blk = 5; /* allowing case fallthrough */ +/* 8889 */ case 5: +/* 8890 */ /* --- end of if --- */ $blk = 1; /* allowing case fallthrough */ +/* 8891 */ case 1: +/* 8892 */ /* --- end of if --- */ +/* 8893 */ // +/* 8894 */ // line 238: +/* 8895 */ // if limit is not None: +/* 8896 */ // ^ +/* 8897 */ // +/* 8898 */ +/* 8899 */ $currLineNo = Sk.currLineNo = 238; +/* 8900 */ $currColNo = Sk.currColNo = 4; +/* 8901 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8902 */ if (limit === undefined) { +/* 8903 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 8904 */ } +/* 8905 */ var $compareres673 = null; +/* 8906 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(limit, Sk.builtin.none.none$, 'IsNot', true)); +/* 8907 */ $blk = 13; /* allowing case fallthrough */ +/* 8908 */ case 13: +/* 8909 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8910 */ return $saveSuspension($ret, 'src/lib/traceback.py', 238, 7); +/* 8911 */ } +/* 8912 */ $compareres673 = $ret; +/* 8913 */ var $jfalse674 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 8914 */ if ($jfalse674) { +/* 8915 */ /*test failed */ +/* 8916 */ $blk = 12; +/* 8917 */ continue; +/* 8918 */ } +/* 8919 */ $blk = 12; /* allowing case fallthrough */ +/* 8920 */ case 12: +/* 8921 */ /* --- done --- */ var $jfalse675 = ($compareres673 === false || !Sk.misceval.isTrue($compareres673)); +/* 8922 */ if ($jfalse675) { +/* 8923 */ /*test failed */ +/* 8924 */ $blk = 11; +/* 8925 */ continue; +/* 8926 */ } +/* 8927 */ // +/* 8928 */ // line 239: +/* 8929 */ // if limit >= 0: +/* 8930 */ // ^ +/* 8931 */ // +/* 8932 */ +/* 8933 */ $currLineNo = Sk.currLineNo = 239; +/* 8934 */ $currColNo = Sk.currColNo = 8; +/* 8935 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8936 */ if (limit === undefined) { +/* 8937 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 8938 */ } +/* 8939 */ var $compareres676 = null; +/* 8940 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(limit, $scope655.$const669, 'GtE', true)); +/* 8941 */ $blk = 17; /* allowing case fallthrough */ +/* 8942 */ case 17: +/* 8943 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8944 */ return $saveSuspension($ret, 'src/lib/traceback.py', 239, 11); +/* 8945 */ } +/* 8946 */ $compareres676 = $ret; +/* 8947 */ var $jfalse677 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 8948 */ if ($jfalse677) { +/* 8949 */ /*test failed */ +/* 8950 */ $blk = 16; +/* 8951 */ continue; +/* 8952 */ } +/* 8953 */ $blk = 16; /* allowing case fallthrough */ +/* 8954 */ case 16: +/* 8955 */ /* --- done --- */ var $jfalse678 = ($compareres676 === false || !Sk.misceval.isTrue($compareres676)); +/* 8956 */ if ($jfalse678) { +/* 8957 */ /*test failed */ +/* 8958 */ $blk = 15; +/* 8959 */ continue; +/* 8960 */ } +/* 8961 */ // +/* 8962 */ // line 240: +/* 8963 */ // frame_gen = islice(frame_gen, limit) +/* 8964 */ // ^ +/* 8965 */ // +/* 8966 */ +/* 8967 */ $currLineNo = Sk.currLineNo = 240; +/* 8968 */ $currColNo = Sk.currColNo = 12; +/* 8969 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8970 */ var $loadgbl679 = Sk.misceval.loadname('islice', $gbl); +/* 8971 */ if (frame_gen === undefined) { +/* 8972 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame_gen\' referenced before assignment'); +/* 8973 */ } +/* 8974 */ if (limit === undefined) { +/* 8975 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 8976 */ } +/* 8977 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl679, [frame_gen, limit]); +/* 8978 */ $blk = 18; /* allowing case fallthrough */ +/* 8979 */ case 18: +/* 8980 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 8981 */ return $saveSuspension($ret, 'src/lib/traceback.py', 240, 24); +/* 8982 */ } +/* 8983 */ var $call680 = $ret; +/* 8984 */ // +/* 8985 */ // line 240: +/* 8986 */ // frame_gen = islice(frame_gen, limit) +/* 8987 */ // ^ +/* 8988 */ // +/* 8989 */ +/* 8990 */ $currLineNo = Sk.currLineNo = 240; +/* 8991 */ $currColNo = Sk.currColNo = 24; +/* 8992 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 8993 */ frame_gen = $call680; +/* 8994 */ $blk = 14; /* allowing case fallthrough */ +/* 8995 */ case 14: +/* 8996 */ /* --- end of if --- */ $blk = 11; /* allowing case fallthrough */ +/* 8997 */ case 11: +/* 8998 */ /* --- end of if --- */ +/* 8999 */ // +/* 9000 */ // line 244: +/* 9001 */ // result = StackSummary() +/* 9002 */ // ^ +/* 9003 */ // +/* 9004 */ +/* 9005 */ $currLineNo = Sk.currLineNo = 244; +/* 9006 */ $currColNo = Sk.currColNo = 4; +/* 9007 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9008 */ var $loadgbl686 = Sk.misceval.loadname('StackSummary', $gbl); +/* 9009 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl686); +/* 9010 */ $blk = 21; /* allowing case fallthrough */ +/* 9011 */ case 21: +/* 9012 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9013 */ return $saveSuspension($ret, 'src/lib/traceback.py', 244, 13); +/* 9014 */ } +/* 9015 */ var $call687 = $ret; +/* 9016 */ // +/* 9017 */ // line 244: +/* 9018 */ // result = StackSummary() +/* 9019 */ // ^ +/* 9020 */ // +/* 9021 */ +/* 9022 */ $currLineNo = Sk.currLineNo = 244; +/* 9023 */ $currColNo = Sk.currColNo = 13; +/* 9024 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9025 */ result = $call687; +/* 9026 */ // +/* 9027 */ // line 245: +/* 9028 */ // fnames = set() +/* 9029 */ // ^ +/* 9030 */ // +/* 9031 */ +/* 9032 */ $currLineNo = Sk.currLineNo = 245; +/* 9033 */ $currColNo = Sk.currColNo = 4; +/* 9034 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9035 */ var $loadgbl688 = Sk.misceval.loadname('set', $gbl); +/* 9036 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl688); +/* 9037 */ $blk = 22; /* allowing case fallthrough */ +/* 9038 */ case 22: +/* 9039 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9040 */ return $saveSuspension($ret, 'src/lib/traceback.py', 245, 13); +/* 9041 */ } +/* 9042 */ var $call689 = $ret; +/* 9043 */ // +/* 9044 */ // line 245: +/* 9045 */ // fnames = set() +/* 9046 */ // ^ +/* 9047 */ // +/* 9048 */ +/* 9049 */ $currLineNo = Sk.currLineNo = 245; +/* 9050 */ $currColNo = Sk.currColNo = 13; +/* 9051 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9052 */ fnames = $call689; +/* 9053 */ // +/* 9054 */ // line 246: +/* 9055 */ // for f, lineno in frame_gen: +/* 9056 */ // ^ +/* 9057 */ // +/* 9058 */ +/* 9059 */ $currLineNo = Sk.currLineNo = 246; +/* 9060 */ $currColNo = Sk.currColNo = 4; +/* 9061 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9062 */ if (frame_gen === undefined) { +/* 9063 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame_gen\' referenced before assignment'); +/* 9064 */ } +/* 9065 */ var $iter690 = Sk.abstr.iter(frame_gen); +/* 9066 */ $blk = 23; /* allowing case fallthrough */ +/* 9067 */ case 23: +/* 9068 */ /* --- for start --- */ $ret = Sk.abstr.iternext($iter690, true); +/* 9069 */ $blk = 26; /* allowing case fallthrough */ +/* 9070 */ case 26: +/* 9071 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9072 */ return $saveSuspension($ret, 'src/lib/traceback.py', 246, 4); +/* 9073 */ } +/* 9074 */ var $next691 = $ret; +/* 9075 */ if ($next691 === undefined) { +/* 9076 */ $blk = 24; +/* 9077 */ continue; +/* 9078 */ } +/* 9079 */ var $items692 = Sk.abstr.sequenceUnpack($next691, 2); +/* 9080 */ f = $items692[0]; +/* 9081 */ lineno = $items692[1]; +/* 9082 */ // +/* 9083 */ // line 247: +/* 9084 */ // co = f.f_code +/* 9085 */ // ^ +/* 9086 */ // +/* 9087 */ +/* 9088 */ $currLineNo = Sk.currLineNo = 247; +/* 9089 */ $currColNo = Sk.currColNo = 8; +/* 9090 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9091 */ if (f === undefined) { +/* 9092 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 9093 */ } +/* 9094 */ $ret = Sk.abstr.gattr(f, $scope655.$const693, true); +/* 9095 */ $blk = 27; /* allowing case fallthrough */ +/* 9096 */ case 27: +/* 9097 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9098 */ return $saveSuspension($ret, 'src/lib/traceback.py', 247, 13); +/* 9099 */ } +/* 9100 */ var $lattr694 = $ret; +/* 9101 */ co = $lattr694; +/* 9102 */ // +/* 9103 */ // line 248: +/* 9104 */ // filename = f.co_filename #co.co_filename +/* 9105 */ // ^ +/* 9106 */ // +/* 9107 */ +/* 9108 */ $currLineNo = Sk.currLineNo = 248; +/* 9109 */ $currColNo = Sk.currColNo = 8; +/* 9110 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9111 */ if (f === undefined) { +/* 9112 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 9113 */ } +/* 9114 */ $ret = Sk.abstr.gattr(f, $scope655.$const695, true); +/* 9115 */ $blk = 28; /* allowing case fallthrough */ +/* 9116 */ case 28: +/* 9117 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9118 */ return $saveSuspension($ret, 'src/lib/traceback.py', 248, 19); +/* 9119 */ } +/* 9120 */ var $lattr696 = $ret; +/* 9121 */ filename = $lattr696; +/* 9122 */ // +/* 9123 */ // line 249: +/* 9124 */ // name = f.co_name #co.co_name +/* 9125 */ // ^ +/* 9126 */ // +/* 9127 */ +/* 9128 */ $currLineNo = Sk.currLineNo = 249; +/* 9129 */ $currColNo = Sk.currColNo = 8; +/* 9130 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9131 */ if (f === undefined) { +/* 9132 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 9133 */ } +/* 9134 */ $ret = Sk.abstr.gattr(f, $scope655.$const697, true); +/* 9135 */ $blk = 29; /* allowing case fallthrough */ +/* 9136 */ case 29: +/* 9137 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9138 */ return $saveSuspension($ret, 'src/lib/traceback.py', 249, 15); +/* 9139 */ } +/* 9140 */ var $lattr698 = $ret; +/* 9141 */ name_$rn$ = $lattr698; +/* 9142 */ // +/* 9143 */ // line 251: +/* 9144 */ // fnames.add(filename) +/* 9145 */ // ^ +/* 9146 */ // +/* 9147 */ +/* 9148 */ $currLineNo = Sk.currLineNo = 251; +/* 9149 */ $currColNo = Sk.currColNo = 8; +/* 9150 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9151 */ if (fnames === undefined) { +/* 9152 */ throw new Sk.builtin.UnboundLocalError('local variable \'fnames\' referenced before assignment'); +/* 9153 */ } +/* 9154 */ $ret = Sk.abstr.gattr(fnames, $scope655.$const699, true); +/* 9155 */ $blk = 30; /* allowing case fallthrough */ +/* 9156 */ case 30: +/* 9157 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9158 */ return $saveSuspension($ret, 'src/lib/traceback.py', 251, 8); +/* 9159 */ } +/* 9160 */ var $lattr700 = $ret; +/* 9161 */ if (filename === undefined) { +/* 9162 */ throw new Sk.builtin.UnboundLocalError('local variable \'filename\' referenced before assignment'); +/* 9163 */ } +/* 9164 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr700, [filename]); +/* 9165 */ $blk = 31; /* allowing case fallthrough */ +/* 9166 */ case 31: +/* 9167 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9168 */ return $saveSuspension($ret, 'src/lib/traceback.py', 251, 8); +/* 9169 */ } +/* 9170 */ var $call701 = $ret; +/* 9171 */ // +/* 9172 */ // line 251: +/* 9173 */ // fnames.add(filename) +/* 9174 */ // ^ +/* 9175 */ // +/* 9176 */ +/* 9177 */ $currLineNo = Sk.currLineNo = 251; +/* 9178 */ $currColNo = Sk.currColNo = 8; +/* 9179 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9180 */ // +/* 9181 */ // line 252: +/* 9182 */ // linecache.lazycache(filename, f.f_globals) +/* 9183 */ // ^ +/* 9184 */ // +/* 9185 */ +/* 9186 */ $currLineNo = Sk.currLineNo = 252; +/* 9187 */ $currColNo = Sk.currColNo = 8; +/* 9188 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9189 */ var $loadgbl702 = Sk.misceval.loadname('linecache', $gbl); +/* 9190 */ $ret = Sk.abstr.gattr($loadgbl702, $scope655.$const703, true); +/* 9191 */ $blk = 32; /* allowing case fallthrough */ +/* 9192 */ case 32: +/* 9193 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9194 */ return $saveSuspension($ret, 'src/lib/traceback.py', 252, 8); +/* 9195 */ } +/* 9196 */ var $lattr704 = $ret; +/* 9197 */ if (filename === undefined) { +/* 9198 */ throw new Sk.builtin.UnboundLocalError('local variable \'filename\' referenced before assignment'); +/* 9199 */ } +/* 9200 */ if (f === undefined) { +/* 9201 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 9202 */ } +/* 9203 */ $ret = Sk.abstr.gattr(f, $scope655.$const705, true); +/* 9204 */ $blk = 33; /* allowing case fallthrough */ +/* 9205 */ case 33: +/* 9206 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9207 */ return $saveSuspension($ret, 'src/lib/traceback.py', 252, 38); +/* 9208 */ } +/* 9209 */ var $lattr706 = $ret; +/* 9210 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr704, [filename, $lattr706]); +/* 9211 */ $blk = 34; /* allowing case fallthrough */ +/* 9212 */ case 34: +/* 9213 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9214 */ return $saveSuspension($ret, 'src/lib/traceback.py', 252, 8); +/* 9215 */ } +/* 9216 */ var $call707 = $ret; +/* 9217 */ // +/* 9218 */ // line 252: +/* 9219 */ // linecache.lazycache(filename, f.f_globals) +/* 9220 */ // ^ +/* 9221 */ // +/* 9222 */ +/* 9223 */ $currLineNo = Sk.currLineNo = 252; +/* 9224 */ $currColNo = Sk.currColNo = 8; +/* 9225 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9226 */ // +/* 9227 */ // line 254: +/* 9228 */ // if capture_locals: +/* 9229 */ // ^ +/* 9230 */ // +/* 9231 */ +/* 9232 */ $currLineNo = Sk.currLineNo = 254; +/* 9233 */ $currColNo = Sk.currColNo = 8; +/* 9234 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9235 */ if (capture_locals === undefined) { +/* 9236 */ throw new Sk.builtin.UnboundLocalError('local variable \'capture_locals\' referenced before assignment'); +/* 9237 */ } +/* 9238 */ var $jfalse708 = (capture_locals === false || !Sk.misceval.isTrue(capture_locals)); +/* 9239 */ if ($jfalse708) { +/* 9240 */ /*test failed */ +/* 9241 */ $blk = 36; +/* 9242 */ continue; +/* 9243 */ } +/* 9244 */ // +/* 9245 */ // line 255: +/* 9246 */ // f_locals = f.f_locals +/* 9247 */ // ^ +/* 9248 */ // +/* 9249 */ +/* 9250 */ $currLineNo = Sk.currLineNo = 255; +/* 9251 */ $currColNo = Sk.currColNo = 12; +/* 9252 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9253 */ if (f === undefined) { +/* 9254 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 9255 */ } +/* 9256 */ $ret = Sk.abstr.gattr(f, $scope655.$const709, true); +/* 9257 */ $blk = 37; /* allowing case fallthrough */ +/* 9258 */ case 37: +/* 9259 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9260 */ return $saveSuspension($ret, 'src/lib/traceback.py', 255, 23); +/* 9261 */ } +/* 9262 */ var $lattr710 = $ret; +/* 9263 */ f_locals = $lattr710; +/* 9264 */ $blk = 35; /* allowing case fallthrough */ +/* 9265 */ case 35: +/* 9266 */ /* --- end of if --- */ +/* 9267 */ // +/* 9268 */ // line 258: +/* 9269 */ // result.append(FrameSummary( +/* 9270 */ // ^ +/* 9271 */ // +/* 9272 */ +/* 9273 */ $currLineNo = Sk.currLineNo = 258; +/* 9274 */ $currColNo = Sk.currColNo = 8; +/* 9275 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9276 */ if (result === undefined) { +/* 9277 */ throw new Sk.builtin.UnboundLocalError('local variable \'result\' referenced before assignment'); +/* 9278 */ } +/* 9279 */ $ret = Sk.abstr.gattr(result, $scope655.$const711, true); +/* 9280 */ $blk = 38; /* allowing case fallthrough */ +/* 9281 */ case 38: +/* 9282 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9283 */ return $saveSuspension($ret, 'src/lib/traceback.py', 258, 8); +/* 9284 */ } +/* 9285 */ var $lattr712 = $ret; +/* 9286 */ var $loadgbl713 = Sk.misceval.loadname('FrameSummary', $gbl); +/* 9287 */ if (filename === undefined) { +/* 9288 */ throw new Sk.builtin.UnboundLocalError('local variable \'filename\' referenced before assignment'); +/* 9289 */ } +/* 9290 */ if (lineno === undefined) { +/* 9291 */ throw new Sk.builtin.UnboundLocalError('local variable \'lineno\' referenced before assignment'); +/* 9292 */ } +/* 9293 */ if (name_$rn$ === undefined) { +/* 9294 */ throw new Sk.builtin.UnboundLocalError('local variable \'name_$rn$\' referenced before assignment'); +/* 9295 */ } +/* 9296 */ if (f_locals === undefined) { +/* 9297 */ throw new Sk.builtin.UnboundLocalError('local variable \'f_locals\' referenced before assignment'); +/* 9298 */ } +/* 9299 */ $ret = Sk.misceval.applyOrSuspend($loadgbl713, undefined, undefined, ['lookup_line', Sk.builtin.bool.false$, 'locals', f_locals], [filename, lineno, name_$rn$]); +/* 9300 */ $blk = 39; /* allowing case fallthrough */ +/* 9301 */ case 39: +/* 9302 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9303 */ return $saveSuspension($ret, 'src/lib/traceback.py', 258, 22); +/* 9304 */ } +/* 9305 */ var $call714 = $ret; +/* 9306 */ // +/* 9307 */ // line 258: +/* 9308 */ // result.append(FrameSummary( +/* 9309 */ // ^ +/* 9310 */ // +/* 9311 */ +/* 9312 */ $currLineNo = Sk.currLineNo = 258; +/* 9313 */ $currColNo = Sk.currColNo = 22; +/* 9314 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9315 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr712, [$call714]); +/* 9316 */ $blk = 40; /* allowing case fallthrough */ +/* 9317 */ case 40: +/* 9318 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9319 */ return $saveSuspension($ret, 'src/lib/traceback.py', 258, 8); +/* 9320 */ } +/* 9321 */ var $call715 = $ret; +/* 9322 */ // +/* 9323 */ // line 258: +/* 9324 */ // result.append(FrameSummary( +/* 9325 */ // ^ +/* 9326 */ // +/* 9327 */ +/* 9328 */ $currLineNo = Sk.currLineNo = 258; +/* 9329 */ $currColNo = Sk.currColNo = 8; +/* 9330 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9331 */ $blk = 23; /* jump */ +/* 9332 */ continue; +/* 9333 */ case 15: +/* 9334 */ /* --- next branch of if --- */ +/* 9335 */ // +/* 9336 */ // line 242: +/* 9337 */ // frame_gen = collections.deque(frame_gen, maxlen=-limit) +/* 9338 */ // ^ +/* 9339 */ // +/* 9340 */ +/* 9341 */ $currLineNo = Sk.currLineNo = 242; +/* 9342 */ $currColNo = Sk.currColNo = 12; +/* 9343 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9344 */ var $loadgbl681 = Sk.misceval.loadname('collections', $gbl); +/* 9345 */ $ret = Sk.abstr.gattr($loadgbl681, $scope655.$const682, true); +/* 9346 */ $blk = 19; /* allowing case fallthrough */ +/* 9347 */ case 19: +/* 9348 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9349 */ return $saveSuspension($ret, 'src/lib/traceback.py', 242, 24); +/* 9350 */ } +/* 9351 */ var $lattr683 = $ret; +/* 9352 */ if (frame_gen === undefined) { +/* 9353 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame_gen\' referenced before assignment'); +/* 9354 */ } +/* 9355 */ if (limit === undefined) { +/* 9356 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 9357 */ } +/* 9358 */ var $unaryop684 = Sk.abstr.numberUnaryOp(limit, 'USub'); +/* 9359 */ $ret = Sk.misceval.applyOrSuspend($lattr683, undefined, undefined, ['maxlen', $unaryop684], [frame_gen]); +/* 9360 */ $blk = 20; /* allowing case fallthrough */ +/* 9361 */ case 20: +/* 9362 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9363 */ return $saveSuspension($ret, 'src/lib/traceback.py', 242, 24); +/* 9364 */ } +/* 9365 */ var $call685 = $ret; +/* 9366 */ // +/* 9367 */ // line 242: +/* 9368 */ // frame_gen = collections.deque(frame_gen, maxlen=-limit) +/* 9369 */ // ^ +/* 9370 */ // +/* 9371 */ +/* 9372 */ $currLineNo = Sk.currLineNo = 242; +/* 9373 */ $currColNo = Sk.currColNo = 24; +/* 9374 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9375 */ frame_gen = $call685; +/* 9376 */ $blk = 14; /* jump */ +/* 9377 */ continue; +/* 9378 */ case 24: +/* 9379 */ /* --- for cleanup --- */ $blk = 25; /* allowing case fallthrough */ +/* 9380 */ case 25: +/* 9381 */ /* --- for end --- */ +/* 9382 */ // +/* 9383 */ // line 260: +/* 9384 */ // for filename in fnames: +/* 9385 */ // ^ +/* 9386 */ // +/* 9387 */ +/* 9388 */ $currLineNo = Sk.currLineNo = 260; +/* 9389 */ $currColNo = Sk.currColNo = 4; +/* 9390 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9391 */ if (fnames === undefined) { +/* 9392 */ throw new Sk.builtin.UnboundLocalError('local variable \'fnames\' referenced before assignment'); +/* 9393 */ } +/* 9394 */ var $iter716 = Sk.abstr.iter(fnames); +/* 9395 */ $blk = 41; /* allowing case fallthrough */ +/* 9396 */ case 41: +/* 9397 */ /* --- for start --- */ $ret = Sk.abstr.iternext($iter716, true); +/* 9398 */ $blk = 44; /* allowing case fallthrough */ +/* 9399 */ case 44: +/* 9400 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9401 */ return $saveSuspension($ret, 'src/lib/traceback.py', 260, 4); +/* 9402 */ } +/* 9403 */ var $next717 = $ret; +/* 9404 */ if ($next717 === undefined) { +/* 9405 */ $blk = 42; +/* 9406 */ continue; +/* 9407 */ } +/* 9408 */ filename = $next717; +/* 9409 */ // +/* 9410 */ // line 261: +/* 9411 */ // linecache.checkcache(filename) +/* 9412 */ // ^ +/* 9413 */ // +/* 9414 */ +/* 9415 */ $currLineNo = Sk.currLineNo = 261; +/* 9416 */ $currColNo = Sk.currColNo = 8; +/* 9417 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9418 */ var $loadgbl718 = Sk.misceval.loadname('linecache', $gbl); +/* 9419 */ $ret = Sk.abstr.gattr($loadgbl718, $scope655.$const719, true); +/* 9420 */ $blk = 45; /* allowing case fallthrough */ +/* 9421 */ case 45: +/* 9422 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9423 */ return $saveSuspension($ret, 'src/lib/traceback.py', 261, 8); +/* 9424 */ } +/* 9425 */ var $lattr720 = $ret; +/* 9426 */ if (filename === undefined) { +/* 9427 */ throw new Sk.builtin.UnboundLocalError('local variable \'filename\' referenced before assignment'); +/* 9428 */ } +/* 9429 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr720, [filename]); +/* 9430 */ $blk = 46; /* allowing case fallthrough */ +/* 9431 */ case 46: +/* 9432 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9433 */ return $saveSuspension($ret, 'src/lib/traceback.py', 261, 8); +/* 9434 */ } +/* 9435 */ var $call721 = $ret; +/* 9436 */ // +/* 9437 */ // line 261: +/* 9438 */ // linecache.checkcache(filename) +/* 9439 */ // ^ +/* 9440 */ // +/* 9441 */ +/* 9442 */ $currLineNo = Sk.currLineNo = 261; +/* 9443 */ $currColNo = Sk.currColNo = 8; +/* 9444 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9445 */ $blk = 41; /* jump */ +/* 9446 */ continue; +/* 9447 */ case 36: +/* 9448 */ /* --- next branch of if --- */ +/* 9449 */ // +/* 9450 */ // line 257: +/* 9451 */ // f_locals = None +/* 9452 */ // ^ +/* 9453 */ // +/* 9454 */ +/* 9455 */ $currLineNo = Sk.currLineNo = 257; +/* 9456 */ $currColNo = Sk.currColNo = 12; +/* 9457 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9458 */ f_locals = Sk.builtin.none.none$; +/* 9459 */ $blk = 35; /* jump */ +/* 9460 */ continue; +/* 9461 */ case 42: +/* 9462 */ /* --- for cleanup --- */ $blk = 43; /* allowing case fallthrough */ +/* 9463 */ case 43: +/* 9464 */ /* --- for end --- */ +/* 9465 */ // +/* 9466 */ // line 263: +/* 9467 */ // if lookup_lines: +/* 9468 */ // ^ +/* 9469 */ // +/* 9470 */ +/* 9471 */ $currLineNo = Sk.currLineNo = 263; +/* 9472 */ $currColNo = Sk.currColNo = 4; +/* 9473 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9474 */ if (lookup_lines === undefined) { +/* 9475 */ throw new Sk.builtin.UnboundLocalError('local variable \'lookup_lines\' referenced before assignment'); +/* 9476 */ } +/* 9477 */ var $jfalse722 = (lookup_lines === false || !Sk.misceval.isTrue(lookup_lines)); +/* 9478 */ if ($jfalse722) { +/* 9479 */ /*test failed */ +/* 9480 */ $blk = 47; +/* 9481 */ continue; +/* 9482 */ } +/* 9483 */ // +/* 9484 */ // line 264: +/* 9485 */ // for f in result: +/* 9486 */ // ^ +/* 9487 */ // +/* 9488 */ +/* 9489 */ $currLineNo = Sk.currLineNo = 264; +/* 9490 */ $currColNo = Sk.currColNo = 8; +/* 9491 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9492 */ if (result === undefined) { +/* 9493 */ throw new Sk.builtin.UnboundLocalError('local variable \'result\' referenced before assignment'); +/* 9494 */ } +/* 9495 */ var $iter723 = Sk.abstr.iter(result); +/* 9496 */ $blk = 48; /* allowing case fallthrough */ +/* 9497 */ case 48: +/* 9498 */ /* --- for start --- */ $ret = Sk.abstr.iternext($iter723, true); +/* 9499 */ $blk = 51; /* allowing case fallthrough */ +/* 9500 */ case 51: +/* 9501 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9502 */ return $saveSuspension($ret, 'src/lib/traceback.py', 264, 8); +/* 9503 */ } +/* 9504 */ var $next724 = $ret; +/* 9505 */ if ($next724 === undefined) { +/* 9506 */ $blk = 49; +/* 9507 */ continue; +/* 9508 */ } +/* 9509 */ f = $next724; +/* 9510 */ // +/* 9511 */ // line 265: +/* 9512 */ // f.line +/* 9513 */ // ^ +/* 9514 */ // +/* 9515 */ +/* 9516 */ $currLineNo = Sk.currLineNo = 265; +/* 9517 */ $currColNo = Sk.currColNo = 12; +/* 9518 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9519 */ if (f === undefined) { +/* 9520 */ throw new Sk.builtin.UnboundLocalError('local variable \'f\' referenced before assignment'); +/* 9521 */ } +/* 9522 */ $ret = Sk.abstr.gattr(f, $scope655.$const725, true); +/* 9523 */ $blk = 52; /* allowing case fallthrough */ +/* 9524 */ case 52: +/* 9525 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9526 */ return $saveSuspension($ret, 'src/lib/traceback.py', 265, 12); +/* 9527 */ } +/* 9528 */ var $lattr726 = $ret; +/* 9529 */ $blk = 48; /* jump */ +/* 9530 */ continue; +/* 9531 */ case 47: +/* 9532 */ /* --- end of if --- */ +/* 9533 */ // +/* 9534 */ // line 266: +/* 9535 */ // return result +/* 9536 */ // ^ +/* 9537 */ // +/* 9538 */ +/* 9539 */ $currLineNo = Sk.currLineNo = 266; +/* 9540 */ $currColNo = Sk.currColNo = 4; +/* 9541 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9542 */ if (result === undefined) { +/* 9543 */ throw new Sk.builtin.UnboundLocalError('local variable \'result\' referenced before assignment'); +/* 9544 */ } +/* 9545 */ return result; +/* 9546 */ return Sk.builtin.none.none$; +/* 9547 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 9548 */ case 49: +/* 9549 */ /* --- for cleanup --- */ $blk = 50; /* allowing case fallthrough */ +/* 9550 */ case 50: +/* 9551 */ /* --- for end --- */ $blk = 47; /* jump */ +/* 9552 */ continue; +/* 9553 */ } +/* 9554 */ } catch (err) { +/* 9555 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 9556 */ Sk.execStart = Date.now(); +/* 9557 */ Sk.execPaused = 0 +/* 9558 */ } +/* 9559 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 9560 */ err = new Sk.builtin.ExternalError(err); +/* 9561 */ } +/* 9562 */ Sk.err = err; +/* 9563 */ err.traceback.push({ +/* 9564 */ lineno: $currLineNo, +/* 9565 */ colno: $currColNo, +/* 9566 */ filename: 'src/lib/traceback.py', +/* 9567 */ scope: 'extract' +/* 9568 */ }); +/* 9569 */ if ($exc.length > 0) { +/* 9570 */ $err = err; +/* 9571 */ $blk = $exc.pop(); +/* 9572 */ continue +/* 9573 */ } else { +/* 9574 */ throw err; +/* 9575 */ } +/* 9576 */ } +/* 9577 */ } +/* 9578 */ }); +/* 9579 */ $scope655.$const662 = new Sk.builtin.str('tracebacklimit'); +/* 9580 */ $scope655.$const669 = new Sk.builtin.int_(0); +/* 9581 */ $scope655.$const682 = new Sk.builtin.str('deque'); +/* 9582 */ $scope655.$const693 = new Sk.builtin.str('f_code'); +/* 9583 */ $scope655.$const695 = new Sk.builtin.str('co_filename'); +/* 9584 */ $scope655.$const697 = new Sk.builtin.str('co_name'); +/* 9585 */ $scope655.$const699 = new Sk.builtin.str('add'); +/* 9586 */ $scope655.$const703 = new Sk.builtin.str('lazycache'); +/* 9587 */ $scope655.$const705 = new Sk.builtin.str('f_globals'); +/* 9588 */ $scope655.$const709 = new Sk.builtin.str('f_locals'); +/* 9589 */ $scope655.$const711 = new Sk.builtin.str('append'); +/* 9590 */ $scope655.$const719 = new Sk.builtin.str('checkcache'); +/* 9591 */ $scope655.$const725 = new Sk.builtin.str('line'); +/* 9592 */ var $scope729 = (function $StackSummary$class_outer($globals, $locals, $cell) { +/* 9593 */ var $gbl = $globals, +/* 9594 */ $loc = $locals; +/* 9595 */ $free = $globals; +/* 9596 */ (function $StackSummary$_closure($cell) { +/* 9597 */ var $blk = 0, +/* 9598 */ $exc = [], +/* 9599 */ $ret = undefined, +/* 9600 */ $postfinally = undefined, +/* 9601 */ $currLineNo = undefined, +/* 9602 */ $currColNo = undefined; +/* 9603 */ if (typeof Sk.execStart === 'undefined') { +/* 9604 */ Sk.execStart = Date.now(); +/* 9605 */ Sk.execPaused = 0 +/* 9606 */ } +/* 9607 */ while (true) { +/* 9608 */ try { +/* 9609 */ var $dateNow = Date.now(); +/* 9610 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 9611 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 9612 */ } +/* 9613 */ switch ($blk) { +/* 9614 */ case 0: +/* 9615 */ /* --- class entry --- */ +/* 9616 */ // +/* 9617 */ // line 273: +/* 9618 */ // @classmethod +/* 9619 */ // ^ +/* 9620 */ // +/* 9621 */ +/* 9622 */ $currLineNo = Sk.currLineNo = 273; +/* 9623 */ $currColNo = Sk.currColNo = 4; +/* 9624 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9625 */ var $loadname730 = $loc.classmethod !== undefined ? $loc.classmethod : Sk.misceval.loadname('classmethod', $gbl);; +/* 9626 */ $scope731.co_name = new Sk.builtins['str']('from_list'); +/* 9627 */ $scope731.$decorators = [$loadname730]; +/* 9628 */ $scope731.co_varnames = ['klass', 'a_list']; +/* 9629 */ $ret = Sk.misceval.callsimOrSuspendArray($scope731.$decorators[0], [new Sk.builtins['function']($scope731, $gbl)]); +/* 9630 */ if ($ret && $ret.$isSuspension) { +/* 9631 */ $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); +/* 9632 */ } +/* 9633 */ var $funcobj749 = $ret; +/* 9634 */ $loc.from_list = $funcobj749; +/* 9635 */ // +/* 9636 */ // line 289: +/* 9637 */ // def format(self): +/* 9638 */ // ^ +/* 9639 */ // +/* 9640 */ +/* 9641 */ $currLineNo = Sk.currLineNo = 289; +/* 9642 */ $currColNo = Sk.currColNo = 4; +/* 9643 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9644 */ $scope750.co_name = new Sk.builtins['str']('format'); +/* 9645 */ $scope750.co_varnames = ['self']; +/* 9646 */ var $funcobj867 = new Sk.builtins['function']($scope750, $gbl); +/* 9647 */ $loc.format = $funcobj867; +/* 9648 */ return; +/* 9649 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 9650 */ } +/* 9651 */ } catch (err) { +/* 9652 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 9653 */ Sk.execStart = Date.now(); +/* 9654 */ Sk.execPaused = 0 +/* 9655 */ } +/* 9656 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 9657 */ err = new Sk.builtin.ExternalError(err); +/* 9658 */ } +/* 9659 */ Sk.err = err; +/* 9660 */ err.traceback.push({ +/* 9661 */ lineno: $currLineNo, +/* 9662 */ colno: $currColNo, +/* 9663 */ filename: 'src/lib/traceback.py', +/* 9664 */ scope: 'StackSummary' +/* 9665 */ }); +/* 9666 */ if ($exc.length > 0) { +/* 9667 */ $err = err; +/* 9668 */ $blk = $exc.pop(); +/* 9669 */ continue +/* 9670 */ } else { +/* 9671 */ throw err; +/* 9672 */ } +/* 9673 */ } +/* 9674 */ } +/* 9675 */ }).call(null, $cell); +/* 9676 */ }); +/* 9677 */ var $scope731 = (function $from_list732$(klass, a_list) { +/* 9678 */ var filename, frame, line, lineno, name_$rn$, result; /* locals */ +/* 9679 */ var a_list, a_list, filename, filename, frame, frame, frame, frame, klass, line, line, lineno, lineno, name_$rn$, name_$rn$, result, result, result, result, $loadgbl733, $iter735, $iter735, $loadgbl737, $loadgbl738, $lattr742, $lattr745, $loadgbl746, $lattr745, $loadgbl746, $call747; +/* 9680 */ var $wakeFromSuspension = function() { +/* 9681 */ var susp = $scope731.$wakingSuspension; +/* 9682 */ $scope731.$wakingSuspension = undefined; +/* 9683 */ $blk = susp.$blk; +/* 9684 */ $loc = susp.$loc; +/* 9685 */ $gbl = susp.$gbl; +/* 9686 */ $exc = susp.$exc; +/* 9687 */ $err = susp.$err; +/* 9688 */ $postfinally = susp.$postfinally; +/* 9689 */ $currLineNo = susp.$lineno; +/* 9690 */ $currColNo = susp.$colno; +/* 9691 */ Sk.lastYield = Date.now(); +/* 9692 */ a_list = susp.$tmps.a_list; +/* 9693 */ filename = susp.$tmps.filename; +/* 9694 */ frame = susp.$tmps.frame; +/* 9695 */ klass = susp.$tmps.klass; +/* 9696 */ line = susp.$tmps.line; +/* 9697 */ lineno = susp.$tmps.lineno; +/* 9698 */ name_$rn$ = susp.$tmps.name_$rn$; +/* 9699 */ result = susp.$tmps.result; +/* 9700 */ $loadgbl733 = susp.$tmps.$loadgbl733; +/* 9701 */ $iter735 = susp.$tmps.$iter735; +/* 9702 */ $loadgbl737 = susp.$tmps.$loadgbl737; +/* 9703 */ $loadgbl738 = susp.$tmps.$loadgbl738; +/* 9704 */ $lattr742 = susp.$tmps.$lattr742; +/* 9705 */ $lattr745 = susp.$tmps.$lattr745; +/* 9706 */ $loadgbl746 = susp.$tmps.$loadgbl746; +/* 9707 */ $call747 = susp.$tmps.$call747; +/* 9708 */ try { +/* 9709 */ $ret = susp.child.resume(); +/* 9710 */ } catch (err) { +/* 9711 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 9712 */ Sk.execStart = Date.now(); +/* 9713 */ Sk.execPaused = 0 +/* 9714 */ } +/* 9715 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 9716 */ err = new Sk.builtin.ExternalError(err); +/* 9717 */ } +/* 9718 */ Sk.err = err; +/* 9719 */ err.traceback.push({ +/* 9720 */ lineno: $currLineNo, +/* 9721 */ colno: $currColNo, +/* 9722 */ filename: 'src/lib/traceback.py', +/* 9723 */ scope: '$scope731' +/* 9724 */ }); +/* 9725 */ if ($exc.length > 0) { +/* 9726 */ $err = err; +/* 9727 */ $blk = $exc.pop(); +/* 9728 */ } else { +/* 9729 */ throw err; +/* 9730 */ } +/* 9731 */ } +/* 9732 */ }; +/* 9733 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 9734 */ var susp = new Sk.misceval.Suspension(); +/* 9735 */ susp.child = $child; +/* 9736 */ susp.resume = function() { +/* 9737 */ $scope731.$wakingSuspension = susp; +/* 9738 */ return $scope731(); +/* 9739 */ }; +/* 9740 */ susp.data = susp.child.data; +/* 9741 */ susp.$blk = $blk; +/* 9742 */ susp.$loc = $loc; +/* 9743 */ susp.$gbl = $gbl; +/* 9744 */ susp.$exc = $exc; +/* 9745 */ susp.$err = $err; +/* 9746 */ susp.$postfinally = $postfinally; +/* 9747 */ susp.$filename = $filename; +/* 9748 */ susp.$lineno = $lineno; +/* 9749 */ susp.$colno = $colno; +/* 9750 */ susp.optional = susp.child.optional; +/* 9751 */ susp.$tmps = { +/* 9752 */ "a_list": a_list, +/* 9753 */ "filename": filename, +/* 9754 */ "frame": frame, +/* 9755 */ "klass": klass, +/* 9756 */ "line": line, +/* 9757 */ "lineno": lineno, +/* 9758 */ "name_$rn$": name_$rn$, +/* 9759 */ "result": result, +/* 9760 */ "$loadgbl733": $loadgbl733, +/* 9761 */ "$iter735": $iter735, +/* 9762 */ "$loadgbl737": $loadgbl737, +/* 9763 */ "$loadgbl738": $loadgbl738, +/* 9764 */ "$lattr742": $lattr742, +/* 9765 */ "$lattr745": $lattr745, +/* 9766 */ "$loadgbl746": $loadgbl746, +/* 9767 */ "$call747": $call747 +/* 9768 */ }; +/* 9769 */ return susp; +/* 9770 */ }; +/* 9771 */ var $blk = 0, +/* 9772 */ $exc = [], +/* 9773 */ $loc = {}, +/* 9774 */ $cell = {}, +/* 9775 */ $gbl = this, +/* 9776 */ $err = undefined, +/* 9777 */ $ret = undefined, +/* 9778 */ $postfinally = undefined, +/* 9779 */ $currLineNo = undefined, +/* 9780 */ $currColNo = undefined; +/* 9781 */ if (typeof Sk.execStart === 'undefined') { +/* 9782 */ Sk.execStart = Date.now(); +/* 9783 */ Sk.execPaused = 0 +/* 9784 */ } +/* 9785 */ if (typeof Sk.lastYield === 'undefined') { +/* 9786 */ Sk.lastYield = Date.now() +/* 9787 */ } +/* 9788 */ if ($scope731.$wakingSuspension !== undefined) { +/* 9789 */ $wakeFromSuspension(); +/* 9790 */ } else {} +/* 9791 */ $gbl.__class__ = this.StackSummary; +/* 9792 */ while (true) { +/* 9793 */ try { +/* 9794 */ var $dateNow = Date.now(); +/* 9795 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 9796 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 9797 */ } +/* 9798 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 9799 */ var $susp = $saveSuspension({ +/* 9800 */ data: { +/* 9801 */ type: 'Sk.yield' +/* 9802 */ }, +/* 9803 */ resume: function() {} +/* 9804 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 9805 */ $susp.$blk = $blk; +/* 9806 */ $susp.optional = true; +/* 9807 */ return $susp; +/* 9808 */ } +/* 9809 */ switch ($blk) { +/* 9810 */ case 0: +/* 9811 */ /* --- codeobj entry --- */ if (klass === undefined) { +/* 9812 */ throw new Sk.builtin.UnboundLocalError('local variable \'klass\' referenced before assignment'); +/* 9813 */ } +/* 9814 */ if (a_list === undefined) { +/* 9815 */ throw new Sk.builtin.UnboundLocalError('local variable \'a_list\' referenced before assignment'); +/* 9816 */ } +/* 9817 */ +/* 9818 */ // +/* 9819 */ // line 280: +/* 9820 */ // result = StackSummary() +/* 9821 */ // ^ +/* 9822 */ // +/* 9823 */ +/* 9824 */ $currLineNo = Sk.currLineNo = 280; +/* 9825 */ $currColNo = Sk.currColNo = 8; +/* 9826 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9827 */ var $loadgbl733 = Sk.misceval.loadname('StackSummary', $gbl); +/* 9828 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl733); +/* 9829 */ $blk = 1; /* allowing case fallthrough */ +/* 9830 */ case 1: +/* 9831 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9832 */ return $saveSuspension($ret, 'src/lib/traceback.py', 280, 17); +/* 9833 */ } +/* 9834 */ var $call734 = $ret; +/* 9835 */ // +/* 9836 */ // line 280: +/* 9837 */ // result = StackSummary() +/* 9838 */ // ^ +/* 9839 */ // +/* 9840 */ +/* 9841 */ $currLineNo = Sk.currLineNo = 280; +/* 9842 */ $currColNo = Sk.currColNo = 17; +/* 9843 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9844 */ result = $call734; +/* 9845 */ // +/* 9846 */ // line 281: +/* 9847 */ // for frame in a_list: +/* 9848 */ // ^ +/* 9849 */ // +/* 9850 */ +/* 9851 */ $currLineNo = Sk.currLineNo = 281; +/* 9852 */ $currColNo = Sk.currColNo = 8; +/* 9853 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9854 */ if (a_list === undefined) { +/* 9855 */ throw new Sk.builtin.UnboundLocalError('local variable \'a_list\' referenced before assignment'); +/* 9856 */ } +/* 9857 */ var $iter735 = Sk.abstr.iter(a_list); +/* 9858 */ $blk = 2; /* allowing case fallthrough */ +/* 9859 */ case 2: +/* 9860 */ /* --- for start --- */ $ret = Sk.abstr.iternext($iter735, true); +/* 9861 */ $blk = 5; /* allowing case fallthrough */ +/* 9862 */ case 5: +/* 9863 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9864 */ return $saveSuspension($ret, 'src/lib/traceback.py', 281, 8); +/* 9865 */ } +/* 9866 */ var $next736 = $ret; +/* 9867 */ if ($next736 === undefined) { +/* 9868 */ $blk = 3; +/* 9869 */ continue; +/* 9870 */ } +/* 9871 */ frame = $next736; +/* 9872 */ // +/* 9873 */ // line 282: +/* 9874 */ // if isinstance(frame, FrameSummary): +/* 9875 */ // ^ +/* 9876 */ // +/* 9877 */ +/* 9878 */ $currLineNo = Sk.currLineNo = 282; +/* 9879 */ $currColNo = Sk.currColNo = 12; +/* 9880 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9881 */ var $loadgbl737 = Sk.misceval.loadname('isinstance', $gbl); +/* 9882 */ if (frame === undefined) { +/* 9883 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 9884 */ } +/* 9885 */ var $loadgbl738 = Sk.misceval.loadname('FrameSummary', $gbl); +/* 9886 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl737, [frame, $loadgbl738]); +/* 9887 */ $blk = 8; /* allowing case fallthrough */ +/* 9888 */ case 8: +/* 9889 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9890 */ return $saveSuspension($ret, 'src/lib/traceback.py', 282, 15); +/* 9891 */ } +/* 9892 */ var $call739 = $ret; +/* 9893 */ // +/* 9894 */ // line 282: +/* 9895 */ // if isinstance(frame, FrameSummary): +/* 9896 */ // ^ +/* 9897 */ // +/* 9898 */ +/* 9899 */ $currLineNo = Sk.currLineNo = 282; +/* 9900 */ $currColNo = Sk.currColNo = 15; +/* 9901 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9902 */ var $jfalse740 = ($call739 === false || !Sk.misceval.isTrue($call739)); +/* 9903 */ if ($jfalse740) { +/* 9904 */ /*test failed */ +/* 9905 */ $blk = 7; +/* 9906 */ continue; +/* 9907 */ } +/* 9908 */ // +/* 9909 */ // line 283: +/* 9910 */ // result.append(frame) +/* 9911 */ // ^ +/* 9912 */ // +/* 9913 */ +/* 9914 */ $currLineNo = Sk.currLineNo = 283; +/* 9915 */ $currColNo = Sk.currColNo = 16; +/* 9916 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9917 */ if (result === undefined) { +/* 9918 */ throw new Sk.builtin.UnboundLocalError('local variable \'result\' referenced before assignment'); +/* 9919 */ } +/* 9920 */ $ret = Sk.abstr.gattr(result, $scope731.$const741, true); +/* 9921 */ $blk = 9; /* allowing case fallthrough */ +/* 9922 */ case 9: +/* 9923 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9924 */ return $saveSuspension($ret, 'src/lib/traceback.py', 283, 16); +/* 9925 */ } +/* 9926 */ var $lattr742 = $ret; +/* 9927 */ if (frame === undefined) { +/* 9928 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 9929 */ } +/* 9930 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr742, [frame]); +/* 9931 */ $blk = 10; /* allowing case fallthrough */ +/* 9932 */ case 10: +/* 9933 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 9934 */ return $saveSuspension($ret, 'src/lib/traceback.py', 283, 16); +/* 9935 */ } +/* 9936 */ var $call743 = $ret; +/* 9937 */ // +/* 9938 */ // line 283: +/* 9939 */ // result.append(frame) +/* 9940 */ // ^ +/* 9941 */ // +/* 9942 */ +/* 9943 */ $currLineNo = Sk.currLineNo = 283; +/* 9944 */ $currColNo = Sk.currColNo = 16; +/* 9945 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9946 */ $blk = 6; /* allowing case fallthrough */ +/* 9947 */ case 6: +/* 9948 */ /* --- end of if --- */ $blk = 2; /* jump */ +/* 9949 */ continue; +/* 9950 */ case 3: +/* 9951 */ /* --- for cleanup --- */ $blk = 4; /* allowing case fallthrough */ +/* 9952 */ case 4: +/* 9953 */ /* --- for end --- */ +/* 9954 */ // +/* 9955 */ // line 287: +/* 9956 */ // return result +/* 9957 */ // ^ +/* 9958 */ // +/* 9959 */ +/* 9960 */ $currLineNo = Sk.currLineNo = 287; +/* 9961 */ $currColNo = Sk.currColNo = 8; +/* 9962 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9963 */ if (result === undefined) { +/* 9964 */ throw new Sk.builtin.UnboundLocalError('local variable \'result\' referenced before assignment'); +/* 9965 */ } +/* 9966 */ return result; +/* 9967 */ return Sk.builtin.none.none$; +/* 9968 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 9969 */ case 7: +/* 9970 */ /* --- next branch of if --- */ +/* 9971 */ // +/* 9972 */ // line 285: +/* 9973 */ // filename, lineno, name, line = frame +/* 9974 */ // ^ +/* 9975 */ // +/* 9976 */ +/* 9977 */ $currLineNo = Sk.currLineNo = 285; +/* 9978 */ $currColNo = Sk.currColNo = 16; +/* 9979 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9980 */ if (frame === undefined) { +/* 9981 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 9982 */ } +/* 9983 */ var $items744 = Sk.abstr.sequenceUnpack(frame, 4); +/* 9984 */ filename = $items744[0]; +/* 9985 */ lineno = $items744[1]; +/* 9986 */ name_$rn$ = $items744[2]; +/* 9987 */ line = $items744[3]; +/* 9988 */ // +/* 9989 */ // line 286: +/* 9990 */ // result.append(FrameSummary(filename, lineno, name, line=line)) +/* 9991 */ // ^ +/* 9992 */ // +/* 9993 */ +/* 9994 */ $currLineNo = Sk.currLineNo = 286; +/* 9995 */ $currColNo = Sk.currColNo = 16; +/* 9996 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 9997 */ if (result === undefined) { +/* 9998 */ throw new Sk.builtin.UnboundLocalError('local variable \'result\' referenced before assignment'); +/* 9999 */ } +/* 10000 */ $ret = Sk.abstr.gattr(result, $scope731.$const741, true); +/* 10001 */ $blk = 11; /* allowing case fallthrough */ +/* 10002 */ case 11: +/* 10003 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10004 */ return $saveSuspension($ret, 'src/lib/traceback.py', 286, 16); +/* 10005 */ } +/* 10006 */ var $lattr745 = $ret; +/* 10007 */ var $loadgbl746 = Sk.misceval.loadname('FrameSummary', $gbl); +/* 10008 */ if (filename === undefined) { +/* 10009 */ throw new Sk.builtin.UnboundLocalError('local variable \'filename\' referenced before assignment'); +/* 10010 */ } +/* 10011 */ if (lineno === undefined) { +/* 10012 */ throw new Sk.builtin.UnboundLocalError('local variable \'lineno\' referenced before assignment'); +/* 10013 */ } +/* 10014 */ if (name_$rn$ === undefined) { +/* 10015 */ throw new Sk.builtin.UnboundLocalError('local variable \'name_$rn$\' referenced before assignment'); +/* 10016 */ } +/* 10017 */ if (line === undefined) { +/* 10018 */ throw new Sk.builtin.UnboundLocalError('local variable \'line\' referenced before assignment'); +/* 10019 */ } +/* 10020 */ $ret = Sk.misceval.applyOrSuspend($loadgbl746, undefined, undefined, ['line', line], [filename, lineno, name_$rn$]); +/* 10021 */ $blk = 12; /* allowing case fallthrough */ +/* 10022 */ case 12: +/* 10023 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10024 */ return $saveSuspension($ret, 'src/lib/traceback.py', 286, 30); +/* 10025 */ } +/* 10026 */ var $call747 = $ret; +/* 10027 */ // +/* 10028 */ // line 286: +/* 10029 */ // result.append(FrameSummary(filename, lineno, name, line=line)) +/* 10030 */ // ^ +/* 10031 */ // +/* 10032 */ +/* 10033 */ $currLineNo = Sk.currLineNo = 286; +/* 10034 */ $currColNo = Sk.currColNo = 30; +/* 10035 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10036 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr745, [$call747]); +/* 10037 */ $blk = 13; /* allowing case fallthrough */ +/* 10038 */ case 13: +/* 10039 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10040 */ return $saveSuspension($ret, 'src/lib/traceback.py', 286, 16); +/* 10041 */ } +/* 10042 */ var $call748 = $ret; +/* 10043 */ // +/* 10044 */ // line 286: +/* 10045 */ // result.append(FrameSummary(filename, lineno, name, line=line)) +/* 10046 */ // ^ +/* 10047 */ // +/* 10048 */ +/* 10049 */ $currLineNo = Sk.currLineNo = 286; +/* 10050 */ $currColNo = Sk.currColNo = 16; +/* 10051 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10052 */ $blk = 6; /* jump */ +/* 10053 */ continue; +/* 10054 */ } +/* 10055 */ } catch (err) { +/* 10056 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 10057 */ Sk.execStart = Date.now(); +/* 10058 */ Sk.execPaused = 0 +/* 10059 */ } +/* 10060 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 10061 */ err = new Sk.builtin.ExternalError(err); +/* 10062 */ } +/* 10063 */ Sk.err = err; +/* 10064 */ err.traceback.push({ +/* 10065 */ lineno: $currLineNo, +/* 10066 */ colno: $currColNo, +/* 10067 */ filename: 'src/lib/traceback.py', +/* 10068 */ scope: 'from_list' +/* 10069 */ }); +/* 10070 */ if ($exc.length > 0) { +/* 10071 */ $err = err; +/* 10072 */ $blk = $exc.pop(); +/* 10073 */ continue +/* 10074 */ } else { +/* 10075 */ throw err; +/* 10076 */ } +/* 10077 */ } +/* 10078 */ } +/* 10079 */ }); +/* 10080 */ $scope731.$const741 = new Sk.builtin.str('append'); +/* 10081 */ var $scope750 = (function $format751$(self) { +/* 10082 */ var count, frame, last_file, last_line, last_name, name_$rn$, result, row, value; /* locals */ +/* 10083 */ var count, count, count, count, count, count, count, count, count, count, count, count, count, count, count, frame, frame, frame, frame, frame, frame, frame, frame, frame, frame, frame, frame, frame, frame, last_file, last_file, last_file, last_file, last_line, last_line, last_line, last_line, last_name, last_name, last_name, last_name, name_$rn$, name_$rn$, result, result, result, result, result, row, row, row, row, row, self, self, value, value, $iter754, $iter754, $compareres756, $compareres756, $jfalse757, $boolopsucc758, $jtrue759, $compareres760, $compareres756, $jfalse757, $boolopsucc758, $jtrue759, $compareres760, $lattr762, $compareres756, $jfalse757, $boolopsucc758, $jtrue759, $compareres760, $lattr762, $jfalse763, $jtrue764, $compareres765, $compareres756, $jfalse757, $boolopsucc758, $jtrue759, $compareres760, $lattr762, $jfalse763, $jtrue764, $compareres765, $jfalse766, $jtrue767, $compareres768, $compareres756, $jfalse757, $boolopsucc758, $jtrue759, $compareres760, $lattr762, $jfalse763, $jtrue764, $compareres765, $jfalse766, $jtrue767, $compareres768, $lattr770, $compareres756, $jfalse757, $boolopsucc758, $jtrue759, $compareres760, $lattr762, $jfalse763, $jtrue764, $compareres765, $jfalse766, $jtrue767, $compareres768, $lattr770, $jfalse771, $jtrue772, $compareres773, $compareres756, $jfalse757, $boolopsucc758, $jtrue759, $compareres760, $lattr762, $jfalse763, $jtrue764, $compareres765, $jfalse766, $jtrue767, $compareres768, $lattr770, $jfalse771, $jtrue772, $compareres773, $jfalse774, $jtrue775, $compareres776, $compareres756, $jfalse757, $boolopsucc758, $jtrue759, $compareres760, $lattr762, $jfalse763, $jtrue764, $compareres765, $jfalse766, $jtrue767, $compareres768, $lattr770, $jfalse771, $jtrue772, $compareres773, $jfalse774, $jtrue775, $compareres776, $lattr778, $compareres782, $loadgbl783, $lattr789, $lattr789, $lattr792, $res793, $compareres794, $lattr789, $lattr792, $res793, $compareres794, $jfalse796, $jfalse797, $lattr789, $lattr792, $res793, $compareres794, $jfalse796, $jfalse797, $call800, $compareres806, $loadgbl807, $lattr811, $lattr811, $lattr813, $lattr811, $lattr813, $lattr814, $lattr811, $lattr813, $lattr814, $lattr815, $lattr811, $lattr813, $lattr814, $lattr815, $lattr816, $lattr811, $lattr813, $lattr814, $lattr815, $lattr816, $call817, $lattr822, $lattr822, $lattr824, $lattr822, $lattr824, $lattr825, $lattr822, $lattr824, $lattr825, $lattr827, $lattr822, $lattr824, $lattr825, $lattr827, $call828, $lattr822, $lattr824, $lattr825, $lattr827, $call828, $call829, $loadgbl834, $loadgbl834, $lattr835, $loadgbl834, $lattr835, $lattr837, $loadgbl834, $lattr835, $lattr837, $call838, $iter840, $loadgbl834, $lattr835, $lattr837, $call838, $call839, $iter840, $lattr843, $lattr843, $lattr845, $lattr843, $lattr845, $call846, $lattr848, $lattr848, $lattr850, $lattr848, $lattr850, $call851, $compareres853, $loadgbl854, $lattr859, $lattr859, $lattr860, $res861, $compareres862, $lattr859, $lattr860, $res861, $compareres862, $jfalse863, $jfalse864, $lattr859, $lattr860, $res861, $compareres862, $jfalse863, $jfalse864, $call865; +/* 10084 */ var $wakeFromSuspension = function() { +/* 10085 */ var susp = $scope750.$wakingSuspension; +/* 10086 */ $scope750.$wakingSuspension = undefined; +/* 10087 */ $blk = susp.$blk; +/* 10088 */ $loc = susp.$loc; +/* 10089 */ $gbl = susp.$gbl; +/* 10090 */ $exc = susp.$exc; +/* 10091 */ $err = susp.$err; +/* 10092 */ $postfinally = susp.$postfinally; +/* 10093 */ $currLineNo = susp.$lineno; +/* 10094 */ $currColNo = susp.$colno; +/* 10095 */ Sk.lastYield = Date.now(); +/* 10096 */ count = susp.$tmps.count; +/* 10097 */ frame = susp.$tmps.frame; +/* 10098 */ last_file = susp.$tmps.last_file; +/* 10099 */ last_line = susp.$tmps.last_line; +/* 10100 */ last_name = susp.$tmps.last_name; +/* 10101 */ name_$rn$ = susp.$tmps.name_$rn$; +/* 10102 */ result = susp.$tmps.result; +/* 10103 */ row = susp.$tmps.row; +/* 10104 */ self = susp.$tmps.self; +/* 10105 */ value = susp.$tmps.value; +/* 10106 */ $iter754 = susp.$tmps.$iter754; +/* 10107 */ $compareres756 = susp.$tmps.$compareres756; +/* 10108 */ $jfalse757 = susp.$tmps.$jfalse757; +/* 10109 */ $boolopsucc758 = susp.$tmps.$boolopsucc758; +/* 10110 */ $jtrue759 = susp.$tmps.$jtrue759; +/* 10111 */ $compareres760 = susp.$tmps.$compareres760; +/* 10112 */ $lattr762 = susp.$tmps.$lattr762; +/* 10113 */ $jfalse763 = susp.$tmps.$jfalse763; +/* 10114 */ $jtrue764 = susp.$tmps.$jtrue764; +/* 10115 */ $compareres765 = susp.$tmps.$compareres765; +/* 10116 */ $jfalse766 = susp.$tmps.$jfalse766; +/* 10117 */ $jtrue767 = susp.$tmps.$jtrue767; +/* 10118 */ $compareres768 = susp.$tmps.$compareres768; +/* 10119 */ $lattr770 = susp.$tmps.$lattr770; +/* 10120 */ $jfalse771 = susp.$tmps.$jfalse771; +/* 10121 */ $jtrue772 = susp.$tmps.$jtrue772; +/* 10122 */ $compareres773 = susp.$tmps.$compareres773; +/* 10123 */ $jfalse774 = susp.$tmps.$jfalse774; +/* 10124 */ $jtrue775 = susp.$tmps.$jtrue775; +/* 10125 */ $compareres776 = susp.$tmps.$compareres776; +/* 10126 */ $lattr778 = susp.$tmps.$lattr778; +/* 10127 */ $compareres782 = susp.$tmps.$compareres782; +/* 10128 */ $loadgbl783 = susp.$tmps.$loadgbl783; +/* 10129 */ $lattr789 = susp.$tmps.$lattr789; +/* 10130 */ $lattr792 = susp.$tmps.$lattr792; +/* 10131 */ $res793 = susp.$tmps.$res793; +/* 10132 */ $compareres794 = susp.$tmps.$compareres794; +/* 10133 */ $jfalse796 = susp.$tmps.$jfalse796; +/* 10134 */ $jfalse797 = susp.$tmps.$jfalse797; +/* 10135 */ $call800 = susp.$tmps.$call800; +/* 10136 */ $compareres806 = susp.$tmps.$compareres806; +/* 10137 */ $loadgbl807 = susp.$tmps.$loadgbl807; +/* 10138 */ $lattr811 = susp.$tmps.$lattr811; +/* 10139 */ $lattr813 = susp.$tmps.$lattr813; +/* 10140 */ $lattr814 = susp.$tmps.$lattr814; +/* 10141 */ $lattr815 = susp.$tmps.$lattr815; +/* 10142 */ $lattr816 = susp.$tmps.$lattr816; +/* 10143 */ $call817 = susp.$tmps.$call817; +/* 10144 */ $lattr822 = susp.$tmps.$lattr822; +/* 10145 */ $lattr824 = susp.$tmps.$lattr824; +/* 10146 */ $lattr825 = susp.$tmps.$lattr825; +/* 10147 */ $lattr827 = susp.$tmps.$lattr827; +/* 10148 */ $call828 = susp.$tmps.$call828; +/* 10149 */ $call829 = susp.$tmps.$call829; +/* 10150 */ $loadgbl834 = susp.$tmps.$loadgbl834; +/* 10151 */ $lattr835 = susp.$tmps.$lattr835; +/* 10152 */ $lattr837 = susp.$tmps.$lattr837; +/* 10153 */ $call838 = susp.$tmps.$call838; +/* 10154 */ $iter840 = susp.$tmps.$iter840; +/* 10155 */ $call839 = susp.$tmps.$call839; +/* 10156 */ $lattr843 = susp.$tmps.$lattr843; +/* 10157 */ $lattr845 = susp.$tmps.$lattr845; +/* 10158 */ $call846 = susp.$tmps.$call846; +/* 10159 */ $lattr848 = susp.$tmps.$lattr848; +/* 10160 */ $lattr850 = susp.$tmps.$lattr850; +/* 10161 */ $call851 = susp.$tmps.$call851; +/* 10162 */ $compareres853 = susp.$tmps.$compareres853; +/* 10163 */ $loadgbl854 = susp.$tmps.$loadgbl854; +/* 10164 */ $lattr859 = susp.$tmps.$lattr859; +/* 10165 */ $lattr860 = susp.$tmps.$lattr860; +/* 10166 */ $res861 = susp.$tmps.$res861; +/* 10167 */ $compareres862 = susp.$tmps.$compareres862; +/* 10168 */ $jfalse863 = susp.$tmps.$jfalse863; +/* 10169 */ $jfalse864 = susp.$tmps.$jfalse864; +/* 10170 */ $call865 = susp.$tmps.$call865; +/* 10171 */ try { +/* 10172 */ $ret = susp.child.resume(); +/* 10173 */ } catch (err) { +/* 10174 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 10175 */ Sk.execStart = Date.now(); +/* 10176 */ Sk.execPaused = 0 +/* 10177 */ } +/* 10178 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 10179 */ err = new Sk.builtin.ExternalError(err); +/* 10180 */ } +/* 10181 */ Sk.err = err; +/* 10182 */ err.traceback.push({ +/* 10183 */ lineno: $currLineNo, +/* 10184 */ colno: $currColNo, +/* 10185 */ filename: 'src/lib/traceback.py', +/* 10186 */ scope: '$scope750' +/* 10187 */ }); +/* 10188 */ if ($exc.length > 0) { +/* 10189 */ $err = err; +/* 10190 */ $blk = $exc.pop(); +/* 10191 */ } else { +/* 10192 */ throw err; +/* 10193 */ } +/* 10194 */ } +/* 10195 */ }; +/* 10196 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 10197 */ var susp = new Sk.misceval.Suspension(); +/* 10198 */ susp.child = $child; +/* 10199 */ susp.resume = function() { +/* 10200 */ $scope750.$wakingSuspension = susp; +/* 10201 */ return $scope750(); +/* 10202 */ }; +/* 10203 */ susp.data = susp.child.data; +/* 10204 */ susp.$blk = $blk; +/* 10205 */ susp.$loc = $loc; +/* 10206 */ susp.$gbl = $gbl; +/* 10207 */ susp.$exc = $exc; +/* 10208 */ susp.$err = $err; +/* 10209 */ susp.$postfinally = $postfinally; +/* 10210 */ susp.$filename = $filename; +/* 10211 */ susp.$lineno = $lineno; +/* 10212 */ susp.$colno = $colno; +/* 10213 */ susp.optional = susp.child.optional; +/* 10214 */ susp.$tmps = { +/* 10215 */ "count": count, +/* 10216 */ "frame": frame, +/* 10217 */ "last_file": last_file, +/* 10218 */ "last_line": last_line, +/* 10219 */ "last_name": last_name, +/* 10220 */ "name_$rn$": name_$rn$, +/* 10221 */ "result": result, +/* 10222 */ "row": row, +/* 10223 */ "self": self, +/* 10224 */ "value": value, +/* 10225 */ "$iter754": $iter754, +/* 10226 */ "$compareres756": $compareres756, +/* 10227 */ "$jfalse757": $jfalse757, +/* 10228 */ "$boolopsucc758": $boolopsucc758, +/* 10229 */ "$jtrue759": $jtrue759, +/* 10230 */ "$compareres760": $compareres760, +/* 10231 */ "$lattr762": $lattr762, +/* 10232 */ "$jfalse763": $jfalse763, +/* 10233 */ "$jtrue764": $jtrue764, +/* 10234 */ "$compareres765": $compareres765, +/* 10235 */ "$jfalse766": $jfalse766, +/* 10236 */ "$jtrue767": $jtrue767, +/* 10237 */ "$compareres768": $compareres768, +/* 10238 */ "$lattr770": $lattr770, +/* 10239 */ "$jfalse771": $jfalse771, +/* 10240 */ "$jtrue772": $jtrue772, +/* 10241 */ "$compareres773": $compareres773, +/* 10242 */ "$jfalse774": $jfalse774, +/* 10243 */ "$jtrue775": $jtrue775, +/* 10244 */ "$compareres776": $compareres776, +/* 10245 */ "$lattr778": $lattr778, +/* 10246 */ "$compareres782": $compareres782, +/* 10247 */ "$loadgbl783": $loadgbl783, +/* 10248 */ "$lattr789": $lattr789, +/* 10249 */ "$lattr792": $lattr792, +/* 10250 */ "$res793": $res793, +/* 10251 */ "$compareres794": $compareres794, +/* 10252 */ "$jfalse796": $jfalse796, +/* 10253 */ "$jfalse797": $jfalse797, +/* 10254 */ "$call800": $call800, +/* 10255 */ "$compareres806": $compareres806, +/* 10256 */ "$loadgbl807": $loadgbl807, +/* 10257 */ "$lattr811": $lattr811, +/* 10258 */ "$lattr813": $lattr813, +/* 10259 */ "$lattr814": $lattr814, +/* 10260 */ "$lattr815": $lattr815, +/* 10261 */ "$lattr816": $lattr816, +/* 10262 */ "$call817": $call817, +/* 10263 */ "$lattr822": $lattr822, +/* 10264 */ "$lattr824": $lattr824, +/* 10265 */ "$lattr825": $lattr825, +/* 10266 */ "$lattr827": $lattr827, +/* 10267 */ "$call828": $call828, +/* 10268 */ "$call829": $call829, +/* 10269 */ "$loadgbl834": $loadgbl834, +/* 10270 */ "$lattr835": $lattr835, +/* 10271 */ "$lattr837": $lattr837, +/* 10272 */ "$call838": $call838, +/* 10273 */ "$iter840": $iter840, +/* 10274 */ "$call839": $call839, +/* 10275 */ "$lattr843": $lattr843, +/* 10276 */ "$lattr845": $lattr845, +/* 10277 */ "$call846": $call846, +/* 10278 */ "$lattr848": $lattr848, +/* 10279 */ "$lattr850": $lattr850, +/* 10280 */ "$call851": $call851, +/* 10281 */ "$compareres853": $compareres853, +/* 10282 */ "$loadgbl854": $loadgbl854, +/* 10283 */ "$lattr859": $lattr859, +/* 10284 */ "$lattr860": $lattr860, +/* 10285 */ "$res861": $res861, +/* 10286 */ "$compareres862": $compareres862, +/* 10287 */ "$jfalse863": $jfalse863, +/* 10288 */ "$jfalse864": $jfalse864, +/* 10289 */ "$call865": $call865 +/* 10290 */ }; +/* 10291 */ return susp; +/* 10292 */ }; +/* 10293 */ var $blk = 0, +/* 10294 */ $exc = [], +/* 10295 */ $loc = {}, +/* 10296 */ $cell = {}, +/* 10297 */ $gbl = this, +/* 10298 */ $err = undefined, +/* 10299 */ $ret = undefined, +/* 10300 */ $postfinally = undefined, +/* 10301 */ $currLineNo = undefined, +/* 10302 */ $currColNo = undefined; +/* 10303 */ if (typeof Sk.execStart === 'undefined') { +/* 10304 */ Sk.execStart = Date.now(); +/* 10305 */ Sk.execPaused = 0 +/* 10306 */ } +/* 10307 */ if (typeof Sk.lastYield === 'undefined') { +/* 10308 */ Sk.lastYield = Date.now() +/* 10309 */ } +/* 10310 */ if ($scope750.$wakingSuspension !== undefined) { +/* 10311 */ $wakeFromSuspension(); +/* 10312 */ } else {} +/* 10313 */ $gbl.__class__ = this.StackSummary; +/* 10314 */ while (true) { +/* 10315 */ try { +/* 10316 */ var $dateNow = Date.now(); +/* 10317 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 10318 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 10319 */ } +/* 10320 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 10321 */ var $susp = $saveSuspension({ +/* 10322 */ data: { +/* 10323 */ type: 'Sk.yield' +/* 10324 */ }, +/* 10325 */ resume: function() {} +/* 10326 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 10327 */ $susp.$blk = $blk; +/* 10328 */ $susp.optional = true; +/* 10329 */ return $susp; +/* 10330 */ } +/* 10331 */ switch ($blk) { +/* 10332 */ case 0: +/* 10333 */ /* --- codeobj entry --- */ if (self === undefined) { +/* 10334 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 10335 */ } +/* 10336 */ +/* 10337 */ // +/* 10338 */ // line 291: +/* 10339 */ // result = [] +/* 10340 */ // ^ +/* 10341 */ // +/* 10342 */ +/* 10343 */ $currLineNo = Sk.currLineNo = 291; +/* 10344 */ $currColNo = Sk.currColNo = 8; +/* 10345 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10346 */ var $loadlist752 = new Sk.builtins['list']([]); +/* 10347 */ result = $loadlist752; +/* 10348 */ // +/* 10349 */ // line 292: +/* 10350 */ // last_file = None +/* 10351 */ // ^ +/* 10352 */ // +/* 10353 */ +/* 10354 */ $currLineNo = Sk.currLineNo = 292; +/* 10355 */ $currColNo = Sk.currColNo = 8; +/* 10356 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10357 */ last_file = Sk.builtin.none.none$; +/* 10358 */ // +/* 10359 */ // line 293: +/* 10360 */ // last_line = None +/* 10361 */ // ^ +/* 10362 */ // +/* 10363 */ +/* 10364 */ $currLineNo = Sk.currLineNo = 293; +/* 10365 */ $currColNo = Sk.currColNo = 8; +/* 10366 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10367 */ last_line = Sk.builtin.none.none$; +/* 10368 */ // +/* 10369 */ // line 294: +/* 10370 */ // last_name = None +/* 10371 */ // ^ +/* 10372 */ // +/* 10373 */ +/* 10374 */ $currLineNo = Sk.currLineNo = 294; +/* 10375 */ $currColNo = Sk.currColNo = 8; +/* 10376 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10377 */ last_name = Sk.builtin.none.none$; +/* 10378 */ // +/* 10379 */ // line 295: +/* 10380 */ // count = 0 +/* 10381 */ // ^ +/* 10382 */ // +/* 10383 */ +/* 10384 */ $currLineNo = Sk.currLineNo = 295; +/* 10385 */ $currColNo = Sk.currColNo = 8; +/* 10386 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10387 */ count = $scope750.$const753; +/* 10388 */ // +/* 10389 */ // line 296: +/* 10390 */ // for frame in self: +/* 10391 */ // ^ +/* 10392 */ // +/* 10393 */ +/* 10394 */ $currLineNo = Sk.currLineNo = 296; +/* 10395 */ $currColNo = Sk.currColNo = 8; +/* 10396 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10397 */ if (self === undefined) { +/* 10398 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 10399 */ } +/* 10400 */ var $iter754 = Sk.abstr.iter(self); +/* 10401 */ $blk = 1; /* allowing case fallthrough */ +/* 10402 */ case 1: +/* 10403 */ /* --- for start --- */ $ret = Sk.abstr.iternext($iter754, true); +/* 10404 */ $blk = 4; /* allowing case fallthrough */ +/* 10405 */ case 4: +/* 10406 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10407 */ return $saveSuspension($ret, 'src/lib/traceback.py', 296, 8); +/* 10408 */ } +/* 10409 */ var $next755 = $ret; +/* 10410 */ if ($next755 === undefined) { +/* 10411 */ $blk = 2; +/* 10412 */ continue; +/* 10413 */ } +/* 10414 */ frame = $next755; +/* 10415 */ // +/* 10416 */ // line 297: +/* 10417 */ // if (last_file is None or last_file != frame.filename or +/* 10418 */ // ^ +/* 10419 */ // +/* 10420 */ +/* 10421 */ $currLineNo = Sk.currLineNo = 297; +/* 10422 */ $currColNo = Sk.currColNo = 12; +/* 10423 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10424 */ if (last_file === undefined) { +/* 10425 */ throw new Sk.builtin.UnboundLocalError('local variable \'last_file\' referenced before assignment'); +/* 10426 */ } +/* 10427 */ var $compareres756 = null; +/* 10428 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(last_file, Sk.builtin.none.none$, 'Is', true)); +/* 10429 */ $blk = 8; /* allowing case fallthrough */ +/* 10430 */ case 8: +/* 10431 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10432 */ return $saveSuspension($ret, 'src/lib/traceback.py', 297, 16); +/* 10433 */ } +/* 10434 */ $compareres756 = $ret; +/* 10435 */ var $jfalse757 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 10436 */ if ($jfalse757) { +/* 10437 */ /*test failed */ +/* 10438 */ $blk = 7; +/* 10439 */ continue; +/* 10440 */ } +/* 10441 */ $blk = 7; /* allowing case fallthrough */ +/* 10442 */ case 7: +/* 10443 */ /* --- done --- */ var $boolopsucc758 = $compareres756; +/* 10444 */ $boolopsucc758 = $compareres756; +/* 10445 */ var $jtrue759 = ($compareres756 === true || Sk.misceval.isTrue($compareres756)); +/* 10446 */ if ($jtrue759) { +/* 10447 */ /*test passed */ +/* 10448 */ $blk = 6; +/* 10449 */ continue; +/* 10450 */ } +/* 10451 */ if (last_file === undefined) { +/* 10452 */ throw new Sk.builtin.UnboundLocalError('local variable \'last_file\' referenced before assignment'); +/* 10453 */ } +/* 10454 */ var $compareres760 = null; +/* 10455 */ if (frame === undefined) { +/* 10456 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 10457 */ } +/* 10458 */ $ret = Sk.abstr.gattr(frame, $scope750.$const761, true); +/* 10459 */ $blk = 10; /* allowing case fallthrough */ +/* 10460 */ case 10: +/* 10461 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10462 */ return $saveSuspension($ret, 'src/lib/traceback.py', 297, 50); +/* 10463 */ } +/* 10464 */ var $lattr762 = $ret; +/* 10465 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(last_file, $lattr762, 'NotEq', true)); +/* 10466 */ $blk = 11; /* allowing case fallthrough */ +/* 10467 */ case 11: +/* 10468 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10469 */ return $saveSuspension($ret, 'src/lib/traceback.py', 297, 37); +/* 10470 */ } +/* 10471 */ $compareres760 = $ret; +/* 10472 */ var $jfalse763 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 10473 */ if ($jfalse763) { +/* 10474 */ /*test failed */ +/* 10475 */ $blk = 9; +/* 10476 */ continue; +/* 10477 */ } +/* 10478 */ $blk = 9; /* allowing case fallthrough */ +/* 10479 */ case 9: +/* 10480 */ /* --- done --- */ $boolopsucc758 = $compareres760; +/* 10481 */ var $jtrue764 = ($compareres760 === true || Sk.misceval.isTrue($compareres760)); +/* 10482 */ if ($jtrue764) { +/* 10483 */ /*test passed */ +/* 10484 */ $blk = 6; +/* 10485 */ continue; +/* 10486 */ } +/* 10487 */ if (last_line === undefined) { +/* 10488 */ throw new Sk.builtin.UnboundLocalError('local variable \'last_line\' referenced before assignment'); +/* 10489 */ } +/* 10490 */ var $compareres765 = null; +/* 10491 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(last_line, Sk.builtin.none.none$, 'Is', true)); +/* 10492 */ $blk = 13; /* allowing case fallthrough */ +/* 10493 */ case 13: +/* 10494 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10495 */ return $saveSuspension($ret, 'src/lib/traceback.py', 298, 16); +/* 10496 */ } +/* 10497 */ $compareres765 = $ret; +/* 10498 */ var $jfalse766 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 10499 */ if ($jfalse766) { +/* 10500 */ /*test failed */ +/* 10501 */ $blk = 12; +/* 10502 */ continue; +/* 10503 */ } +/* 10504 */ $blk = 12; /* allowing case fallthrough */ +/* 10505 */ case 12: +/* 10506 */ /* --- done --- */ $boolopsucc758 = $compareres765; +/* 10507 */ var $jtrue767 = ($compareres765 === true || Sk.misceval.isTrue($compareres765)); +/* 10508 */ if ($jtrue767) { +/* 10509 */ /*test passed */ +/* 10510 */ $blk = 6; +/* 10511 */ continue; +/* 10512 */ } +/* 10513 */ if (last_line === undefined) { +/* 10514 */ throw new Sk.builtin.UnboundLocalError('local variable \'last_line\' referenced before assignment'); +/* 10515 */ } +/* 10516 */ var $compareres768 = null; +/* 10517 */ if (frame === undefined) { +/* 10518 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 10519 */ } +/* 10520 */ $ret = Sk.abstr.gattr(frame, $scope750.$const769, true); +/* 10521 */ $blk = 15; /* allowing case fallthrough */ +/* 10522 */ case 15: +/* 10523 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10524 */ return $saveSuspension($ret, 'src/lib/traceback.py', 298, 50); +/* 10525 */ } +/* 10526 */ var $lattr770 = $ret; +/* 10527 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(last_line, $lattr770, 'NotEq', true)); +/* 10528 */ $blk = 16; /* allowing case fallthrough */ +/* 10529 */ case 16: +/* 10530 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10531 */ return $saveSuspension($ret, 'src/lib/traceback.py', 298, 37); +/* 10532 */ } +/* 10533 */ $compareres768 = $ret; +/* 10534 */ var $jfalse771 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 10535 */ if ($jfalse771) { +/* 10536 */ /*test failed */ +/* 10537 */ $blk = 14; +/* 10538 */ continue; +/* 10539 */ } +/* 10540 */ $blk = 14; /* allowing case fallthrough */ +/* 10541 */ case 14: +/* 10542 */ /* --- done --- */ $boolopsucc758 = $compareres768; +/* 10543 */ var $jtrue772 = ($compareres768 === true || Sk.misceval.isTrue($compareres768)); +/* 10544 */ if ($jtrue772) { +/* 10545 */ /*test passed */ +/* 10546 */ $blk = 6; +/* 10547 */ continue; +/* 10548 */ } +/* 10549 */ if (last_name === undefined) { +/* 10550 */ throw new Sk.builtin.UnboundLocalError('local variable \'last_name\' referenced before assignment'); +/* 10551 */ } +/* 10552 */ var $compareres773 = null; +/* 10553 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(last_name, Sk.builtin.none.none$, 'Is', true)); +/* 10554 */ $blk = 18; /* allowing case fallthrough */ +/* 10555 */ case 18: +/* 10556 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10557 */ return $saveSuspension($ret, 'src/lib/traceback.py', 299, 16); +/* 10558 */ } +/* 10559 */ $compareres773 = $ret; +/* 10560 */ var $jfalse774 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 10561 */ if ($jfalse774) { +/* 10562 */ /*test failed */ +/* 10563 */ $blk = 17; +/* 10564 */ continue; +/* 10565 */ } +/* 10566 */ $blk = 17; /* allowing case fallthrough */ +/* 10567 */ case 17: +/* 10568 */ /* --- done --- */ $boolopsucc758 = $compareres773; +/* 10569 */ var $jtrue775 = ($compareres773 === true || Sk.misceval.isTrue($compareres773)); +/* 10570 */ if ($jtrue775) { +/* 10571 */ /*test passed */ +/* 10572 */ $blk = 6; +/* 10573 */ continue; +/* 10574 */ } +/* 10575 */ if (last_name === undefined) { +/* 10576 */ throw new Sk.builtin.UnboundLocalError('local variable \'last_name\' referenced before assignment'); +/* 10577 */ } +/* 10578 */ var $compareres776 = null; +/* 10579 */ if (frame === undefined) { +/* 10580 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 10581 */ } +/* 10582 */ $ret = Sk.abstr.gattr(frame, $scope750.$const777, true); +/* 10583 */ $blk = 20; /* allowing case fallthrough */ +/* 10584 */ case 20: +/* 10585 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10586 */ return $saveSuspension($ret, 'src/lib/traceback.py', 299, 50); +/* 10587 */ } +/* 10588 */ var $lattr778 = $ret; +/* 10589 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(last_name, $lattr778, 'NotEq', true)); +/* 10590 */ $blk = 21; /* allowing case fallthrough */ +/* 10591 */ case 21: +/* 10592 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10593 */ return $saveSuspension($ret, 'src/lib/traceback.py', 299, 37); +/* 10594 */ } +/* 10595 */ $compareres776 = $ret; +/* 10596 */ var $jfalse779 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 10597 */ if ($jfalse779) { +/* 10598 */ /*test failed */ +/* 10599 */ $blk = 19; +/* 10600 */ continue; +/* 10601 */ } +/* 10602 */ $blk = 19; /* allowing case fallthrough */ +/* 10603 */ case 19: +/* 10604 */ /* --- done --- */ $boolopsucc758 = $compareres776; +/* 10605 */ var $jtrue780 = ($compareres776 === true || Sk.misceval.isTrue($compareres776)); +/* 10606 */ if ($jtrue780) { +/* 10607 */ /*test passed */ +/* 10608 */ $blk = 6; +/* 10609 */ continue; +/* 10610 */ } +/* 10611 */ $blk = 6; /* allowing case fallthrough */ +/* 10612 */ case 6: +/* 10613 */ /* --- end of boolop --- */ var $jfalse781 = ($boolopsucc758 === false || !Sk.misceval.isTrue($boolopsucc758)); +/* 10614 */ if ($jfalse781) { +/* 10615 */ /*test failed */ +/* 10616 */ $blk = 5; +/* 10617 */ continue; +/* 10618 */ } +/* 10619 */ // +/* 10620 */ // line 300: +/* 10621 */ // if count > _RECURSIVE_CUTOFF: +/* 10622 */ // ^ +/* 10623 */ // +/* 10624 */ +/* 10625 */ $currLineNo = Sk.currLineNo = 300; +/* 10626 */ $currColNo = Sk.currColNo = 16; +/* 10627 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10628 */ if (count === undefined) { +/* 10629 */ throw new Sk.builtin.UnboundLocalError('local variable \'count\' referenced before assignment'); +/* 10630 */ } +/* 10631 */ var $compareres782 = null; +/* 10632 */ var $loadgbl783 = Sk.misceval.loadname('_RECURSIVE_CUTOFF', $gbl); +/* 10633 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(count, $loadgbl783, 'Gt', true)); +/* 10634 */ $blk = 24; /* allowing case fallthrough */ +/* 10635 */ case 24: +/* 10636 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10637 */ return $saveSuspension($ret, 'src/lib/traceback.py', 300, 19); +/* 10638 */ } +/* 10639 */ $compareres782 = $ret; +/* 10640 */ var $jfalse784 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 10641 */ if ($jfalse784) { +/* 10642 */ /*test failed */ +/* 10643 */ $blk = 23; +/* 10644 */ continue; +/* 10645 */ } +/* 10646 */ $blk = 23; /* allowing case fallthrough */ +/* 10647 */ case 23: +/* 10648 */ /* --- done --- */ var $jfalse785 = ($compareres782 === false || !Sk.misceval.isTrue($compareres782)); +/* 10649 */ if ($jfalse785) { +/* 10650 */ /*test failed */ +/* 10651 */ $blk = 22; +/* 10652 */ continue; +/* 10653 */ } +/* 10654 */ // +/* 10655 */ // line 301: +/* 10656 */ // count -= _RECURSIVE_CUTOFF +/* 10657 */ // ^ +/* 10658 */ // +/* 10659 */ +/* 10660 */ $currLineNo = Sk.currLineNo = 301; +/* 10661 */ $currColNo = Sk.currColNo = 20; +/* 10662 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10663 */ if (count === undefined) { +/* 10664 */ throw new Sk.builtin.UnboundLocalError('local variable \'count\' referenced before assignment'); +/* 10665 */ } +/* 10666 */ var $loadgbl786 = Sk.misceval.loadname('_RECURSIVE_CUTOFF', $gbl); +/* 10667 */ var $inplbinop787 = Sk.abstr.numberInplaceBinOp(count, $loadgbl786, 'Sub'); +/* 10668 */ count = $inplbinop787; +/* 10669 */ // +/* 10670 */ // line 302: +/* 10671 */ // result.append(( +/* 10672 */ // ^ +/* 10673 */ // +/* 10674 */ +/* 10675 */ $currLineNo = Sk.currLineNo = 302; +/* 10676 */ $currColNo = Sk.currColNo = 20; +/* 10677 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10678 */ if (result === undefined) { +/* 10679 */ throw new Sk.builtin.UnboundLocalError('local variable \'result\' referenced before assignment'); +/* 10680 */ } +/* 10681 */ $ret = Sk.abstr.gattr(result, $scope750.$const788, true); +/* 10682 */ $blk = 25; /* allowing case fallthrough */ +/* 10683 */ case 25: +/* 10684 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10685 */ return $saveSuspension($ret, 'src/lib/traceback.py', 302, 20); +/* 10686 */ } +/* 10687 */ var $lattr789 = $ret; +/* 10688 */ $ret = Sk.abstr.gattr($scope750.$const790, $scope750.$const791, true); +/* 10689 */ $blk = 26; /* allowing case fallthrough */ +/* 10690 */ case 26: +/* 10691 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10692 */ return $saveSuspension($ret, 'src/lib/traceback.py', 303, 24); +/* 10693 */ } +/* 10694 */ var $lattr792 = $ret; +/* 10695 */ if (count === undefined) { +/* 10696 */ throw new Sk.builtin.UnboundLocalError('local variable \'count\' referenced before assignment'); +/* 10697 */ } +/* 10698 */ var $res793 = null; +/* 10699 */ if (count === undefined) { +/* 10700 */ throw new Sk.builtin.UnboundLocalError('local variable \'count\' referenced before assignment'); +/* 10701 */ } +/* 10702 */ var $compareres794 = null; +/* 10703 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(count, $scope750.$const795, 'Gt', true)); +/* 10704 */ $blk = 30; /* allowing case fallthrough */ +/* 10705 */ case 30: +/* 10706 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10707 */ return $saveSuspension($ret, 'src/lib/traceback.py', 305, 57); +/* 10708 */ } +/* 10709 */ $compareres794 = $ret; +/* 10710 */ var $jfalse796 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 10711 */ if ($jfalse796) { +/* 10712 */ /*test failed */ +/* 10713 */ $blk = 29; +/* 10714 */ continue; +/* 10715 */ } +/* 10716 */ $blk = 29; /* allowing case fallthrough */ +/* 10717 */ case 29: +/* 10718 */ /* --- done --- */ var $jfalse797 = ($compareres794 === false || !Sk.misceval.isTrue($compareres794)); +/* 10719 */ if ($jfalse797) { +/* 10720 */ /*test failed */ +/* 10721 */ $blk = 27; +/* 10722 */ continue; +/* 10723 */ } +/* 10724 */ $res793 = $scope750.$const798; +/* 10725 */ $blk = 28; /* allowing case fallthrough */ +/* 10726 */ case 28: +/* 10727 */ /* --- end of ifexp --- */ $ret = Sk.misceval.applyOrSuspend($lattr792, undefined, undefined, ['count', count, 's_count', $res793], []); +/* 10728 */ $blk = 31; /* allowing case fallthrough */ +/* 10729 */ case 31: +/* 10730 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10731 */ return $saveSuspension($ret, 'src/lib/traceback.py', 303, 24); +/* 10732 */ } +/* 10733 */ var $call800 = $ret; +/* 10734 */ // +/* 10735 */ // line 303: +/* 10736 */ // ' [Previous line repeated {count} more ' +/* 10737 */ // ^ +/* 10738 */ // +/* 10739 */ +/* 10740 */ $currLineNo = Sk.currLineNo = 303; +/* 10741 */ $currColNo = Sk.currColNo = 24; +/* 10742 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10743 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr789, [$call800]); +/* 10744 */ $blk = 32; /* allowing case fallthrough */ +/* 10745 */ case 32: +/* 10746 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10747 */ return $saveSuspension($ret, 'src/lib/traceback.py', 302, 20); +/* 10748 */ } +/* 10749 */ var $call801 = $ret; +/* 10750 */ // +/* 10751 */ // line 302: +/* 10752 */ // result.append(( +/* 10753 */ // ^ +/* 10754 */ // +/* 10755 */ +/* 10756 */ $currLineNo = Sk.currLineNo = 302; +/* 10757 */ $currColNo = Sk.currColNo = 20; +/* 10758 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10759 */ $blk = 22; /* allowing case fallthrough */ +/* 10760 */ case 22: +/* 10761 */ /* --- end of if --- */ +/* 10762 */ // +/* 10763 */ // line 306: +/* 10764 */ // last_file = frame.filename +/* 10765 */ // ^ +/* 10766 */ // +/* 10767 */ +/* 10768 */ $currLineNo = Sk.currLineNo = 306; +/* 10769 */ $currColNo = Sk.currColNo = 16; +/* 10770 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10771 */ if (frame === undefined) { +/* 10772 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 10773 */ } +/* 10774 */ $ret = Sk.abstr.gattr(frame, $scope750.$const761, true); +/* 10775 */ $blk = 33; /* allowing case fallthrough */ +/* 10776 */ case 33: +/* 10777 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10778 */ return $saveSuspension($ret, 'src/lib/traceback.py', 306, 28); +/* 10779 */ } +/* 10780 */ var $lattr802 = $ret; +/* 10781 */ last_file = $lattr802; +/* 10782 */ // +/* 10783 */ // line 307: +/* 10784 */ // last_line = frame.lineno +/* 10785 */ // ^ +/* 10786 */ // +/* 10787 */ +/* 10788 */ $currLineNo = Sk.currLineNo = 307; +/* 10789 */ $currColNo = Sk.currColNo = 16; +/* 10790 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10791 */ if (frame === undefined) { +/* 10792 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 10793 */ } +/* 10794 */ $ret = Sk.abstr.gattr(frame, $scope750.$const769, true); +/* 10795 */ $blk = 34; /* allowing case fallthrough */ +/* 10796 */ case 34: +/* 10797 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10798 */ return $saveSuspension($ret, 'src/lib/traceback.py', 307, 28); +/* 10799 */ } +/* 10800 */ var $lattr803 = $ret; +/* 10801 */ last_line = $lattr803; +/* 10802 */ // +/* 10803 */ // line 308: +/* 10804 */ // last_name = frame.name +/* 10805 */ // ^ +/* 10806 */ // +/* 10807 */ +/* 10808 */ $currLineNo = Sk.currLineNo = 308; +/* 10809 */ $currColNo = Sk.currColNo = 16; +/* 10810 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10811 */ if (frame === undefined) { +/* 10812 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 10813 */ } +/* 10814 */ $ret = Sk.abstr.gattr(frame, $scope750.$const777, true); +/* 10815 */ $blk = 35; /* allowing case fallthrough */ +/* 10816 */ case 35: +/* 10817 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10818 */ return $saveSuspension($ret, 'src/lib/traceback.py', 308, 28); +/* 10819 */ } +/* 10820 */ var $lattr804 = $ret; +/* 10821 */ last_name = $lattr804; +/* 10822 */ // +/* 10823 */ // line 309: +/* 10824 */ // count = 0 +/* 10825 */ // ^ +/* 10826 */ // +/* 10827 */ +/* 10828 */ $currLineNo = Sk.currLineNo = 309; +/* 10829 */ $currColNo = Sk.currColNo = 16; +/* 10830 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10831 */ count = $scope750.$const753; +/* 10832 */ $blk = 5; /* allowing case fallthrough */ +/* 10833 */ case 5: +/* 10834 */ /* --- end of if --- */ +/* 10835 */ // +/* 10836 */ // line 310: +/* 10837 */ // count += 1 +/* 10838 */ // ^ +/* 10839 */ // +/* 10840 */ +/* 10841 */ $currLineNo = Sk.currLineNo = 310; +/* 10842 */ $currColNo = Sk.currColNo = 12; +/* 10843 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10844 */ if (count === undefined) { +/* 10845 */ throw new Sk.builtin.UnboundLocalError('local variable \'count\' referenced before assignment'); +/* 10846 */ } +/* 10847 */ var $inplbinop805 = Sk.abstr.numberInplaceBinOp(count, $scope750.$const795, 'Add'); +/* 10848 */ count = $inplbinop805; +/* 10849 */ // +/* 10850 */ // line 311: +/* 10851 */ // if count > _RECURSIVE_CUTOFF: +/* 10852 */ // ^ +/* 10853 */ // +/* 10854 */ +/* 10855 */ $currLineNo = Sk.currLineNo = 311; +/* 10856 */ $currColNo = Sk.currColNo = 12; +/* 10857 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10858 */ if (count === undefined) { +/* 10859 */ throw new Sk.builtin.UnboundLocalError('local variable \'count\' referenced before assignment'); +/* 10860 */ } +/* 10861 */ var $compareres806 = null; +/* 10862 */ var $loadgbl807 = Sk.misceval.loadname('_RECURSIVE_CUTOFF', $gbl); +/* 10863 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(count, $loadgbl807, 'Gt', true)); +/* 10864 */ $blk = 38; /* allowing case fallthrough */ +/* 10865 */ case 38: +/* 10866 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10867 */ return $saveSuspension($ret, 'src/lib/traceback.py', 311, 15); +/* 10868 */ } +/* 10869 */ $compareres806 = $ret; +/* 10870 */ var $jfalse808 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 10871 */ if ($jfalse808) { +/* 10872 */ /*test failed */ +/* 10873 */ $blk = 37; +/* 10874 */ continue; +/* 10875 */ } +/* 10876 */ $blk = 37; /* allowing case fallthrough */ +/* 10877 */ case 37: +/* 10878 */ /* --- done --- */ var $jfalse809 = ($compareres806 === false || !Sk.misceval.isTrue($compareres806)); +/* 10879 */ if ($jfalse809) { +/* 10880 */ /*test failed */ +/* 10881 */ $blk = 36; +/* 10882 */ continue; +/* 10883 */ } +/* 10884 */ // +/* 10885 */ // line 312: +/* 10886 */ // continue +/* 10887 */ // ^ +/* 10888 */ // +/* 10889 */ +/* 10890 */ $currLineNo = Sk.currLineNo = 312; +/* 10891 */ $currColNo = Sk.currColNo = 16; +/* 10892 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10893 */ $blk = 1; /* jump */ +/* 10894 */ continue; +/* 10895 */ case 2: +/* 10896 */ /* --- for cleanup --- */ $blk = 3; /* allowing case fallthrough */ +/* 10897 */ case 3: +/* 10898 */ /* --- for end --- */ +/* 10899 */ // +/* 10900 */ // line 322: +/* 10901 */ // if count > _RECURSIVE_CUTOFF: +/* 10902 */ // ^ +/* 10903 */ // +/* 10904 */ +/* 10905 */ $currLineNo = Sk.currLineNo = 322; +/* 10906 */ $currColNo = Sk.currColNo = 8; +/* 10907 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10908 */ if (count === undefined) { +/* 10909 */ throw new Sk.builtin.UnboundLocalError('local variable \'count\' referenced before assignment'); +/* 10910 */ } +/* 10911 */ var $compareres853 = null; +/* 10912 */ var $loadgbl854 = Sk.misceval.loadname('_RECURSIVE_CUTOFF', $gbl); +/* 10913 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(count, $loadgbl854, 'Gt', true)); +/* 10914 */ $blk = 75; /* allowing case fallthrough */ +/* 10915 */ case 75: +/* 10916 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10917 */ return $saveSuspension($ret, 'src/lib/traceback.py', 322, 11); +/* 10918 */ } +/* 10919 */ $compareres853 = $ret; +/* 10920 */ var $jfalse855 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 10921 */ if ($jfalse855) { +/* 10922 */ /*test failed */ +/* 10923 */ $blk = 74; +/* 10924 */ continue; +/* 10925 */ } +/* 10926 */ $blk = 74; /* allowing case fallthrough */ +/* 10927 */ case 74: +/* 10928 */ /* --- done --- */ var $jfalse856 = ($compareres853 === false || !Sk.misceval.isTrue($compareres853)); +/* 10929 */ if ($jfalse856) { +/* 10930 */ /*test failed */ +/* 10931 */ $blk = 73; +/* 10932 */ continue; +/* 10933 */ } +/* 10934 */ // +/* 10935 */ // line 323: +/* 10936 */ // count -= _RECURSIVE_CUTOFF +/* 10937 */ // ^ +/* 10938 */ // +/* 10939 */ +/* 10940 */ $currLineNo = Sk.currLineNo = 323; +/* 10941 */ $currColNo = Sk.currColNo = 12; +/* 10942 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10943 */ if (count === undefined) { +/* 10944 */ throw new Sk.builtin.UnboundLocalError('local variable \'count\' referenced before assignment'); +/* 10945 */ } +/* 10946 */ var $loadgbl857 = Sk.misceval.loadname('_RECURSIVE_CUTOFF', $gbl); +/* 10947 */ var $inplbinop858 = Sk.abstr.numberInplaceBinOp(count, $loadgbl857, 'Sub'); +/* 10948 */ count = $inplbinop858; +/* 10949 */ // +/* 10950 */ // line 324: +/* 10951 */ // result.append(( +/* 10952 */ // ^ +/* 10953 */ // +/* 10954 */ +/* 10955 */ $currLineNo = Sk.currLineNo = 324; +/* 10956 */ $currColNo = Sk.currColNo = 12; +/* 10957 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 10958 */ if (result === undefined) { +/* 10959 */ throw new Sk.builtin.UnboundLocalError('local variable \'result\' referenced before assignment'); +/* 10960 */ } +/* 10961 */ $ret = Sk.abstr.gattr(result, $scope750.$const788, true); +/* 10962 */ $blk = 76; /* allowing case fallthrough */ +/* 10963 */ case 76: +/* 10964 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10965 */ return $saveSuspension($ret, 'src/lib/traceback.py', 324, 12); +/* 10966 */ } +/* 10967 */ var $lattr859 = $ret; +/* 10968 */ $ret = Sk.abstr.gattr($scope750.$const790, $scope750.$const791, true); +/* 10969 */ $blk = 77; /* allowing case fallthrough */ +/* 10970 */ case 77: +/* 10971 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10972 */ return $saveSuspension($ret, 'src/lib/traceback.py', 325, 16); +/* 10973 */ } +/* 10974 */ var $lattr860 = $ret; +/* 10975 */ if (count === undefined) { +/* 10976 */ throw new Sk.builtin.UnboundLocalError('local variable \'count\' referenced before assignment'); +/* 10977 */ } +/* 10978 */ var $res861 = null; +/* 10979 */ if (count === undefined) { +/* 10980 */ throw new Sk.builtin.UnboundLocalError('local variable \'count\' referenced before assignment'); +/* 10981 */ } +/* 10982 */ var $compareres862 = null; +/* 10983 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(count, $scope750.$const795, 'Gt', true)); +/* 10984 */ $blk = 81; /* allowing case fallthrough */ +/* 10985 */ case 81: +/* 10986 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 10987 */ return $saveSuspension($ret, 'src/lib/traceback.py', 327, 49); +/* 10988 */ } +/* 10989 */ $compareres862 = $ret; +/* 10990 */ var $jfalse863 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 10991 */ if ($jfalse863) { +/* 10992 */ /*test failed */ +/* 10993 */ $blk = 80; +/* 10994 */ continue; +/* 10995 */ } +/* 10996 */ $blk = 80; /* allowing case fallthrough */ +/* 10997 */ case 80: +/* 10998 */ /* --- done --- */ var $jfalse864 = ($compareres862 === false || !Sk.misceval.isTrue($compareres862)); +/* 10999 */ if ($jfalse864) { +/* 11000 */ /*test failed */ +/* 11001 */ $blk = 78; +/* 11002 */ continue; +/* 11003 */ } +/* 11004 */ $res861 = $scope750.$const798; +/* 11005 */ $blk = 79; /* allowing case fallthrough */ +/* 11006 */ case 79: +/* 11007 */ /* --- end of ifexp --- */ $ret = Sk.misceval.applyOrSuspend($lattr860, undefined, undefined, ['count', count, 's_count', $res861], []); +/* 11008 */ $blk = 82; /* allowing case fallthrough */ +/* 11009 */ case 82: +/* 11010 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11011 */ return $saveSuspension($ret, 'src/lib/traceback.py', 325, 16); +/* 11012 */ } +/* 11013 */ var $call865 = $ret; +/* 11014 */ // +/* 11015 */ // line 325: +/* 11016 */ // ' [Previous line repeated {count} more ' +/* 11017 */ // ^ +/* 11018 */ // +/* 11019 */ +/* 11020 */ $currLineNo = Sk.currLineNo = 325; +/* 11021 */ $currColNo = Sk.currColNo = 16; +/* 11022 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11023 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr859, [$call865]); +/* 11024 */ $blk = 83; /* allowing case fallthrough */ +/* 11025 */ case 83: +/* 11026 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11027 */ return $saveSuspension($ret, 'src/lib/traceback.py', 324, 12); +/* 11028 */ } +/* 11029 */ var $call866 = $ret; +/* 11030 */ // +/* 11031 */ // line 324: +/* 11032 */ // result.append(( +/* 11033 */ // ^ +/* 11034 */ // +/* 11035 */ +/* 11036 */ $currLineNo = Sk.currLineNo = 324; +/* 11037 */ $currColNo = Sk.currColNo = 12; +/* 11038 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11039 */ $blk = 73; /* allowing case fallthrough */ +/* 11040 */ case 73: +/* 11041 */ /* --- end of if --- */ +/* 11042 */ // +/* 11043 */ // line 328: +/* 11044 */ // return result +/* 11045 */ // ^ +/* 11046 */ // +/* 11047 */ +/* 11048 */ $currLineNo = Sk.currLineNo = 328; +/* 11049 */ $currColNo = Sk.currColNo = 8; +/* 11050 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11051 */ if (result === undefined) { +/* 11052 */ throw new Sk.builtin.UnboundLocalError('local variable \'result\' referenced before assignment'); +/* 11053 */ } +/* 11054 */ return result; +/* 11055 */ return Sk.builtin.none.none$; +/* 11056 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 11057 */ case 27: +/* 11058 */ /* --- next of ifexp --- */ $res793 = $scope750.$const799; +/* 11059 */ $blk = 28; /* jump */ +/* 11060 */ continue; +/* 11061 */ case 36: +/* 11062 */ /* --- end of if --- */ +/* 11063 */ // +/* 11064 */ // line 313: +/* 11065 */ // row = [] +/* 11066 */ // ^ +/* 11067 */ // +/* 11068 */ +/* 11069 */ $currLineNo = Sk.currLineNo = 313; +/* 11070 */ $currColNo = Sk.currColNo = 12; +/* 11071 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11072 */ var $loadlist810 = new Sk.builtins['list']([]); +/* 11073 */ row = $loadlist810; +/* 11074 */ // +/* 11075 */ // line 314: +/* 11076 */ // row.append(' File "{}", line {}, in {}\n'.format( +/* 11077 */ // ^ +/* 11078 */ // +/* 11079 */ +/* 11080 */ $currLineNo = Sk.currLineNo = 314; +/* 11081 */ $currColNo = Sk.currColNo = 12; +/* 11082 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11083 */ if (row === undefined) { +/* 11084 */ throw new Sk.builtin.UnboundLocalError('local variable \'row\' referenced before assignment'); +/* 11085 */ } +/* 11086 */ $ret = Sk.abstr.gattr(row, $scope750.$const788, true); +/* 11087 */ $blk = 39; /* allowing case fallthrough */ +/* 11088 */ case 39: +/* 11089 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11090 */ return $saveSuspension($ret, 'src/lib/traceback.py', 314, 12); +/* 11091 */ } +/* 11092 */ var $lattr811 = $ret; +/* 11093 */ $ret = Sk.abstr.gattr($scope750.$const812, $scope750.$const791, true); +/* 11094 */ $blk = 40; /* allowing case fallthrough */ +/* 11095 */ case 40: +/* 11096 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11097 */ return $saveSuspension($ret, 'src/lib/traceback.py', 314, 23); +/* 11098 */ } +/* 11099 */ var $lattr813 = $ret; +/* 11100 */ if (frame === undefined) { +/* 11101 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 11102 */ } +/* 11103 */ $ret = Sk.abstr.gattr(frame, $scope750.$const761, true); +/* 11104 */ $blk = 41; /* allowing case fallthrough */ +/* 11105 */ case 41: +/* 11106 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11107 */ return $saveSuspension($ret, 'src/lib/traceback.py', 315, 16); +/* 11108 */ } +/* 11109 */ var $lattr814 = $ret; +/* 11110 */ if (frame === undefined) { +/* 11111 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 11112 */ } +/* 11113 */ $ret = Sk.abstr.gattr(frame, $scope750.$const769, true); +/* 11114 */ $blk = 42; /* allowing case fallthrough */ +/* 11115 */ case 42: +/* 11116 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11117 */ return $saveSuspension($ret, 'src/lib/traceback.py', 315, 32); +/* 11118 */ } +/* 11119 */ var $lattr815 = $ret; +/* 11120 */ if (frame === undefined) { +/* 11121 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 11122 */ } +/* 11123 */ $ret = Sk.abstr.gattr(frame, $scope750.$const777, true); +/* 11124 */ $blk = 43; /* allowing case fallthrough */ +/* 11125 */ case 43: +/* 11126 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11127 */ return $saveSuspension($ret, 'src/lib/traceback.py', 315, 46); +/* 11128 */ } +/* 11129 */ var $lattr816 = $ret; +/* 11130 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr813, [$lattr814, $lattr815, $lattr816]); +/* 11131 */ $blk = 44; /* allowing case fallthrough */ +/* 11132 */ case 44: +/* 11133 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11134 */ return $saveSuspension($ret, 'src/lib/traceback.py', 314, 23); +/* 11135 */ } +/* 11136 */ var $call817 = $ret; +/* 11137 */ // +/* 11138 */ // line 314: +/* 11139 */ // row.append(' File "{}", line {}, in {}\n'.format( +/* 11140 */ // ^ +/* 11141 */ // +/* 11142 */ +/* 11143 */ $currLineNo = Sk.currLineNo = 314; +/* 11144 */ $currColNo = Sk.currColNo = 23; +/* 11145 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11146 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr811, [$call817]); +/* 11147 */ $blk = 45; /* allowing case fallthrough */ +/* 11148 */ case 45: +/* 11149 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11150 */ return $saveSuspension($ret, 'src/lib/traceback.py', 314, 12); +/* 11151 */ } +/* 11152 */ var $call818 = $ret; +/* 11153 */ // +/* 11154 */ // line 314: +/* 11155 */ // row.append(' File "{}", line {}, in {}\n'.format( +/* 11156 */ // ^ +/* 11157 */ // +/* 11158 */ +/* 11159 */ $currLineNo = Sk.currLineNo = 314; +/* 11160 */ $currColNo = Sk.currColNo = 12; +/* 11161 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11162 */ // +/* 11163 */ // line 316: +/* 11164 */ // if frame.line: +/* 11165 */ // ^ +/* 11166 */ // +/* 11167 */ +/* 11168 */ $currLineNo = Sk.currLineNo = 316; +/* 11169 */ $currColNo = Sk.currColNo = 12; +/* 11170 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11171 */ if (frame === undefined) { +/* 11172 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 11173 */ } +/* 11174 */ $ret = Sk.abstr.gattr(frame, $scope750.$const819, true); +/* 11175 */ $blk = 47; /* allowing case fallthrough */ +/* 11176 */ case 47: +/* 11177 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11178 */ return $saveSuspension($ret, 'src/lib/traceback.py', 316, 15); +/* 11179 */ } +/* 11180 */ var $lattr820 = $ret; +/* 11181 */ var $jfalse821 = ($lattr820 === false || !Sk.misceval.isTrue($lattr820)); +/* 11182 */ if ($jfalse821) { +/* 11183 */ /*test failed */ +/* 11184 */ $blk = 46; +/* 11185 */ continue; +/* 11186 */ } +/* 11187 */ // +/* 11188 */ // line 317: +/* 11189 */ // row.append(' {}\n'.format(frame.line.strip())) +/* 11190 */ // ^ +/* 11191 */ // +/* 11192 */ +/* 11193 */ $currLineNo = Sk.currLineNo = 317; +/* 11194 */ $currColNo = Sk.currColNo = 16; +/* 11195 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11196 */ if (row === undefined) { +/* 11197 */ throw new Sk.builtin.UnboundLocalError('local variable \'row\' referenced before assignment'); +/* 11198 */ } +/* 11199 */ $ret = Sk.abstr.gattr(row, $scope750.$const788, true); +/* 11200 */ $blk = 48; /* allowing case fallthrough */ +/* 11201 */ case 48: +/* 11202 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11203 */ return $saveSuspension($ret, 'src/lib/traceback.py', 317, 16); +/* 11204 */ } +/* 11205 */ var $lattr822 = $ret; +/* 11206 */ $ret = Sk.abstr.gattr($scope750.$const823, $scope750.$const791, true); +/* 11207 */ $blk = 49; /* allowing case fallthrough */ +/* 11208 */ case 49: +/* 11209 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11210 */ return $saveSuspension($ret, 'src/lib/traceback.py', 317, 27); +/* 11211 */ } +/* 11212 */ var $lattr824 = $ret; +/* 11213 */ if (frame === undefined) { +/* 11214 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 11215 */ } +/* 11216 */ $ret = Sk.abstr.gattr(frame, $scope750.$const819, true); +/* 11217 */ $blk = 50; /* allowing case fallthrough */ +/* 11218 */ case 50: +/* 11219 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11220 */ return $saveSuspension($ret, 'src/lib/traceback.py', 317, 45); +/* 11221 */ } +/* 11222 */ var $lattr825 = $ret; +/* 11223 */ $ret = Sk.abstr.gattr($lattr825, $scope750.$const826, true); +/* 11224 */ $blk = 51; /* allowing case fallthrough */ +/* 11225 */ case 51: +/* 11226 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11227 */ return $saveSuspension($ret, 'src/lib/traceback.py', 317, 45); +/* 11228 */ } +/* 11229 */ var $lattr827 = $ret; +/* 11230 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr827); +/* 11231 */ $blk = 52; /* allowing case fallthrough */ +/* 11232 */ case 52: +/* 11233 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11234 */ return $saveSuspension($ret, 'src/lib/traceback.py', 317, 45); +/* 11235 */ } +/* 11236 */ var $call828 = $ret; +/* 11237 */ // +/* 11238 */ // line 317: +/* 11239 */ // row.append(' {}\n'.format(frame.line.strip())) +/* 11240 */ // ^ +/* 11241 */ // +/* 11242 */ +/* 11243 */ $currLineNo = Sk.currLineNo = 317; +/* 11244 */ $currColNo = Sk.currColNo = 45; +/* 11245 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11246 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr824, [$call828]); +/* 11247 */ $blk = 53; /* allowing case fallthrough */ +/* 11248 */ case 53: +/* 11249 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11250 */ return $saveSuspension($ret, 'src/lib/traceback.py', 317, 27); +/* 11251 */ } +/* 11252 */ var $call829 = $ret; +/* 11253 */ // +/* 11254 */ // line 317: +/* 11255 */ // row.append(' {}\n'.format(frame.line.strip())) +/* 11256 */ // ^ +/* 11257 */ // +/* 11258 */ +/* 11259 */ $currLineNo = Sk.currLineNo = 317; +/* 11260 */ $currColNo = Sk.currColNo = 27; +/* 11261 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11262 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr822, [$call829]); +/* 11263 */ $blk = 54; /* allowing case fallthrough */ +/* 11264 */ case 54: +/* 11265 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11266 */ return $saveSuspension($ret, 'src/lib/traceback.py', 317, 16); +/* 11267 */ } +/* 11268 */ var $call830 = $ret; +/* 11269 */ // +/* 11270 */ // line 317: +/* 11271 */ // row.append(' {}\n'.format(frame.line.strip())) +/* 11272 */ // ^ +/* 11273 */ // +/* 11274 */ +/* 11275 */ $currLineNo = Sk.currLineNo = 317; +/* 11276 */ $currColNo = Sk.currColNo = 16; +/* 11277 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11278 */ $blk = 46; /* allowing case fallthrough */ +/* 11279 */ case 46: +/* 11280 */ /* --- end of if --- */ +/* 11281 */ // +/* 11282 */ // line 318: +/* 11283 */ // if frame.locals: +/* 11284 */ // ^ +/* 11285 */ // +/* 11286 */ +/* 11287 */ $currLineNo = Sk.currLineNo = 318; +/* 11288 */ $currColNo = Sk.currColNo = 12; +/* 11289 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11290 */ if (frame === undefined) { +/* 11291 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 11292 */ } +/* 11293 */ $ret = Sk.abstr.gattr(frame, $scope750.$const831, true); +/* 11294 */ $blk = 56; /* allowing case fallthrough */ +/* 11295 */ case 56: +/* 11296 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11297 */ return $saveSuspension($ret, 'src/lib/traceback.py', 318, 15); +/* 11298 */ } +/* 11299 */ var $lattr832 = $ret; +/* 11300 */ var $jfalse833 = ($lattr832 === false || !Sk.misceval.isTrue($lattr832)); +/* 11301 */ if ($jfalse833) { +/* 11302 */ /*test failed */ +/* 11303 */ $blk = 55; +/* 11304 */ continue; +/* 11305 */ } +/* 11306 */ // +/* 11307 */ // line 319: +/* 11308 */ // for name, value in sorted(frame.locals.items()): +/* 11309 */ // ^ +/* 11310 */ // +/* 11311 */ +/* 11312 */ $currLineNo = Sk.currLineNo = 319; +/* 11313 */ $currColNo = Sk.currColNo = 16; +/* 11314 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11315 */ var $loadgbl834 = Sk.misceval.loadname('sorted', $gbl); +/* 11316 */ if (frame === undefined) { +/* 11317 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 11318 */ } +/* 11319 */ $ret = Sk.abstr.gattr(frame, $scope750.$const831, true); +/* 11320 */ $blk = 60; /* allowing case fallthrough */ +/* 11321 */ case 60: +/* 11322 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11323 */ return $saveSuspension($ret, 'src/lib/traceback.py', 319, 42); +/* 11324 */ } +/* 11325 */ var $lattr835 = $ret; +/* 11326 */ $ret = Sk.abstr.gattr($lattr835, $scope750.$const836, true); +/* 11327 */ $blk = 61; /* allowing case fallthrough */ +/* 11328 */ case 61: +/* 11329 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11330 */ return $saveSuspension($ret, 'src/lib/traceback.py', 319, 42); +/* 11331 */ } +/* 11332 */ var $lattr837 = $ret; +/* 11333 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr837); +/* 11334 */ $blk = 62; /* allowing case fallthrough */ +/* 11335 */ case 62: +/* 11336 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11337 */ return $saveSuspension($ret, 'src/lib/traceback.py', 319, 42); +/* 11338 */ } +/* 11339 */ var $call838 = $ret; +/* 11340 */ // +/* 11341 */ // line 319: +/* 11342 */ // for name, value in sorted(frame.locals.items()): +/* 11343 */ // ^ +/* 11344 */ // +/* 11345 */ +/* 11346 */ $currLineNo = Sk.currLineNo = 319; +/* 11347 */ $currColNo = Sk.currColNo = 42; +/* 11348 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11349 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl834, [$call838]); +/* 11350 */ $blk = 63; /* allowing case fallthrough */ +/* 11351 */ case 63: +/* 11352 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11353 */ return $saveSuspension($ret, 'src/lib/traceback.py', 319, 35); +/* 11354 */ } +/* 11355 */ var $call839 = $ret; +/* 11356 */ // +/* 11357 */ // line 319: +/* 11358 */ // for name, value in sorted(frame.locals.items()): +/* 11359 */ // ^ +/* 11360 */ // +/* 11361 */ +/* 11362 */ $currLineNo = Sk.currLineNo = 319; +/* 11363 */ $currColNo = Sk.currColNo = 35; +/* 11364 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11365 */ var $iter840 = Sk.abstr.iter($call839); +/* 11366 */ $blk = 57; /* allowing case fallthrough */ +/* 11367 */ case 57: +/* 11368 */ /* --- for start --- */ $ret = Sk.abstr.iternext($iter840, true); +/* 11369 */ $blk = 64; /* allowing case fallthrough */ +/* 11370 */ case 64: +/* 11371 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11372 */ return $saveSuspension($ret, 'src/lib/traceback.py', 319, 16); +/* 11373 */ } +/* 11374 */ var $next841 = $ret; +/* 11375 */ if ($next841 === undefined) { +/* 11376 */ $blk = 58; +/* 11377 */ continue; +/* 11378 */ } +/* 11379 */ var $items842 = Sk.abstr.sequenceUnpack($next841, 2); +/* 11380 */ name_$rn$ = $items842[0]; +/* 11381 */ value = $items842[1]; +/* 11382 */ // +/* 11383 */ // line 320: +/* 11384 */ // row.append(' {name} = {value}\n'.format(name=name, value=value)) +/* 11385 */ // ^ +/* 11386 */ // +/* 11387 */ +/* 11388 */ $currLineNo = Sk.currLineNo = 320; +/* 11389 */ $currColNo = Sk.currColNo = 20; +/* 11390 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11391 */ if (row === undefined) { +/* 11392 */ throw new Sk.builtin.UnboundLocalError('local variable \'row\' referenced before assignment'); +/* 11393 */ } +/* 11394 */ $ret = Sk.abstr.gattr(row, $scope750.$const788, true); +/* 11395 */ $blk = 65; /* allowing case fallthrough */ +/* 11396 */ case 65: +/* 11397 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11398 */ return $saveSuspension($ret, 'src/lib/traceback.py', 320, 20); +/* 11399 */ } +/* 11400 */ var $lattr843 = $ret; +/* 11401 */ $ret = Sk.abstr.gattr($scope750.$const844, $scope750.$const791, true); +/* 11402 */ $blk = 66; /* allowing case fallthrough */ +/* 11403 */ case 66: +/* 11404 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11405 */ return $saveSuspension($ret, 'src/lib/traceback.py', 320, 31); +/* 11406 */ } +/* 11407 */ var $lattr845 = $ret; +/* 11408 */ if (name_$rn$ === undefined) { +/* 11409 */ throw new Sk.builtin.UnboundLocalError('local variable \'name_$rn$\' referenced before assignment'); +/* 11410 */ } +/* 11411 */ if (value === undefined) { +/* 11412 */ throw new Sk.builtin.UnboundLocalError('local variable \'value\' referenced before assignment'); +/* 11413 */ } +/* 11414 */ $ret = Sk.misceval.applyOrSuspend($lattr845, undefined, undefined, ['name', name_$rn$, 'value', value], []); +/* 11415 */ $blk = 67; /* allowing case fallthrough */ +/* 11416 */ case 67: +/* 11417 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11418 */ return $saveSuspension($ret, 'src/lib/traceback.py', 320, 31); +/* 11419 */ } +/* 11420 */ var $call846 = $ret; +/* 11421 */ // +/* 11422 */ // line 320: +/* 11423 */ // row.append(' {name} = {value}\n'.format(name=name, value=value)) +/* 11424 */ // ^ +/* 11425 */ // +/* 11426 */ +/* 11427 */ $currLineNo = Sk.currLineNo = 320; +/* 11428 */ $currColNo = Sk.currColNo = 31; +/* 11429 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11430 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr843, [$call846]); +/* 11431 */ $blk = 68; /* allowing case fallthrough */ +/* 11432 */ case 68: +/* 11433 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11434 */ return $saveSuspension($ret, 'src/lib/traceback.py', 320, 20); +/* 11435 */ } +/* 11436 */ var $call847 = $ret; +/* 11437 */ // +/* 11438 */ // line 320: +/* 11439 */ // row.append(' {name} = {value}\n'.format(name=name, value=value)) +/* 11440 */ // ^ +/* 11441 */ // +/* 11442 */ +/* 11443 */ $currLineNo = Sk.currLineNo = 320; +/* 11444 */ $currColNo = Sk.currColNo = 20; +/* 11445 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11446 */ $blk = 57; /* jump */ +/* 11447 */ continue; +/* 11448 */ case 55: +/* 11449 */ /* --- end of if --- */ +/* 11450 */ // +/* 11451 */ // line 321: +/* 11452 */ // result.append(''.join(row)) +/* 11453 */ // ^ +/* 11454 */ // +/* 11455 */ +/* 11456 */ $currLineNo = Sk.currLineNo = 321; +/* 11457 */ $currColNo = Sk.currColNo = 12; +/* 11458 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11459 */ if (result === undefined) { +/* 11460 */ throw new Sk.builtin.UnboundLocalError('local variable \'result\' referenced before assignment'); +/* 11461 */ } +/* 11462 */ $ret = Sk.abstr.gattr(result, $scope750.$const788, true); +/* 11463 */ $blk = 69; /* allowing case fallthrough */ +/* 11464 */ case 69: +/* 11465 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11466 */ return $saveSuspension($ret, 'src/lib/traceback.py', 321, 12); +/* 11467 */ } +/* 11468 */ var $lattr848 = $ret; +/* 11469 */ $ret = Sk.abstr.gattr($scope750.$const799, $scope750.$const849, true); +/* 11470 */ $blk = 70; /* allowing case fallthrough */ +/* 11471 */ case 70: +/* 11472 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11473 */ return $saveSuspension($ret, 'src/lib/traceback.py', 321, 26); +/* 11474 */ } +/* 11475 */ var $lattr850 = $ret; +/* 11476 */ if (row === undefined) { +/* 11477 */ throw new Sk.builtin.UnboundLocalError('local variable \'row\' referenced before assignment'); +/* 11478 */ } +/* 11479 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr850, [row]); +/* 11480 */ $blk = 71; /* allowing case fallthrough */ +/* 11481 */ case 71: +/* 11482 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11483 */ return $saveSuspension($ret, 'src/lib/traceback.py', 321, 26); +/* 11484 */ } +/* 11485 */ var $call851 = $ret; +/* 11486 */ // +/* 11487 */ // line 321: +/* 11488 */ // result.append(''.join(row)) +/* 11489 */ // ^ +/* 11490 */ // +/* 11491 */ +/* 11492 */ $currLineNo = Sk.currLineNo = 321; +/* 11493 */ $currColNo = Sk.currColNo = 26; +/* 11494 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11495 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr848, [$call851]); +/* 11496 */ $blk = 72; /* allowing case fallthrough */ +/* 11497 */ case 72: +/* 11498 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 11499 */ return $saveSuspension($ret, 'src/lib/traceback.py', 321, 12); +/* 11500 */ } +/* 11501 */ var $call852 = $ret; +/* 11502 */ // +/* 11503 */ // line 321: +/* 11504 */ // result.append(''.join(row)) +/* 11505 */ // ^ +/* 11506 */ // +/* 11507 */ +/* 11508 */ $currLineNo = Sk.currLineNo = 321; +/* 11509 */ $currColNo = Sk.currColNo = 12; +/* 11510 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11511 */ $blk = 1; /* jump */ +/* 11512 */ continue; +/* 11513 */ case 58: +/* 11514 */ /* --- for cleanup --- */ $blk = 59; /* allowing case fallthrough */ +/* 11515 */ case 59: +/* 11516 */ /* --- for end --- */ $blk = 55; /* jump */ +/* 11517 */ continue; +/* 11518 */ case 78: +/* 11519 */ /* --- next of ifexp --- */ $res861 = $scope750.$const799; +/* 11520 */ $blk = 79; /* jump */ +/* 11521 */ continue; +/* 11522 */ } +/* 11523 */ } catch (err) { +/* 11524 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 11525 */ Sk.execStart = Date.now(); +/* 11526 */ Sk.execPaused = 0 +/* 11527 */ } +/* 11528 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 11529 */ err = new Sk.builtin.ExternalError(err); +/* 11530 */ } +/* 11531 */ Sk.err = err; +/* 11532 */ err.traceback.push({ +/* 11533 */ lineno: $currLineNo, +/* 11534 */ colno: $currColNo, +/* 11535 */ filename: 'src/lib/traceback.py', +/* 11536 */ scope: 'format' +/* 11537 */ }); +/* 11538 */ if ($exc.length > 0) { +/* 11539 */ $err = err; +/* 11540 */ $blk = $exc.pop(); +/* 11541 */ continue +/* 11542 */ } else { +/* 11543 */ throw err; +/* 11544 */ } +/* 11545 */ } +/* 11546 */ } +/* 11547 */ }); +/* 11548 */ $scope750.$const753 = new Sk.builtin.int_(0); +/* 11549 */ $scope750.$const761 = new Sk.builtin.str('filename'); +/* 11550 */ $scope750.$const769 = new Sk.builtin.str('lineno'); +/* 11551 */ $scope750.$const777 = new Sk.builtin.str('name_$rn$'); +/* 11552 */ $scope750.$const788 = new Sk.builtin.str('append'); +/* 11553 */ $scope750.$const790 = new Sk.builtin.str(' [Previous line repeated {count} more time{s_count}]\n'); +/* 11554 */ $scope750.$const791 = new Sk.builtin.str('format'); +/* 11555 */ $scope750.$const795 = new Sk.builtin.int_(1); +/* 11556 */ $scope750.$const798 = new Sk.builtin.str('s'); +/* 11557 */ $scope750.$const799 = new Sk.builtin.str(''); +/* 11558 */ $scope750.$const812 = new Sk.builtin.str(' File "{}", line {}, in {}\n'); +/* 11559 */ $scope750.$const819 = new Sk.builtin.str('line'); +/* 11560 */ $scope750.$const823 = new Sk.builtin.str(' {}\n'); +/* 11561 */ $scope750.$const826 = new Sk.builtin.str('strip'); +/* 11562 */ $scope750.$const831 = new Sk.builtin.str('locals'); +/* 11563 */ $scope750.$const836 = new Sk.builtin.str('items'); +/* 11564 */ $scope750.$const844 = new Sk.builtin.str(' {name} = {value}\n'); +/* 11565 */ $scope750.$const849 = new Sk.builtin.str('join'); +/* 11566 */ var $scope869 = (function $TracebackException$class_outer($globals, $locals, $cell) { +/* 11567 */ var $gbl = $globals, +/* 11568 */ $loc = $locals; +/* 11569 */ $free = $globals; +/* 11570 */ (function $TracebackException$_closure($cell) { +/* 11571 */ var $blk = 0, +/* 11572 */ $exc = [], +/* 11573 */ $ret = undefined, +/* 11574 */ $postfinally = undefined, +/* 11575 */ $currLineNo = undefined, +/* 11576 */ $currColNo = undefined; +/* 11577 */ if (typeof Sk.execStart === 'undefined') { +/* 11578 */ Sk.execStart = Date.now(); +/* 11579 */ Sk.execPaused = 0 +/* 11580 */ } +/* 11581 */ while (true) { +/* 11582 */ try { +/* 11583 */ var $dateNow = Date.now(); +/* 11584 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 11585 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 11586 */ } +/* 11587 */ switch ($blk) { +/* 11588 */ case 0: +/* 11589 */ /* --- class entry --- */ +/* 11590 */ // +/* 11591 */ // line 334: +/* 11592 */ // def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, +/* 11593 */ // ^ +/* 11594 */ // +/* 11595 */ +/* 11596 */ $currLineNo = Sk.currLineNo = 334; +/* 11597 */ $currColNo = Sk.currColNo = 4; +/* 11598 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11599 */ $scope870.co_name = new Sk.builtins['str']('__init__'); +/* 11600 */ $scope870.co_argcount = 4; +/* 11601 */ $scope870.co_kwonlyargcount = 4; +/* 11602 */ $scope870.$kwdefs = [Sk.builtin.none.none$, Sk.builtin.bool.true$, Sk.builtin.bool.false$, Sk.builtin.none.none$]; +/* 11603 */ $scope870.co_varnames = ['self', 'exc_type', 'exc_value', 'exc_traceback', 'limit', 'lookup_lines', 'capture_locals', '_seen']; +/* 11604 */ var $funcobj966 = new Sk.builtins['function']($scope870, $gbl); +/* 11605 */ $loc.__init__ = $funcobj966; +/* 11606 */ // +/* 11607 */ // line 392: +/* 11608 */ // @classmethod +/* 11609 */ // ^ +/* 11610 */ // +/* 11611 */ +/* 11612 */ $currLineNo = Sk.currLineNo = 392; +/* 11613 */ $currColNo = Sk.currColNo = 4; +/* 11614 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11615 */ var $loadname967 = $loc.classmethod !== undefined ? $loc.classmethod : Sk.misceval.loadname('classmethod', $gbl);; +/* 11616 */ $scope968.co_name = new Sk.builtins['str']('from_exception'); +/* 11617 */ $scope968.$decorators = [$loadname967]; +/* 11618 */ $scope968.co_varnames = ['cls', 'exc']; +/* 11619 */ $scope968.co_kwargs = 1; +/* 11620 */ $scope968.co_varargs = 1; +/* 11621 */ $ret = Sk.misceval.callsimOrSuspendArray($scope968.$decorators[0], [new Sk.builtins['function']($scope968, $gbl)]); +/* 11622 */ if ($ret && $ret.$isSuspension) { +/* 11623 */ $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); +/* 11624 */ } +/* 11625 */ var $funcobj977 = $ret; +/* 11626 */ $loc.from_exception = $funcobj977; +/* 11627 */ // +/* 11628 */ // line 397: +/* 11629 */ // def _load_lines(self): +/* 11630 */ // ^ +/* 11631 */ // +/* 11632 */ +/* 11633 */ $currLineNo = Sk.currLineNo = 397; +/* 11634 */ $currColNo = Sk.currColNo = 4; +/* 11635 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11636 */ $scope978.co_name = new Sk.builtins['str']('_load_lines'); +/* 11637 */ $scope978.co_varnames = ['self']; +/* 11638 */ var $funcobj999 = new Sk.builtins['function']($scope978, $gbl); +/* 11639 */ $loc._load_lines = $funcobj999; +/* 11640 */ // +/* 11641 */ // line 406: +/* 11642 */ // def __eq__(self, other): +/* 11643 */ // ^ +/* 11644 */ // +/* 11645 */ +/* 11646 */ $currLineNo = Sk.currLineNo = 406; +/* 11647 */ $currColNo = Sk.currColNo = 4; +/* 11648 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11649 */ $scope1000.co_name = new Sk.builtins['str']('__eq__'); +/* 11650 */ $scope1000.co_varnames = ['self', 'other']; +/* 11651 */ var $funcobj1007 = new Sk.builtins['function']($scope1000, $gbl); +/* 11652 */ $loc.__eq__ = $funcobj1007; +/* 11653 */ // +/* 11654 */ // line 409: +/* 11655 */ // def __str__(self): +/* 11656 */ // ^ +/* 11657 */ // +/* 11658 */ +/* 11659 */ $currLineNo = Sk.currLineNo = 409; +/* 11660 */ $currColNo = Sk.currColNo = 4; +/* 11661 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11662 */ $scope1008.co_name = new Sk.builtins['str']('__str__'); +/* 11663 */ $scope1008.co_varnames = ['self']; +/* 11664 */ var $funcobj1012 = new Sk.builtins['function']($scope1008, $gbl); +/* 11665 */ $loc.__str__ = $funcobj1012; +/* 11666 */ // +/* 11667 */ // line 412: +/* 11668 */ // def format_exception_only(self): +/* 11669 */ // ^ +/* 11670 */ // +/* 11671 */ +/* 11672 */ $currLineNo = Sk.currLineNo = 412; +/* 11673 */ $currColNo = Sk.currColNo = 4; +/* 11674 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11675 */ $scope1013.co_name = new Sk.builtins['str']('format_exception_only'); +/* 11676 */ $scope1013.co_varnames = ['self']; +/* 11677 */ var $gener1159 = new Sk.builtins['function']((function() { +/* 11678 */ var $origargs = Array.prototype.slice.call(arguments); +/* 11679 */ Sk.builtin.pyCheckArgsLen("format_exception_only", arguments.length, 1, 1); +/* 11680 */ return new Sk.builtins['generator']($scope1013, $gbl, $origargs); +/* 11681 */ })); +/* 11682 */ $loc.format_exception_only = $gener1159; +/* 11683 */ // +/* 11684 */ // line 446: +/* 11685 */ // def format(self, *, chain=True): +/* 11686 */ // ^ +/* 11687 */ // +/* 11688 */ +/* 11689 */ $currLineNo = Sk.currLineNo = 446; +/* 11690 */ $currColNo = Sk.currColNo = 4; +/* 11691 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 11692 */ $scope1160.co_name = new Sk.builtins['str']('format'); +/* 11693 */ $scope1160.co_argcount = 1; +/* 11694 */ $scope1160.co_kwonlyargcount = 1; +/* 11695 */ $scope1160.$kwdefs = [Sk.builtin.bool.true$]; +/* 11696 */ $scope1160.co_varnames = ['self', 'chain']; +/* 11697 */ var $gener1224 = new Sk.builtins['function']((function() { +/* 11698 */ var $origargs = Array.prototype.slice.call(arguments); +/* 11699 */ Sk.builtin.pyCheckArgsLen("format", arguments.length, 1, 1); +/* 11700 */ return new Sk.builtins['generator']($scope1160, $gbl, $origargs); +/* 11701 */ })); +/* 11702 */ $loc.format = $gener1224; +/* 11703 */ return; +/* 11704 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 11705 */ } +/* 11706 */ } catch (err) { +/* 11707 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 11708 */ Sk.execStart = Date.now(); +/* 11709 */ Sk.execPaused = 0 +/* 11710 */ } +/* 11711 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 11712 */ err = new Sk.builtin.ExternalError(err); +/* 11713 */ } +/* 11714 */ Sk.err = err; +/* 11715 */ err.traceback.push({ +/* 11716 */ lineno: $currLineNo, +/* 11717 */ colno: $currColNo, +/* 11718 */ filename: 'src/lib/traceback.py', +/* 11719 */ scope: 'TracebackException' +/* 11720 */ }); +/* 11721 */ if ($exc.length > 0) { +/* 11722 */ $err = err; +/* 11723 */ $blk = $exc.pop(); +/* 11724 */ continue +/* 11725 */ } else { +/* 11726 */ throw err; +/* 11727 */ } +/* 11728 */ } +/* 11729 */ } +/* 11730 */ }).call(null, $cell); +/* 11731 */ }); +/* 11732 */ var $scope870 = (function $__init__871$(self, exc_type, exc_value, exc_traceback, limit, lookup_lines, capture_locals, _seen) { +/* 11733 */ var cause, context; /* locals */ +/* 11734 */ var _seen, _seen, _seen, _seen, _seen, _seen, _seen, _seen, capture_locals, capture_locals, capture_locals, capture_locals, cause, cause, cause, context, context, context, exc_traceback, exc_traceback, exc_traceback, exc_type, exc_type, exc_type, exc_type, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, exc_value, limit, limit, limit, limit, lookup_lines, lookup_lines, lookup_lines, self, self, self, self, self, self, self, self, self, self, self, self, self, self, $compareres872, $loadgbl875, $lattr878, $loadgbl879, $lattr878, $loadgbl879, $call880, $boolopsucc882, $jfalse883, $boolopsucc882, $jfalse883, $lattr885, $compareres886, $boolopsucc882, $jfalse883, $lattr885, $compareres886, $jfalse887, $jfalse888, $loadgbl889, $boolopsucc882, $jfalse883, $lattr885, $compareres886, $jfalse887, $jfalse888, $loadgbl889, $lattr890, $boolopsucc882, $jfalse883, $lattr885, $compareres886, $jfalse887, $jfalse888, $loadgbl889, $lattr890, $call891, $compareres892, $loadgbl896, $loadgbl897, $loadgbl896, $loadgbl897, $lattr898, $loadgbl896, $loadgbl897, $lattr898, $call899, $loadgbl896, $loadgbl897, $lattr898, $call899, $lattr900, $loadgbl896, $loadgbl897, $lattr898, $call899, $lattr900, $lattr901, $loadgbl896, $loadgbl897, $lattr898, $call899, $lattr900, $lattr901, $lattr903, $boolopsucc905, $jfalse906, $boolopsucc905, $jfalse906, $lattr908, $compareres909, $boolopsucc905, $jfalse906, $lattr908, $compareres909, $jfalse910, $jfalse911, $loadgbl912, $boolopsucc905, $jfalse906, $lattr908, $compareres909, $jfalse910, $jfalse911, $loadgbl912, $lattr913, $boolopsucc905, $jfalse906, $lattr908, $compareres909, $jfalse910, $jfalse911, $loadgbl912, $lattr913, $call914, $compareres915, $loadgbl919, $loadgbl920, $loadgbl919, $loadgbl920, $lattr921, $loadgbl919, $loadgbl920, $lattr921, $call922, $loadgbl919, $loadgbl920, $lattr921, $call922, $lattr923, $loadgbl919, $loadgbl920, $lattr921, $call922, $lattr923, $lattr924, $loadgbl919, $loadgbl920, $lattr921, $call922, $lattr923, $lattr924, $lattr925, $res928, $jfalse929, $res928, $jfalse929, $lattr931, $loadgbl932, $loadgbl932, $lattr934, $loadgbl935, $loadgbl932, $lattr934, $loadgbl935, $call936, $loadgbl932, $lattr934, $loadgbl935, $call936, $call937, $loadgbl940, $loadgbl940, $call941, $boolopsucc943, $jfalse944, $loadgbl945, $loadgbl946, $lattr951, $loadgbl952, $loadgbl952, $lattr954, $loadgbl952, $lattr954, $call955, $lattr957, $lattr959, $lattr961, $lattr964; +/* 11735 */ var $wakeFromSuspension = function() { +/* 11736 */ var susp = $scope870.$wakingSuspension; +/* 11737 */ $scope870.$wakingSuspension = undefined; +/* 11738 */ $blk = susp.$blk; +/* 11739 */ $loc = susp.$loc; +/* 11740 */ $gbl = susp.$gbl; +/* 11741 */ $exc = susp.$exc; +/* 11742 */ $err = susp.$err; +/* 11743 */ $postfinally = susp.$postfinally; +/* 11744 */ $currLineNo = susp.$lineno; +/* 11745 */ $currColNo = susp.$colno; +/* 11746 */ Sk.lastYield = Date.now(); +/* 11747 */ _seen = susp.$tmps._seen; +/* 11748 */ capture_locals = susp.$tmps.capture_locals; +/* 11749 */ cause = susp.$tmps.cause; +/* 11750 */ context = susp.$tmps.context; +/* 11751 */ exc_traceback = susp.$tmps.exc_traceback; +/* 11752 */ exc_type = susp.$tmps.exc_type; +/* 11753 */ exc_value = susp.$tmps.exc_value; +/* 11754 */ limit = susp.$tmps.limit; +/* 11755 */ lookup_lines = susp.$tmps.lookup_lines; +/* 11756 */ self = susp.$tmps.self; +/* 11757 */ $compareres872 = susp.$tmps.$compareres872; +/* 11758 */ $loadgbl875 = susp.$tmps.$loadgbl875; +/* 11759 */ $lattr878 = susp.$tmps.$lattr878; +/* 11760 */ $loadgbl879 = susp.$tmps.$loadgbl879; +/* 11761 */ $call880 = susp.$tmps.$call880; +/* 11762 */ $boolopsucc882 = susp.$tmps.$boolopsucc882; +/* 11763 */ $jfalse883 = susp.$tmps.$jfalse883; +/* 11764 */ $lattr885 = susp.$tmps.$lattr885; +/* 11765 */ $compareres886 = susp.$tmps.$compareres886; +/* 11766 */ $jfalse887 = susp.$tmps.$jfalse887; +/* 11767 */ $jfalse888 = susp.$tmps.$jfalse888; +/* 11768 */ $loadgbl889 = susp.$tmps.$loadgbl889; +/* 11769 */ $lattr890 = susp.$tmps.$lattr890; +/* 11770 */ $call891 = susp.$tmps.$call891; +/* 11771 */ $compareres892 = susp.$tmps.$compareres892; +/* 11772 */ $loadgbl896 = susp.$tmps.$loadgbl896; +/* 11773 */ $loadgbl897 = susp.$tmps.$loadgbl897; +/* 11774 */ $lattr898 = susp.$tmps.$lattr898; +/* 11775 */ $call899 = susp.$tmps.$call899; +/* 11776 */ $lattr900 = susp.$tmps.$lattr900; +/* 11777 */ $lattr901 = susp.$tmps.$lattr901; +/* 11778 */ $lattr903 = susp.$tmps.$lattr903; +/* 11779 */ $boolopsucc905 = susp.$tmps.$boolopsucc905; +/* 11780 */ $jfalse906 = susp.$tmps.$jfalse906; +/* 11781 */ $lattr908 = susp.$tmps.$lattr908; +/* 11782 */ $compareres909 = susp.$tmps.$compareres909; +/* 11783 */ $jfalse910 = susp.$tmps.$jfalse910; +/* 11784 */ $jfalse911 = susp.$tmps.$jfalse911; +/* 11785 */ $loadgbl912 = susp.$tmps.$loadgbl912; +/* 11786 */ $lattr913 = susp.$tmps.$lattr913; +/* 11787 */ $call914 = susp.$tmps.$call914; +/* 11788 */ $compareres915 = susp.$tmps.$compareres915; +/* 11789 */ $loadgbl919 = susp.$tmps.$loadgbl919; +/* 11790 */ $loadgbl920 = susp.$tmps.$loadgbl920; +/* 11791 */ $lattr921 = susp.$tmps.$lattr921; +/* 11792 */ $call922 = susp.$tmps.$call922; +/* 11793 */ $lattr923 = susp.$tmps.$lattr923; +/* 11794 */ $lattr924 = susp.$tmps.$lattr924; +/* 11795 */ $lattr925 = susp.$tmps.$lattr925; +/* 11796 */ $res928 = susp.$tmps.$res928; +/* 11797 */ $jfalse929 = susp.$tmps.$jfalse929; +/* 11798 */ $lattr931 = susp.$tmps.$lattr931; +/* 11799 */ $loadgbl932 = susp.$tmps.$loadgbl932; +/* 11800 */ $lattr934 = susp.$tmps.$lattr934; +/* 11801 */ $loadgbl935 = susp.$tmps.$loadgbl935; +/* 11802 */ $call936 = susp.$tmps.$call936; +/* 11803 */ $call937 = susp.$tmps.$call937; +/* 11804 */ $loadgbl940 = susp.$tmps.$loadgbl940; +/* 11805 */ $call941 = susp.$tmps.$call941; +/* 11806 */ $boolopsucc943 = susp.$tmps.$boolopsucc943; +/* 11807 */ $jfalse944 = susp.$tmps.$jfalse944; +/* 11808 */ $loadgbl945 = susp.$tmps.$loadgbl945; +/* 11809 */ $loadgbl946 = susp.$tmps.$loadgbl946; +/* 11810 */ $lattr951 = susp.$tmps.$lattr951; +/* 11811 */ $loadgbl952 = susp.$tmps.$loadgbl952; +/* 11812 */ $lattr954 = susp.$tmps.$lattr954; +/* 11813 */ $call955 = susp.$tmps.$call955; +/* 11814 */ $lattr957 = susp.$tmps.$lattr957; +/* 11815 */ $lattr959 = susp.$tmps.$lattr959; +/* 11816 */ $lattr961 = susp.$tmps.$lattr961; +/* 11817 */ $lattr964 = susp.$tmps.$lattr964; +/* 11818 */ try { +/* 11819 */ $ret = susp.child.resume(); +/* 11820 */ } catch (err) { +/* 11821 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 11822 */ Sk.execStart = Date.now(); +/* 11823 */ Sk.execPaused = 0 +/* 11824 */ } +/* 11825 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 11826 */ err = new Sk.builtin.ExternalError(err); +/* 11827 */ } +/* 11828 */ Sk.err = err; +/* 11829 */ err.traceback.push({ +/* 11830 */ lineno: $currLineNo, +/* 11831 */ colno: $currColNo, +/* 11832 */ filename: 'src/lib/traceback.py', +/* 11833 */ scope: '$scope870' +/* 11834 */ }); +/* 11835 */ if ($exc.length > 0) { +/* 11836 */ $err = err; +/* 11837 */ $blk = $exc.pop(); +/* 11838 */ } else { +/* 11839 */ throw err; +/* 11840 */ } +/* 11841 */ } +/* 11842 */ }; +/* 11843 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 11844 */ var susp = new Sk.misceval.Suspension(); +/* 11845 */ susp.child = $child; +/* 11846 */ susp.resume = function() { +/* 11847 */ $scope870.$wakingSuspension = susp; +/* 11848 */ return $scope870(); +/* 11849 */ }; +/* 11850 */ susp.data = susp.child.data; +/* 11851 */ susp.$blk = $blk; +/* 11852 */ susp.$loc = $loc; +/* 11853 */ susp.$gbl = $gbl; +/* 11854 */ susp.$exc = $exc; +/* 11855 */ susp.$err = $err; +/* 11856 */ susp.$postfinally = $postfinally; +/* 11857 */ susp.$filename = $filename; +/* 11858 */ susp.$lineno = $lineno; +/* 11859 */ susp.$colno = $colno; +/* 11860 */ susp.optional = susp.child.optional; +/* 11861 */ susp.$tmps = { +/* 11862 */ "_seen": _seen, +/* 11863 */ "capture_locals": capture_locals, +/* 11864 */ "cause": cause, +/* 11865 */ "context": context, +/* 11866 */ "exc_traceback": exc_traceback, +/* 11867 */ "exc_type": exc_type, +/* 11868 */ "exc_value": exc_value, +/* 11869 */ "limit": limit, +/* 11870 */ "lookup_lines": lookup_lines, +/* 11871 */ "self": self, +/* 11872 */ "$compareres872": $compareres872, +/* 11873 */ "$loadgbl875": $loadgbl875, +/* 11874 */ "$lattr878": $lattr878, +/* 11875 */ "$loadgbl879": $loadgbl879, +/* 11876 */ "$call880": $call880, +/* 11877 */ "$boolopsucc882": $boolopsucc882, +/* 11878 */ "$jfalse883": $jfalse883, +/* 11879 */ "$lattr885": $lattr885, +/* 11880 */ "$compareres886": $compareres886, +/* 11881 */ "$jfalse887": $jfalse887, +/* 11882 */ "$jfalse888": $jfalse888, +/* 11883 */ "$loadgbl889": $loadgbl889, +/* 11884 */ "$lattr890": $lattr890, +/* 11885 */ "$call891": $call891, +/* 11886 */ "$compareres892": $compareres892, +/* 11887 */ "$loadgbl896": $loadgbl896, +/* 11888 */ "$loadgbl897": $loadgbl897, +/* 11889 */ "$lattr898": $lattr898, +/* 11890 */ "$call899": $call899, +/* 11891 */ "$lattr900": $lattr900, +/* 11892 */ "$lattr901": $lattr901, +/* 11893 */ "$lattr903": $lattr903, +/* 11894 */ "$boolopsucc905": $boolopsucc905, +/* 11895 */ "$jfalse906": $jfalse906, +/* 11896 */ "$lattr908": $lattr908, +/* 11897 */ "$compareres909": $compareres909, +/* 11898 */ "$jfalse910": $jfalse910, +/* 11899 */ "$jfalse911": $jfalse911, +/* 11900 */ "$loadgbl912": $loadgbl912, +/* 11901 */ "$lattr913": $lattr913, +/* 11902 */ "$call914": $call914, +/* 11903 */ "$compareres915": $compareres915, +/* 11904 */ "$loadgbl919": $loadgbl919, +/* 11905 */ "$loadgbl920": $loadgbl920, +/* 11906 */ "$lattr921": $lattr921, +/* 11907 */ "$call922": $call922, +/* 11908 */ "$lattr923": $lattr923, +/* 11909 */ "$lattr924": $lattr924, +/* 11910 */ "$lattr925": $lattr925, +/* 11911 */ "$res928": $res928, +/* 11912 */ "$jfalse929": $jfalse929, +/* 11913 */ "$lattr931": $lattr931, +/* 11914 */ "$loadgbl932": $loadgbl932, +/* 11915 */ "$lattr934": $lattr934, +/* 11916 */ "$loadgbl935": $loadgbl935, +/* 11917 */ "$call936": $call936, +/* 11918 */ "$call937": $call937, +/* 11919 */ "$loadgbl940": $loadgbl940, +/* 11920 */ "$call941": $call941, +/* 11921 */ "$boolopsucc943": $boolopsucc943, +/* 11922 */ "$jfalse944": $jfalse944, +/* 11923 */ "$loadgbl945": $loadgbl945, +/* 11924 */ "$loadgbl946": $loadgbl946, +/* 11925 */ "$lattr951": $lattr951, +/* 11926 */ "$loadgbl952": $loadgbl952, +/* 11927 */ "$lattr954": $lattr954, +/* 11928 */ "$call955": $call955, +/* 11929 */ "$lattr957": $lattr957, +/* 11930 */ "$lattr959": $lattr959, +/* 11931 */ "$lattr961": $lattr961, +/* 11932 */ "$lattr964": $lattr964 +/* 11933 */ }; +/* 11934 */ return susp; +/* 11935 */ }; +/* 11936 */ var $blk = 0, +/* 11937 */ $exc = [], +/* 11938 */ $loc = {}, +/* 11939 */ $cell = {}, +/* 11940 */ $gbl = this, +/* 11941 */ $err = undefined, +/* 11942 */ $ret = undefined, +/* 11943 */ $postfinally = undefined, +/* 11944 */ $currLineNo = undefined, +/* 11945 */ $currColNo = undefined; +/* 11946 */ if (typeof Sk.execStart === 'undefined') { +/* 11947 */ Sk.execStart = Date.now(); +/* 11948 */ Sk.execPaused = 0 +/* 11949 */ } +/* 11950 */ if (typeof Sk.lastYield === 'undefined') { +/* 11951 */ Sk.lastYield = Date.now() +/* 11952 */ } +/* 11953 */ if ($scope870.$wakingSuspension !== undefined) { +/* 11954 */ $wakeFromSuspension(); +/* 11955 */ } else {} +/* 11956 */ $gbl.__class__ = this.TracebackException; +/* 11957 */ while (true) { +/* 11958 */ try { +/* 11959 */ var $dateNow = Date.now(); +/* 11960 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 11961 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 11962 */ } +/* 11963 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 11964 */ var $susp = $saveSuspension({ +/* 11965 */ data: { +/* 11966 */ type: 'Sk.yield' +/* 11967 */ }, +/* 11968 */ resume: function() {} +/* 11969 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 11970 */ $susp.$blk = $blk; +/* 11971 */ $susp.optional = true; +/* 11972 */ return $susp; +/* 11973 */ } +/* 11974 */ switch ($blk) { +/* 11975 */ case 0: +/* 11976 */ /* --- codeobj entry --- */ if (self === undefined) { +/* 11977 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 11978 */ } +/* 11979 */ if (exc_type === undefined) { +/* 11980 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_type\' referenced before assignment'); +/* 11981 */ } +/* 11982 */ if (exc_value === undefined) { +/* 11983 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 11984 */ } +/* 11985 */ if (exc_traceback === undefined) { +/* 11986 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_traceback\' referenced before assignment'); +/* 11987 */ } +/* 11988 */ if (limit === undefined) { +/* 11989 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 11990 */ } +/* 11991 */ if (lookup_lines === undefined) { +/* 11992 */ throw new Sk.builtin.UnboundLocalError('local variable \'lookup_lines\' referenced before assignment'); +/* 11993 */ } +/* 11994 */ if (capture_locals === undefined) { +/* 11995 */ throw new Sk.builtin.UnboundLocalError('local variable \'capture_locals\' referenced before assignment'); +/* 11996 */ } +/* 11997 */ if (_seen === undefined) { +/* 11998 */ throw new Sk.builtin.UnboundLocalError('local variable \'_seen\' referenced before assignment'); +/* 11999 */ } +/* 12000 */ +/* 12001 */ // +/* 12002 */ // line 340: +/* 12003 */ // if _seen is None: +/* 12004 */ // ^ +/* 12005 */ // +/* 12006 */ +/* 12007 */ $currLineNo = Sk.currLineNo = 340; +/* 12008 */ $currColNo = Sk.currColNo = 8; +/* 12009 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12010 */ if (_seen === undefined) { +/* 12011 */ throw new Sk.builtin.UnboundLocalError('local variable \'_seen\' referenced before assignment'); +/* 12012 */ } +/* 12013 */ var $compareres872 = null; +/* 12014 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool(_seen, Sk.builtin.none.none$, 'Is', true)); +/* 12015 */ $blk = 3; /* allowing case fallthrough */ +/* 12016 */ case 3: +/* 12017 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12018 */ return $saveSuspension($ret, 'src/lib/traceback.py', 340, 11); +/* 12019 */ } +/* 12020 */ $compareres872 = $ret; +/* 12021 */ var $jfalse873 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 12022 */ if ($jfalse873) { +/* 12023 */ /*test failed */ +/* 12024 */ $blk = 2; +/* 12025 */ continue; +/* 12026 */ } +/* 12027 */ $blk = 2; /* allowing case fallthrough */ +/* 12028 */ case 2: +/* 12029 */ /* --- done --- */ var $jfalse874 = ($compareres872 === false || !Sk.misceval.isTrue($compareres872)); +/* 12030 */ if ($jfalse874) { +/* 12031 */ /*test failed */ +/* 12032 */ $blk = 1; +/* 12033 */ continue; +/* 12034 */ } +/* 12035 */ // +/* 12036 */ // line 341: +/* 12037 */ // _seen = set() +/* 12038 */ // ^ +/* 12039 */ // +/* 12040 */ +/* 12041 */ $currLineNo = Sk.currLineNo = 341; +/* 12042 */ $currColNo = Sk.currColNo = 12; +/* 12043 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12044 */ var $loadgbl875 = Sk.misceval.loadname('set', $gbl); +/* 12045 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl875); +/* 12046 */ $blk = 4; /* allowing case fallthrough */ +/* 12047 */ case 4: +/* 12048 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12049 */ return $saveSuspension($ret, 'src/lib/traceback.py', 341, 20); +/* 12050 */ } +/* 12051 */ var $call876 = $ret; +/* 12052 */ // +/* 12053 */ // line 341: +/* 12054 */ // _seen = set() +/* 12055 */ // ^ +/* 12056 */ // +/* 12057 */ +/* 12058 */ $currLineNo = Sk.currLineNo = 341; +/* 12059 */ $currColNo = Sk.currColNo = 20; +/* 12060 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12061 */ _seen = $call876; +/* 12062 */ $blk = 1; /* allowing case fallthrough */ +/* 12063 */ case 1: +/* 12064 */ /* --- end of if --- */ +/* 12065 */ // +/* 12066 */ // line 342: +/* 12067 */ // _seen.add(id(exc_value)) +/* 12068 */ // ^ +/* 12069 */ // +/* 12070 */ +/* 12071 */ $currLineNo = Sk.currLineNo = 342; +/* 12072 */ $currColNo = Sk.currColNo = 8; +/* 12073 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12074 */ if (_seen === undefined) { +/* 12075 */ throw new Sk.builtin.UnboundLocalError('local variable \'_seen\' referenced before assignment'); +/* 12076 */ } +/* 12077 */ $ret = Sk.abstr.gattr(_seen, $scope870.$const877, true); +/* 12078 */ $blk = 5; /* allowing case fallthrough */ +/* 12079 */ case 5: +/* 12080 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12081 */ return $saveSuspension($ret, 'src/lib/traceback.py', 342, 8); +/* 12082 */ } +/* 12083 */ var $lattr878 = $ret; +/* 12084 */ var $loadgbl879 = Sk.misceval.loadname('id', $gbl); +/* 12085 */ if (exc_value === undefined) { +/* 12086 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12087 */ } +/* 12088 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl879, [exc_value]); +/* 12089 */ $blk = 6; /* allowing case fallthrough */ +/* 12090 */ case 6: +/* 12091 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12092 */ return $saveSuspension($ret, 'src/lib/traceback.py', 342, 18); +/* 12093 */ } +/* 12094 */ var $call880 = $ret; +/* 12095 */ // +/* 12096 */ // line 342: +/* 12097 */ // _seen.add(id(exc_value)) +/* 12098 */ // ^ +/* 12099 */ // +/* 12100 */ +/* 12101 */ $currLineNo = Sk.currLineNo = 342; +/* 12102 */ $currColNo = Sk.currColNo = 18; +/* 12103 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12104 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr878, [$call880]); +/* 12105 */ $blk = 7; /* allowing case fallthrough */ +/* 12106 */ case 7: +/* 12107 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12108 */ return $saveSuspension($ret, 'src/lib/traceback.py', 342, 8); +/* 12109 */ } +/* 12110 */ var $call881 = $ret; +/* 12111 */ // +/* 12112 */ // line 342: +/* 12113 */ // _seen.add(id(exc_value)) +/* 12114 */ // ^ +/* 12115 */ // +/* 12116 */ +/* 12117 */ $currLineNo = Sk.currLineNo = 342; +/* 12118 */ $currColNo = Sk.currColNo = 8; +/* 12119 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12120 */ // +/* 12121 */ // line 345: +/* 12122 */ // if (exc_value and exc_value.__cause__ is not None +/* 12123 */ // ^ +/* 12124 */ // +/* 12125 */ +/* 12126 */ $currLineNo = Sk.currLineNo = 345; +/* 12127 */ $currColNo = Sk.currColNo = 8; +/* 12128 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12129 */ if (exc_value === undefined) { +/* 12130 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12131 */ } +/* 12132 */ var $boolopsucc882 = exc_value; +/* 12133 */ $boolopsucc882 = exc_value; +/* 12134 */ var $jfalse883 = (exc_value === false || !Sk.misceval.isTrue(exc_value)); +/* 12135 */ if ($jfalse883) { +/* 12136 */ /*test failed */ +/* 12137 */ $blk = 10; +/* 12138 */ continue; +/* 12139 */ } +/* 12140 */ if (exc_value === undefined) { +/* 12141 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12142 */ } +/* 12143 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const884, true); +/* 12144 */ $blk = 11; /* allowing case fallthrough */ +/* 12145 */ case 11: +/* 12146 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12147 */ return $saveSuspension($ret, 'src/lib/traceback.py', 345, 26); +/* 12148 */ } +/* 12149 */ var $lattr885 = $ret; +/* 12150 */ var $compareres886 = null; +/* 12151 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($lattr885, Sk.builtin.none.none$, 'IsNot', true)); +/* 12152 */ $blk = 13; /* allowing case fallthrough */ +/* 12153 */ case 13: +/* 12154 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12155 */ return $saveSuspension($ret, 'src/lib/traceback.py', 345, 26); +/* 12156 */ } +/* 12157 */ $compareres886 = $ret; +/* 12158 */ var $jfalse887 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 12159 */ if ($jfalse887) { +/* 12160 */ /*test failed */ +/* 12161 */ $blk = 12; +/* 12162 */ continue; +/* 12163 */ } +/* 12164 */ $blk = 12; /* allowing case fallthrough */ +/* 12165 */ case 12: +/* 12166 */ /* --- done --- */ $boolopsucc882 = $compareres886; +/* 12167 */ var $jfalse888 = ($compareres886 === false || !Sk.misceval.isTrue($compareres886)); +/* 12168 */ if ($jfalse888) { +/* 12169 */ /*test failed */ +/* 12170 */ $blk = 10; +/* 12171 */ continue; +/* 12172 */ } +/* 12173 */ var $loadgbl889 = Sk.misceval.loadname('id', $gbl); +/* 12174 */ if (exc_value === undefined) { +/* 12175 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12176 */ } +/* 12177 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const884, true); +/* 12178 */ $blk = 14; /* allowing case fallthrough */ +/* 12179 */ case 14: +/* 12180 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12181 */ return $saveSuspension($ret, 'src/lib/traceback.py', 346, 19); +/* 12182 */ } +/* 12183 */ var $lattr890 = $ret; +/* 12184 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl889, [$lattr890]); +/* 12185 */ $blk = 15; /* allowing case fallthrough */ +/* 12186 */ case 15: +/* 12187 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12188 */ return $saveSuspension($ret, 'src/lib/traceback.py', 346, 16); +/* 12189 */ } +/* 12190 */ var $call891 = $ret; +/* 12191 */ // +/* 12192 */ // line 346: +/* 12193 */ // and id(exc_value.__cause__) not in _seen): +/* 12194 */ // ^ +/* 12195 */ // +/* 12196 */ +/* 12197 */ $currLineNo = Sk.currLineNo = 346; +/* 12198 */ $currColNo = Sk.currColNo = 16; +/* 12199 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12200 */ var $compareres892 = null; +/* 12201 */ if (_seen === undefined) { +/* 12202 */ throw new Sk.builtin.UnboundLocalError('local variable \'_seen\' referenced before assignment'); +/* 12203 */ } +/* 12204 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($call891, _seen, 'NotIn', true)); +/* 12205 */ $blk = 17; /* allowing case fallthrough */ +/* 12206 */ case 17: +/* 12207 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12208 */ return $saveSuspension($ret, 'src/lib/traceback.py', 346, 16); +/* 12209 */ } +/* 12210 */ $compareres892 = $ret; +/* 12211 */ var $jfalse893 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 12212 */ if ($jfalse893) { +/* 12213 */ /*test failed */ +/* 12214 */ $blk = 16; +/* 12215 */ continue; +/* 12216 */ } +/* 12217 */ $blk = 16; /* allowing case fallthrough */ +/* 12218 */ case 16: +/* 12219 */ /* --- done --- */ $boolopsucc882 = $compareres892; +/* 12220 */ var $jfalse894 = ($compareres892 === false || !Sk.misceval.isTrue($compareres892)); +/* 12221 */ if ($jfalse894) { +/* 12222 */ /*test failed */ +/* 12223 */ $blk = 10; +/* 12224 */ continue; +/* 12225 */ } +/* 12226 */ $blk = 10; /* allowing case fallthrough */ +/* 12227 */ case 10: +/* 12228 */ /* --- end of boolop --- */ var $jfalse895 = ($boolopsucc882 === false || !Sk.misceval.isTrue($boolopsucc882)); +/* 12229 */ if ($jfalse895) { +/* 12230 */ /*test failed */ +/* 12231 */ $blk = 9; +/* 12232 */ continue; +/* 12233 */ } +/* 12234 */ // +/* 12235 */ // line 347: +/* 12236 */ // cause = TracebackException( +/* 12237 */ // ^ +/* 12238 */ // +/* 12239 */ +/* 12240 */ $currLineNo = Sk.currLineNo = 347; +/* 12241 */ $currColNo = Sk.currColNo = 12; +/* 12242 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12243 */ var $loadgbl896 = Sk.misceval.loadname('TracebackException', $gbl); +/* 12244 */ var $loadgbl897 = Sk.misceval.loadname('type', $gbl); +/* 12245 */ if (exc_value === undefined) { +/* 12246 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12247 */ } +/* 12248 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const884, true); +/* 12249 */ $blk = 18; /* allowing case fallthrough */ +/* 12250 */ case 18: +/* 12251 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12252 */ return $saveSuspension($ret, 'src/lib/traceback.py', 348, 21); +/* 12253 */ } +/* 12254 */ var $lattr898 = $ret; +/* 12255 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl897, [$lattr898]); +/* 12256 */ $blk = 19; /* allowing case fallthrough */ +/* 12257 */ case 19: +/* 12258 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12259 */ return $saveSuspension($ret, 'src/lib/traceback.py', 348, 16); +/* 12260 */ } +/* 12261 */ var $call899 = $ret; +/* 12262 */ // +/* 12263 */ // line 348: +/* 12264 */ // type(exc_value.__cause__), +/* 12265 */ // ^ +/* 12266 */ // +/* 12267 */ +/* 12268 */ $currLineNo = Sk.currLineNo = 348; +/* 12269 */ $currColNo = Sk.currColNo = 16; +/* 12270 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12271 */ if (exc_value === undefined) { +/* 12272 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12273 */ } +/* 12274 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const884, true); +/* 12275 */ $blk = 20; /* allowing case fallthrough */ +/* 12276 */ case 20: +/* 12277 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12278 */ return $saveSuspension($ret, 'src/lib/traceback.py', 349, 16); +/* 12279 */ } +/* 12280 */ var $lattr900 = $ret; +/* 12281 */ if (exc_value === undefined) { +/* 12282 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12283 */ } +/* 12284 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const884, true); +/* 12285 */ $blk = 21; /* allowing case fallthrough */ +/* 12286 */ case 21: +/* 12287 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12288 */ return $saveSuspension($ret, 'src/lib/traceback.py', 350, 16); +/* 12289 */ } +/* 12290 */ var $lattr901 = $ret; +/* 12291 */ $ret = Sk.abstr.gattr($lattr901, $scope870.$const902, true); +/* 12292 */ $blk = 22; /* allowing case fallthrough */ +/* 12293 */ case 22: +/* 12294 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12295 */ return $saveSuspension($ret, 'src/lib/traceback.py', 350, 16); +/* 12296 */ } +/* 12297 */ var $lattr903 = $ret; +/* 12298 */ if (limit === undefined) { +/* 12299 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 12300 */ } +/* 12301 */ if (capture_locals === undefined) { +/* 12302 */ throw new Sk.builtin.UnboundLocalError('local variable \'capture_locals\' referenced before assignment'); +/* 12303 */ } +/* 12304 */ if (_seen === undefined) { +/* 12305 */ throw new Sk.builtin.UnboundLocalError('local variable \'_seen\' referenced before assignment'); +/* 12306 */ } +/* 12307 */ $ret = Sk.misceval.applyOrSuspend($loadgbl896, undefined, undefined, ['limit', limit, 'lookup_lines', Sk.builtin.bool.false$, 'capture_locals', capture_locals, '_seen', _seen], [$call899, $lattr900, $lattr903]); +/* 12308 */ $blk = 23; /* allowing case fallthrough */ +/* 12309 */ case 23: +/* 12310 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12311 */ return $saveSuspension($ret, 'src/lib/traceback.py', 347, 20); +/* 12312 */ } +/* 12313 */ var $call904 = $ret; +/* 12314 */ // +/* 12315 */ // line 347: +/* 12316 */ // cause = TracebackException( +/* 12317 */ // ^ +/* 12318 */ // +/* 12319 */ +/* 12320 */ $currLineNo = Sk.currLineNo = 347; +/* 12321 */ $currColNo = Sk.currColNo = 20; +/* 12322 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12323 */ cause = $call904; +/* 12324 */ $blk = 8; /* allowing case fallthrough */ +/* 12325 */ case 8: +/* 12326 */ /* --- end of if --- */ +/* 12327 */ // +/* 12328 */ // line 357: +/* 12329 */ // if (exc_value and exc_value.__context__ is not None +/* 12330 */ // ^ +/* 12331 */ // +/* 12332 */ +/* 12333 */ $currLineNo = Sk.currLineNo = 357; +/* 12334 */ $currColNo = Sk.currColNo = 8; +/* 12335 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12336 */ if (exc_value === undefined) { +/* 12337 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12338 */ } +/* 12339 */ var $boolopsucc905 = exc_value; +/* 12340 */ $boolopsucc905 = exc_value; +/* 12341 */ var $jfalse906 = (exc_value === false || !Sk.misceval.isTrue(exc_value)); +/* 12342 */ if ($jfalse906) { +/* 12343 */ /*test failed */ +/* 12344 */ $blk = 26; +/* 12345 */ continue; +/* 12346 */ } +/* 12347 */ if (exc_value === undefined) { +/* 12348 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12349 */ } +/* 12350 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const907, true); +/* 12351 */ $blk = 27; /* allowing case fallthrough */ +/* 12352 */ case 27: +/* 12353 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12354 */ return $saveSuspension($ret, 'src/lib/traceback.py', 357, 26); +/* 12355 */ } +/* 12356 */ var $lattr908 = $ret; +/* 12357 */ var $compareres909 = null; +/* 12358 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($lattr908, Sk.builtin.none.none$, 'IsNot', true)); +/* 12359 */ $blk = 29; /* allowing case fallthrough */ +/* 12360 */ case 29: +/* 12361 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12362 */ return $saveSuspension($ret, 'src/lib/traceback.py', 357, 26); +/* 12363 */ } +/* 12364 */ $compareres909 = $ret; +/* 12365 */ var $jfalse910 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 12366 */ if ($jfalse910) { +/* 12367 */ /*test failed */ +/* 12368 */ $blk = 28; +/* 12369 */ continue; +/* 12370 */ } +/* 12371 */ $blk = 28; /* allowing case fallthrough */ +/* 12372 */ case 28: +/* 12373 */ /* --- done --- */ $boolopsucc905 = $compareres909; +/* 12374 */ var $jfalse911 = ($compareres909 === false || !Sk.misceval.isTrue($compareres909)); +/* 12375 */ if ($jfalse911) { +/* 12376 */ /*test failed */ +/* 12377 */ $blk = 26; +/* 12378 */ continue; +/* 12379 */ } +/* 12380 */ var $loadgbl912 = Sk.misceval.loadname('id', $gbl); +/* 12381 */ if (exc_value === undefined) { +/* 12382 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12383 */ } +/* 12384 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const907, true); +/* 12385 */ $blk = 30; /* allowing case fallthrough */ +/* 12386 */ case 30: +/* 12387 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12388 */ return $saveSuspension($ret, 'src/lib/traceback.py', 358, 19); +/* 12389 */ } +/* 12390 */ var $lattr913 = $ret; +/* 12391 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl912, [$lattr913]); +/* 12392 */ $blk = 31; /* allowing case fallthrough */ +/* 12393 */ case 31: +/* 12394 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12395 */ return $saveSuspension($ret, 'src/lib/traceback.py', 358, 16); +/* 12396 */ } +/* 12397 */ var $call914 = $ret; +/* 12398 */ // +/* 12399 */ // line 358: +/* 12400 */ // and id(exc_value.__context__) not in _seen): +/* 12401 */ // ^ +/* 12402 */ // +/* 12403 */ +/* 12404 */ $currLineNo = Sk.currLineNo = 358; +/* 12405 */ $currColNo = Sk.currColNo = 16; +/* 12406 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12407 */ var $compareres915 = null; +/* 12408 */ if (_seen === undefined) { +/* 12409 */ throw new Sk.builtin.UnboundLocalError('local variable \'_seen\' referenced before assignment'); +/* 12410 */ } +/* 12411 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($call914, _seen, 'NotIn', true)); +/* 12412 */ $blk = 33; /* allowing case fallthrough */ +/* 12413 */ case 33: +/* 12414 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12415 */ return $saveSuspension($ret, 'src/lib/traceback.py', 358, 16); +/* 12416 */ } +/* 12417 */ $compareres915 = $ret; +/* 12418 */ var $jfalse916 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 12419 */ if ($jfalse916) { +/* 12420 */ /*test failed */ +/* 12421 */ $blk = 32; +/* 12422 */ continue; +/* 12423 */ } +/* 12424 */ $blk = 32; /* allowing case fallthrough */ +/* 12425 */ case 32: +/* 12426 */ /* --- done --- */ $boolopsucc905 = $compareres915; +/* 12427 */ var $jfalse917 = ($compareres915 === false || !Sk.misceval.isTrue($compareres915)); +/* 12428 */ if ($jfalse917) { +/* 12429 */ /*test failed */ +/* 12430 */ $blk = 26; +/* 12431 */ continue; +/* 12432 */ } +/* 12433 */ $blk = 26; /* allowing case fallthrough */ +/* 12434 */ case 26: +/* 12435 */ /* --- end of boolop --- */ var $jfalse918 = ($boolopsucc905 === false || !Sk.misceval.isTrue($boolopsucc905)); +/* 12436 */ if ($jfalse918) { +/* 12437 */ /*test failed */ +/* 12438 */ $blk = 25; +/* 12439 */ continue; +/* 12440 */ } +/* 12441 */ // +/* 12442 */ // line 359: +/* 12443 */ // context = TracebackException( +/* 12444 */ // ^ +/* 12445 */ // +/* 12446 */ +/* 12447 */ $currLineNo = Sk.currLineNo = 359; +/* 12448 */ $currColNo = Sk.currColNo = 12; +/* 12449 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12450 */ var $loadgbl919 = Sk.misceval.loadname('TracebackException', $gbl); +/* 12451 */ var $loadgbl920 = Sk.misceval.loadname('type', $gbl); +/* 12452 */ if (exc_value === undefined) { +/* 12453 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12454 */ } +/* 12455 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const907, true); +/* 12456 */ $blk = 34; /* allowing case fallthrough */ +/* 12457 */ case 34: +/* 12458 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12459 */ return $saveSuspension($ret, 'src/lib/traceback.py', 360, 21); +/* 12460 */ } +/* 12461 */ var $lattr921 = $ret; +/* 12462 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl920, [$lattr921]); +/* 12463 */ $blk = 35; /* allowing case fallthrough */ +/* 12464 */ case 35: +/* 12465 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12466 */ return $saveSuspension($ret, 'src/lib/traceback.py', 360, 16); +/* 12467 */ } +/* 12468 */ var $call922 = $ret; +/* 12469 */ // +/* 12470 */ // line 360: +/* 12471 */ // type(exc_value.__context__), +/* 12472 */ // ^ +/* 12473 */ // +/* 12474 */ +/* 12475 */ $currLineNo = Sk.currLineNo = 360; +/* 12476 */ $currColNo = Sk.currColNo = 16; +/* 12477 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12478 */ if (exc_value === undefined) { +/* 12479 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12480 */ } +/* 12481 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const907, true); +/* 12482 */ $blk = 36; /* allowing case fallthrough */ +/* 12483 */ case 36: +/* 12484 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12485 */ return $saveSuspension($ret, 'src/lib/traceback.py', 361, 16); +/* 12486 */ } +/* 12487 */ var $lattr923 = $ret; +/* 12488 */ if (exc_value === undefined) { +/* 12489 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12490 */ } +/* 12491 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const907, true); +/* 12492 */ $blk = 37; /* allowing case fallthrough */ +/* 12493 */ case 37: +/* 12494 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12495 */ return $saveSuspension($ret, 'src/lib/traceback.py', 362, 16); +/* 12496 */ } +/* 12497 */ var $lattr924 = $ret; +/* 12498 */ $ret = Sk.abstr.gattr($lattr924, $scope870.$const902, true); +/* 12499 */ $blk = 38; /* allowing case fallthrough */ +/* 12500 */ case 38: +/* 12501 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12502 */ return $saveSuspension($ret, 'src/lib/traceback.py', 362, 16); +/* 12503 */ } +/* 12504 */ var $lattr925 = $ret; +/* 12505 */ if (limit === undefined) { +/* 12506 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 12507 */ } +/* 12508 */ if (capture_locals === undefined) { +/* 12509 */ throw new Sk.builtin.UnboundLocalError('local variable \'capture_locals\' referenced before assignment'); +/* 12510 */ } +/* 12511 */ if (_seen === undefined) { +/* 12512 */ throw new Sk.builtin.UnboundLocalError('local variable \'_seen\' referenced before assignment'); +/* 12513 */ } +/* 12514 */ $ret = Sk.misceval.applyOrSuspend($loadgbl919, undefined, undefined, ['limit', limit, 'lookup_lines', Sk.builtin.bool.false$, 'capture_locals', capture_locals, '_seen', _seen], [$call922, $lattr923, $lattr925]); +/* 12515 */ $blk = 39; /* allowing case fallthrough */ +/* 12516 */ case 39: +/* 12517 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12518 */ return $saveSuspension($ret, 'src/lib/traceback.py', 359, 22); +/* 12519 */ } +/* 12520 */ var $call926 = $ret; +/* 12521 */ // +/* 12522 */ // line 359: +/* 12523 */ // context = TracebackException( +/* 12524 */ // ^ +/* 12525 */ // +/* 12526 */ +/* 12527 */ $currLineNo = Sk.currLineNo = 359; +/* 12528 */ $currColNo = Sk.currColNo = 22; +/* 12529 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12530 */ context = $call926; +/* 12531 */ $blk = 24; /* allowing case fallthrough */ +/* 12532 */ case 24: +/* 12533 */ /* --- end of if --- */ +/* 12534 */ // +/* 12535 */ // line 369: +/* 12536 */ // self.exc_traceback = exc_traceback +/* 12537 */ // ^ +/* 12538 */ // +/* 12539 */ +/* 12540 */ $currLineNo = Sk.currLineNo = 369; +/* 12541 */ $currColNo = Sk.currColNo = 8; +/* 12542 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12543 */ if (exc_traceback === undefined) { +/* 12544 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_traceback\' referenced before assignment'); +/* 12545 */ } +/* 12546 */ if (self === undefined) { +/* 12547 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 12548 */ } +/* 12549 */ $ret = Sk.abstr.sattr(self, $scope870.$const927, exc_traceback, true); +/* 12550 */ $blk = 40; /* allowing case fallthrough */ +/* 12551 */ case 40: +/* 12552 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12553 */ return $saveSuspension($ret, 'src/lib/traceback.py', 369, 8); +/* 12554 */ } +/* 12555 */ // +/* 12556 */ // line 370: +/* 12557 */ // self.__cause__ = cause +/* 12558 */ // ^ +/* 12559 */ // +/* 12560 */ +/* 12561 */ $currLineNo = Sk.currLineNo = 370; +/* 12562 */ $currColNo = Sk.currColNo = 8; +/* 12563 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12564 */ if (cause === undefined) { +/* 12565 */ throw new Sk.builtin.UnboundLocalError('local variable \'cause\' referenced before assignment'); +/* 12566 */ } +/* 12567 */ if (self === undefined) { +/* 12568 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 12569 */ } +/* 12570 */ $ret = Sk.abstr.sattr(self, $scope870.$const884, cause, true); +/* 12571 */ $blk = 41; /* allowing case fallthrough */ +/* 12572 */ case 41: +/* 12573 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12574 */ return $saveSuspension($ret, 'src/lib/traceback.py', 370, 8); +/* 12575 */ } +/* 12576 */ // +/* 12577 */ // line 371: +/* 12578 */ // self.__context__ = context +/* 12579 */ // ^ +/* 12580 */ // +/* 12581 */ +/* 12582 */ $currLineNo = Sk.currLineNo = 371; +/* 12583 */ $currColNo = Sk.currColNo = 8; +/* 12584 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12585 */ if (context === undefined) { +/* 12586 */ throw new Sk.builtin.UnboundLocalError('local variable \'context\' referenced before assignment'); +/* 12587 */ } +/* 12588 */ if (self === undefined) { +/* 12589 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 12590 */ } +/* 12591 */ $ret = Sk.abstr.sattr(self, $scope870.$const907, context, true); +/* 12592 */ $blk = 42; /* allowing case fallthrough */ +/* 12593 */ case 42: +/* 12594 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12595 */ return $saveSuspension($ret, 'src/lib/traceback.py', 371, 8); +/* 12596 */ } +/* 12597 */ // +/* 12598 */ // line 372: +/* 12599 */ // self.__suppress_context__ = \ +/* 12600 */ // ^ +/* 12601 */ // +/* 12602 */ +/* 12603 */ $currLineNo = Sk.currLineNo = 372; +/* 12604 */ $currColNo = Sk.currColNo = 8; +/* 12605 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12606 */ var $res928 = null; +/* 12607 */ if (exc_value === undefined) { +/* 12608 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12609 */ } +/* 12610 */ var $jfalse929 = (exc_value === false || !Sk.misceval.isTrue(exc_value)); +/* 12611 */ if ($jfalse929) { +/* 12612 */ /*test failed */ +/* 12613 */ $blk = 43; +/* 12614 */ continue; +/* 12615 */ } +/* 12616 */ if (exc_value === undefined) { +/* 12617 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12618 */ } +/* 12619 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const930, true); +/* 12620 */ $blk = 45; /* allowing case fallthrough */ +/* 12621 */ case 45: +/* 12622 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12623 */ return $saveSuspension($ret, 'src/lib/traceback.py', 373, 12); +/* 12624 */ } +/* 12625 */ var $lattr931 = $ret; +/* 12626 */ $res928 = $lattr931; +/* 12627 */ $blk = 44; /* allowing case fallthrough */ +/* 12628 */ case 44: +/* 12629 */ /* --- end of ifexp --- */ if (self === undefined) { +/* 12630 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 12631 */ } +/* 12632 */ $ret = Sk.abstr.sattr(self, $scope870.$const930, $res928, true); +/* 12633 */ $blk = 46; /* allowing case fallthrough */ +/* 12634 */ case 46: +/* 12635 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12636 */ return $saveSuspension($ret, 'src/lib/traceback.py', 372, 8); +/* 12637 */ } +/* 12638 */ // +/* 12639 */ // line 375: +/* 12640 */ // self.stack = StackSummary.extract( +/* 12641 */ // ^ +/* 12642 */ // +/* 12643 */ +/* 12644 */ $currLineNo = Sk.currLineNo = 375; +/* 12645 */ $currColNo = Sk.currColNo = 8; +/* 12646 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12647 */ var $loadgbl932 = Sk.misceval.loadname('StackSummary', $gbl); +/* 12648 */ $ret = Sk.abstr.gattr($loadgbl932, $scope870.$const933, true); +/* 12649 */ $blk = 47; /* allowing case fallthrough */ +/* 12650 */ case 47: +/* 12651 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12652 */ return $saveSuspension($ret, 'src/lib/traceback.py', 375, 21); +/* 12653 */ } +/* 12654 */ var $lattr934 = $ret; +/* 12655 */ var $loadgbl935 = Sk.misceval.loadname('walk_tb', $gbl); +/* 12656 */ if (exc_traceback === undefined) { +/* 12657 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_traceback\' referenced before assignment'); +/* 12658 */ } +/* 12659 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl935, [exc_traceback]); +/* 12660 */ $blk = 48; /* allowing case fallthrough */ +/* 12661 */ case 48: +/* 12662 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12663 */ return $saveSuspension($ret, 'src/lib/traceback.py', 376, 12); +/* 12664 */ } +/* 12665 */ var $call936 = $ret; +/* 12666 */ // +/* 12667 */ // line 376: +/* 12668 */ // walk_tb(exc_traceback), limit=limit, lookup_lines=lookup_lines, +/* 12669 */ // ^ +/* 12670 */ // +/* 12671 */ +/* 12672 */ $currLineNo = Sk.currLineNo = 376; +/* 12673 */ $currColNo = Sk.currColNo = 12; +/* 12674 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12675 */ if (limit === undefined) { +/* 12676 */ throw new Sk.builtin.UnboundLocalError('local variable \'limit\' referenced before assignment'); +/* 12677 */ } +/* 12678 */ if (lookup_lines === undefined) { +/* 12679 */ throw new Sk.builtin.UnboundLocalError('local variable \'lookup_lines\' referenced before assignment'); +/* 12680 */ } +/* 12681 */ if (capture_locals === undefined) { +/* 12682 */ throw new Sk.builtin.UnboundLocalError('local variable \'capture_locals\' referenced before assignment'); +/* 12683 */ } +/* 12684 */ $ret = Sk.misceval.applyOrSuspend($lattr934, undefined, undefined, ['limit', limit, 'lookup_lines', lookup_lines, 'capture_locals', capture_locals], [$call936]); +/* 12685 */ $blk = 49; /* allowing case fallthrough */ +/* 12686 */ case 49: +/* 12687 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12688 */ return $saveSuspension($ret, 'src/lib/traceback.py', 375, 21); +/* 12689 */ } +/* 12690 */ var $call937 = $ret; +/* 12691 */ // +/* 12692 */ // line 375: +/* 12693 */ // self.stack = StackSummary.extract( +/* 12694 */ // ^ +/* 12695 */ // +/* 12696 */ +/* 12697 */ $currLineNo = Sk.currLineNo = 375; +/* 12698 */ $currColNo = Sk.currColNo = 21; +/* 12699 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12700 */ if (self === undefined) { +/* 12701 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 12702 */ } +/* 12703 */ $ret = Sk.abstr.sattr(self, $scope870.$const938, $call937, true); +/* 12704 */ $blk = 50; /* allowing case fallthrough */ +/* 12705 */ case 50: +/* 12706 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12707 */ return $saveSuspension($ret, 'src/lib/traceback.py', 375, 8); +/* 12708 */ } +/* 12709 */ // +/* 12710 */ // line 378: +/* 12711 */ // self.exc_type = exc_type +/* 12712 */ // ^ +/* 12713 */ // +/* 12714 */ +/* 12715 */ $currLineNo = Sk.currLineNo = 378; +/* 12716 */ $currColNo = Sk.currColNo = 8; +/* 12717 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12718 */ if (exc_type === undefined) { +/* 12719 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_type\' referenced before assignment'); +/* 12720 */ } +/* 12721 */ if (self === undefined) { +/* 12722 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 12723 */ } +/* 12724 */ $ret = Sk.abstr.sattr(self, $scope870.$const939, exc_type, true); +/* 12725 */ $blk = 51; /* allowing case fallthrough */ +/* 12726 */ case 51: +/* 12727 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12728 */ return $saveSuspension($ret, 'src/lib/traceback.py', 378, 8); +/* 12729 */ } +/* 12730 */ // +/* 12731 */ // line 381: +/* 12732 */ // self._str = _some_str(exc_value) +/* 12733 */ // ^ +/* 12734 */ // +/* 12735 */ +/* 12736 */ $currLineNo = Sk.currLineNo = 381; +/* 12737 */ $currColNo = Sk.currColNo = 8; +/* 12738 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12739 */ var $loadgbl940 = Sk.misceval.loadname('_some_str', $gbl); +/* 12740 */ if (exc_value === undefined) { +/* 12741 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12742 */ } +/* 12743 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl940, [exc_value]); +/* 12744 */ $blk = 52; /* allowing case fallthrough */ +/* 12745 */ case 52: +/* 12746 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12747 */ return $saveSuspension($ret, 'src/lib/traceback.py', 381, 20); +/* 12748 */ } +/* 12749 */ var $call941 = $ret; +/* 12750 */ // +/* 12751 */ // line 381: +/* 12752 */ // self._str = _some_str(exc_value) +/* 12753 */ // ^ +/* 12754 */ // +/* 12755 */ +/* 12756 */ $currLineNo = Sk.currLineNo = 381; +/* 12757 */ $currColNo = Sk.currColNo = 20; +/* 12758 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12759 */ if (self === undefined) { +/* 12760 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 12761 */ } +/* 12762 */ $ret = Sk.abstr.sattr(self, $scope870.$const942, $call941, true); +/* 12763 */ $blk = 53; /* allowing case fallthrough */ +/* 12764 */ case 53: +/* 12765 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12766 */ return $saveSuspension($ret, 'src/lib/traceback.py', 381, 8); +/* 12767 */ } +/* 12768 */ // +/* 12769 */ // line 382: +/* 12770 */ // if exc_type and issubclass(exc_type, SyntaxError): +/* 12771 */ // ^ +/* 12772 */ // +/* 12773 */ +/* 12774 */ $currLineNo = Sk.currLineNo = 382; +/* 12775 */ $currColNo = Sk.currColNo = 8; +/* 12776 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12777 */ if (exc_type === undefined) { +/* 12778 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_type\' referenced before assignment'); +/* 12779 */ } +/* 12780 */ var $boolopsucc943 = exc_type; +/* 12781 */ $boolopsucc943 = exc_type; +/* 12782 */ var $jfalse944 = (exc_type === false || !Sk.misceval.isTrue(exc_type)); +/* 12783 */ if ($jfalse944) { +/* 12784 */ /*test failed */ +/* 12785 */ $blk = 55; +/* 12786 */ continue; +/* 12787 */ } +/* 12788 */ var $loadgbl945 = Sk.misceval.loadname('issubclass', $gbl); +/* 12789 */ if (exc_type === undefined) { +/* 12790 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_type\' referenced before assignment'); +/* 12791 */ } +/* 12792 */ var $loadgbl946 = Sk.misceval.loadname('SyntaxError', $gbl); +/* 12793 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl945, [exc_type, $loadgbl946]); +/* 12794 */ $blk = 56; /* allowing case fallthrough */ +/* 12795 */ case 56: +/* 12796 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12797 */ return $saveSuspension($ret, 'src/lib/traceback.py', 382, 24); +/* 12798 */ } +/* 12799 */ var $call947 = $ret; +/* 12800 */ // +/* 12801 */ // line 382: +/* 12802 */ // if exc_type and issubclass(exc_type, SyntaxError): +/* 12803 */ // ^ +/* 12804 */ // +/* 12805 */ +/* 12806 */ $currLineNo = Sk.currLineNo = 382; +/* 12807 */ $currColNo = Sk.currColNo = 24; +/* 12808 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12809 */ $boolopsucc943 = $call947; +/* 12810 */ var $jfalse948 = ($call947 === false || !Sk.misceval.isTrue($call947)); +/* 12811 */ if ($jfalse948) { +/* 12812 */ /*test failed */ +/* 12813 */ $blk = 55; +/* 12814 */ continue; +/* 12815 */ } +/* 12816 */ $blk = 55; /* allowing case fallthrough */ +/* 12817 */ case 55: +/* 12818 */ /* --- end of boolop --- */ var $jfalse949 = ($boolopsucc943 === false || !Sk.misceval.isTrue($boolopsucc943)); +/* 12819 */ if ($jfalse949) { +/* 12820 */ /*test failed */ +/* 12821 */ $blk = 54; +/* 12822 */ continue; +/* 12823 */ } +/* 12824 */ // +/* 12825 */ // line 384: +/* 12826 */ // self.filename = exc_value.filename +/* 12827 */ // ^ +/* 12828 */ // +/* 12829 */ +/* 12830 */ $currLineNo = Sk.currLineNo = 384; +/* 12831 */ $currColNo = Sk.currColNo = 12; +/* 12832 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12833 */ if (exc_value === undefined) { +/* 12834 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12835 */ } +/* 12836 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const950, true); +/* 12837 */ $blk = 57; /* allowing case fallthrough */ +/* 12838 */ case 57: +/* 12839 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12840 */ return $saveSuspension($ret, 'src/lib/traceback.py', 384, 28); +/* 12841 */ } +/* 12842 */ var $lattr951 = $ret; +/* 12843 */ if (self === undefined) { +/* 12844 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 12845 */ } +/* 12846 */ $ret = Sk.abstr.sattr(self, $scope870.$const950, $lattr951, true); +/* 12847 */ $blk = 58; /* allowing case fallthrough */ +/* 12848 */ case 58: +/* 12849 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12850 */ return $saveSuspension($ret, 'src/lib/traceback.py', 384, 12); +/* 12851 */ } +/* 12852 */ // +/* 12853 */ // line 385: +/* 12854 */ // self.lineno = str(exc_value.lineno) +/* 12855 */ // ^ +/* 12856 */ // +/* 12857 */ +/* 12858 */ $currLineNo = Sk.currLineNo = 385; +/* 12859 */ $currColNo = Sk.currColNo = 12; +/* 12860 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12861 */ var $loadgbl952 = Sk.misceval.loadname('str', $gbl); +/* 12862 */ if (exc_value === undefined) { +/* 12863 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12864 */ } +/* 12865 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const953, true); +/* 12866 */ $blk = 59; /* allowing case fallthrough */ +/* 12867 */ case 59: +/* 12868 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12869 */ return $saveSuspension($ret, 'src/lib/traceback.py', 385, 30); +/* 12870 */ } +/* 12871 */ var $lattr954 = $ret; +/* 12872 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl952, [$lattr954]); +/* 12873 */ $blk = 60; /* allowing case fallthrough */ +/* 12874 */ case 60: +/* 12875 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12876 */ return $saveSuspension($ret, 'src/lib/traceback.py', 385, 26); +/* 12877 */ } +/* 12878 */ var $call955 = $ret; +/* 12879 */ // +/* 12880 */ // line 385: +/* 12881 */ // self.lineno = str(exc_value.lineno) +/* 12882 */ // ^ +/* 12883 */ // +/* 12884 */ +/* 12885 */ $currLineNo = Sk.currLineNo = 385; +/* 12886 */ $currColNo = Sk.currColNo = 26; +/* 12887 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12888 */ if (self === undefined) { +/* 12889 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 12890 */ } +/* 12891 */ $ret = Sk.abstr.sattr(self, $scope870.$const953, $call955, true); +/* 12892 */ $blk = 61; /* allowing case fallthrough */ +/* 12893 */ case 61: +/* 12894 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12895 */ return $saveSuspension($ret, 'src/lib/traceback.py', 385, 12); +/* 12896 */ } +/* 12897 */ // +/* 12898 */ // line 386: +/* 12899 */ // self.text = exc_value.text +/* 12900 */ // ^ +/* 12901 */ // +/* 12902 */ +/* 12903 */ $currLineNo = Sk.currLineNo = 386; +/* 12904 */ $currColNo = Sk.currColNo = 12; +/* 12905 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12906 */ if (exc_value === undefined) { +/* 12907 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12908 */ } +/* 12909 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const956, true); +/* 12910 */ $blk = 62; /* allowing case fallthrough */ +/* 12911 */ case 62: +/* 12912 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12913 */ return $saveSuspension($ret, 'src/lib/traceback.py', 386, 24); +/* 12914 */ } +/* 12915 */ var $lattr957 = $ret; +/* 12916 */ if (self === undefined) { +/* 12917 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 12918 */ } +/* 12919 */ $ret = Sk.abstr.sattr(self, $scope870.$const956, $lattr957, true); +/* 12920 */ $blk = 63; /* allowing case fallthrough */ +/* 12921 */ case 63: +/* 12922 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12923 */ return $saveSuspension($ret, 'src/lib/traceback.py', 386, 12); +/* 12924 */ } +/* 12925 */ // +/* 12926 */ // line 387: +/* 12927 */ // self.offset = exc_value.offset +/* 12928 */ // ^ +/* 12929 */ // +/* 12930 */ +/* 12931 */ $currLineNo = Sk.currLineNo = 387; +/* 12932 */ $currColNo = Sk.currColNo = 12; +/* 12933 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12934 */ if (exc_value === undefined) { +/* 12935 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12936 */ } +/* 12937 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const958, true); +/* 12938 */ $blk = 64; /* allowing case fallthrough */ +/* 12939 */ case 64: +/* 12940 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12941 */ return $saveSuspension($ret, 'src/lib/traceback.py', 387, 26); +/* 12942 */ } +/* 12943 */ var $lattr959 = $ret; +/* 12944 */ if (self === undefined) { +/* 12945 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 12946 */ } +/* 12947 */ $ret = Sk.abstr.sattr(self, $scope870.$const958, $lattr959, true); +/* 12948 */ $blk = 65; /* allowing case fallthrough */ +/* 12949 */ case 65: +/* 12950 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12951 */ return $saveSuspension($ret, 'src/lib/traceback.py', 387, 12); +/* 12952 */ } +/* 12953 */ // +/* 12954 */ // line 388: +/* 12955 */ // self.msg = exc_value.msg +/* 12956 */ // ^ +/* 12957 */ // +/* 12958 */ +/* 12959 */ $currLineNo = Sk.currLineNo = 388; +/* 12960 */ $currColNo = Sk.currColNo = 12; +/* 12961 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12962 */ if (exc_value === undefined) { +/* 12963 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc_value\' referenced before assignment'); +/* 12964 */ } +/* 12965 */ $ret = Sk.abstr.gattr(exc_value, $scope870.$const960, true); +/* 12966 */ $blk = 66; /* allowing case fallthrough */ +/* 12967 */ case 66: +/* 12968 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12969 */ return $saveSuspension($ret, 'src/lib/traceback.py', 388, 23); +/* 12970 */ } +/* 12971 */ var $lattr961 = $ret; +/* 12972 */ if (self === undefined) { +/* 12973 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 12974 */ } +/* 12975 */ $ret = Sk.abstr.sattr(self, $scope870.$const960, $lattr961, true); +/* 12976 */ $blk = 67; /* allowing case fallthrough */ +/* 12977 */ case 67: +/* 12978 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 12979 */ return $saveSuspension($ret, 'src/lib/traceback.py', 388, 12); +/* 12980 */ } +/* 12981 */ $blk = 54; /* allowing case fallthrough */ +/* 12982 */ case 54: +/* 12983 */ /* --- end of if --- */ +/* 12984 */ // +/* 12985 */ // line 389: +/* 12986 */ // if lookup_lines: +/* 12987 */ // ^ +/* 12988 */ // +/* 12989 */ +/* 12990 */ $currLineNo = Sk.currLineNo = 389; +/* 12991 */ $currColNo = Sk.currColNo = 8; +/* 12992 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 12993 */ if (lookup_lines === undefined) { +/* 12994 */ throw new Sk.builtin.UnboundLocalError('local variable \'lookup_lines\' referenced before assignment'); +/* 12995 */ } +/* 12996 */ var $jfalse962 = (lookup_lines === false || !Sk.misceval.isTrue(lookup_lines)); +/* 12997 */ if ($jfalse962) { +/* 12998 */ /*test failed */ +/* 12999 */ $blk = 68; +/* 13000 */ continue; +/* 13001 */ } +/* 13002 */ // +/* 13003 */ // line 390: +/* 13004 */ // self._load_lines() +/* 13005 */ // ^ +/* 13006 */ // +/* 13007 */ +/* 13008 */ $currLineNo = Sk.currLineNo = 390; +/* 13009 */ $currColNo = Sk.currColNo = 12; +/* 13010 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13011 */ if (self === undefined) { +/* 13012 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 13013 */ } +/* 13014 */ $ret = Sk.abstr.gattr(self, $scope870.$const963, true); +/* 13015 */ $blk = 69; /* allowing case fallthrough */ +/* 13016 */ case 69: +/* 13017 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13018 */ return $saveSuspension($ret, 'src/lib/traceback.py', 390, 12); +/* 13019 */ } +/* 13020 */ var $lattr964 = $ret; +/* 13021 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr964); +/* 13022 */ $blk = 70; /* allowing case fallthrough */ +/* 13023 */ case 70: +/* 13024 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13025 */ return $saveSuspension($ret, 'src/lib/traceback.py', 390, 12); +/* 13026 */ } +/* 13027 */ var $call965 = $ret; +/* 13028 */ // +/* 13029 */ // line 390: +/* 13030 */ // self._load_lines() +/* 13031 */ // ^ +/* 13032 */ // +/* 13033 */ +/* 13034 */ $currLineNo = Sk.currLineNo = 390; +/* 13035 */ $currColNo = Sk.currColNo = 12; +/* 13036 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13037 */ $blk = 68; /* allowing case fallthrough */ +/* 13038 */ case 68: +/* 13039 */ /* --- end of if --- */ return Sk.builtin.none.none$; +/* 13040 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 13041 */ case 9: +/* 13042 */ /* --- next branch of if --- */ +/* 13043 */ // +/* 13044 */ // line 356: +/* 13045 */ // cause = None +/* 13046 */ // ^ +/* 13047 */ // +/* 13048 */ +/* 13049 */ $currLineNo = Sk.currLineNo = 356; +/* 13050 */ $currColNo = Sk.currColNo = 12; +/* 13051 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13052 */ cause = Sk.builtin.none.none$; +/* 13053 */ $blk = 8; /* jump */ +/* 13054 */ continue; +/* 13055 */ case 25: +/* 13056 */ /* --- next branch of if --- */ +/* 13057 */ // +/* 13058 */ // line 368: +/* 13059 */ // context = None +/* 13060 */ // ^ +/* 13061 */ // +/* 13062 */ +/* 13063 */ $currLineNo = Sk.currLineNo = 368; +/* 13064 */ $currColNo = Sk.currColNo = 12; +/* 13065 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13066 */ context = Sk.builtin.none.none$; +/* 13067 */ $blk = 24; /* jump */ +/* 13068 */ continue; +/* 13069 */ case 43: +/* 13070 */ /* --- next of ifexp --- */ $res928 = Sk.builtin.bool.false$; +/* 13071 */ $blk = 44; /* jump */ +/* 13072 */ continue; +/* 13073 */ } +/* 13074 */ } catch (err) { +/* 13075 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 13076 */ Sk.execStart = Date.now(); +/* 13077 */ Sk.execPaused = 0 +/* 13078 */ } +/* 13079 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 13080 */ err = new Sk.builtin.ExternalError(err); +/* 13081 */ } +/* 13082 */ Sk.err = err; +/* 13083 */ err.traceback.push({ +/* 13084 */ lineno: $currLineNo, +/* 13085 */ colno: $currColNo, +/* 13086 */ filename: 'src/lib/traceback.py', +/* 13087 */ scope: '__init__' +/* 13088 */ }); +/* 13089 */ if ($exc.length > 0) { +/* 13090 */ $err = err; +/* 13091 */ $blk = $exc.pop(); +/* 13092 */ continue +/* 13093 */ } else { +/* 13094 */ throw err; +/* 13095 */ } +/* 13096 */ } +/* 13097 */ } +/* 13098 */ }); +/* 13099 */ $scope870.$const877 = new Sk.builtin.str('add'); +/* 13100 */ $scope870.$const884 = new Sk.builtin.str('__cause__'); +/* 13101 */ $scope870.$const902 = new Sk.builtin.str('__traceback__'); +/* 13102 */ $scope870.$const907 = new Sk.builtin.str('__context__'); +/* 13103 */ $scope870.$const927 = new Sk.builtin.str('exc_traceback'); +/* 13104 */ $scope870.$const930 = new Sk.builtin.str('__suppress_context__'); +/* 13105 */ $scope870.$const933 = new Sk.builtin.str('extract'); +/* 13106 */ $scope870.$const938 = new Sk.builtin.str('stack'); +/* 13107 */ $scope870.$const939 = new Sk.builtin.str('exc_type'); +/* 13108 */ $scope870.$const942 = new Sk.builtin.str('_str'); +/* 13109 */ $scope870.$const950 = new Sk.builtin.str('filename'); +/* 13110 */ $scope870.$const953 = new Sk.builtin.str('lineno'); +/* 13111 */ $scope870.$const956 = new Sk.builtin.str('text'); +/* 13112 */ $scope870.$const958 = new Sk.builtin.str('offset'); +/* 13113 */ $scope870.$const960 = new Sk.builtin.str('msg'); +/* 13114 */ $scope870.$const963 = new Sk.builtin.str('_load_lines'); +/* 13115 */ var $scope968 = (function $from_exception969$($kwa, cls, exc, args) { +/* 13116 */ var args, kwargs; /* locals */ +/* 13117 */ var args, args, cls, cls, exc, exc, exc, exc, kwargs, kwargs, $kwa, $unpack970, $loadgbl971, $unpack970, $loadgbl971, $call972, $unpack970, $loadgbl971, $call972, $lattr974, $unpack970, $loadgbl971, $call972, $lattr974, $keywordArgs975, $unpack970, $loadgbl971, $call972, $lattr974, $keywordArgs975; +/* 13118 */ var $wakeFromSuspension = function() { +/* 13119 */ var susp = $scope968.$wakingSuspension; +/* 13120 */ $scope968.$wakingSuspension = undefined; +/* 13121 */ $blk = susp.$blk; +/* 13122 */ $loc = susp.$loc; +/* 13123 */ $gbl = susp.$gbl; +/* 13124 */ $exc = susp.$exc; +/* 13125 */ $err = susp.$err; +/* 13126 */ $postfinally = susp.$postfinally; +/* 13127 */ $currLineNo = susp.$lineno; +/* 13128 */ $currColNo = susp.$colno; +/* 13129 */ Sk.lastYield = Date.now(); +/* 13130 */ args = susp.$tmps.args; +/* 13131 */ cls = susp.$tmps.cls; +/* 13132 */ exc = susp.$tmps.exc; +/* 13133 */ kwargs = susp.$tmps.kwargs; +/* 13134 */ $kwa = susp.$tmps.$kwa; +/* 13135 */ $unpack970 = susp.$tmps.$unpack970; +/* 13136 */ $loadgbl971 = susp.$tmps.$loadgbl971; +/* 13137 */ $call972 = susp.$tmps.$call972; +/* 13138 */ $lattr974 = susp.$tmps.$lattr974; +/* 13139 */ $keywordArgs975 = susp.$tmps.$keywordArgs975; +/* 13140 */ try { +/* 13141 */ $ret = susp.child.resume(); +/* 13142 */ } catch (err) { +/* 13143 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 13144 */ Sk.execStart = Date.now(); +/* 13145 */ Sk.execPaused = 0 +/* 13146 */ } +/* 13147 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 13148 */ err = new Sk.builtin.ExternalError(err); +/* 13149 */ } +/* 13150 */ Sk.err = err; +/* 13151 */ err.traceback.push({ +/* 13152 */ lineno: $currLineNo, +/* 13153 */ colno: $currColNo, +/* 13154 */ filename: 'src/lib/traceback.py', +/* 13155 */ scope: '$scope968' +/* 13156 */ }); +/* 13157 */ if ($exc.length > 0) { +/* 13158 */ $err = err; +/* 13159 */ $blk = $exc.pop(); +/* 13160 */ } else { +/* 13161 */ throw err; +/* 13162 */ } +/* 13163 */ } +/* 13164 */ }; +/* 13165 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 13166 */ var susp = new Sk.misceval.Suspension(); +/* 13167 */ susp.child = $child; +/* 13168 */ susp.resume = function() { +/* 13169 */ $scope968.$wakingSuspension = susp; +/* 13170 */ return $scope968(); +/* 13171 */ }; +/* 13172 */ susp.data = susp.child.data; +/* 13173 */ susp.$blk = $blk; +/* 13174 */ susp.$loc = $loc; +/* 13175 */ susp.$gbl = $gbl; +/* 13176 */ susp.$exc = $exc; +/* 13177 */ susp.$err = $err; +/* 13178 */ susp.$postfinally = $postfinally; +/* 13179 */ susp.$filename = $filename; +/* 13180 */ susp.$lineno = $lineno; +/* 13181 */ susp.$colno = $colno; +/* 13182 */ susp.optional = susp.child.optional; +/* 13183 */ susp.$tmps = { +/* 13184 */ "args": args, +/* 13185 */ "cls": cls, +/* 13186 */ "exc": exc, +/* 13187 */ "kwargs": kwargs, +/* 13188 */ "$kwa": $kwa, +/* 13189 */ "$unpack970": $unpack970, +/* 13190 */ "$loadgbl971": $loadgbl971, +/* 13191 */ "$call972": $call972, +/* 13192 */ "$lattr974": $lattr974, +/* 13193 */ "$keywordArgs975": $keywordArgs975 +/* 13194 */ }; +/* 13195 */ return susp; +/* 13196 */ }; +/* 13197 */ var $blk = 0, +/* 13198 */ $exc = [], +/* 13199 */ $loc = {}, +/* 13200 */ $cell = {}, +/* 13201 */ $gbl = this, +/* 13202 */ $err = undefined, +/* 13203 */ $ret = undefined, +/* 13204 */ $postfinally = undefined, +/* 13205 */ $currLineNo = undefined, +/* 13206 */ $currColNo = undefined; +/* 13207 */ if (typeof Sk.execStart === 'undefined') { +/* 13208 */ Sk.execStart = Date.now(); +/* 13209 */ Sk.execPaused = 0 +/* 13210 */ } +/* 13211 */ if (typeof Sk.lastYield === 'undefined') { +/* 13212 */ Sk.lastYield = Date.now() +/* 13213 */ } +/* 13214 */ if ($scope968.$wakingSuspension !== undefined) { +/* 13215 */ $wakeFromSuspension(); +/* 13216 */ } else { +/* 13217 */ kwargs = new Sk.builtins['dict']($kwa); +/* 13218 */ } +/* 13219 */ $gbl.__class__ = this.TracebackException; +/* 13220 */ while (true) { +/* 13221 */ try { +/* 13222 */ var $dateNow = Date.now(); +/* 13223 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 13224 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 13225 */ } +/* 13226 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 13227 */ var $susp = $saveSuspension({ +/* 13228 */ data: { +/* 13229 */ type: 'Sk.yield' +/* 13230 */ }, +/* 13231 */ resume: function() {} +/* 13232 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 13233 */ $susp.$blk = $blk; +/* 13234 */ $susp.optional = true; +/* 13235 */ return $susp; +/* 13236 */ } +/* 13237 */ switch ($blk) { +/* 13238 */ case 0: +/* 13239 */ /* --- codeobj entry --- */ if (cls === undefined) { +/* 13240 */ throw new Sk.builtin.UnboundLocalError('local variable \'cls\' referenced before assignment'); +/* 13241 */ } +/* 13242 */ if (exc === undefined) { +/* 13243 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc\' referenced before assignment'); +/* 13244 */ } +/* 13245 */ if (args === undefined) { +/* 13246 */ throw new Sk.builtin.UnboundLocalError('local variable \'args\' referenced before assignment'); +/* 13247 */ } +/* 13248 */ +/* 13249 */ // +/* 13250 */ // line 395: +/* 13251 */ // return cls(type(exc), exc, exc.__traceback__, *args, **kwargs) +/* 13252 */ // ^ +/* 13253 */ // +/* 13254 */ +/* 13255 */ $currLineNo = Sk.currLineNo = 395; +/* 13256 */ $currColNo = Sk.currColNo = 8; +/* 13257 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13258 */ if (cls === undefined) { +/* 13259 */ throw new Sk.builtin.UnboundLocalError('local variable \'cls\' referenced before assignment'); +/* 13260 */ } +/* 13261 */ var $unpack970 = []; +/* 13262 */ var $loadgbl971 = Sk.misceval.loadname('type', $gbl); +/* 13263 */ if (exc === undefined) { +/* 13264 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc\' referenced before assignment'); +/* 13265 */ } +/* 13266 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl971, [exc]); +/* 13267 */ $blk = 1; /* allowing case fallthrough */ +/* 13268 */ case 1: +/* 13269 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13270 */ return $saveSuspension($ret, 'src/lib/traceback.py', 395, 19); +/* 13271 */ } +/* 13272 */ var $call972 = $ret; +/* 13273 */ // +/* 13274 */ // line 395: +/* 13275 */ // return cls(type(exc), exc, exc.__traceback__, *args, **kwargs) +/* 13276 */ // ^ +/* 13277 */ // +/* 13278 */ +/* 13279 */ $currLineNo = Sk.currLineNo = 395; +/* 13280 */ $currColNo = Sk.currColNo = 19; +/* 13281 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13282 */ $unpack970.push($call972); +/* 13283 */ if (exc === undefined) { +/* 13284 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc\' referenced before assignment'); +/* 13285 */ } +/* 13286 */ $unpack970.push(exc); +/* 13287 */ if (exc === undefined) { +/* 13288 */ throw new Sk.builtin.UnboundLocalError('local variable \'exc\' referenced before assignment'); +/* 13289 */ } +/* 13290 */ $ret = Sk.abstr.gattr(exc, $scope968.$const973, true); +/* 13291 */ $blk = 2; /* allowing case fallthrough */ +/* 13292 */ case 2: +/* 13293 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13294 */ return $saveSuspension($ret, 'src/lib/traceback.py', 395, 35); +/* 13295 */ } +/* 13296 */ var $lattr974 = $ret; +/* 13297 */ $unpack970.push($lattr974); +/* 13298 */ if (args === undefined) { +/* 13299 */ throw new Sk.builtin.UnboundLocalError('local variable \'args\' referenced before assignment'); +/* 13300 */ } +/* 13301 */ $ret = Sk.misceval.iterFor(Sk.abstr.iter(args), function(e) { +/* 13302 */ $unpack970.push(e); +/* 13303 */ }); +/* 13304 */ $blk = 3; /* allowing case fallthrough */ +/* 13305 */ case 3: +/* 13306 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13307 */ return $saveSuspension($ret, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 13308 */ } +/* 13309 */ var $keywordArgs975 = []; +/* 13310 */ if (kwargs === undefined) { +/* 13311 */ throw new Sk.builtin.UnboundLocalError('local variable \'kwargs\' referenced before assignment'); +/* 13312 */ } +/* 13313 */ $ret = Sk.abstr.mappingUnpackIntoKeywordArray($keywordArgs975, kwargs, cls); +/* 13314 */ $blk = 4; /* allowing case fallthrough */ +/* 13315 */ case 4: +/* 13316 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13317 */ return $saveSuspension($ret, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 13318 */ } +/* 13319 */ $ret = Sk.misceval.applyOrSuspend(cls, undefined, undefined, $keywordArgs975, $unpack970); +/* 13320 */ $blk = 5; /* allowing case fallthrough */ +/* 13321 */ case 5: +/* 13322 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13323 */ return $saveSuspension($ret, 'src/lib/traceback.py', 395, 15); +/* 13324 */ } +/* 13325 */ var $call976 = $ret; +/* 13326 */ // +/* 13327 */ // line 395: +/* 13328 */ // return cls(type(exc), exc, exc.__traceback__, *args, **kwargs) +/* 13329 */ // ^ +/* 13330 */ // +/* 13331 */ +/* 13332 */ $currLineNo = Sk.currLineNo = 395; +/* 13333 */ $currColNo = Sk.currColNo = 15; +/* 13334 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13335 */ return $call976; +/* 13336 */ return Sk.builtin.none.none$; +/* 13337 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 13338 */ } +/* 13339 */ } catch (err) { +/* 13340 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 13341 */ Sk.execStart = Date.now(); +/* 13342 */ Sk.execPaused = 0 +/* 13343 */ } +/* 13344 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 13345 */ err = new Sk.builtin.ExternalError(err); +/* 13346 */ } +/* 13347 */ Sk.err = err; +/* 13348 */ err.traceback.push({ +/* 13349 */ lineno: $currLineNo, +/* 13350 */ colno: $currColNo, +/* 13351 */ filename: 'src/lib/traceback.py', +/* 13352 */ scope: 'from_exception' +/* 13353 */ }); +/* 13354 */ if ($exc.length > 0) { +/* 13355 */ $err = err; +/* 13356 */ $blk = $exc.pop(); +/* 13357 */ continue +/* 13358 */ } else { +/* 13359 */ throw err; +/* 13360 */ } +/* 13361 */ } +/* 13362 */ } +/* 13363 */ }); +/* 13364 */ $scope968.$const973 = new Sk.builtin.str('__traceback__'); +/* 13365 */ var $scope978 = (function $_load_lines979$(self) { +/* 13366 */ var frame; /* locals */ +/* 13367 */ var frame, frame, self, self, self, self, self, self, $iter982, $lattr981, $iter982, $lattr989, $lattr989, $lattr991, $lattr996, $lattr996, $lattr997; +/* 13368 */ var $wakeFromSuspension = function() { +/* 13369 */ var susp = $scope978.$wakingSuspension; +/* 13370 */ $scope978.$wakingSuspension = undefined; +/* 13371 */ $blk = susp.$blk; +/* 13372 */ $loc = susp.$loc; +/* 13373 */ $gbl = susp.$gbl; +/* 13374 */ $exc = susp.$exc; +/* 13375 */ $err = susp.$err; +/* 13376 */ $postfinally = susp.$postfinally; +/* 13377 */ $currLineNo = susp.$lineno; +/* 13378 */ $currColNo = susp.$colno; +/* 13379 */ Sk.lastYield = Date.now(); +/* 13380 */ frame = susp.$tmps.frame; +/* 13381 */ self = susp.$tmps.self; +/* 13382 */ $iter982 = susp.$tmps.$iter982; +/* 13383 */ $lattr981 = susp.$tmps.$lattr981; +/* 13384 */ $lattr989 = susp.$tmps.$lattr989; +/* 13385 */ $lattr991 = susp.$tmps.$lattr991; +/* 13386 */ $lattr996 = susp.$tmps.$lattr996; +/* 13387 */ $lattr997 = susp.$tmps.$lattr997; +/* 13388 */ try { +/* 13389 */ $ret = susp.child.resume(); +/* 13390 */ } catch (err) { +/* 13391 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 13392 */ Sk.execStart = Date.now(); +/* 13393 */ Sk.execPaused = 0 +/* 13394 */ } +/* 13395 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 13396 */ err = new Sk.builtin.ExternalError(err); +/* 13397 */ } +/* 13398 */ Sk.err = err; +/* 13399 */ err.traceback.push({ +/* 13400 */ lineno: $currLineNo, +/* 13401 */ colno: $currColNo, +/* 13402 */ filename: 'src/lib/traceback.py', +/* 13403 */ scope: '$scope978' +/* 13404 */ }); +/* 13405 */ if ($exc.length > 0) { +/* 13406 */ $err = err; +/* 13407 */ $blk = $exc.pop(); +/* 13408 */ } else { +/* 13409 */ throw err; +/* 13410 */ } +/* 13411 */ } +/* 13412 */ }; +/* 13413 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 13414 */ var susp = new Sk.misceval.Suspension(); +/* 13415 */ susp.child = $child; +/* 13416 */ susp.resume = function() { +/* 13417 */ $scope978.$wakingSuspension = susp; +/* 13418 */ return $scope978(); +/* 13419 */ }; +/* 13420 */ susp.data = susp.child.data; +/* 13421 */ susp.$blk = $blk; +/* 13422 */ susp.$loc = $loc; +/* 13423 */ susp.$gbl = $gbl; +/* 13424 */ susp.$exc = $exc; +/* 13425 */ susp.$err = $err; +/* 13426 */ susp.$postfinally = $postfinally; +/* 13427 */ susp.$filename = $filename; +/* 13428 */ susp.$lineno = $lineno; +/* 13429 */ susp.$colno = $colno; +/* 13430 */ susp.optional = susp.child.optional; +/* 13431 */ susp.$tmps = { +/* 13432 */ "frame": frame, +/* 13433 */ "self": self, +/* 13434 */ "$iter982": $iter982, +/* 13435 */ "$lattr981": $lattr981, +/* 13436 */ "$lattr989": $lattr989, +/* 13437 */ "$lattr991": $lattr991, +/* 13438 */ "$lattr996": $lattr996, +/* 13439 */ "$lattr997": $lattr997 +/* 13440 */ }; +/* 13441 */ return susp; +/* 13442 */ }; +/* 13443 */ var $blk = 0, +/* 13444 */ $exc = [], +/* 13445 */ $loc = {}, +/* 13446 */ $cell = {}, +/* 13447 */ $gbl = this, +/* 13448 */ $err = undefined, +/* 13449 */ $ret = undefined, +/* 13450 */ $postfinally = undefined, +/* 13451 */ $currLineNo = undefined, +/* 13452 */ $currColNo = undefined; +/* 13453 */ if (typeof Sk.execStart === 'undefined') { +/* 13454 */ Sk.execStart = Date.now(); +/* 13455 */ Sk.execPaused = 0 +/* 13456 */ } +/* 13457 */ if (typeof Sk.lastYield === 'undefined') { +/* 13458 */ Sk.lastYield = Date.now() +/* 13459 */ } +/* 13460 */ if ($scope978.$wakingSuspension !== undefined) { +/* 13461 */ $wakeFromSuspension(); +/* 13462 */ } else {} +/* 13463 */ $gbl.__class__ = this.TracebackException; +/* 13464 */ while (true) { +/* 13465 */ try { +/* 13466 */ var $dateNow = Date.now(); +/* 13467 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 13468 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 13469 */ } +/* 13470 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 13471 */ var $susp = $saveSuspension({ +/* 13472 */ data: { +/* 13473 */ type: 'Sk.yield' +/* 13474 */ }, +/* 13475 */ resume: function() {} +/* 13476 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 13477 */ $susp.$blk = $blk; +/* 13478 */ $susp.optional = true; +/* 13479 */ return $susp; +/* 13480 */ } +/* 13481 */ switch ($blk) { +/* 13482 */ case 0: +/* 13483 */ /* --- codeobj entry --- */ if (self === undefined) { +/* 13484 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 13485 */ } +/* 13486 */ +/* 13487 */ // +/* 13488 */ // line 399: +/* 13489 */ // for frame in self.stack: +/* 13490 */ // ^ +/* 13491 */ // +/* 13492 */ +/* 13493 */ $currLineNo = Sk.currLineNo = 399; +/* 13494 */ $currColNo = Sk.currColNo = 8; +/* 13495 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13496 */ if (self === undefined) { +/* 13497 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 13498 */ } +/* 13499 */ $ret = Sk.abstr.gattr(self, $scope978.$const980, true); +/* 13500 */ $blk = 4; /* allowing case fallthrough */ +/* 13501 */ case 4: +/* 13502 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13503 */ return $saveSuspension($ret, 'src/lib/traceback.py', 399, 21); +/* 13504 */ } +/* 13505 */ var $lattr981 = $ret; +/* 13506 */ var $iter982 = Sk.abstr.iter($lattr981); +/* 13507 */ $blk = 1; /* allowing case fallthrough */ +/* 13508 */ case 1: +/* 13509 */ /* --- for start --- */ $ret = Sk.abstr.iternext($iter982, true); +/* 13510 */ $blk = 5; /* allowing case fallthrough */ +/* 13511 */ case 5: +/* 13512 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13513 */ return $saveSuspension($ret, 'src/lib/traceback.py', 399, 8); +/* 13514 */ } +/* 13515 */ var $next983 = $ret; +/* 13516 */ if ($next983 === undefined) { +/* 13517 */ $blk = 2; +/* 13518 */ continue; +/* 13519 */ } +/* 13520 */ frame = $next983; +/* 13521 */ // +/* 13522 */ // line 400: +/* 13523 */ // frame.line +/* 13524 */ // ^ +/* 13525 */ // +/* 13526 */ +/* 13527 */ $currLineNo = Sk.currLineNo = 400; +/* 13528 */ $currColNo = Sk.currColNo = 12; +/* 13529 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13530 */ if (frame === undefined) { +/* 13531 */ throw new Sk.builtin.UnboundLocalError('local variable \'frame\' referenced before assignment'); +/* 13532 */ } +/* 13533 */ $ret = Sk.abstr.gattr(frame, $scope978.$const984, true); +/* 13534 */ $blk = 6; /* allowing case fallthrough */ +/* 13535 */ case 6: +/* 13536 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13537 */ return $saveSuspension($ret, 'src/lib/traceback.py', 400, 12); +/* 13538 */ } +/* 13539 */ var $lattr985 = $ret; +/* 13540 */ $blk = 1; /* jump */ +/* 13541 */ continue; +/* 13542 */ case 2: +/* 13543 */ /* --- for cleanup --- */ $blk = 3; /* allowing case fallthrough */ +/* 13544 */ case 3: +/* 13545 */ /* --- for end --- */ +/* 13546 */ // +/* 13547 */ // line 401: +/* 13548 */ // if self.__context__: +/* 13549 */ // ^ +/* 13550 */ // +/* 13551 */ +/* 13552 */ $currLineNo = Sk.currLineNo = 401; +/* 13553 */ $currColNo = Sk.currColNo = 8; +/* 13554 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13555 */ if (self === undefined) { +/* 13556 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 13557 */ } +/* 13558 */ $ret = Sk.abstr.gattr(self, $scope978.$const986, true); +/* 13559 */ $blk = 8; /* allowing case fallthrough */ +/* 13560 */ case 8: +/* 13561 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13562 */ return $saveSuspension($ret, 'src/lib/traceback.py', 401, 11); +/* 13563 */ } +/* 13564 */ var $lattr987 = $ret; +/* 13565 */ var $jfalse988 = ($lattr987 === false || !Sk.misceval.isTrue($lattr987)); +/* 13566 */ if ($jfalse988) { +/* 13567 */ /*test failed */ +/* 13568 */ $blk = 7; +/* 13569 */ continue; +/* 13570 */ } +/* 13571 */ // +/* 13572 */ // line 402: +/* 13573 */ // self.__context__._load_lines() +/* 13574 */ // ^ +/* 13575 */ // +/* 13576 */ +/* 13577 */ $currLineNo = Sk.currLineNo = 402; +/* 13578 */ $currColNo = Sk.currColNo = 12; +/* 13579 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13580 */ if (self === undefined) { +/* 13581 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 13582 */ } +/* 13583 */ $ret = Sk.abstr.gattr(self, $scope978.$const986, true); +/* 13584 */ $blk = 9; /* allowing case fallthrough */ +/* 13585 */ case 9: +/* 13586 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13587 */ return $saveSuspension($ret, 'src/lib/traceback.py', 402, 12); +/* 13588 */ } +/* 13589 */ var $lattr989 = $ret; +/* 13590 */ $ret = Sk.abstr.gattr($lattr989, $scope978.$const990, true); +/* 13591 */ $blk = 10; /* allowing case fallthrough */ +/* 13592 */ case 10: +/* 13593 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13594 */ return $saveSuspension($ret, 'src/lib/traceback.py', 402, 12); +/* 13595 */ } +/* 13596 */ var $lattr991 = $ret; +/* 13597 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr991); +/* 13598 */ $blk = 11; /* allowing case fallthrough */ +/* 13599 */ case 11: +/* 13600 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13601 */ return $saveSuspension($ret, 'src/lib/traceback.py', 402, 12); +/* 13602 */ } +/* 13603 */ var $call992 = $ret; +/* 13604 */ // +/* 13605 */ // line 402: +/* 13606 */ // self.__context__._load_lines() +/* 13607 */ // ^ +/* 13608 */ // +/* 13609 */ +/* 13610 */ $currLineNo = Sk.currLineNo = 402; +/* 13611 */ $currColNo = Sk.currColNo = 12; +/* 13612 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13613 */ $blk = 7; /* allowing case fallthrough */ +/* 13614 */ case 7: +/* 13615 */ /* --- end of if --- */ +/* 13616 */ // +/* 13617 */ // line 403: +/* 13618 */ // if self.__cause__: +/* 13619 */ // ^ +/* 13620 */ // +/* 13621 */ +/* 13622 */ $currLineNo = Sk.currLineNo = 403; +/* 13623 */ $currColNo = Sk.currColNo = 8; +/* 13624 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13625 */ if (self === undefined) { +/* 13626 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 13627 */ } +/* 13628 */ $ret = Sk.abstr.gattr(self, $scope978.$const993, true); +/* 13629 */ $blk = 13; /* allowing case fallthrough */ +/* 13630 */ case 13: +/* 13631 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13632 */ return $saveSuspension($ret, 'src/lib/traceback.py', 403, 11); +/* 13633 */ } +/* 13634 */ var $lattr994 = $ret; +/* 13635 */ var $jfalse995 = ($lattr994 === false || !Sk.misceval.isTrue($lattr994)); +/* 13636 */ if ($jfalse995) { +/* 13637 */ /*test failed */ +/* 13638 */ $blk = 12; +/* 13639 */ continue; +/* 13640 */ } +/* 13641 */ // +/* 13642 */ // line 404: +/* 13643 */ // self.__cause__._load_lines() +/* 13644 */ // ^ +/* 13645 */ // +/* 13646 */ +/* 13647 */ $currLineNo = Sk.currLineNo = 404; +/* 13648 */ $currColNo = Sk.currColNo = 12; +/* 13649 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13650 */ if (self === undefined) { +/* 13651 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 13652 */ } +/* 13653 */ $ret = Sk.abstr.gattr(self, $scope978.$const993, true); +/* 13654 */ $blk = 14; /* allowing case fallthrough */ +/* 13655 */ case 14: +/* 13656 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13657 */ return $saveSuspension($ret, 'src/lib/traceback.py', 404, 12); +/* 13658 */ } +/* 13659 */ var $lattr996 = $ret; +/* 13660 */ $ret = Sk.abstr.gattr($lattr996, $scope978.$const990, true); +/* 13661 */ $blk = 15; /* allowing case fallthrough */ +/* 13662 */ case 15: +/* 13663 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13664 */ return $saveSuspension($ret, 'src/lib/traceback.py', 404, 12); +/* 13665 */ } +/* 13666 */ var $lattr997 = $ret; +/* 13667 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr997); +/* 13668 */ $blk = 16; /* allowing case fallthrough */ +/* 13669 */ case 16: +/* 13670 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13671 */ return $saveSuspension($ret, 'src/lib/traceback.py', 404, 12); +/* 13672 */ } +/* 13673 */ var $call998 = $ret; +/* 13674 */ // +/* 13675 */ // line 404: +/* 13676 */ // self.__cause__._load_lines() +/* 13677 */ // ^ +/* 13678 */ // +/* 13679 */ +/* 13680 */ $currLineNo = Sk.currLineNo = 404; +/* 13681 */ $currColNo = Sk.currColNo = 12; +/* 13682 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13683 */ $blk = 12; /* allowing case fallthrough */ +/* 13684 */ case 12: +/* 13685 */ /* --- end of if --- */ return Sk.builtin.none.none$; +/* 13686 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 13687 */ } +/* 13688 */ } catch (err) { +/* 13689 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 13690 */ Sk.execStart = Date.now(); +/* 13691 */ Sk.execPaused = 0 +/* 13692 */ } +/* 13693 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 13694 */ err = new Sk.builtin.ExternalError(err); +/* 13695 */ } +/* 13696 */ Sk.err = err; +/* 13697 */ err.traceback.push({ +/* 13698 */ lineno: $currLineNo, +/* 13699 */ colno: $currColNo, +/* 13700 */ filename: 'src/lib/traceback.py', +/* 13701 */ scope: '_load_lines' +/* 13702 */ }); +/* 13703 */ if ($exc.length > 0) { +/* 13704 */ $err = err; +/* 13705 */ $blk = $exc.pop(); +/* 13706 */ continue +/* 13707 */ } else { +/* 13708 */ throw err; +/* 13709 */ } +/* 13710 */ } +/* 13711 */ } +/* 13712 */ }); +/* 13713 */ $scope978.$const980 = new Sk.builtin.str('stack'); +/* 13714 */ $scope978.$const984 = new Sk.builtin.str('line'); +/* 13715 */ $scope978.$const986 = new Sk.builtin.str('__context__'); +/* 13716 */ $scope978.$const990 = new Sk.builtin.str('_load_lines'); +/* 13717 */ $scope978.$const993 = new Sk.builtin.str('__cause__'); +/* 13718 */ var $scope1000 = (function $__eq__1001$(self, other) { +/* 13719 */ var other, other, self, self, $lattr1003, $compareres1004, $lattr1003, $compareres1004, $lattr1005; +/* 13720 */ var $wakeFromSuspension = function() { +/* 13721 */ var susp = $scope1000.$wakingSuspension; +/* 13722 */ $scope1000.$wakingSuspension = undefined; +/* 13723 */ $blk = susp.$blk; +/* 13724 */ $loc = susp.$loc; +/* 13725 */ $gbl = susp.$gbl; +/* 13726 */ $exc = susp.$exc; +/* 13727 */ $err = susp.$err; +/* 13728 */ $postfinally = susp.$postfinally; +/* 13729 */ $currLineNo = susp.$lineno; +/* 13730 */ $currColNo = susp.$colno; +/* 13731 */ Sk.lastYield = Date.now(); +/* 13732 */ other = susp.$tmps.other; +/* 13733 */ self = susp.$tmps.self; +/* 13734 */ $lattr1003 = susp.$tmps.$lattr1003; +/* 13735 */ $compareres1004 = susp.$tmps.$compareres1004; +/* 13736 */ $lattr1005 = susp.$tmps.$lattr1005; +/* 13737 */ try { +/* 13738 */ $ret = susp.child.resume(); +/* 13739 */ } catch (err) { +/* 13740 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 13741 */ Sk.execStart = Date.now(); +/* 13742 */ Sk.execPaused = 0 +/* 13743 */ } +/* 13744 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 13745 */ err = new Sk.builtin.ExternalError(err); +/* 13746 */ } +/* 13747 */ Sk.err = err; +/* 13748 */ err.traceback.push({ +/* 13749 */ lineno: $currLineNo, +/* 13750 */ colno: $currColNo, +/* 13751 */ filename: 'src/lib/traceback.py', +/* 13752 */ scope: '$scope1000' +/* 13753 */ }); +/* 13754 */ if ($exc.length > 0) { +/* 13755 */ $err = err; +/* 13756 */ $blk = $exc.pop(); +/* 13757 */ } else { +/* 13758 */ throw err; +/* 13759 */ } +/* 13760 */ } +/* 13761 */ }; +/* 13762 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 13763 */ var susp = new Sk.misceval.Suspension(); +/* 13764 */ susp.child = $child; +/* 13765 */ susp.resume = function() { +/* 13766 */ $scope1000.$wakingSuspension = susp; +/* 13767 */ return $scope1000(); +/* 13768 */ }; +/* 13769 */ susp.data = susp.child.data; +/* 13770 */ susp.$blk = $blk; +/* 13771 */ susp.$loc = $loc; +/* 13772 */ susp.$gbl = $gbl; +/* 13773 */ susp.$exc = $exc; +/* 13774 */ susp.$err = $err; +/* 13775 */ susp.$postfinally = $postfinally; +/* 13776 */ susp.$filename = $filename; +/* 13777 */ susp.$lineno = $lineno; +/* 13778 */ susp.$colno = $colno; +/* 13779 */ susp.optional = susp.child.optional; +/* 13780 */ susp.$tmps = { +/* 13781 */ "other": other, +/* 13782 */ "self": self, +/* 13783 */ "$lattr1003": $lattr1003, +/* 13784 */ "$compareres1004": $compareres1004, +/* 13785 */ "$lattr1005": $lattr1005 +/* 13786 */ }; +/* 13787 */ return susp; +/* 13788 */ }; +/* 13789 */ var $blk = 0, +/* 13790 */ $exc = [], +/* 13791 */ $loc = {}, +/* 13792 */ $cell = {}, +/* 13793 */ $gbl = this, +/* 13794 */ $err = undefined, +/* 13795 */ $ret = undefined, +/* 13796 */ $postfinally = undefined, +/* 13797 */ $currLineNo = undefined, +/* 13798 */ $currColNo = undefined; +/* 13799 */ if (typeof Sk.execStart === 'undefined') { +/* 13800 */ Sk.execStart = Date.now(); +/* 13801 */ Sk.execPaused = 0 +/* 13802 */ } +/* 13803 */ if (typeof Sk.lastYield === 'undefined') { +/* 13804 */ Sk.lastYield = Date.now() +/* 13805 */ } +/* 13806 */ if ($scope1000.$wakingSuspension !== undefined) { +/* 13807 */ $wakeFromSuspension(); +/* 13808 */ } else {} +/* 13809 */ $gbl.__class__ = this.TracebackException; +/* 13810 */ while (true) { +/* 13811 */ try { +/* 13812 */ var $dateNow = Date.now(); +/* 13813 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 13814 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 13815 */ } +/* 13816 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 13817 */ var $susp = $saveSuspension({ +/* 13818 */ data: { +/* 13819 */ type: 'Sk.yield' +/* 13820 */ }, +/* 13821 */ resume: function() {} +/* 13822 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 13823 */ $susp.$blk = $blk; +/* 13824 */ $susp.optional = true; +/* 13825 */ return $susp; +/* 13826 */ } +/* 13827 */ switch ($blk) { +/* 13828 */ case 0: +/* 13829 */ /* --- codeobj entry --- */ if (self === undefined) { +/* 13830 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 13831 */ } +/* 13832 */ if (other === undefined) { +/* 13833 */ throw new Sk.builtin.UnboundLocalError('local variable \'other\' referenced before assignment'); +/* 13834 */ } +/* 13835 */ +/* 13836 */ // +/* 13837 */ // line 407: +/* 13838 */ // return self.__dict__ == other.__dict__ +/* 13839 */ // ^ +/* 13840 */ // +/* 13841 */ +/* 13842 */ $currLineNo = Sk.currLineNo = 407; +/* 13843 */ $currColNo = Sk.currColNo = 8; +/* 13844 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 13845 */ if (self === undefined) { +/* 13846 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 13847 */ } +/* 13848 */ $ret = Sk.abstr.gattr(self, $scope1000.$const1002, true); +/* 13849 */ $blk = 1; /* allowing case fallthrough */ +/* 13850 */ case 1: +/* 13851 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13852 */ return $saveSuspension($ret, 'src/lib/traceback.py', 407, 15); +/* 13853 */ } +/* 13854 */ var $lattr1003 = $ret; +/* 13855 */ var $compareres1004 = null; +/* 13856 */ if (other === undefined) { +/* 13857 */ throw new Sk.builtin.UnboundLocalError('local variable \'other\' referenced before assignment'); +/* 13858 */ } +/* 13859 */ $ret = Sk.abstr.gattr(other, $scope1000.$const1002, true); +/* 13860 */ $blk = 3; /* allowing case fallthrough */ +/* 13861 */ case 3: +/* 13862 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13863 */ return $saveSuspension($ret, 'src/lib/traceback.py', 407, 32); +/* 13864 */ } +/* 13865 */ var $lattr1005 = $ret; +/* 13866 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($lattr1003, $lattr1005, 'Eq', true)); +/* 13867 */ $blk = 4; /* allowing case fallthrough */ +/* 13868 */ case 4: +/* 13869 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 13870 */ return $saveSuspension($ret, 'src/lib/traceback.py', 407, 15); +/* 13871 */ } +/* 13872 */ $compareres1004 = $ret; +/* 13873 */ var $jfalse1006 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 13874 */ if ($jfalse1006) { +/* 13875 */ /*test failed */ +/* 13876 */ $blk = 2; +/* 13877 */ continue; +/* 13878 */ } +/* 13879 */ $blk = 2; /* allowing case fallthrough */ +/* 13880 */ case 2: +/* 13881 */ /* --- done --- */ return $compareres1004; +/* 13882 */ return Sk.builtin.none.none$; +/* 13883 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 13884 */ } +/* 13885 */ } catch (err) { +/* 13886 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 13887 */ Sk.execStart = Date.now(); +/* 13888 */ Sk.execPaused = 0 +/* 13889 */ } +/* 13890 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 13891 */ err = new Sk.builtin.ExternalError(err); +/* 13892 */ } +/* 13893 */ Sk.err = err; +/* 13894 */ err.traceback.push({ +/* 13895 */ lineno: $currLineNo, +/* 13896 */ colno: $currColNo, +/* 13897 */ filename: 'src/lib/traceback.py', +/* 13898 */ scope: '__eq__' +/* 13899 */ }); +/* 13900 */ if ($exc.length > 0) { +/* 13901 */ $err = err; +/* 13902 */ $blk = $exc.pop(); +/* 13903 */ continue +/* 13904 */ } else { +/* 13905 */ throw err; +/* 13906 */ } +/* 13907 */ } +/* 13908 */ } +/* 13909 */ }); +/* 13910 */ $scope1000.$const1002 = new Sk.builtin.str('__dict__'); +/* 13911 */ var $scope1008 = (function $__str__1009$(self) { +/* 13912 */ var self, self; +/* 13913 */ var $wakeFromSuspension = function() { +/* 13914 */ var susp = $scope1008.$wakingSuspension; +/* 13915 */ $scope1008.$wakingSuspension = undefined; +/* 13916 */ $blk = susp.$blk; +/* 13917 */ $loc = susp.$loc; +/* 13918 */ $gbl = susp.$gbl; +/* 13919 */ $exc = susp.$exc; +/* 13920 */ $err = susp.$err; +/* 13921 */ $postfinally = susp.$postfinally; +/* 13922 */ $currLineNo = susp.$lineno; +/* 13923 */ $currColNo = susp.$colno; +/* 13924 */ Sk.lastYield = Date.now(); +/* 13925 */ self = susp.$tmps.self; +/* 13926 */ try { +/* 13927 */ $ret = susp.child.resume(); +/* 13928 */ } catch (err) { +/* 13929 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 13930 */ Sk.execStart = Date.now(); +/* 13931 */ Sk.execPaused = 0 +/* 13932 */ } +/* 13933 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 13934 */ err = new Sk.builtin.ExternalError(err); +/* 13935 */ } +/* 13936 */ Sk.err = err; +/* 13937 */ err.traceback.push({ +/* 13938 */ lineno: $currLineNo, +/* 13939 */ colno: $currColNo, +/* 13940 */ filename: 'src/lib/traceback.py', +/* 13941 */ scope: '$scope1008' +/* 13942 */ }); +/* 13943 */ if ($exc.length > 0) { +/* 13944 */ $err = err; +/* 13945 */ $blk = $exc.pop(); +/* 13946 */ } else { +/* 13947 */ throw err; +/* 13948 */ } +/* 13949 */ } +/* 13950 */ }; +/* 13951 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 13952 */ var susp = new Sk.misceval.Suspension(); +/* 13953 */ susp.child = $child; +/* 13954 */ susp.resume = function() { +/* 13955 */ $scope1008.$wakingSuspension = susp; +/* 13956 */ return $scope1008(); +/* 13957 */ }; +/* 13958 */ susp.data = susp.child.data; +/* 13959 */ susp.$blk = $blk; +/* 13960 */ susp.$loc = $loc; +/* 13961 */ susp.$gbl = $gbl; +/* 13962 */ susp.$exc = $exc; +/* 13963 */ susp.$err = $err; +/* 13964 */ susp.$postfinally = $postfinally; +/* 13965 */ susp.$filename = $filename; +/* 13966 */ susp.$lineno = $lineno; +/* 13967 */ susp.$colno = $colno; +/* 13968 */ susp.optional = susp.child.optional; +/* 13969 */ susp.$tmps = { +/* 13970 */ "self": self +/* 13971 */ }; +/* 13972 */ return susp; +/* 13973 */ }; +/* 13974 */ var $blk = 0, +/* 13975 */ $exc = [], +/* 13976 */ $loc = {}, +/* 13977 */ $cell = {}, +/* 13978 */ $gbl = this, +/* 13979 */ $err = undefined, +/* 13980 */ $ret = undefined, +/* 13981 */ $postfinally = undefined, +/* 13982 */ $currLineNo = undefined, +/* 13983 */ $currColNo = undefined; +/* 13984 */ if (typeof Sk.execStart === 'undefined') { +/* 13985 */ Sk.execStart = Date.now(); +/* 13986 */ Sk.execPaused = 0 +/* 13987 */ } +/* 13988 */ if (typeof Sk.lastYield === 'undefined') { +/* 13989 */ Sk.lastYield = Date.now() +/* 13990 */ } +/* 13991 */ if ($scope1008.$wakingSuspension !== undefined) { +/* 13992 */ $wakeFromSuspension(); +/* 13993 */ } else {} +/* 13994 */ $gbl.__class__ = this.TracebackException; +/* 13995 */ while (true) { +/* 13996 */ try { +/* 13997 */ var $dateNow = Date.now(); +/* 13998 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 13999 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 14000 */ } +/* 14001 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 14002 */ var $susp = $saveSuspension({ +/* 14003 */ data: { +/* 14004 */ type: 'Sk.yield' +/* 14005 */ }, +/* 14006 */ resume: function() {} +/* 14007 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 14008 */ $susp.$blk = $blk; +/* 14009 */ $susp.optional = true; +/* 14010 */ return $susp; +/* 14011 */ } +/* 14012 */ switch ($blk) { +/* 14013 */ case 0: +/* 14014 */ /* --- codeobj entry --- */ if (self === undefined) { +/* 14015 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 14016 */ } +/* 14017 */ +/* 14018 */ // +/* 14019 */ // line 410: +/* 14020 */ // return self._str +/* 14021 */ // ^ +/* 14022 */ // +/* 14023 */ +/* 14024 */ $currLineNo = Sk.currLineNo = 410; +/* 14025 */ $currColNo = Sk.currColNo = 8; +/* 14026 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14027 */ if (self === undefined) { +/* 14028 */ throw new Sk.builtin.UnboundLocalError('local variable \'self\' referenced before assignment'); +/* 14029 */ } +/* 14030 */ $ret = Sk.abstr.gattr(self, $scope1008.$const1010, true); +/* 14031 */ $blk = 1; /* allowing case fallthrough */ +/* 14032 */ case 1: +/* 14033 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14034 */ return $saveSuspension($ret, 'src/lib/traceback.py', 410, 15); +/* 14035 */ } +/* 14036 */ var $lattr1011 = $ret; +/* 14037 */ return $lattr1011; +/* 14038 */ return Sk.builtin.none.none$; +/* 14039 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 14040 */ } +/* 14041 */ } catch (err) { +/* 14042 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 14043 */ Sk.execStart = Date.now(); +/* 14044 */ Sk.execPaused = 0 +/* 14045 */ } +/* 14046 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 14047 */ err = new Sk.builtin.ExternalError(err); +/* 14048 */ } +/* 14049 */ Sk.err = err; +/* 14050 */ err.traceback.push({ +/* 14051 */ lineno: $currLineNo, +/* 14052 */ colno: $currColNo, +/* 14053 */ filename: 'src/lib/traceback.py', +/* 14054 */ scope: '__str__' +/* 14055 */ }); +/* 14056 */ if ($exc.length > 0) { +/* 14057 */ $err = err; +/* 14058 */ $blk = $exc.pop(); +/* 14059 */ continue +/* 14060 */ } else { +/* 14061 */ throw err; +/* 14062 */ } +/* 14063 */ } +/* 14064 */ } +/* 14065 */ }); +/* 14066 */ $scope1008.$const1010 = new Sk.builtin.str('_str'); +/* 14067 */ var $scope1013 = (function $format_exception_only1014$($gen) { +/* 14068 */ // generator +/* 14069 */ var $loadname1015, $loadname1015, $lattr1017, $compareres1018, $loadgbl1021, $loadname1022, $loadgbl1021, $loadname1022, $lattr1024, $loadname1026, $loadname1026, $lattr1027, $loadname1030, $loadname1030, $lattr1031, $loadname1034, $compareres1035, $loadgbl1046, $loadname1047, $loadgbl1046, $loadname1047, $lattr1048, $loadgbl1049, $loadgbl1053, $loadname1054, $loadname1055, $loadgbl1053, $loadname1054, $loadname1055, $lattr1056, $loadname1058, $loadgbl1065, $loadname1066, $loadgbl1065, $loadname1066, $lattr1068, $lattr1076, $loadname1077, $loadname1078, $loadname1080, $loadname1083, $loadname1086, $compareres1087, $lattr1091, $loadname1092, $lattr1091, $loadname1092, $lattr1094, $lattr1091, $loadname1092, $lattr1094, $call1095, $loadname1097, $compareres1098, $loadname1101, $loadname1101, $lattr1103, $loadgbl1106, $loadgbl1107, $loadname1108, $loadgbl1106, $loadgbl1107, $loadname1108, $call1109, $loadname1110, $loadname1114, $loadname1115, $slice1116, $loadname1114, $loadname1115, $slice1116, $lsubscr1117, $loadname1114, $loadname1115, $slice1116, $lsubscr1117, $lattr1119, $lattr1140, $lattr1140, $lattr1143, $loadname1144, $lattr1140, $lattr1143, $loadname1144, $call1145, $loadname1147, $lattr1155, $loadname1156, $loadname1157; +/* 14070 */ var $wakeFromSuspension = function() { +/* 14071 */ var susp = $scope1013.$wakingSuspension; +/* 14072 */ $scope1013.$wakingSuspension = undefined; +/* 14073 */ $blk = susp.$blk; +/* 14074 */ $loc = susp.$loc; +/* 14075 */ $gbl = susp.$gbl; +/* 14076 */ $exc = susp.$exc; +/* 14077 */ $err = susp.$err; +/* 14078 */ $postfinally = susp.$postfinally; +/* 14079 */ $currLineNo = susp.$lineno; +/* 14080 */ $currColNo = susp.$colno; +/* 14081 */ Sk.lastYield = Date.now(); +/* 14082 */ $loadname1015 = susp.$tmps.$loadname1015; +/* 14083 */ $lattr1017 = susp.$tmps.$lattr1017; +/* 14084 */ $compareres1018 = susp.$tmps.$compareres1018; +/* 14085 */ $loadgbl1021 = susp.$tmps.$loadgbl1021; +/* 14086 */ $loadname1022 = susp.$tmps.$loadname1022; +/* 14087 */ $lattr1024 = susp.$tmps.$lattr1024; +/* 14088 */ $loadname1026 = susp.$tmps.$loadname1026; +/* 14089 */ $lattr1027 = susp.$tmps.$lattr1027; +/* 14090 */ $loadname1030 = susp.$tmps.$loadname1030; +/* 14091 */ $lattr1031 = susp.$tmps.$lattr1031; +/* 14092 */ $loadname1034 = susp.$tmps.$loadname1034; +/* 14093 */ $compareres1035 = susp.$tmps.$compareres1035; +/* 14094 */ $loadgbl1046 = susp.$tmps.$loadgbl1046; +/* 14095 */ $loadname1047 = susp.$tmps.$loadname1047; +/* 14096 */ $lattr1048 = susp.$tmps.$lattr1048; +/* 14097 */ $loadgbl1049 = susp.$tmps.$loadgbl1049; +/* 14098 */ $loadgbl1053 = susp.$tmps.$loadgbl1053; +/* 14099 */ $loadname1054 = susp.$tmps.$loadname1054; +/* 14100 */ $loadname1055 = susp.$tmps.$loadname1055; +/* 14101 */ $lattr1056 = susp.$tmps.$lattr1056; +/* 14102 */ $loadname1058 = susp.$tmps.$loadname1058; +/* 14103 */ $loadgbl1065 = susp.$tmps.$loadgbl1065; +/* 14104 */ $loadname1066 = susp.$tmps.$loadname1066; +/* 14105 */ $lattr1068 = susp.$tmps.$lattr1068; +/* 14106 */ $lattr1076 = susp.$tmps.$lattr1076; +/* 14107 */ $loadname1077 = susp.$tmps.$loadname1077; +/* 14108 */ $loadname1078 = susp.$tmps.$loadname1078; +/* 14109 */ $loadname1080 = susp.$tmps.$loadname1080; +/* 14110 */ $loadname1083 = susp.$tmps.$loadname1083; +/* 14111 */ $loadname1086 = susp.$tmps.$loadname1086; +/* 14112 */ $compareres1087 = susp.$tmps.$compareres1087; +/* 14113 */ $lattr1091 = susp.$tmps.$lattr1091; +/* 14114 */ $loadname1092 = susp.$tmps.$loadname1092; +/* 14115 */ $lattr1094 = susp.$tmps.$lattr1094; +/* 14116 */ $call1095 = susp.$tmps.$call1095; +/* 14117 */ $loadname1097 = susp.$tmps.$loadname1097; +/* 14118 */ $compareres1098 = susp.$tmps.$compareres1098; +/* 14119 */ $loadname1101 = susp.$tmps.$loadname1101; +/* 14120 */ $lattr1103 = susp.$tmps.$lattr1103; +/* 14121 */ $loadgbl1106 = susp.$tmps.$loadgbl1106; +/* 14122 */ $loadgbl1107 = susp.$tmps.$loadgbl1107; +/* 14123 */ $loadname1108 = susp.$tmps.$loadname1108; +/* 14124 */ $call1109 = susp.$tmps.$call1109; +/* 14125 */ $loadname1110 = susp.$tmps.$loadname1110; +/* 14126 */ $loadname1114 = susp.$tmps.$loadname1114; +/* 14127 */ $loadname1115 = susp.$tmps.$loadname1115; +/* 14128 */ $slice1116 = susp.$tmps.$slice1116; +/* 14129 */ $lsubscr1117 = susp.$tmps.$lsubscr1117; +/* 14130 */ $lattr1119 = susp.$tmps.$lattr1119; +/* 14131 */ $lattr1140 = susp.$tmps.$lattr1140; +/* 14132 */ $lattr1143 = susp.$tmps.$lattr1143; +/* 14133 */ $loadname1144 = susp.$tmps.$loadname1144; +/* 14134 */ $call1145 = susp.$tmps.$call1145; +/* 14135 */ $loadname1147 = susp.$tmps.$loadname1147; +/* 14136 */ $lattr1155 = susp.$tmps.$lattr1155; +/* 14137 */ $loadname1156 = susp.$tmps.$loadname1156; +/* 14138 */ $loadname1157 = susp.$tmps.$loadname1157; +/* 14139 */ try { +/* 14140 */ $ret = susp.child.resume(); +/* 14141 */ } catch (err) { +/* 14142 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 14143 */ Sk.execStart = Date.now(); +/* 14144 */ Sk.execPaused = 0 +/* 14145 */ } +/* 14146 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 14147 */ err = new Sk.builtin.ExternalError(err); +/* 14148 */ } +/* 14149 */ Sk.err = err; +/* 14150 */ err.traceback.push({ +/* 14151 */ lineno: $currLineNo, +/* 14152 */ colno: $currColNo, +/* 14153 */ filename: 'src/lib/traceback.py', +/* 14154 */ scope: '$scope1013' +/* 14155 */ }); +/* 14156 */ if ($exc.length > 0) { +/* 14157 */ $err = err; +/* 14158 */ $blk = $exc.pop(); +/* 14159 */ } else { +/* 14160 */ throw err; +/* 14161 */ } +/* 14162 */ } +/* 14163 */ }; +/* 14164 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 14165 */ var susp = new Sk.misceval.Suspension(); +/* 14166 */ susp.child = $child; +/* 14167 */ susp.resume = function() { +/* 14168 */ $scope1013.$wakingSuspension = susp; +/* 14169 */ return $scope1013($gen); +/* 14170 */ }; +/* 14171 */ susp.data = susp.child.data; +/* 14172 */ susp.$blk = $blk; +/* 14173 */ susp.$loc = $loc; +/* 14174 */ susp.$gbl = $gbl; +/* 14175 */ susp.$exc = $exc; +/* 14176 */ susp.$err = $err; +/* 14177 */ susp.$postfinally = $postfinally; +/* 14178 */ susp.$filename = $filename; +/* 14179 */ susp.$lineno = $lineno; +/* 14180 */ susp.$colno = $colno; +/* 14181 */ susp.optional = susp.child.optional; +/* 14182 */ susp.$tmps = { +/* 14183 */ "$loadname1015": $loadname1015, +/* 14184 */ "$lattr1017": $lattr1017, +/* 14185 */ "$compareres1018": $compareres1018, +/* 14186 */ "$loadgbl1021": $loadgbl1021, +/* 14187 */ "$loadname1022": $loadname1022, +/* 14188 */ "$lattr1024": $lattr1024, +/* 14189 */ "$loadname1026": $loadname1026, +/* 14190 */ "$lattr1027": $lattr1027, +/* 14191 */ "$loadname1030": $loadname1030, +/* 14192 */ "$lattr1031": $lattr1031, +/* 14193 */ "$loadname1034": $loadname1034, +/* 14194 */ "$compareres1035": $compareres1035, +/* 14195 */ "$loadgbl1046": $loadgbl1046, +/* 14196 */ "$loadname1047": $loadname1047, +/* 14197 */ "$lattr1048": $lattr1048, +/* 14198 */ "$loadgbl1049": $loadgbl1049, +/* 14199 */ "$loadgbl1053": $loadgbl1053, +/* 14200 */ "$loadname1054": $loadname1054, +/* 14201 */ "$loadname1055": $loadname1055, +/* 14202 */ "$lattr1056": $lattr1056, +/* 14203 */ "$loadname1058": $loadname1058, +/* 14204 */ "$loadgbl1065": $loadgbl1065, +/* 14205 */ "$loadname1066": $loadname1066, +/* 14206 */ "$lattr1068": $lattr1068, +/* 14207 */ "$lattr1076": $lattr1076, +/* 14208 */ "$loadname1077": $loadname1077, +/* 14209 */ "$loadname1078": $loadname1078, +/* 14210 */ "$loadname1080": $loadname1080, +/* 14211 */ "$loadname1083": $loadname1083, +/* 14212 */ "$loadname1086": $loadname1086, +/* 14213 */ "$compareres1087": $compareres1087, +/* 14214 */ "$lattr1091": $lattr1091, +/* 14215 */ "$loadname1092": $loadname1092, +/* 14216 */ "$lattr1094": $lattr1094, +/* 14217 */ "$call1095": $call1095, +/* 14218 */ "$loadname1097": $loadname1097, +/* 14219 */ "$compareres1098": $compareres1098, +/* 14220 */ "$loadname1101": $loadname1101, +/* 14221 */ "$lattr1103": $lattr1103, +/* 14222 */ "$loadgbl1106": $loadgbl1106, +/* 14223 */ "$loadgbl1107": $loadgbl1107, +/* 14224 */ "$loadname1108": $loadname1108, +/* 14225 */ "$call1109": $call1109, +/* 14226 */ "$loadname1110": $loadname1110, +/* 14227 */ "$loadname1114": $loadname1114, +/* 14228 */ "$loadname1115": $loadname1115, +/* 14229 */ "$slice1116": $slice1116, +/* 14230 */ "$lsubscr1117": $lsubscr1117, +/* 14231 */ "$lattr1119": $lattr1119, +/* 14232 */ "$lattr1140": $lattr1140, +/* 14233 */ "$lattr1143": $lattr1143, +/* 14234 */ "$loadname1144": $loadname1144, +/* 14235 */ "$call1145": $call1145, +/* 14236 */ "$loadname1147": $loadname1147, +/* 14237 */ "$lattr1155": $lattr1155, +/* 14238 */ "$loadname1156": $loadname1156, +/* 14239 */ "$loadname1157": $loadname1157 +/* 14240 */ }; +/* 14241 */ return susp; +/* 14242 */ }; +/* 14243 */ var $blk = $gen.gi$resumeat, +/* 14244 */ $exc = [], +/* 14245 */ $loc = $gen.gi$locals, +/* 14246 */ $cell = {}, +/* 14247 */ $gbl = this, +/* 14248 */ $err = undefined, +/* 14249 */ $ret = undefined, +/* 14250 */ $postfinally = undefined, +/* 14251 */ $currLineNo = undefined, +/* 14252 */ $currColNo = undefined; +/* 14253 */ if (typeof Sk.execStart === 'undefined') { +/* 14254 */ Sk.execStart = Date.now(); +/* 14255 */ Sk.execPaused = 0 +/* 14256 */ } +/* 14257 */ if (typeof Sk.lastYield === 'undefined') { +/* 14258 */ Sk.lastYield = Date.now() +/* 14259 */ } +/* 14260 */ if ($scope1013.$wakingSuspension !== undefined) { +/* 14261 */ $wakeFromSuspension(); +/* 14262 */ } else {} +/* 14263 */ $gbl.__class__ = this.TracebackException; +/* 14264 */ while (true) { +/* 14265 */ try { +/* 14266 */ var $dateNow = Date.now(); +/* 14267 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 14268 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 14269 */ } +/* 14270 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 14271 */ var $susp = $saveSuspension({ +/* 14272 */ data: { +/* 14273 */ type: 'Sk.yield' +/* 14274 */ }, +/* 14275 */ resume: function() {} +/* 14276 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 14277 */ $susp.$blk = $blk; +/* 14278 */ $susp.optional = true; +/* 14279 */ return $susp; +/* 14280 */ } +/* 14281 */ switch ($blk) { +/* 14282 */ case 0: +/* 14283 */ /* --- codeobj entry --- */ +/* 14284 */ // +/* 14285 */ // line 414: +/* 14286 */ // if self.exc_type is None: +/* 14287 */ // ^ +/* 14288 */ // +/* 14289 */ +/* 14290 */ $currLineNo = Sk.currLineNo = 414; +/* 14291 */ $currColNo = Sk.currColNo = 8; +/* 14292 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14293 */ var $loadname1015 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 14294 */ $ret = Sk.abstr.gattr($loadname1015, $scope1013.$const1016, true); +/* 14295 */ $blk = 2; /* allowing case fallthrough */ +/* 14296 */ case 2: +/* 14297 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14298 */ return $saveSuspension($ret, 'src/lib/traceback.py', 414, 11); +/* 14299 */ } +/* 14300 */ var $lattr1017 = $ret; +/* 14301 */ var $compareres1018 = null; +/* 14302 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($lattr1017, Sk.builtin.none.none$, 'Is', true)); +/* 14303 */ $blk = 4; /* allowing case fallthrough */ +/* 14304 */ case 4: +/* 14305 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14306 */ return $saveSuspension($ret, 'src/lib/traceback.py', 414, 11); +/* 14307 */ } +/* 14308 */ $compareres1018 = $ret; +/* 14309 */ var $jfalse1019 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 14310 */ if ($jfalse1019) { +/* 14311 */ /*test failed */ +/* 14312 */ $blk = 3; +/* 14313 */ continue; +/* 14314 */ } +/* 14315 */ $blk = 3; /* allowing case fallthrough */ +/* 14316 */ case 3: +/* 14317 */ /* --- done --- */ var $jfalse1020 = ($compareres1018 === false || !Sk.misceval.isTrue($compareres1018)); +/* 14318 */ if ($jfalse1020) { +/* 14319 */ /*test failed */ +/* 14320 */ $blk = 1; +/* 14321 */ continue; +/* 14322 */ } +/* 14323 */ // +/* 14324 */ // line 415: +/* 14325 */ // yield _format_final_exc_line(None, self._str) +/* 14326 */ // ^ +/* 14327 */ // +/* 14328 */ +/* 14329 */ $currLineNo = Sk.currLineNo = 415; +/* 14330 */ $currColNo = Sk.currColNo = 12; +/* 14331 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14332 */ var $loadgbl1021 = Sk.misceval.loadname('_format_final_exc_line', $gbl); +/* 14333 */ var $loadname1022 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 14334 */ $ret = Sk.abstr.gattr($loadname1022, $scope1013.$const1023, true); +/* 14335 */ $blk = 5; /* allowing case fallthrough */ +/* 14336 */ case 5: +/* 14337 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14338 */ return $saveSuspension($ret, 'src/lib/traceback.py', 415, 47); +/* 14339 */ } +/* 14340 */ var $lattr1024 = $ret; +/* 14341 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl1021, [Sk.builtin.none.none$, $lattr1024]); +/* 14342 */ $blk = 6; /* allowing case fallthrough */ +/* 14343 */ case 6: +/* 14344 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14345 */ return $saveSuspension($ret, 'src/lib/traceback.py', 415, 18); +/* 14346 */ } +/* 14347 */ var $call1025 = $ret; +/* 14348 */ // +/* 14349 */ // line 415: +/* 14350 */ // yield _format_final_exc_line(None, self._str) +/* 14351 */ // ^ +/* 14352 */ // +/* 14353 */ +/* 14354 */ $currLineNo = Sk.currLineNo = 415; +/* 14355 */ $currColNo = Sk.currColNo = 18; +/* 14356 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14357 */ return [ /*resume*/ 7, /*ret*/ $call1025]; +/* 14358 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 14359 */ case 1: +/* 14360 */ /* --- end of if --- */ +/* 14361 */ // +/* 14362 */ // line 418: +/* 14363 */ // stype = self.exc_type.__qualname__ +/* 14364 */ // ^ +/* 14365 */ // +/* 14366 */ +/* 14367 */ $currLineNo = Sk.currLineNo = 418; +/* 14368 */ $currColNo = Sk.currColNo = 8; +/* 14369 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14370 */ var $loadname1026 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 14371 */ $ret = Sk.abstr.gattr($loadname1026, $scope1013.$const1016, true); +/* 14372 */ $blk = 8; /* allowing case fallthrough */ +/* 14373 */ case 8: +/* 14374 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14375 */ return $saveSuspension($ret, 'src/lib/traceback.py', 418, 16); +/* 14376 */ } +/* 14377 */ var $lattr1027 = $ret; +/* 14378 */ $ret = Sk.abstr.gattr($lattr1027, $scope1013.$const1028, true); +/* 14379 */ $blk = 9; /* allowing case fallthrough */ +/* 14380 */ case 9: +/* 14381 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14382 */ return $saveSuspension($ret, 'src/lib/traceback.py', 418, 16); +/* 14383 */ } +/* 14384 */ var $lattr1029 = $ret; +/* 14385 */ $loc.stype = $lattr1029; +/* 14386 */ // +/* 14387 */ // line 419: +/* 14388 */ // smod = self.exc_type.__module__ +/* 14389 */ // ^ +/* 14390 */ // +/* 14391 */ +/* 14392 */ $currLineNo = Sk.currLineNo = 419; +/* 14393 */ $currColNo = Sk.currColNo = 8; +/* 14394 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14395 */ var $loadname1030 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 14396 */ $ret = Sk.abstr.gattr($loadname1030, $scope1013.$const1016, true); +/* 14397 */ $blk = 10; /* allowing case fallthrough */ +/* 14398 */ case 10: +/* 14399 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14400 */ return $saveSuspension($ret, 'src/lib/traceback.py', 419, 15); +/* 14401 */ } +/* 14402 */ var $lattr1031 = $ret; +/* 14403 */ $ret = Sk.abstr.gattr($lattr1031, $scope1013.$const1032, true); +/* 14404 */ $blk = 11; /* allowing case fallthrough */ +/* 14405 */ case 11: +/* 14406 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14407 */ return $saveSuspension($ret, 'src/lib/traceback.py', 419, 15); +/* 14408 */ } +/* 14409 */ var $lattr1033 = $ret; +/* 14410 */ $loc.smod = $lattr1033; +/* 14411 */ // +/* 14412 */ // line 420: +/* 14413 */ // if smod not in ("__main__", "builtins"): +/* 14414 */ // ^ +/* 14415 */ // +/* 14416 */ +/* 14417 */ $currLineNo = Sk.currLineNo = 420; +/* 14418 */ $currColNo = Sk.currColNo = 8; +/* 14419 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14420 */ var $loadname1034 = $loc.smod !== undefined ? $loc.smod : Sk.misceval.loadname('smod', $gbl);; +/* 14421 */ var $compareres1035 = null; +/* 14422 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($loadname1034, $scope1013.$const1038, 'NotIn', true)); +/* 14423 */ $blk = 14; /* allowing case fallthrough */ +/* 14424 */ case 14: +/* 14425 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14426 */ return $saveSuspension($ret, 'src/lib/traceback.py', 420, 11); +/* 14427 */ } +/* 14428 */ $compareres1035 = $ret; +/* 14429 */ var $jfalse1039 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 14430 */ if ($jfalse1039) { +/* 14431 */ /*test failed */ +/* 14432 */ $blk = 13; +/* 14433 */ continue; +/* 14434 */ } +/* 14435 */ $blk = 13; /* allowing case fallthrough */ +/* 14436 */ case 13: +/* 14437 */ /* --- done --- */ var $jfalse1040 = ($compareres1035 === false || !Sk.misceval.isTrue($compareres1035)); +/* 14438 */ if ($jfalse1040) { +/* 14439 */ /*test failed */ +/* 14440 */ $blk = 12; +/* 14441 */ continue; +/* 14442 */ } +/* 14443 */ // +/* 14444 */ // line 421: +/* 14445 */ // stype = smod + '.' + stype +/* 14446 */ // ^ +/* 14447 */ // +/* 14448 */ +/* 14449 */ $currLineNo = Sk.currLineNo = 421; +/* 14450 */ $currColNo = Sk.currColNo = 12; +/* 14451 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14452 */ var $loadname1041 = $loc.smod !== undefined ? $loc.smod : Sk.misceval.loadname('smod', $gbl);; +/* 14453 */ var $binop1043 = Sk.abstr.numberBinOp($loadname1041, $scope1013.$const1042, 'Add'); +/* 14454 */ var $loadname1044 = $loc.stype !== undefined ? $loc.stype : Sk.misceval.loadname('stype', $gbl);; +/* 14455 */ var $binop1045 = Sk.abstr.numberBinOp($binop1043, $loadname1044, 'Add'); +/* 14456 */ $loc.stype = $binop1045; +/* 14457 */ $blk = 12; /* allowing case fallthrough */ +/* 14458 */ case 12: +/* 14459 */ /* --- end of if --- */ +/* 14460 */ // +/* 14461 */ // line 423: +/* 14462 */ // if not issubclass(self.exc_type, SyntaxError): +/* 14463 */ // ^ +/* 14464 */ // +/* 14465 */ +/* 14466 */ $currLineNo = Sk.currLineNo = 423; +/* 14467 */ $currColNo = Sk.currColNo = 8; +/* 14468 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14469 */ var $loadgbl1046 = Sk.misceval.loadname('issubclass', $gbl); +/* 14470 */ var $loadname1047 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 14471 */ $ret = Sk.abstr.gattr($loadname1047, $scope1013.$const1016, true); +/* 14472 */ $blk = 16; /* allowing case fallthrough */ +/* 14473 */ case 16: +/* 14474 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14475 */ return $saveSuspension($ret, 'src/lib/traceback.py', 423, 26); +/* 14476 */ } +/* 14477 */ var $lattr1048 = $ret; +/* 14478 */ var $loadgbl1049 = Sk.misceval.loadname('SyntaxError', $gbl); +/* 14479 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl1046, [$lattr1048, $loadgbl1049]); +/* 14480 */ $blk = 17; /* allowing case fallthrough */ +/* 14481 */ case 17: +/* 14482 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14483 */ return $saveSuspension($ret, 'src/lib/traceback.py', 423, 15); +/* 14484 */ } +/* 14485 */ var $call1050 = $ret; +/* 14486 */ // +/* 14487 */ // line 423: +/* 14488 */ // if not issubclass(self.exc_type, SyntaxError): +/* 14489 */ // ^ +/* 14490 */ // +/* 14491 */ +/* 14492 */ $currLineNo = Sk.currLineNo = 423; +/* 14493 */ $currColNo = Sk.currColNo = 15; +/* 14494 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14495 */ var $unaryop1051 = Sk.abstr.numberUnaryOp($call1050, 'Not'); +/* 14496 */ var $jfalse1052 = ($unaryop1051 === false || !Sk.misceval.isTrue($unaryop1051)); +/* 14497 */ if ($jfalse1052) { +/* 14498 */ /*test failed */ +/* 14499 */ $blk = 15; +/* 14500 */ continue; +/* 14501 */ } +/* 14502 */ // +/* 14503 */ // line 424: +/* 14504 */ // yield _format_final_exc_line(stype, self._str) +/* 14505 */ // ^ +/* 14506 */ // +/* 14507 */ +/* 14508 */ $currLineNo = Sk.currLineNo = 424; +/* 14509 */ $currColNo = Sk.currColNo = 12; +/* 14510 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14511 */ var $loadgbl1053 = Sk.misceval.loadname('_format_final_exc_line', $gbl); +/* 14512 */ var $loadname1054 = $loc.stype !== undefined ? $loc.stype : Sk.misceval.loadname('stype', $gbl);; +/* 14513 */ var $loadname1055 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 14514 */ $ret = Sk.abstr.gattr($loadname1055, $scope1013.$const1023, true); +/* 14515 */ $blk = 18; /* allowing case fallthrough */ +/* 14516 */ case 18: +/* 14517 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14518 */ return $saveSuspension($ret, 'src/lib/traceback.py', 424, 48); +/* 14519 */ } +/* 14520 */ var $lattr1056 = $ret; +/* 14521 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl1053, [$loadname1054, $lattr1056]); +/* 14522 */ $blk = 19; /* allowing case fallthrough */ +/* 14523 */ case 19: +/* 14524 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14525 */ return $saveSuspension($ret, 'src/lib/traceback.py', 424, 18); +/* 14526 */ } +/* 14527 */ var $call1057 = $ret; +/* 14528 */ // +/* 14529 */ // line 424: +/* 14530 */ // yield _format_final_exc_line(stype, self._str) +/* 14531 */ // ^ +/* 14532 */ // +/* 14533 */ +/* 14534 */ $currLineNo = Sk.currLineNo = 424; +/* 14535 */ $currColNo = Sk.currColNo = 18; +/* 14536 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14537 */ return [ /*resume*/ 20, /*ret*/ $call1057]; +/* 14538 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 14539 */ case 7: +/* 14540 */ /* --- after yield --- */ +/* 14541 */ // +/* 14542 */ // line 416: +/* 14543 */ // return +/* 14544 */ // ^ +/* 14545 */ // +/* 14546 */ +/* 14547 */ $currLineNo = Sk.currLineNo = 416; +/* 14548 */ $currColNo = Sk.currColNo = 12; +/* 14549 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14550 */ return Sk.builtin.none.none$; +/* 14551 */ $blk = 1; /* jump */ +/* 14552 */ continue; +/* 14553 */ case 15: +/* 14554 */ /* --- end of if --- */ +/* 14555 */ // +/* 14556 */ // line 428: +/* 14557 */ // filename = self.filename or "" +/* 14558 */ // ^ +/* 14559 */ // +/* 14560 */ +/* 14561 */ $currLineNo = Sk.currLineNo = 428; +/* 14562 */ $currColNo = Sk.currColNo = 8; +/* 14563 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14564 */ var $loadname1058 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 14565 */ $ret = Sk.abstr.gattr($loadname1058, $scope1013.$const1059, true); +/* 14566 */ $blk = 22; /* allowing case fallthrough */ +/* 14567 */ case 22: +/* 14568 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14569 */ return $saveSuspension($ret, 'src/lib/traceback.py', 428, 19); +/* 14570 */ } +/* 14571 */ var $lattr1060 = $ret; +/* 14572 */ var $boolopsucc1061 = $lattr1060; +/* 14573 */ $boolopsucc1061 = $lattr1060; +/* 14574 */ var $jtrue1062 = ($lattr1060 === true || Sk.misceval.isTrue($lattr1060)); +/* 14575 */ if ($jtrue1062) { +/* 14576 */ /*test passed */ +/* 14577 */ $blk = 21; +/* 14578 */ continue; +/* 14579 */ } +/* 14580 */ $boolopsucc1061 = $scope1013.$const1063; +/* 14581 */ var $jtrue1064 = ($scope1013.$const1063 === true || Sk.misceval.isTrue($scope1013.$const1063)); +/* 14582 */ if ($jtrue1064) { +/* 14583 */ /*test passed */ +/* 14584 */ $blk = 21; +/* 14585 */ continue; +/* 14586 */ } +/* 14587 */ $blk = 21; /* allowing case fallthrough */ +/* 14588 */ case 21: +/* 14589 */ /* --- end of boolop --- */ $loc.filename = $boolopsucc1061; +/* 14590 */ // +/* 14591 */ // line 429: +/* 14592 */ // lineno = str(self.lineno) or '?' +/* 14593 */ // ^ +/* 14594 */ // +/* 14595 */ +/* 14596 */ $currLineNo = Sk.currLineNo = 429; +/* 14597 */ $currColNo = Sk.currColNo = 8; +/* 14598 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14599 */ var $loadgbl1065 = Sk.misceval.loadname('str', $gbl); +/* 14600 */ var $loadname1066 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 14601 */ $ret = Sk.abstr.gattr($loadname1066, $scope1013.$const1067, true); +/* 14602 */ $blk = 24; /* allowing case fallthrough */ +/* 14603 */ case 24: +/* 14604 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14605 */ return $saveSuspension($ret, 'src/lib/traceback.py', 429, 21); +/* 14606 */ } +/* 14607 */ var $lattr1068 = $ret; +/* 14608 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl1065, [$lattr1068]); +/* 14609 */ $blk = 25; /* allowing case fallthrough */ +/* 14610 */ case 25: +/* 14611 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14612 */ return $saveSuspension($ret, 'src/lib/traceback.py', 429, 17); +/* 14613 */ } +/* 14614 */ var $call1069 = $ret; +/* 14615 */ // +/* 14616 */ // line 429: +/* 14617 */ // lineno = str(self.lineno) or '?' +/* 14618 */ // ^ +/* 14619 */ // +/* 14620 */ +/* 14621 */ $currLineNo = Sk.currLineNo = 429; +/* 14622 */ $currColNo = Sk.currColNo = 17; +/* 14623 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14624 */ var $boolopsucc1070 = $call1069; +/* 14625 */ $boolopsucc1070 = $call1069; +/* 14626 */ var $jtrue1071 = ($call1069 === true || Sk.misceval.isTrue($call1069)); +/* 14627 */ if ($jtrue1071) { +/* 14628 */ /*test passed */ +/* 14629 */ $blk = 23; +/* 14630 */ continue; +/* 14631 */ } +/* 14632 */ $boolopsucc1070 = $scope1013.$const1072; +/* 14633 */ var $jtrue1073 = ($scope1013.$const1072 === true || Sk.misceval.isTrue($scope1013.$const1072)); +/* 14634 */ if ($jtrue1073) { +/* 14635 */ /*test passed */ +/* 14636 */ $blk = 23; +/* 14637 */ continue; +/* 14638 */ } +/* 14639 */ $blk = 23; /* allowing case fallthrough */ +/* 14640 */ case 23: +/* 14641 */ /* --- end of boolop --- */ $loc.lineno = $boolopsucc1070; +/* 14642 */ // +/* 14643 */ // line 430: +/* 14644 */ // yield ' File "{}", line {}\n'.format(filename, lineno) +/* 14645 */ // ^ +/* 14646 */ // +/* 14647 */ +/* 14648 */ $currLineNo = Sk.currLineNo = 430; +/* 14649 */ $currColNo = Sk.currColNo = 8; +/* 14650 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14651 */ $ret = Sk.abstr.gattr($scope1013.$const1074, $scope1013.$const1075, true); +/* 14652 */ $blk = 26; /* allowing case fallthrough */ +/* 14653 */ case 26: +/* 14654 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14655 */ return $saveSuspension($ret, 'src/lib/traceback.py', 430, 14); +/* 14656 */ } +/* 14657 */ var $lattr1076 = $ret; +/* 14658 */ var $loadname1077 = $loc.filename !== undefined ? $loc.filename : Sk.misceval.loadname('filename', $gbl);; +/* 14659 */ var $loadname1078 = $loc.lineno !== undefined ? $loc.lineno : Sk.misceval.loadname('lineno', $gbl);; +/* 14660 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr1076, [$loadname1077, $loadname1078]); +/* 14661 */ $blk = 27; /* allowing case fallthrough */ +/* 14662 */ case 27: +/* 14663 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14664 */ return $saveSuspension($ret, 'src/lib/traceback.py', 430, 14); +/* 14665 */ } +/* 14666 */ var $call1079 = $ret; +/* 14667 */ // +/* 14668 */ // line 430: +/* 14669 */ // yield ' File "{}", line {}\n'.format(filename, lineno) +/* 14670 */ // ^ +/* 14671 */ // +/* 14672 */ +/* 14673 */ $currLineNo = Sk.currLineNo = 430; +/* 14674 */ $currColNo = Sk.currColNo = 14; +/* 14675 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14676 */ return [ /*resume*/ 28, /*ret*/ $call1079]; +/* 14677 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 14678 */ case 20: +/* 14679 */ /* --- after yield --- */ +/* 14680 */ // +/* 14681 */ // line 425: +/* 14682 */ // return +/* 14683 */ // ^ +/* 14684 */ // +/* 14685 */ +/* 14686 */ $currLineNo = Sk.currLineNo = 425; +/* 14687 */ $currColNo = Sk.currColNo = 12; +/* 14688 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14689 */ return Sk.builtin.none.none$; +/* 14690 */ $blk = 15; /* jump */ +/* 14691 */ continue; +/* 14692 */ case 28: +/* 14693 */ /* --- after yield --- */ +/* 14694 */ // +/* 14695 */ // line 432: +/* 14696 */ // badline = self.text +/* 14697 */ // ^ +/* 14698 */ // +/* 14699 */ +/* 14700 */ $currLineNo = Sk.currLineNo = 432; +/* 14701 */ $currColNo = Sk.currColNo = 8; +/* 14702 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14703 */ var $loadname1080 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 14704 */ $ret = Sk.abstr.gattr($loadname1080, $scope1013.$const1081, true); +/* 14705 */ $blk = 29; /* allowing case fallthrough */ +/* 14706 */ case 29: +/* 14707 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14708 */ return $saveSuspension($ret, 'src/lib/traceback.py', 432, 18); +/* 14709 */ } +/* 14710 */ var $lattr1082 = $ret; +/* 14711 */ $loc.badline = $lattr1082; +/* 14712 */ // +/* 14713 */ // line 433: +/* 14714 */ // offset = self.offset +/* 14715 */ // ^ +/* 14716 */ // +/* 14717 */ +/* 14718 */ $currLineNo = Sk.currLineNo = 433; +/* 14719 */ $currColNo = Sk.currColNo = 8; +/* 14720 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14721 */ var $loadname1083 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 14722 */ $ret = Sk.abstr.gattr($loadname1083, $scope1013.$const1084, true); +/* 14723 */ $blk = 30; /* allowing case fallthrough */ +/* 14724 */ case 30: +/* 14725 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14726 */ return $saveSuspension($ret, 'src/lib/traceback.py', 433, 17); +/* 14727 */ } +/* 14728 */ var $lattr1085 = $ret; +/* 14729 */ $loc.offset = $lattr1085; +/* 14730 */ // +/* 14731 */ // line 434: +/* 14732 */ // if badline is not None: +/* 14733 */ // ^ +/* 14734 */ // +/* 14735 */ +/* 14736 */ $currLineNo = Sk.currLineNo = 434; +/* 14737 */ $currColNo = Sk.currColNo = 8; +/* 14738 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14739 */ var $loadname1086 = $loc.badline !== undefined ? $loc.badline : Sk.misceval.loadname('badline', $gbl);; +/* 14740 */ var $compareres1087 = null; +/* 14741 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($loadname1086, Sk.builtin.none.none$, 'IsNot', true)); +/* 14742 */ $blk = 33; /* allowing case fallthrough */ +/* 14743 */ case 33: +/* 14744 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14745 */ return $saveSuspension($ret, 'src/lib/traceback.py', 434, 11); +/* 14746 */ } +/* 14747 */ $compareres1087 = $ret; +/* 14748 */ var $jfalse1088 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 14749 */ if ($jfalse1088) { +/* 14750 */ /*test failed */ +/* 14751 */ $blk = 32; +/* 14752 */ continue; +/* 14753 */ } +/* 14754 */ $blk = 32; /* allowing case fallthrough */ +/* 14755 */ case 32: +/* 14756 */ /* --- done --- */ var $jfalse1089 = ($compareres1087 === false || !Sk.misceval.isTrue($compareres1087)); +/* 14757 */ if ($jfalse1089) { +/* 14758 */ /*test failed */ +/* 14759 */ $blk = 31; +/* 14760 */ continue; +/* 14761 */ } +/* 14762 */ // +/* 14763 */ // line 435: +/* 14764 */ // yield ' {}\n'.format(badline.strip()) +/* 14765 */ // ^ +/* 14766 */ // +/* 14767 */ +/* 14768 */ $currLineNo = Sk.currLineNo = 435; +/* 14769 */ $currColNo = Sk.currColNo = 12; +/* 14770 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14771 */ $ret = Sk.abstr.gattr($scope1013.$const1090, $scope1013.$const1075, true); +/* 14772 */ $blk = 34; /* allowing case fallthrough */ +/* 14773 */ case 34: +/* 14774 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14775 */ return $saveSuspension($ret, 'src/lib/traceback.py', 435, 18); +/* 14776 */ } +/* 14777 */ var $lattr1091 = $ret; +/* 14778 */ var $loadname1092 = $loc.badline !== undefined ? $loc.badline : Sk.misceval.loadname('badline', $gbl);; +/* 14779 */ $ret = Sk.abstr.gattr($loadname1092, $scope1013.$const1093, true); +/* 14780 */ $blk = 35; /* allowing case fallthrough */ +/* 14781 */ case 35: +/* 14782 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14783 */ return $saveSuspension($ret, 'src/lib/traceback.py', 435, 36); +/* 14784 */ } +/* 14785 */ var $lattr1094 = $ret; +/* 14786 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr1094); +/* 14787 */ $blk = 36; /* allowing case fallthrough */ +/* 14788 */ case 36: +/* 14789 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14790 */ return $saveSuspension($ret, 'src/lib/traceback.py', 435, 36); +/* 14791 */ } +/* 14792 */ var $call1095 = $ret; +/* 14793 */ // +/* 14794 */ // line 435: +/* 14795 */ // yield ' {}\n'.format(badline.strip()) +/* 14796 */ // ^ +/* 14797 */ // +/* 14798 */ +/* 14799 */ $currLineNo = Sk.currLineNo = 435; +/* 14800 */ $currColNo = Sk.currColNo = 36; +/* 14801 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14802 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr1091, [$call1095]); +/* 14803 */ $blk = 37; /* allowing case fallthrough */ +/* 14804 */ case 37: +/* 14805 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14806 */ return $saveSuspension($ret, 'src/lib/traceback.py', 435, 18); +/* 14807 */ } +/* 14808 */ var $call1096 = $ret; +/* 14809 */ // +/* 14810 */ // line 435: +/* 14811 */ // yield ' {}\n'.format(badline.strip()) +/* 14812 */ // ^ +/* 14813 */ // +/* 14814 */ +/* 14815 */ $currLineNo = Sk.currLineNo = 435; +/* 14816 */ $currColNo = Sk.currColNo = 18; +/* 14817 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14818 */ return [ /*resume*/ 38, /*ret*/ $call1096]; +/* 14819 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 14820 */ case 31: +/* 14821 */ /* --- end of if --- */ +/* 14822 */ // +/* 14823 */ // line 443: +/* 14824 */ // msg = self.msg or "" +/* 14825 */ // ^ +/* 14826 */ // +/* 14827 */ +/* 14828 */ $currLineNo = Sk.currLineNo = 443; +/* 14829 */ $currColNo = Sk.currColNo = 8; +/* 14830 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14831 */ var $loadname1147 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 14832 */ $ret = Sk.abstr.gattr($loadname1147, $scope1013.$const1148, true); +/* 14833 */ $blk = 55; /* allowing case fallthrough */ +/* 14834 */ case 55: +/* 14835 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14836 */ return $saveSuspension($ret, 'src/lib/traceback.py', 443, 14); +/* 14837 */ } +/* 14838 */ var $lattr1149 = $ret; +/* 14839 */ var $boolopsucc1150 = $lattr1149; +/* 14840 */ $boolopsucc1150 = $lattr1149; +/* 14841 */ var $jtrue1151 = ($lattr1149 === true || Sk.misceval.isTrue($lattr1149)); +/* 14842 */ if ($jtrue1151) { +/* 14843 */ /*test passed */ +/* 14844 */ $blk = 54; +/* 14845 */ continue; +/* 14846 */ } +/* 14847 */ $boolopsucc1150 = $scope1013.$const1152; +/* 14848 */ var $jtrue1153 = ($scope1013.$const1152 === true || Sk.misceval.isTrue($scope1013.$const1152)); +/* 14849 */ if ($jtrue1153) { +/* 14850 */ /*test passed */ +/* 14851 */ $blk = 54; +/* 14852 */ continue; +/* 14853 */ } +/* 14854 */ $blk = 54; /* allowing case fallthrough */ +/* 14855 */ case 54: +/* 14856 */ /* --- end of boolop --- */ $loc.msg = $boolopsucc1150; +/* 14857 */ // +/* 14858 */ // line 444: +/* 14859 */ // yield "{}: {}\n".format(stype, msg) +/* 14860 */ // ^ +/* 14861 */ // +/* 14862 */ +/* 14863 */ $currLineNo = Sk.currLineNo = 444; +/* 14864 */ $currColNo = Sk.currColNo = 8; +/* 14865 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14866 */ $ret = Sk.abstr.gattr($scope1013.$const1154, $scope1013.$const1075, true); +/* 14867 */ $blk = 56; /* allowing case fallthrough */ +/* 14868 */ case 56: +/* 14869 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14870 */ return $saveSuspension($ret, 'src/lib/traceback.py', 444, 14); +/* 14871 */ } +/* 14872 */ var $lattr1155 = $ret; +/* 14873 */ var $loadname1156 = $loc.stype !== undefined ? $loc.stype : Sk.misceval.loadname('stype', $gbl);; +/* 14874 */ var $loadname1157 = $loc.msg !== undefined ? $loc.msg : Sk.misceval.loadname('msg', $gbl);; +/* 14875 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr1155, [$loadname1156, $loadname1157]); +/* 14876 */ $blk = 57; /* allowing case fallthrough */ +/* 14877 */ case 57: +/* 14878 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14879 */ return $saveSuspension($ret, 'src/lib/traceback.py', 444, 14); +/* 14880 */ } +/* 14881 */ var $call1158 = $ret; +/* 14882 */ // +/* 14883 */ // line 444: +/* 14884 */ // yield "{}: {}\n".format(stype, msg) +/* 14885 */ // ^ +/* 14886 */ // +/* 14887 */ +/* 14888 */ $currLineNo = Sk.currLineNo = 444; +/* 14889 */ $currColNo = Sk.currColNo = 14; +/* 14890 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14891 */ return [ /*resume*/ 58, /*ret*/ $call1158]; +/* 14892 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 14893 */ case 38: +/* 14894 */ /* --- after yield --- */ +/* 14895 */ // +/* 14896 */ // line 436: +/* 14897 */ // if offset is not None: +/* 14898 */ // ^ +/* 14899 */ // +/* 14900 */ +/* 14901 */ $currLineNo = Sk.currLineNo = 436; +/* 14902 */ $currColNo = Sk.currColNo = 12; +/* 14903 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14904 */ var $loadname1097 = $loc.offset !== undefined ? $loc.offset : Sk.misceval.loadname('offset', $gbl);; +/* 14905 */ var $compareres1098 = null; +/* 14906 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($loadname1097, Sk.builtin.none.none$, 'IsNot', true)); +/* 14907 */ $blk = 41; /* allowing case fallthrough */ +/* 14908 */ case 41: +/* 14909 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14910 */ return $saveSuspension($ret, 'src/lib/traceback.py', 436, 15); +/* 14911 */ } +/* 14912 */ $compareres1098 = $ret; +/* 14913 */ var $jfalse1099 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 14914 */ if ($jfalse1099) { +/* 14915 */ /*test failed */ +/* 14916 */ $blk = 40; +/* 14917 */ continue; +/* 14918 */ } +/* 14919 */ $blk = 40; /* allowing case fallthrough */ +/* 14920 */ case 40: +/* 14921 */ /* --- done --- */ var $jfalse1100 = ($compareres1098 === false || !Sk.misceval.isTrue($compareres1098)); +/* 14922 */ if ($jfalse1100) { +/* 14923 */ /*test failed */ +/* 14924 */ $blk = 39; +/* 14925 */ continue; +/* 14926 */ } +/* 14927 */ // +/* 14928 */ // line 437: +/* 14929 */ // caretspace = badline.rstrip('\n') +/* 14930 */ // ^ +/* 14931 */ // +/* 14932 */ +/* 14933 */ $currLineNo = Sk.currLineNo = 437; +/* 14934 */ $currColNo = Sk.currColNo = 16; +/* 14935 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14936 */ var $loadname1101 = $loc.badline !== undefined ? $loc.badline : Sk.misceval.loadname('badline', $gbl);; +/* 14937 */ $ret = Sk.abstr.gattr($loadname1101, $scope1013.$const1102, true); +/* 14938 */ $blk = 42; /* allowing case fallthrough */ +/* 14939 */ case 42: +/* 14940 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14941 */ return $saveSuspension($ret, 'src/lib/traceback.py', 437, 29); +/* 14942 */ } +/* 14943 */ var $lattr1103 = $ret; +/* 14944 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr1103, [$scope1013.$const1104]); +/* 14945 */ $blk = 43; /* allowing case fallthrough */ +/* 14946 */ case 43: +/* 14947 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14948 */ return $saveSuspension($ret, 'src/lib/traceback.py', 437, 29); +/* 14949 */ } +/* 14950 */ var $call1105 = $ret; +/* 14951 */ // +/* 14952 */ // line 437: +/* 14953 */ // caretspace = badline.rstrip('\n') +/* 14954 */ // ^ +/* 14955 */ // +/* 14956 */ +/* 14957 */ $currLineNo = Sk.currLineNo = 437; +/* 14958 */ $currColNo = Sk.currColNo = 29; +/* 14959 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14960 */ $loc.caretspace = $call1105; +/* 14961 */ // +/* 14962 */ // line 438: +/* 14963 */ // offset = min(len(caretspace), offset) - 1 +/* 14964 */ // ^ +/* 14965 */ // +/* 14966 */ +/* 14967 */ $currLineNo = Sk.currLineNo = 438; +/* 14968 */ $currColNo = Sk.currColNo = 16; +/* 14969 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14970 */ var $loadgbl1106 = Sk.misceval.loadname('min', $gbl); +/* 14971 */ var $loadgbl1107 = Sk.misceval.loadname('len', $gbl); +/* 14972 */ var $loadname1108 = $loc.caretspace !== undefined ? $loc.caretspace : Sk.misceval.loadname('caretspace', $gbl);; +/* 14973 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl1107, [$loadname1108]); +/* 14974 */ $blk = 44; /* allowing case fallthrough */ +/* 14975 */ case 44: +/* 14976 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14977 */ return $saveSuspension($ret, 'src/lib/traceback.py', 438, 29); +/* 14978 */ } +/* 14979 */ var $call1109 = $ret; +/* 14980 */ // +/* 14981 */ // line 438: +/* 14982 */ // offset = min(len(caretspace), offset) - 1 +/* 14983 */ // ^ +/* 14984 */ // +/* 14985 */ +/* 14986 */ $currLineNo = Sk.currLineNo = 438; +/* 14987 */ $currColNo = Sk.currColNo = 29; +/* 14988 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 14989 */ var $loadname1110 = $loc.offset !== undefined ? $loc.offset : Sk.misceval.loadname('offset', $gbl);; +/* 14990 */ $ret = Sk.misceval.callsimOrSuspendArray($loadgbl1106, [$call1109, $loadname1110]); +/* 14991 */ $blk = 45; /* allowing case fallthrough */ +/* 14992 */ case 45: +/* 14993 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 14994 */ return $saveSuspension($ret, 'src/lib/traceback.py', 438, 25); +/* 14995 */ } +/* 14996 */ var $call1111 = $ret; +/* 14997 */ // +/* 14998 */ // line 438: +/* 14999 */ // offset = min(len(caretspace), offset) - 1 +/* 15000 */ // ^ +/* 15001 */ // +/* 15002 */ +/* 15003 */ $currLineNo = Sk.currLineNo = 438; +/* 15004 */ $currColNo = Sk.currColNo = 25; +/* 15005 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15006 */ var $binop1113 = Sk.abstr.numberBinOp($call1111, $scope1013.$const1112, 'Sub'); +/* 15007 */ $loc.offset = $binop1113; +/* 15008 */ // +/* 15009 */ // line 439: +/* 15010 */ // caretspace = caretspace[:offset].lstrip() +/* 15011 */ // ^ +/* 15012 */ // +/* 15013 */ +/* 15014 */ $currLineNo = Sk.currLineNo = 439; +/* 15015 */ $currColNo = Sk.currColNo = 16; +/* 15016 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15017 */ var $loadname1114 = $loc.caretspace !== undefined ? $loc.caretspace : Sk.misceval.loadname('caretspace', $gbl);; +/* 15018 */ var $loadname1115 = $loc.offset !== undefined ? $loc.offset : Sk.misceval.loadname('offset', $gbl);; +/* 15019 */ var $slice1116 = new Sk.builtins['slice'](Sk.builtin.none.none$, $loadname1115, Sk.builtin.none.none$); +/* 15020 */ $ret = Sk.abstr.objectGetItem($loadname1114, $slice1116, true); +/* 15021 */ $blk = 46; /* allowing case fallthrough */ +/* 15022 */ case 46: +/* 15023 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15024 */ return $saveSuspension($ret, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 15025 */ } +/* 15026 */ var $lsubscr1117 = $ret; +/* 15027 */ $ret = Sk.abstr.gattr($lsubscr1117, $scope1013.$const1118, true); +/* 15028 */ $blk = 47; /* allowing case fallthrough */ +/* 15029 */ case 47: +/* 15030 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15031 */ return $saveSuspension($ret, 'src/lib/traceback.py', 439, 29); +/* 15032 */ } +/* 15033 */ var $lattr1119 = $ret; +/* 15034 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr1119); +/* 15035 */ $blk = 48; /* allowing case fallthrough */ +/* 15036 */ case 48: +/* 15037 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15038 */ return $saveSuspension($ret, 'src/lib/traceback.py', 439, 29); +/* 15039 */ } +/* 15040 */ var $call1120 = $ret; +/* 15041 */ // +/* 15042 */ // line 439: +/* 15043 */ // caretspace = caretspace[:offset].lstrip() +/* 15044 */ // ^ +/* 15045 */ // +/* 15046 */ +/* 15047 */ $currLineNo = Sk.currLineNo = 439; +/* 15048 */ $currColNo = Sk.currColNo = 29; +/* 15049 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15050 */ $loc.caretspace = $call1120; +/* 15051 */ // +/* 15052 */ // line 441: +/* 15053 */ // caretspace = ((c.isspace() and c or ' ') for c in caretspace) +/* 15054 */ // ^ +/* 15055 */ // +/* 15056 */ +/* 15057 */ $currLineNo = Sk.currLineNo = 441; +/* 15058 */ $currColNo = Sk.currColNo = 16; +/* 15059 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15060 */ $scope1121.co_name = new Sk.builtins['str'](''); +/* 15061 */ $scope1121.co_varnames = []; +/* 15062 */ var $gener1136 = new Sk.builtins['function']((function() { +/* 15063 */ Sk.builtin.pyCheckArgsLen("", arguments.length, 0, 0); +/* 15064 */ return new Sk.builtins['generator']($scope1121, $gbl, []); +/* 15065 */ })); +/* 15066 */ var $gener1137 = Sk.misceval.callsimArray($gener1136);; +/* 15067 */ var $loadname1138 = $loc.caretspace !== undefined ? $loc.caretspace : Sk.misceval.loadname('caretspace', $gbl);; +/* 15068 */ $gener1137.gi$locals.$iter0 = Sk.abstr.iter($loadname1138); +/* 15069 */ $loc.caretspace = $gener1137; +/* 15070 */ // +/* 15071 */ // line 442: +/* 15072 */ // yield ' {}^\n'.format(''.join(caretspace)) +/* 15073 */ // ^ +/* 15074 */ // +/* 15075 */ +/* 15076 */ $currLineNo = Sk.currLineNo = 442; +/* 15077 */ $currColNo = Sk.currColNo = 16; +/* 15078 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15079 */ $ret = Sk.abstr.gattr($scope1013.$const1139, $scope1013.$const1075, true); +/* 15080 */ $blk = 49; /* allowing case fallthrough */ +/* 15081 */ case 49: +/* 15082 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15083 */ return $saveSuspension($ret, 'src/lib/traceback.py', 442, 22); +/* 15084 */ } +/* 15085 */ var $lattr1140 = $ret; +/* 15086 */ $ret = Sk.abstr.gattr($scope1013.$const1141, $scope1013.$const1142, true); +/* 15087 */ $blk = 50; /* allowing case fallthrough */ +/* 15088 */ case 50: +/* 15089 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15090 */ return $saveSuspension($ret, 'src/lib/traceback.py', 442, 41); +/* 15091 */ } +/* 15092 */ var $lattr1143 = $ret; +/* 15093 */ var $loadname1144 = $loc.caretspace !== undefined ? $loc.caretspace : Sk.misceval.loadname('caretspace', $gbl);; +/* 15094 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr1143, [$loadname1144]); +/* 15095 */ $blk = 51; /* allowing case fallthrough */ +/* 15096 */ case 51: +/* 15097 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15098 */ return $saveSuspension($ret, 'src/lib/traceback.py', 442, 41); +/* 15099 */ } +/* 15100 */ var $call1145 = $ret; +/* 15101 */ // +/* 15102 */ // line 442: +/* 15103 */ // yield ' {}^\n'.format(''.join(caretspace)) +/* 15104 */ // ^ +/* 15105 */ // +/* 15106 */ +/* 15107 */ $currLineNo = Sk.currLineNo = 442; +/* 15108 */ $currColNo = Sk.currColNo = 41; +/* 15109 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15110 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr1140, [$call1145]); +/* 15111 */ $blk = 52; /* allowing case fallthrough */ +/* 15112 */ case 52: +/* 15113 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15114 */ return $saveSuspension($ret, 'src/lib/traceback.py', 442, 22); +/* 15115 */ } +/* 15116 */ var $call1146 = $ret; +/* 15117 */ // +/* 15118 */ // line 442: +/* 15119 */ // yield ' {}^\n'.format(''.join(caretspace)) +/* 15120 */ // ^ +/* 15121 */ // +/* 15122 */ +/* 15123 */ $currLineNo = Sk.currLineNo = 442; +/* 15124 */ $currColNo = Sk.currColNo = 22; +/* 15125 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15126 */ return [ /*resume*/ 53, /*ret*/ $call1146]; +/* 15127 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 15128 */ case 39: +/* 15129 */ /* --- end of if --- */ $blk = 31; /* jump */ +/* 15130 */ continue; +/* 15131 */ case 53: +/* 15132 */ /* --- after yield --- */ $blk = 39; /* jump */ +/* 15133 */ continue; +/* 15134 */ case 58: +/* 15135 */ /* --- after yield --- */ return Sk.builtin.none.none$; +/* 15136 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 15137 */ } +/* 15138 */ } catch (err) { +/* 15139 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 15140 */ Sk.execStart = Date.now(); +/* 15141 */ Sk.execPaused = 0 +/* 15142 */ } +/* 15143 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 15144 */ err = new Sk.builtin.ExternalError(err); +/* 15145 */ } +/* 15146 */ Sk.err = err; +/* 15147 */ err.traceback.push({ +/* 15148 */ lineno: $currLineNo, +/* 15149 */ colno: $currColNo, +/* 15150 */ filename: 'src/lib/traceback.py', +/* 15151 */ scope: 'format_exception_only' +/* 15152 */ }); +/* 15153 */ if ($exc.length > 0) { +/* 15154 */ $err = err; +/* 15155 */ $blk = $exc.pop(); +/* 15156 */ continue +/* 15157 */ } else { +/* 15158 */ throw err; +/* 15159 */ } +/* 15160 */ } +/* 15161 */ } +/* 15162 */ }); +/* 15163 */ $scope1013.$const1016 = new Sk.builtin.str('exc_type'); +/* 15164 */ $scope1013.$const1023 = new Sk.builtin.str('_str'); +/* 15165 */ $scope1013.$const1028 = new Sk.builtin.str('__qualname__'); +/* 15166 */ $scope1013.$const1032 = new Sk.builtin.str('__module__'); +/* 15167 */ $scope1013.$const1036 = new Sk.builtin.str('__main__'); +/* 15168 */ $scope1013.$const1037 = new Sk.builtin.str('builtins'); +/* 15169 */ $scope1013.$const1038 = new Sk.builtin.tuple([$scope1013.$const1036, $scope1013.$const1037]); +/* 15170 */ $scope1013.$const1042 = new Sk.builtin.str('.'); +/* 15171 */ $scope1013.$const1059 = new Sk.builtin.str('filename'); +/* 15172 */ $scope1013.$const1063 = new Sk.builtin.str(''); +/* 15173 */ $scope1013.$const1067 = new Sk.builtin.str('lineno'); +/* 15174 */ $scope1013.$const1072 = new Sk.builtin.str('?'); +/* 15175 */ $scope1013.$const1074 = new Sk.builtin.str(' File "{}", line {}\n'); +/* 15176 */ $scope1013.$const1075 = new Sk.builtin.str('format'); +/* 15177 */ $scope1013.$const1081 = new Sk.builtin.str('text'); +/* 15178 */ $scope1013.$const1084 = new Sk.builtin.str('offset'); +/* 15179 */ $scope1013.$const1090 = new Sk.builtin.str(' {}\n'); +/* 15180 */ $scope1013.$const1093 = new Sk.builtin.str('strip'); +/* 15181 */ $scope1013.$const1102 = new Sk.builtin.str('rstrip'); +/* 15182 */ $scope1013.$const1104 = new Sk.builtin.str('\n'); +/* 15183 */ $scope1013.$const1112 = new Sk.builtin.int_(1); +/* 15184 */ $scope1013.$const1118 = new Sk.builtin.str('lstrip'); +/* 15185 */ $scope1013.$const1139 = new Sk.builtin.str(' {}^\n'); +/* 15186 */ $scope1013.$const1141 = new Sk.builtin.str(''); +/* 15187 */ $scope1013.$const1142 = new Sk.builtin.str('join'); +/* 15188 */ $scope1013.$const1148 = new Sk.builtin.str('msg'); +/* 15189 */ $scope1013.$const1152 = new Sk.builtin.str(''); +/* 15190 */ $scope1013.$const1154 = new Sk.builtin.str('{}: {}\n'); +/* 15191 */ var $scope1121 = (function $genexpr1122$($gen) { +/* 15192 */ // generator +/* 15193 */ var $next1123, $loadname1124, $next1123, $loadname1124, $lattr1126; +/* 15194 */ var $wakeFromSuspension = function() { +/* 15195 */ var susp = $scope1121.$wakingSuspension; +/* 15196 */ $scope1121.$wakingSuspension = undefined; +/* 15197 */ $blk = susp.$blk; +/* 15198 */ $loc = susp.$loc; +/* 15199 */ $gbl = susp.$gbl; +/* 15200 */ $exc = susp.$exc; +/* 15201 */ $err = susp.$err; +/* 15202 */ $postfinally = susp.$postfinally; +/* 15203 */ $currLineNo = susp.$lineno; +/* 15204 */ $currColNo = susp.$colno; +/* 15205 */ Sk.lastYield = Date.now(); +/* 15206 */ $next1123 = susp.$tmps.$next1123; +/* 15207 */ $loadname1124 = susp.$tmps.$loadname1124; +/* 15208 */ $lattr1126 = susp.$tmps.$lattr1126; +/* 15209 */ try { +/* 15210 */ $ret = susp.child.resume(); +/* 15211 */ } catch (err) { +/* 15212 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 15213 */ Sk.execStart = Date.now(); +/* 15214 */ Sk.execPaused = 0 +/* 15215 */ } +/* 15216 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 15217 */ err = new Sk.builtin.ExternalError(err); +/* 15218 */ } +/* 15219 */ Sk.err = err; +/* 15220 */ err.traceback.push({ +/* 15221 */ lineno: $currLineNo, +/* 15222 */ colno: $currColNo, +/* 15223 */ filename: 'src/lib/traceback.py', +/* 15224 */ scope: '$scope1121' +/* 15225 */ }); +/* 15226 */ if ($exc.length > 0) { +/* 15227 */ $err = err; +/* 15228 */ $blk = $exc.pop(); +/* 15229 */ } else { +/* 15230 */ throw err; +/* 15231 */ } +/* 15232 */ } +/* 15233 */ }; +/* 15234 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 15235 */ var susp = new Sk.misceval.Suspension(); +/* 15236 */ susp.child = $child; +/* 15237 */ susp.resume = function() { +/* 15238 */ $scope1121.$wakingSuspension = susp; +/* 15239 */ return $scope1121($gen); +/* 15240 */ }; +/* 15241 */ susp.data = susp.child.data; +/* 15242 */ susp.$blk = $blk; +/* 15243 */ susp.$loc = $loc; +/* 15244 */ susp.$gbl = $gbl; +/* 15245 */ susp.$exc = $exc; +/* 15246 */ susp.$err = $err; +/* 15247 */ susp.$postfinally = $postfinally; +/* 15248 */ susp.$filename = $filename; +/* 15249 */ susp.$lineno = $lineno; +/* 15250 */ susp.$colno = $colno; +/* 15251 */ susp.optional = susp.child.optional; +/* 15252 */ susp.$tmps = { +/* 15253 */ "$next1123": $next1123, +/* 15254 */ "$loadname1124": $loadname1124, +/* 15255 */ "$lattr1126": $lattr1126 +/* 15256 */ }; +/* 15257 */ return susp; +/* 15258 */ }; +/* 15259 */ var $blk = $gen.gi$resumeat, +/* 15260 */ $exc = [], +/* 15261 */ $loc = $gen.gi$locals, +/* 15262 */ $cell = {}, +/* 15263 */ $gbl = this, +/* 15264 */ $err = undefined, +/* 15265 */ $ret = undefined, +/* 15266 */ $postfinally = undefined, +/* 15267 */ $currLineNo = undefined, +/* 15268 */ $currColNo = undefined; +/* 15269 */ if (typeof Sk.execStart === 'undefined') { +/* 15270 */ Sk.execStart = Date.now(); +/* 15271 */ Sk.execPaused = 0 +/* 15272 */ } +/* 15273 */ if (typeof Sk.lastYield === 'undefined') { +/* 15274 */ Sk.lastYield = Date.now() +/* 15275 */ } +/* 15276 */ if ($scope1121.$wakingSuspension !== undefined) { +/* 15277 */ $wakeFromSuspension(); +/* 15278 */ } else {} +/* 15279 */ while (true) { +/* 15280 */ try { +/* 15281 */ var $dateNow = Date.now(); +/* 15282 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 15283 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 15284 */ } +/* 15285 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 15286 */ var $susp = $saveSuspension({ +/* 15287 */ data: { +/* 15288 */ type: 'Sk.yield' +/* 15289 */ }, +/* 15290 */ resume: function() {} +/* 15291 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 15292 */ $susp.$blk = $blk; +/* 15293 */ $susp.optional = true; +/* 15294 */ return $susp; +/* 15295 */ } +/* 15296 */ switch ($blk) { +/* 15297 */ case 0: +/* 15298 */ /* --- codeobj entry --- */ $blk = 1; /* allowing case fallthrough */ +/* 15299 */ case 1: +/* 15300 */ /* --- start for 0 --- */ +/* 15301 */ // +/* 15302 */ // line 441: +/* 15303 */ // caretspace = ((c.isspace() and c or ' ') for c in caretspace) +/* 15304 */ // ^ +/* 15305 */ // +/* 15306 */ +/* 15307 */ $currLineNo = Sk.currLineNo = 441; +/* 15308 */ $currColNo = Sk.currColNo = 31; +/* 15309 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15310 */ $ret = Sk.abstr.iternext($loc.$iter0, true); +/* 15311 */ $blk = 5; /* allowing case fallthrough */ +/* 15312 */ case 5: +/* 15313 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15314 */ return $saveSuspension($ret, 'src/lib/traceback.py', 441, 31); +/* 15315 */ } +/* 15316 */ var $next1123 = $ret; +/* 15317 */ if ($next1123 === undefined) { +/* 15318 */ $blk = 4; +/* 15319 */ continue; +/* 15320 */ } +/* 15321 */ $loc.c = $next1123; +/* 15322 */ // +/* 15323 */ // line 441: +/* 15324 */ // caretspace = ((c.isspace() and c or ' ') for c in caretspace) +/* 15325 */ // ^ +/* 15326 */ // +/* 15327 */ +/* 15328 */ $currLineNo = Sk.currLineNo = 441; +/* 15329 */ $currColNo = Sk.currColNo = 31; +/* 15330 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15331 */ var $loadname1124 = $loc.c !== undefined ? $loc.c : Sk.misceval.loadname('c', $gbl);; +/* 15332 */ $ret = Sk.abstr.gattr($loadname1124, $scope1121.$const1125, true); +/* 15333 */ $blk = 8; /* allowing case fallthrough */ +/* 15334 */ case 8: +/* 15335 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15336 */ return $saveSuspension($ret, 'src/lib/traceback.py', 441, 31); +/* 15337 */ } +/* 15338 */ var $lattr1126 = $ret; +/* 15339 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr1126); +/* 15340 */ $blk = 9; /* allowing case fallthrough */ +/* 15341 */ case 9: +/* 15342 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15343 */ return $saveSuspension($ret, 'src/lib/traceback.py', 441, 31); +/* 15344 */ } +/* 15345 */ var $call1127 = $ret; +/* 15346 */ // +/* 15347 */ // line 441: +/* 15348 */ // caretspace = ((c.isspace() and c or ' ') for c in caretspace) +/* 15349 */ // ^ +/* 15350 */ // +/* 15351 */ +/* 15352 */ $currLineNo = Sk.currLineNo = 441; +/* 15353 */ $currColNo = Sk.currColNo = 31; +/* 15354 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15355 */ var $boolopsucc1128 = $call1127; +/* 15356 */ $boolopsucc1128 = $call1127; +/* 15357 */ var $jfalse1129 = ($call1127 === false || !Sk.misceval.isTrue($call1127)); +/* 15358 */ if ($jfalse1129) { +/* 15359 */ /*test failed */ +/* 15360 */ $blk = 7; +/* 15361 */ continue; +/* 15362 */ } +/* 15363 */ var $loadname1130 = $loc.c !== undefined ? $loc.c : Sk.misceval.loadname('c', $gbl);; +/* 15364 */ $boolopsucc1128 = $loadname1130; +/* 15365 */ var $jfalse1131 = ($loadname1130 === false || !Sk.misceval.isTrue($loadname1130)); +/* 15366 */ if ($jfalse1131) { +/* 15367 */ /*test failed */ +/* 15368 */ $blk = 7; +/* 15369 */ continue; +/* 15370 */ } +/* 15371 */ $blk = 7; /* allowing case fallthrough */ +/* 15372 */ case 7: +/* 15373 */ /* --- end of boolop --- */ var $boolopsucc1132 = $boolopsucc1128; +/* 15374 */ $boolopsucc1132 = $boolopsucc1128; +/* 15375 */ var $jtrue1133 = ($boolopsucc1128 === true || Sk.misceval.isTrue($boolopsucc1128)); +/* 15376 */ if ($jtrue1133) { +/* 15377 */ /*test passed */ +/* 15378 */ $blk = 6; +/* 15379 */ continue; +/* 15380 */ } +/* 15381 */ $boolopsucc1132 = $scope1121.$const1134; +/* 15382 */ var $jtrue1135 = ($scope1121.$const1134 === true || Sk.misceval.isTrue($scope1121.$const1134)); +/* 15383 */ if ($jtrue1135) { +/* 15384 */ /*test passed */ +/* 15385 */ $blk = 6; +/* 15386 */ continue; +/* 15387 */ } +/* 15388 */ $blk = 6; /* allowing case fallthrough */ +/* 15389 */ case 6: +/* 15390 */ /* --- end of boolop --- */ return [2 /*resume*/ , $boolopsucc1132 /*ret*/ ]; +/* 15391 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 15392 */ case 2: +/* 15393 */ /* --- skip for 0 --- */ $blk = 1; /* jump */ +/* 15394 */ continue; +/* 15395 */ case 3: +/* 15396 */ /* --- if cleanup for 0 --- */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 15397 */ case 4: +/* 15398 */ /* --- end for 0 --- */ return Sk.builtin.none.none$; +/* 15399 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 15400 */ } +/* 15401 */ } catch (err) { +/* 15402 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 15403 */ Sk.execStart = Date.now(); +/* 15404 */ Sk.execPaused = 0 +/* 15405 */ } +/* 15406 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 15407 */ err = new Sk.builtin.ExternalError(err); +/* 15408 */ } +/* 15409 */ Sk.err = err; +/* 15410 */ err.traceback.push({ +/* 15411 */ lineno: $currLineNo, +/* 15412 */ colno: $currColNo, +/* 15413 */ filename: 'src/lib/traceback.py', +/* 15414 */ scope: '' +/* 15415 */ }); +/* 15416 */ if ($exc.length > 0) { +/* 15417 */ $err = err; +/* 15418 */ $blk = $exc.pop(); +/* 15419 */ continue +/* 15420 */ } else { +/* 15421 */ throw err; +/* 15422 */ } +/* 15423 */ } +/* 15424 */ } +/* 15425 */ }); +/* 15426 */ $scope1121.$const1125 = new Sk.builtin.str('isspace'); +/* 15427 */ $scope1121.$const1134 = new Sk.builtin.str(' '); +/* 15428 */ var $scope1160 = (function $format1161$($gen) { +/* 15429 */ // generator +/* 15430 */ var $loadname1164, $loadname1164, $lattr1166, $compareres1167, $loadname1170, $loadname1170, $lattr1171, $loadname1170, $lattr1171, $lattr1173, $loadname1174, $loadname1170, $lattr1171, $lattr1173, $loadname1174, $call1175, $loadname1180, $loadname1180, $lattr1182, $compareres1183, $loadname1180, $lattr1182, $compareres1183, $jfalse1184, $boolopsucc1185, $jfalse1186, $loadname1187, $loadname1193, $loadname1193, $lattr1194, $loadname1193, $lattr1194, $lattr1195, $loadname1196, $loadname1193, $lattr1194, $lattr1195, $loadname1196, $call1197, $loadname1202, $loadname1202, $lattr1204, $compareres1205, $loadname1209, $loadname1209, $lattr1211, $loadname1209, $lattr1211, $lattr1212, $loadname1209, $lattr1211, $lattr1212, $call1213, $loadname1217, $loadname1217, $lattr1219, $loadname1217, $lattr1219, $call1220; +/* 15431 */ var $wakeFromSuspension = function() { +/* 15432 */ var susp = $scope1160.$wakingSuspension; +/* 15433 */ $scope1160.$wakingSuspension = undefined; +/* 15434 */ $blk = susp.$blk; +/* 15435 */ $loc = susp.$loc; +/* 15436 */ $gbl = susp.$gbl; +/* 15437 */ $exc = susp.$exc; +/* 15438 */ $err = susp.$err; +/* 15439 */ $postfinally = susp.$postfinally; +/* 15440 */ $currLineNo = susp.$lineno; +/* 15441 */ $currColNo = susp.$colno; +/* 15442 */ Sk.lastYield = Date.now(); +/* 15443 */ $loadname1164 = susp.$tmps.$loadname1164; +/* 15444 */ $lattr1166 = susp.$tmps.$lattr1166; +/* 15445 */ $compareres1167 = susp.$tmps.$compareres1167; +/* 15446 */ $loadname1170 = susp.$tmps.$loadname1170; +/* 15447 */ $lattr1171 = susp.$tmps.$lattr1171; +/* 15448 */ $lattr1173 = susp.$tmps.$lattr1173; +/* 15449 */ $loadname1174 = susp.$tmps.$loadname1174; +/* 15450 */ $call1175 = susp.$tmps.$call1175; +/* 15451 */ $loadname1180 = susp.$tmps.$loadname1180; +/* 15452 */ $lattr1182 = susp.$tmps.$lattr1182; +/* 15453 */ $compareres1183 = susp.$tmps.$compareres1183; +/* 15454 */ $jfalse1184 = susp.$tmps.$jfalse1184; +/* 15455 */ $boolopsucc1185 = susp.$tmps.$boolopsucc1185; +/* 15456 */ $jfalse1186 = susp.$tmps.$jfalse1186; +/* 15457 */ $loadname1187 = susp.$tmps.$loadname1187; +/* 15458 */ $loadname1193 = susp.$tmps.$loadname1193; +/* 15459 */ $lattr1194 = susp.$tmps.$lattr1194; +/* 15460 */ $lattr1195 = susp.$tmps.$lattr1195; +/* 15461 */ $loadname1196 = susp.$tmps.$loadname1196; +/* 15462 */ $call1197 = susp.$tmps.$call1197; +/* 15463 */ $loadname1202 = susp.$tmps.$loadname1202; +/* 15464 */ $lattr1204 = susp.$tmps.$lattr1204; +/* 15465 */ $compareres1205 = susp.$tmps.$compareres1205; +/* 15466 */ $loadname1209 = susp.$tmps.$loadname1209; +/* 15467 */ $lattr1211 = susp.$tmps.$lattr1211; +/* 15468 */ $lattr1212 = susp.$tmps.$lattr1212; +/* 15469 */ $call1213 = susp.$tmps.$call1213; +/* 15470 */ $loadname1217 = susp.$tmps.$loadname1217; +/* 15471 */ $lattr1219 = susp.$tmps.$lattr1219; +/* 15472 */ $call1220 = susp.$tmps.$call1220; +/* 15473 */ try { +/* 15474 */ $ret = susp.child.resume(); +/* 15475 */ } catch (err) { +/* 15476 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 15477 */ Sk.execStart = Date.now(); +/* 15478 */ Sk.execPaused = 0 +/* 15479 */ } +/* 15480 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 15481 */ err = new Sk.builtin.ExternalError(err); +/* 15482 */ } +/* 15483 */ Sk.err = err; +/* 15484 */ err.traceback.push({ +/* 15485 */ lineno: $currLineNo, +/* 15486 */ colno: $currColNo, +/* 15487 */ filename: 'src/lib/traceback.py', +/* 15488 */ scope: '$scope1160' +/* 15489 */ }); +/* 15490 */ if ($exc.length > 0) { +/* 15491 */ $err = err; +/* 15492 */ $blk = $exc.pop(); +/* 15493 */ } else { +/* 15494 */ throw err; +/* 15495 */ } +/* 15496 */ } +/* 15497 */ }; +/* 15498 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { +/* 15499 */ var susp = new Sk.misceval.Suspension(); +/* 15500 */ susp.child = $child; +/* 15501 */ susp.resume = function() { +/* 15502 */ $scope1160.$wakingSuspension = susp; +/* 15503 */ return $scope1160($gen); +/* 15504 */ }; +/* 15505 */ susp.data = susp.child.data; +/* 15506 */ susp.$blk = $blk; +/* 15507 */ susp.$loc = $loc; +/* 15508 */ susp.$gbl = $gbl; +/* 15509 */ susp.$exc = $exc; +/* 15510 */ susp.$err = $err; +/* 15511 */ susp.$postfinally = $postfinally; +/* 15512 */ susp.$filename = $filename; +/* 15513 */ susp.$lineno = $lineno; +/* 15514 */ susp.$colno = $colno; +/* 15515 */ susp.optional = susp.child.optional; +/* 15516 */ susp.$tmps = { +/* 15517 */ "$loadname1164": $loadname1164, +/* 15518 */ "$lattr1166": $lattr1166, +/* 15519 */ "$compareres1167": $compareres1167, +/* 15520 */ "$loadname1170": $loadname1170, +/* 15521 */ "$lattr1171": $lattr1171, +/* 15522 */ "$lattr1173": $lattr1173, +/* 15523 */ "$loadname1174": $loadname1174, +/* 15524 */ "$call1175": $call1175, +/* 15525 */ "$loadname1180": $loadname1180, +/* 15526 */ "$lattr1182": $lattr1182, +/* 15527 */ "$compareres1183": $compareres1183, +/* 15528 */ "$jfalse1184": $jfalse1184, +/* 15529 */ "$boolopsucc1185": $boolopsucc1185, +/* 15530 */ "$jfalse1186": $jfalse1186, +/* 15531 */ "$loadname1187": $loadname1187, +/* 15532 */ "$loadname1193": $loadname1193, +/* 15533 */ "$lattr1194": $lattr1194, +/* 15534 */ "$lattr1195": $lattr1195, +/* 15535 */ "$loadname1196": $loadname1196, +/* 15536 */ "$call1197": $call1197, +/* 15537 */ "$loadname1202": $loadname1202, +/* 15538 */ "$lattr1204": $lattr1204, +/* 15539 */ "$compareres1205": $compareres1205, +/* 15540 */ "$loadname1209": $loadname1209, +/* 15541 */ "$lattr1211": $lattr1211, +/* 15542 */ "$lattr1212": $lattr1212, +/* 15543 */ "$call1213": $call1213, +/* 15544 */ "$loadname1217": $loadname1217, +/* 15545 */ "$lattr1219": $lattr1219, +/* 15546 */ "$call1220": $call1220 +/* 15547 */ }; +/* 15548 */ return susp; +/* 15549 */ }; +/* 15550 */ var $blk = $gen.gi$resumeat, +/* 15551 */ $exc = [], +/* 15552 */ $loc = $gen.gi$locals, +/* 15553 */ $cell = {}, +/* 15554 */ $gbl = this, +/* 15555 */ $err = undefined, +/* 15556 */ $ret = undefined, +/* 15557 */ $postfinally = undefined, +/* 15558 */ $currLineNo = undefined, +/* 15559 */ $currColNo = undefined; +/* 15560 */ if (typeof Sk.execStart === 'undefined') { +/* 15561 */ Sk.execStart = Date.now(); +/* 15562 */ Sk.execPaused = 0 +/* 15563 */ } +/* 15564 */ if (typeof Sk.lastYield === 'undefined') { +/* 15565 */ Sk.lastYield = Date.now() +/* 15566 */ } +/* 15567 */ if ($scope1160.$wakingSuspension !== undefined) { +/* 15568 */ $wakeFromSuspension(); +/* 15569 */ } else {} +/* 15570 */ $gbl.__class__ = this.TracebackException; +/* 15571 */ while (true) { +/* 15572 */ try { +/* 15573 */ var $dateNow = Date.now(); +/* 15574 */ if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit) { +/* 15575 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) +/* 15576 */ } +/* 15577 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { +/* 15578 */ var $susp = $saveSuspension({ +/* 15579 */ data: { +/* 15580 */ type: 'Sk.yield' +/* 15581 */ }, +/* 15582 */ resume: function() {} +/* 15583 */ }, 'src/lib/traceback.py', $currLineNo, $currColNo); +/* 15584 */ $susp.$blk = $blk; +/* 15585 */ $susp.optional = true; +/* 15586 */ return $susp; +/* 15587 */ } +/* 15588 */ switch ($blk) { +/* 15589 */ case 0: +/* 15590 */ /* --- codeobj entry --- */ +/* 15591 */ // +/* 15592 */ // line 448: +/* 15593 */ // if chain: +/* 15594 */ // ^ +/* 15595 */ // +/* 15596 */ +/* 15597 */ $currLineNo = Sk.currLineNo = 448; +/* 15598 */ $currColNo = Sk.currColNo = 8; +/* 15599 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15600 */ var $loadname1162 = $loc.chain !== undefined ? $loc.chain : Sk.misceval.loadname('chain', $gbl);; +/* 15601 */ var $jfalse1163 = ($loadname1162 === false || !Sk.misceval.isTrue($loadname1162)); +/* 15602 */ if ($jfalse1163) { +/* 15603 */ /*test failed */ +/* 15604 */ $blk = 1; +/* 15605 */ continue; +/* 15606 */ } +/* 15607 */ // +/* 15608 */ // line 449: +/* 15609 */ // if self.__cause__ is not None: +/* 15610 */ // ^ +/* 15611 */ // +/* 15612 */ +/* 15613 */ $currLineNo = Sk.currLineNo = 449; +/* 15614 */ $currColNo = Sk.currColNo = 12; +/* 15615 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15616 */ var $loadname1164 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 15617 */ $ret = Sk.abstr.gattr($loadname1164, $scope1160.$const1165, true); +/* 15618 */ $blk = 4; /* allowing case fallthrough */ +/* 15619 */ case 4: +/* 15620 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15621 */ return $saveSuspension($ret, 'src/lib/traceback.py', 449, 15); +/* 15622 */ } +/* 15623 */ var $lattr1166 = $ret; +/* 15624 */ var $compareres1167 = null; +/* 15625 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($lattr1166, Sk.builtin.none.none$, 'IsNot', true)); +/* 15626 */ $blk = 6; /* allowing case fallthrough */ +/* 15627 */ case 6: +/* 15628 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15629 */ return $saveSuspension($ret, 'src/lib/traceback.py', 449, 15); +/* 15630 */ } +/* 15631 */ $compareres1167 = $ret; +/* 15632 */ var $jfalse1168 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 15633 */ if ($jfalse1168) { +/* 15634 */ /*test failed */ +/* 15635 */ $blk = 5; +/* 15636 */ continue; +/* 15637 */ } +/* 15638 */ $blk = 5; /* allowing case fallthrough */ +/* 15639 */ case 5: +/* 15640 */ /* --- done --- */ var $jfalse1169 = ($compareres1167 === false || !Sk.misceval.isTrue($compareres1167)); +/* 15641 */ if ($jfalse1169) { +/* 15642 */ /*test failed */ +/* 15643 */ $blk = 3; +/* 15644 */ continue; +/* 15645 */ } +/* 15646 */ // +/* 15647 */ // line 451: +/* 15648 */ // for g in self.__cause__.format(chain=chain): +/* 15649 */ // ^ +/* 15650 */ // +/* 15651 */ +/* 15652 */ $currLineNo = Sk.currLineNo = 451; +/* 15653 */ $currColNo = Sk.currColNo = 16; +/* 15654 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15655 */ var $loadname1170 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 15656 */ $ret = Sk.abstr.gattr($loadname1170, $scope1160.$const1165, true); +/* 15657 */ $blk = 10; /* allowing case fallthrough */ +/* 15658 */ case 10: +/* 15659 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15660 */ return $saveSuspension($ret, 'src/lib/traceback.py', 451, 25); +/* 15661 */ } +/* 15662 */ var $lattr1171 = $ret; +/* 15663 */ $ret = Sk.abstr.gattr($lattr1171, $scope1160.$const1172, true); +/* 15664 */ $blk = 11; /* allowing case fallthrough */ +/* 15665 */ case 11: +/* 15666 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15667 */ return $saveSuspension($ret, 'src/lib/traceback.py', 451, 25); +/* 15668 */ } +/* 15669 */ var $lattr1173 = $ret; +/* 15670 */ var $loadname1174 = $loc.chain !== undefined ? $loc.chain : Sk.misceval.loadname('chain', $gbl);; +/* 15671 */ $ret = Sk.misceval.applyOrSuspend($lattr1173, undefined, undefined, ['chain', $loadname1174], []); +/* 15672 */ $blk = 12; /* allowing case fallthrough */ +/* 15673 */ case 12: +/* 15674 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15675 */ return $saveSuspension($ret, 'src/lib/traceback.py', 451, 25); +/* 15676 */ } +/* 15677 */ var $call1175 = $ret; +/* 15678 */ // +/* 15679 */ // line 451: +/* 15680 */ // for g in self.__cause__.format(chain=chain): +/* 15681 */ // ^ +/* 15682 */ // +/* 15683 */ +/* 15684 */ $currLineNo = Sk.currLineNo = 451; +/* 15685 */ $currColNo = Sk.currColNo = 25; +/* 15686 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15687 */ $loc.$iter1176 = Sk.abstr.iter($call1175); +/* 15688 */ $blk = 7; /* allowing case fallthrough */ +/* 15689 */ case 7: +/* 15690 */ /* --- for start --- */ $ret = Sk.abstr.iternext($loc.$iter1176, true); +/* 15691 */ $blk = 13; /* allowing case fallthrough */ +/* 15692 */ case 13: +/* 15693 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15694 */ return $saveSuspension($ret, 'src/lib/traceback.py', 451, 16); +/* 15695 */ } +/* 15696 */ var $next1177 = $ret; +/* 15697 */ if ($next1177 === undefined) { +/* 15698 */ $blk = 8; +/* 15699 */ continue; +/* 15700 */ } +/* 15701 */ $loc.g = $next1177; +/* 15702 */ // +/* 15703 */ // line 452: +/* 15704 */ // yield g +/* 15705 */ // ^ +/* 15706 */ // +/* 15707 */ +/* 15708 */ $currLineNo = Sk.currLineNo = 452; +/* 15709 */ $currColNo = Sk.currColNo = 20; +/* 15710 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15711 */ var $loadname1178 = $loc.g !== undefined ? $loc.g : Sk.misceval.loadname('g', $gbl);; +/* 15712 */ return [ /*resume*/ 14, /*ret*/ $loadname1178]; +/* 15713 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 15714 */ case 1: +/* 15715 */ /* --- end of if --- */ +/* 15716 */ // +/* 15717 */ // line 460: +/* 15718 */ // if self.exc_traceback is not None: +/* 15719 */ // ^ +/* 15720 */ // +/* 15721 */ +/* 15722 */ $currLineNo = Sk.currLineNo = 460; +/* 15723 */ $currColNo = Sk.currColNo = 8; +/* 15724 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15725 */ var $loadname1202 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 15726 */ $ret = Sk.abstr.gattr($loadname1202, $scope1160.$const1203, true); +/* 15727 */ $blk = 32; /* allowing case fallthrough */ +/* 15728 */ case 32: +/* 15729 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15730 */ return $saveSuspension($ret, 'src/lib/traceback.py', 460, 11); +/* 15731 */ } +/* 15732 */ var $lattr1204 = $ret; +/* 15733 */ var $compareres1205 = null; +/* 15734 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($lattr1204, Sk.builtin.none.none$, 'IsNot', true)); +/* 15735 */ $blk = 34; /* allowing case fallthrough */ +/* 15736 */ case 34: +/* 15737 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15738 */ return $saveSuspension($ret, 'src/lib/traceback.py', 460, 11); +/* 15739 */ } +/* 15740 */ $compareres1205 = $ret; +/* 15741 */ var $jfalse1206 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 15742 */ if ($jfalse1206) { +/* 15743 */ /*test failed */ +/* 15744 */ $blk = 33; +/* 15745 */ continue; +/* 15746 */ } +/* 15747 */ $blk = 33; /* allowing case fallthrough */ +/* 15748 */ case 33: +/* 15749 */ /* --- done --- */ var $jfalse1207 = ($compareres1205 === false || !Sk.misceval.isTrue($compareres1205)); +/* 15750 */ if ($jfalse1207) { +/* 15751 */ /*test failed */ +/* 15752 */ $blk = 31; +/* 15753 */ continue; +/* 15754 */ } +/* 15755 */ // +/* 15756 */ // line 461: +/* 15757 */ // yield 'Traceback (most recent call last):\n' +/* 15758 */ // ^ +/* 15759 */ // +/* 15760 */ +/* 15761 */ $currLineNo = Sk.currLineNo = 461; +/* 15762 */ $currColNo = Sk.currColNo = 12; +/* 15763 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15764 */ return [ /*resume*/ 35, /*ret*/ $scope1160.$const1208]; +/* 15765 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 15766 */ case 2: +/* 15767 */ /* --- end of if --- */ $blk = 1; /* jump */ +/* 15768 */ continue; +/* 15769 */ case 3: +/* 15770 */ /* --- next branch of if --- */ +/* 15771 */ // +/* 15772 */ // line 454: +/* 15773 */ // elif (self.__context__ is not None and +/* 15774 */ // ^ +/* 15775 */ // +/* 15776 */ +/* 15777 */ $currLineNo = Sk.currLineNo = 454; +/* 15778 */ $currColNo = Sk.currColNo = 17; +/* 15779 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15780 */ var $loadname1180 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 15781 */ $ret = Sk.abstr.gattr($loadname1180, $scope1160.$const1181, true); +/* 15782 */ $blk = 18; /* allowing case fallthrough */ +/* 15783 */ case 18: +/* 15784 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15785 */ return $saveSuspension($ret, 'src/lib/traceback.py', 454, 18); +/* 15786 */ } +/* 15787 */ var $lattr1182 = $ret; +/* 15788 */ var $compareres1183 = null; +/* 15789 */ $ret = Sk.builtin.bool(Sk.misceval.richCompareBool($lattr1182, Sk.builtin.none.none$, 'IsNot', true)); +/* 15790 */ $blk = 20; /* allowing case fallthrough */ +/* 15791 */ case 20: +/* 15792 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15793 */ return $saveSuspension($ret, 'src/lib/traceback.py', 454, 18); +/* 15794 */ } +/* 15795 */ $compareres1183 = $ret; +/* 15796 */ var $jfalse1184 = ($ret === false || !Sk.misceval.isTrue($ret)); +/* 15797 */ if ($jfalse1184) { +/* 15798 */ /*test failed */ +/* 15799 */ $blk = 19; +/* 15800 */ continue; +/* 15801 */ } +/* 15802 */ $blk = 19; /* allowing case fallthrough */ +/* 15803 */ case 19: +/* 15804 */ /* --- done --- */ var $boolopsucc1185 = $compareres1183; +/* 15805 */ $boolopsucc1185 = $compareres1183; +/* 15806 */ var $jfalse1186 = ($compareres1183 === false || !Sk.misceval.isTrue($compareres1183)); +/* 15807 */ if ($jfalse1186) { +/* 15808 */ /*test failed */ +/* 15809 */ $blk = 17; +/* 15810 */ continue; +/* 15811 */ } +/* 15812 */ var $loadname1187 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 15813 */ $ret = Sk.abstr.gattr($loadname1187, $scope1160.$const1188, true); +/* 15814 */ $blk = 21; /* allowing case fallthrough */ +/* 15815 */ case 21: +/* 15816 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15817 */ return $saveSuspension($ret, 'src/lib/traceback.py', 455, 20); +/* 15818 */ } +/* 15819 */ var $lattr1189 = $ret; +/* 15820 */ var $unaryop1190 = Sk.abstr.numberUnaryOp($lattr1189, 'Not'); +/* 15821 */ $boolopsucc1185 = $unaryop1190; +/* 15822 */ var $jfalse1191 = ($unaryop1190 === false || !Sk.misceval.isTrue($unaryop1190)); +/* 15823 */ if ($jfalse1191) { +/* 15824 */ /*test failed */ +/* 15825 */ $blk = 17; +/* 15826 */ continue; +/* 15827 */ } +/* 15828 */ $blk = 17; /* allowing case fallthrough */ +/* 15829 */ case 17: +/* 15830 */ /* --- end of boolop --- */ var $jfalse1192 = ($boolopsucc1185 === false || !Sk.misceval.isTrue($boolopsucc1185)); +/* 15831 */ if ($jfalse1192) { +/* 15832 */ /*test failed */ +/* 15833 */ $blk = 16; +/* 15834 */ continue; +/* 15835 */ } +/* 15836 */ // +/* 15837 */ // line 457: +/* 15838 */ // for g in self.__cause__.format(chain=chain): +/* 15839 */ // ^ +/* 15840 */ // +/* 15841 */ +/* 15842 */ $currLineNo = Sk.currLineNo = 457; +/* 15843 */ $currColNo = Sk.currColNo = 16; +/* 15844 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15845 */ var $loadname1193 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 15846 */ $ret = Sk.abstr.gattr($loadname1193, $scope1160.$const1165, true); +/* 15847 */ $blk = 25; /* allowing case fallthrough */ +/* 15848 */ case 25: +/* 15849 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15850 */ return $saveSuspension($ret, 'src/lib/traceback.py', 457, 25); +/* 15851 */ } +/* 15852 */ var $lattr1194 = $ret; +/* 15853 */ $ret = Sk.abstr.gattr($lattr1194, $scope1160.$const1172, true); +/* 15854 */ $blk = 26; /* allowing case fallthrough */ +/* 15855 */ case 26: +/* 15856 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15857 */ return $saveSuspension($ret, 'src/lib/traceback.py', 457, 25); +/* 15858 */ } +/* 15859 */ var $lattr1195 = $ret; +/* 15860 */ var $loadname1196 = $loc.chain !== undefined ? $loc.chain : Sk.misceval.loadname('chain', $gbl);; +/* 15861 */ $ret = Sk.misceval.applyOrSuspend($lattr1195, undefined, undefined, ['chain', $loadname1196], []); +/* 15862 */ $blk = 27; /* allowing case fallthrough */ +/* 15863 */ case 27: +/* 15864 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15865 */ return $saveSuspension($ret, 'src/lib/traceback.py', 457, 25); +/* 15866 */ } +/* 15867 */ var $call1197 = $ret; +/* 15868 */ // +/* 15869 */ // line 457: +/* 15870 */ // for g in self.__cause__.format(chain=chain): +/* 15871 */ // ^ +/* 15872 */ // +/* 15873 */ +/* 15874 */ $currLineNo = Sk.currLineNo = 457; +/* 15875 */ $currColNo = Sk.currColNo = 25; +/* 15876 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15877 */ $loc.$iter1198 = Sk.abstr.iter($call1197); +/* 15878 */ $blk = 22; /* allowing case fallthrough */ +/* 15879 */ case 22: +/* 15880 */ /* --- for start --- */ $ret = Sk.abstr.iternext($loc.$iter1198, true); +/* 15881 */ $blk = 28; /* allowing case fallthrough */ +/* 15882 */ case 28: +/* 15883 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15884 */ return $saveSuspension($ret, 'src/lib/traceback.py', 457, 16); +/* 15885 */ } +/* 15886 */ var $next1199 = $ret; +/* 15887 */ if ($next1199 === undefined) { +/* 15888 */ $blk = 23; +/* 15889 */ continue; +/* 15890 */ } +/* 15891 */ $loc.g = $next1199; +/* 15892 */ // +/* 15893 */ // line 458: +/* 15894 */ // yield g +/* 15895 */ // ^ +/* 15896 */ // +/* 15897 */ +/* 15898 */ $currLineNo = Sk.currLineNo = 458; +/* 15899 */ $currColNo = Sk.currColNo = 20; +/* 15900 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15901 */ var $loadname1200 = $loc.g !== undefined ? $loc.g : Sk.misceval.loadname('g', $gbl);; +/* 15902 */ return [ /*resume*/ 29, /*ret*/ $loadname1200]; +/* 15903 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 15904 */ case 8: +/* 15905 */ /* --- for cleanup --- */ $blk = 9; /* allowing case fallthrough */ +/* 15906 */ case 9: +/* 15907 */ /* --- for end --- */ +/* 15908 */ // +/* 15909 */ // line 453: +/* 15910 */ // yield _cause_message +/* 15911 */ // ^ +/* 15912 */ // +/* 15913 */ +/* 15914 */ $currLineNo = Sk.currLineNo = 453; +/* 15915 */ $currColNo = Sk.currColNo = 16; +/* 15916 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15917 */ var $loadgbl1179 = Sk.misceval.loadname('_cause_message', $gbl); +/* 15918 */ return [ /*resume*/ 15, /*ret*/ $loadgbl1179]; +/* 15919 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 15920 */ case 14: +/* 15921 */ /* --- after yield --- */ $blk = 7; /* jump */ +/* 15922 */ continue; +/* 15923 */ case 15: +/* 15924 */ /* --- after yield --- */ $blk = 2; /* jump */ +/* 15925 */ continue; +/* 15926 */ case 16: +/* 15927 */ /* --- end of if --- */ $blk = 2; /* jump */ +/* 15928 */ continue; +/* 15929 */ case 23: +/* 15930 */ /* --- for cleanup --- */ $blk = 24; /* allowing case fallthrough */ +/* 15931 */ case 24: +/* 15932 */ /* --- for end --- */ +/* 15933 */ // +/* 15934 */ // line 459: +/* 15935 */ // yield _context_message +/* 15936 */ // ^ +/* 15937 */ // +/* 15938 */ +/* 15939 */ $currLineNo = Sk.currLineNo = 459; +/* 15940 */ $currColNo = Sk.currColNo = 16; +/* 15941 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15942 */ var $loadgbl1201 = Sk.misceval.loadname('_context_message', $gbl); +/* 15943 */ return [ /*resume*/ 30, /*ret*/ $loadgbl1201]; +/* 15944 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 15945 */ case 29: +/* 15946 */ /* --- after yield --- */ $blk = 22; /* jump */ +/* 15947 */ continue; +/* 15948 */ case 30: +/* 15949 */ /* --- after yield --- */ $blk = 16; /* jump */ +/* 15950 */ continue; +/* 15951 */ case 31: +/* 15952 */ /* --- end of if --- */ +/* 15953 */ // +/* 15954 */ // line 463: +/* 15955 */ // for g in self.stack.format(): +/* 15956 */ // ^ +/* 15957 */ // +/* 15958 */ +/* 15959 */ $currLineNo = Sk.currLineNo = 463; +/* 15960 */ $currColNo = Sk.currColNo = 8; +/* 15961 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15962 */ var $loadname1209 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 15963 */ $ret = Sk.abstr.gattr($loadname1209, $scope1160.$const1210, true); +/* 15964 */ $blk = 39; /* allowing case fallthrough */ +/* 15965 */ case 39: +/* 15966 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15967 */ return $saveSuspension($ret, 'src/lib/traceback.py', 463, 17); +/* 15968 */ } +/* 15969 */ var $lattr1211 = $ret; +/* 15970 */ $ret = Sk.abstr.gattr($lattr1211, $scope1160.$const1172, true); +/* 15971 */ $blk = 40; /* allowing case fallthrough */ +/* 15972 */ case 40: +/* 15973 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15974 */ return $saveSuspension($ret, 'src/lib/traceback.py', 463, 17); +/* 15975 */ } +/* 15976 */ var $lattr1212 = $ret; +/* 15977 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr1212); +/* 15978 */ $blk = 41; /* allowing case fallthrough */ +/* 15979 */ case 41: +/* 15980 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 15981 */ return $saveSuspension($ret, 'src/lib/traceback.py', 463, 17); +/* 15982 */ } +/* 15983 */ var $call1213 = $ret; +/* 15984 */ // +/* 15985 */ // line 463: +/* 15986 */ // for g in self.stack.format(): +/* 15987 */ // ^ +/* 15988 */ // +/* 15989 */ +/* 15990 */ $currLineNo = Sk.currLineNo = 463; +/* 15991 */ $currColNo = Sk.currColNo = 17; +/* 15992 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 15993 */ $loc.$iter1214 = Sk.abstr.iter($call1213); +/* 15994 */ $blk = 36; /* allowing case fallthrough */ +/* 15995 */ case 36: +/* 15996 */ /* --- for start --- */ $ret = Sk.abstr.iternext($loc.$iter1214, true); +/* 15997 */ $blk = 42; /* allowing case fallthrough */ +/* 15998 */ case 42: +/* 15999 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 16000 */ return $saveSuspension($ret, 'src/lib/traceback.py', 463, 8); +/* 16001 */ } +/* 16002 */ var $next1215 = $ret; +/* 16003 */ if ($next1215 === undefined) { +/* 16004 */ $blk = 37; +/* 16005 */ continue; +/* 16006 */ } +/* 16007 */ $loc.g = $next1215; +/* 16008 */ // +/* 16009 */ // line 464: +/* 16010 */ // yield g +/* 16011 */ // ^ +/* 16012 */ // +/* 16013 */ +/* 16014 */ $currLineNo = Sk.currLineNo = 464; +/* 16015 */ $currColNo = Sk.currColNo = 12; +/* 16016 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 16017 */ var $loadname1216 = $loc.g !== undefined ? $loc.g : Sk.misceval.loadname('g', $gbl);; +/* 16018 */ return [ /*resume*/ 43, /*ret*/ $loadname1216]; +/* 16019 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 16020 */ case 35: +/* 16021 */ /* --- after yield --- */ $blk = 31; /* jump */ +/* 16022 */ continue; +/* 16023 */ case 37: +/* 16024 */ /* --- for cleanup --- */ $blk = 38; /* allowing case fallthrough */ +/* 16025 */ case 38: +/* 16026 */ /* --- for end --- */ +/* 16027 */ // +/* 16028 */ // line 466: +/* 16029 */ // for g in self.format_exception_only(): +/* 16030 */ // ^ +/* 16031 */ // +/* 16032 */ +/* 16033 */ $currLineNo = Sk.currLineNo = 466; +/* 16034 */ $currColNo = Sk.currColNo = 8; +/* 16035 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 16036 */ var $loadname1217 = $loc.self !== undefined ? $loc.self : Sk.misceval.loadname('self', $gbl);; +/* 16037 */ $ret = Sk.abstr.gattr($loadname1217, $scope1160.$const1218, true); +/* 16038 */ $blk = 47; /* allowing case fallthrough */ +/* 16039 */ case 47: +/* 16040 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 16041 */ return $saveSuspension($ret, 'src/lib/traceback.py', 466, 17); +/* 16042 */ } +/* 16043 */ var $lattr1219 = $ret; +/* 16044 */ $ret = Sk.misceval.callsimOrSuspendArray($lattr1219); +/* 16045 */ $blk = 48; /* allowing case fallthrough */ +/* 16046 */ case 48: +/* 16047 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 16048 */ return $saveSuspension($ret, 'src/lib/traceback.py', 466, 17); +/* 16049 */ } +/* 16050 */ var $call1220 = $ret; +/* 16051 */ // +/* 16052 */ // line 466: +/* 16053 */ // for g in self.format_exception_only(): +/* 16054 */ // ^ +/* 16055 */ // +/* 16056 */ +/* 16057 */ $currLineNo = Sk.currLineNo = 466; +/* 16058 */ $currColNo = Sk.currColNo = 17; +/* 16059 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 16060 */ $loc.$iter1221 = Sk.abstr.iter($call1220); +/* 16061 */ $blk = 44; /* allowing case fallthrough */ +/* 16062 */ case 44: +/* 16063 */ /* --- for start --- */ $ret = Sk.abstr.iternext($loc.$iter1221, true); +/* 16064 */ $blk = 49; /* allowing case fallthrough */ +/* 16065 */ case 49: +/* 16066 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { +/* 16067 */ return $saveSuspension($ret, 'src/lib/traceback.py', 466, 8); +/* 16068 */ } +/* 16069 */ var $next1222 = $ret; +/* 16070 */ if ($next1222 === undefined) { +/* 16071 */ $blk = 45; +/* 16072 */ continue; +/* 16073 */ } +/* 16074 */ $loc.g = $next1222; +/* 16075 */ // +/* 16076 */ // line 467: +/* 16077 */ // yield g +/* 16078 */ // ^ +/* 16079 */ // +/* 16080 */ +/* 16081 */ $currLineNo = Sk.currLineNo = 467; +/* 16082 */ $currColNo = Sk.currColNo = 12; +/* 16083 */ Sk.currFilename = 'src/lib/traceback.py'; +/* 16084 */ var $loadname1223 = $loc.g !== undefined ? $loc.g : Sk.misceval.loadname('g', $gbl);; +/* 16085 */ return [ /*resume*/ 50, /*ret*/ $loadname1223]; +/* 16086 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 16087 */ case 43: +/* 16088 */ /* --- after yield --- */ $blk = 36; /* jump */ +/* 16089 */ continue; +/* 16090 */ case 45: +/* 16091 */ /* --- for cleanup --- */ $blk = 46; /* allowing case fallthrough */ +/* 16092 */ case 46: +/* 16093 */ /* --- for end --- */ return Sk.builtin.none.none$; +/* 16094 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); +/* 16095 */ case 50: +/* 16096 */ /* --- after yield --- */ $blk = 44; /* jump */ +/* 16097 */ continue; +/* 16098 */ } +/* 16099 */ } catch (err) { +/* 16100 */ if (err instanceof Sk.builtin.TimeLimitError) { +/* 16101 */ Sk.execStart = Date.now(); +/* 16102 */ Sk.execPaused = 0 +/* 16103 */ } +/* 16104 */ if (!(err instanceof Sk.builtin.BaseException)) { +/* 16105 */ err = new Sk.builtin.ExternalError(err); +/* 16106 */ } +/* 16107 */ Sk.err = err; +/* 16108 */ err.traceback.push({ +/* 16109 */ lineno: $currLineNo, +/* 16110 */ colno: $currColNo, +/* 16111 */ filename: 'src/lib/traceback.py', +/* 16112 */ scope: 'format' +/* 16113 */ }); +/* 16114 */ if ($exc.length > 0) { +/* 16115 */ $err = err; +/* 16116 */ $blk = $exc.pop(); +/* 16117 */ continue +/* 16118 */ } else { +/* 16119 */ throw err; +/* 16120 */ } +/* 16121 */ } +/* 16122 */ } +/* 16123 */ }); +/* 16124 */ $scope1160.$const1165 = new Sk.builtin.str('__cause__'); +/* 16125 */ $scope1160.$const1172 = new Sk.builtin.str('format'); +/* 16126 */ $scope1160.$const1181 = new Sk.builtin.str('__context__'); +/* 16127 */ $scope1160.$const1188 = new Sk.builtin.str('__suppress_context__'); +/* 16128 */ $scope1160.$const1203 = new Sk.builtin.str('exc_traceback'); +/* 16129 */ $scope1160.$const1208 = new Sk.builtin.str('Traceback (most recent call last):\n'); +/* 16130 */ $scope1160.$const1210 = new Sk.builtin.str('stack'); +/* 16131 */ $scope1160.$const1218 = new Sk.builtin.str('format_exception_only'); +/* 16132 */ return $scope149; +/* 16133 */ }(); +Parse src/lib/keyword.py 15 +AST src/lib/keyword.py 1 +Parse src/lib/itertools.py 3 +AST src/lib/itertools.py 1 +----- +Run time: 0.749s diff --git a/src/builtin.js b/src/builtin.js index 1c9d63abeb..8fa2ef738d 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -1429,29 +1429,43 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { if (!new_globals_copy.__package__) { new_globals_copy.__package__ = Sk.builtin.none.none$; } - var backupGlobals = Sk.globals, - backupSysmodules = new Sk.builtin.dict([]); + var backupGlobals = Sk.globals; + /*var backupSysmodules = new Sk.builtin.dict([]); Sk.misceval.iterFor(Sk.sysmodules.tp$iter(), function(key) { var value = Sk.sysmodules.mp$subscript(key); backupSysmodules.mp$ass_subscript(key, value); - }); + });*/ Sk.globals = new_globals_copy; // Possibly copy over some "default" ones? //Sk.importMainWithBody(filename, false, python_code, true); var name = filename.endsWith(".py") ? filename.slice(0, -3) : filename; var modname = name; - Sk.importModuleInternal_(name, false, modname, pythonCode, undefined, false, true) + var caughtError = null; + /*var friendlyKeys = []; + Sk.misceval.iterFor(Sk.sysmodules.tp$iter(), function(key) { + friendlyKeys.push(key.v); + }); + console.log("LOADING", friendlyKeys);*/ + try { + Sk.importModuleInternal_(name, false, modname, pythonCode, undefined, false, true) + } catch (e) { + caughtError = e; + } Sk.globals = backupGlobals; - Sk.misceval.iterFor(backupSysmodules.tp$iter(), function(key) { + /*Sk.misceval.iterFor(backupSysmodules.tp$iter(), function(key) { var value = backupSysmodules.mp$subscript(key); Sk.sysmodules.mp$ass_subscript(key, value); - }); + });*/ + Sk.getCurrentSysModules().mp$del_subscript(Sk.builtin.str(name)); for (var key in new_globals_copy) { if (new_globals_copy.hasOwnProperty(key)) { var pykey = Sk.ffi.remapToPy(key); - Sk.builtin.dict.prototype.mp$ass_subscript.call(new_globals, pykey, new_globals_copy[key]) + Sk.builtin.dict.prototype.mp$ass_subscript.call(new_globals, pykey, new_globals_copy[key]); } } Sk.retainGlobals = backupRG; + if (caughtError !== null) { + throw caughtError; + } }; Sk.builtin.frozenset = function frozenset () { diff --git a/src/builtin/sys.js b/src/builtin/sys.js index 69ae29654a..5c35d49ca6 100644 --- a/src/builtin/sys.js +++ b/src/builtin/sys.js @@ -24,7 +24,7 @@ var $builtinmodule = function (name) { sys.path = Sk.realsyspath; - sys.platform = Sk.builtin.str('skulpt'); + sys.platform = Sk.builtin.str("skulpt"); sys.getExecutionLimit = new Sk.builtin.func(function () { if (Sk.execLimit === null) { @@ -75,7 +75,6 @@ var $builtinmodule = function (name) { sys.exc_info = new Sk.builtin.func(function () { if (Sk.err) { var type = Sk.err.ob$type; - var value = Sk.builtin.none.none$; var traceback = new Sk.builtin.traceback.fromList(Sk.err.traceback); //print(traceback.tp$setattr) //traceback.tp$setattr('tb_lineno', traceback.tb_lineno); diff --git a/src/builtindict.js b/src/builtindict.js index 34829daef8..4b8bf82bd3 100644 --- a/src/builtindict.js +++ b/src/builtindict.js @@ -42,6 +42,7 @@ Sk.builtins = { "KeyError" : Sk.builtin.KeyError, "TypeError" : Sk.builtin.TypeError, "NameError" : Sk.builtin.NameError, + "OSError" : Sk.builtin.OSError, "IOError" : Sk.builtin.IOError, "NotImplementedError": Sk.builtin.NotImplementedError, "StandardError" : Sk.builtin.StandardError, diff --git a/src/compile.js b/src/compile.js index b3f3c9cd12..abeedb5b28 100644 --- a/src/compile.js +++ b/src/compile.js @@ -103,18 +103,20 @@ Compiler.prototype.annotateSource = function (ast, shouldStep) { var i; var col_offset; var lineno; + var sourceLine; if (this.source) { lineno = ast.lineno; col_offset = ast.col_offset; - out("\n//\n// line ", lineno, ":\n// ", this.getSourceLine(lineno), "\n// "); + sourceLine = this.getSourceLine(lineno); + /*out("\n//\n// line ", lineno, ":\n// ", sourceLine, "\n// "); for (i = 0; i < col_offset; ++i) { out(" "); } - out("^\n//\n"); + out("^\n//\n");*/ Sk.asserts.assert(ast.lineno !== undefined && ast.col_offset !== undefined); out("\n$currLineNo=Sk.currLineNo=",lineno, ";$currColNo=Sk.currColNo=",col_offset,";"); - out("Sk.currFilename='",this.filename,"';"); + out("Sk.currFilename='",this.filename,"';$currSource=",JSON.stringify(sourceLine),";"); // Do not trace the standard library if (shouldStep && (!this.filename || !this.filename.startsWith("src/lib/"))) { @@ -331,7 +333,7 @@ Compiler.prototype.outputInterruptTest = function () { // Added by RNL } if (Sk.yieldLimit !== null && this.u.canSuspend) { output += "if ($dateNow - Sk.lastYield > Sk.yieldLimit) {"; - output += "var $susp = $saveSuspension({data: {type: 'Sk.yield'}, resume: function() {}}, '"+this.filename+"',$currLineNo,$currColNo);"; + output += "var $susp = $saveSuspension({data: {type: 'Sk.yield'}, resume: function() {}}, '"+this.filename+"',$currLineNo,$currColNo, $currSource);"; output += "$susp.$blk = $blk;"; output += "$susp.optional = true;"; output += "return $susp;"; @@ -378,9 +380,9 @@ Compiler.prototype._checkSuspension = function(e) { this._jump(retblk); this.setBlock(retblk); - e = e || {lineno: "$currLineNo", col_offset: "$currColNo"}; + e = e || {lineno: "$currLineNo", col_offset: "$currColNo", source: "$currSource"}; - out ("if ($ret && $ret.$isSuspension) { return $saveSuspension($ret,'"+this.filename+"',"+e.lineno+","+e.col_offset+"); }"); + out ("if ($ret && $ret.$isSuspension) { return $saveSuspension($ret,'"+this.filename+"',"+e.lineno+","+e.col_offset+","+e.source+"); }"); this.u.doesSuspend = true; this.u.tempsToSave = this.u.tempsToSave.concat(this.u.localtemps); @@ -1125,7 +1127,7 @@ Compiler.prototype.outputSuspensionHelpers = function (unit) { "var $wakeFromSuspension = function() {" + "var susp = "+unit.scopename+".$wakingSuspension; "+unit.scopename+".$wakingSuspension = undefined;" + "$blk=susp.$blk; $loc=susp.$loc; $gbl=susp.$gbl; $exc=susp.$exc; $err=susp.$err; $postfinally=susp.$postfinally;" + - "$currLineNo=susp.$lineno; $currColNo=susp.$colno; Sk.lastYield=Date.now();" + + "$currLineNo=susp.$lineno;$currColNo=susp.$colno;$currSource=susp.$source;Sk.lastYield=Date.now();" + (hasCell?"$cell=susp.$cell;":""); for (i = 0; i < localsToSave.length; i++) { @@ -1142,11 +1144,11 @@ Compiler.prototype.outputSuspensionHelpers = function (unit) { // Close out function ";"); - output += "var $saveSuspension = function($child, $filename, $lineno, $colno) {" + + output += "var $saveSuspension = function($child, $filename, $lineno, $colno, $source) {" + "var susp = new Sk.misceval.Suspension(); susp.child=$child;" + "susp.resume=function(){"+unit.scopename+".$wakingSuspension=susp; return "+unit.scopename+"("+(unit.ste.generator?"$gen":"")+"); };" + "susp.data=susp.child.data;susp.$blk=$blk;susp.$loc=$loc;susp.$gbl=$gbl;susp.$exc=$exc;susp.$err=$err;susp.$postfinally=$postfinally;" + - "susp.$filename=$filename;susp.$lineno=$lineno;susp.$colno=$colno;" + + "susp.$filename=$filename;susp.$lineno=$lineno;susp.$colno=$colno;susp.source=$source;" + "susp.optional=susp.child.optional;" + (hasCell ? "susp.$cell=$cell;" : ""); @@ -1282,7 +1284,7 @@ Compiler.prototype.cwhile = function (s) { var suspType = "Sk.delay"; var debugBlock = this.newBlock("debug breakpoint for line "+s.lineno); out("if (Sk.breakpoints('"+this.filename+"',"+s.lineno+","+s.col_offset+")) {", - "var $susp = $saveSuspension({data: {type: '"+suspType+"'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+");", + "var $susp = $saveSuspension({data: {type: '"+suspType+"'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+","+s.source+");", "$susp.$blk = "+debugBlock+";", "$susp.optional = true;", "return $susp;", @@ -1350,7 +1352,7 @@ Compiler.prototype.cfor = function (s) { var suspType = "Sk.delay"; var debugBlock = this.newBlock("debug breakpoint for line "+s.lineno); out("if (Sk.breakpoints('"+this.filename+"',"+s.lineno+","+s.col_offset+")) {", - "var $susp = $saveSuspension({data: {type: '"+suspType+"'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+");", + "var $susp = $saveSuspension({data: {type: '"+suspType+"'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+","+s.source+");", "$susp.$blk = "+debugBlock+";", "$susp.optional = true;", "return $susp;", @@ -1901,7 +1903,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal // note special usage of 'this' to avoid having to slice globals into // all function invocations in call - this.u.varDeclsCode += "var $blk=" + entryBlock + ",$exc=[],$loc=" + locals + cells + ",$gbl=this,$err=undefined,$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;"; + this.u.varDeclsCode += "var $blk=" + entryBlock + ",$exc=[],$loc=" + locals + cells + ",$gbl=this,$err=undefined,$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;$currSource=undefined;"; if (Sk.execLimit !== null) { this.u.varDeclsCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now();Sk.execPaused=0}"; } @@ -2254,7 +2256,7 @@ Compiler.prototype.cclass = function (s) { this.u.prefixCode = "var " + scopename + "=(function $" + s.name.v + "$class_outer($globals,$locals,$cell){var $gbl=$globals,$loc=$locals;$free=$globals;"; this.u.switchCode += "(function $" + s.name.v + "$_closure($cell){"; - this.u.switchCode += "var $blk=" + entryBlock + ",$exc=[],$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;"; + this.u.switchCode += "var $blk=" + entryBlock + ",$exc=[],$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;$currSource=undefined;"; if (Sk.execLimit !== null) { this.u.switchCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now();Sk.execPaused=0}"; @@ -2339,7 +2341,7 @@ Compiler.prototype.vstmt = function (s, class_for_super) { if (Sk.debugging && this.u.canSuspend) { debugBlock = this.newBlock("debug breakpoint for line "+s.lineno); out("if (Sk.breakpoints('"+this.filename+"',"+s.lineno+","+s.col_offset+")) {", - "var $susp = $saveSuspension({data: {type: 'Sk.debug'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+");", + "var $susp = $saveSuspension({data: {type: 'Sk.debug'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+","+s.source+");", "$susp.$blk = " + debugBlock + ";", "$susp.optional = true;", "return $susp;", @@ -2695,7 +2697,7 @@ Compiler.prototype.cmod = function (mod) { "var $gbl = $forcegbl || {}, $blk=" + entryBlock + ",$exc=[],$loc=$gbl,$cell={},$err=undefined;" + "$loc.__file__=new Sk.builtins.str('" + this.filename + - "');var $ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;"; + "');var $ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;$currSource=undefined;"; if (Sk.execLimit !== null) { this.u.varDeclsCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now();Sk.execPaused=0}"; @@ -2765,6 +2767,7 @@ Compiler.prototype.handleTraceback = function(doContinue, scopeName) { "}Sk.err=err;err.traceback.push({"+ "lineno:$currLineNo,"+ "colno:$currColNo,"+ + "source:$currSource,"+ "filename:'"+this.filename+"'," + "scope:'"+scopeName+"'"+ "});"+ @@ -2794,6 +2797,7 @@ Sk.compile = function (source, filename, mode, canSuspend, annotate) { var flags = {}; flags.cf_flags = parse.flags; + var st = Sk.symboltable(ast, filename); var c = new Compiler(filename, st, flags.cf_flags, canSuspend, annotate ? source : false); // todo; CO_xxx var funcname = c.cmod(ast); diff --git a/src/constants.js b/src/constants.js index 585975fe54..ed2e427a47 100644 --- a/src/constants.js +++ b/src/constants.js @@ -47,6 +47,7 @@ Sk.builtin.str.$contains = new Sk.builtin.str("__contains__"); Sk.builtin.str.$copy = new Sk.builtin.str("__copy__"); Sk.builtin.str.$dict = new Sk.builtin.str("__dict__"); Sk.builtin.str.$dir = new Sk.builtin.str("__dir__"); +Sk.builtin.str.$doc = new Sk.builtin.str("__doc__"); Sk.builtin.str.$enter = new Sk.builtin.str("__enter__"); Sk.builtin.str.$eq = new Sk.builtin.str("__eq__"); Sk.builtin.str.$exit = new Sk.builtin.str("__exit__"); @@ -70,6 +71,7 @@ Sk.builtin.str.$new = new Sk.builtin.str("__new__"); Sk.builtin.str.$next2 = new Sk.builtin.str("next"); Sk.builtin.str.$next3 = new Sk.builtin.str("__next__"); Sk.builtin.str.$path = new Sk.builtin.str("__path__"); +Sk.builtin.str.$package = new Sk.builtin.str("__package__"); Sk.builtin.str.$repr = new Sk.builtin.str("__repr__"); Sk.builtin.str.$reversed = new Sk.builtin.str("__reversed__"); Sk.builtin.str.$round = new Sk.builtin.str("__round__"); @@ -90,6 +92,11 @@ Sk.misceval.op2method_ = { var builtinNames = [ "int_", + "float_", + "bool", + "dict", + "list", + "str", "lng", "sorted", "range", @@ -128,11 +135,45 @@ var builtinNames = [ "divmod", "format", "globals", - "issubclass" + "issubclass", + + "BaseException", + "Exception", + "StandardError", + "AssertionError", + "AttributeError", + "ImportError", + "IndentationError", + "IndexError", + "KeyError", + "NameError", + "UnboundLocalError", + "OverflowError", + "SyntaxError", + "RuntimeError", + "OSError", + "SuspensionError", + "SystemExit", + "TypeError", + "ValueError", + "ZeroDivisionError", + "TimeLimitError", + "IOError", + "NotImplementedError", + "NegativePowerError", + "ExternalError", + "OperationError", + "SystemError", + "StopIteration", ]; for (var i = 0; i < builtinNames.length; i++) { - Sk.builtin[builtinNames[i]].co_name = new Sk.builtin.str(builtinNames[i]); + var renamed = builtinNames[i]; + if (renamed.endsWith("_")) { + renamed = renamed.slice(0, -1); + } + Sk.builtin[builtinNames[i]].co_name = new Sk.builtin.str(renamed); + Sk.builtin[builtinNames[i]].__name__ = new Sk.builtin.str(renamed); } Sk.builtin.str.prototype["split"].co_varnames = ["sep", "maxsplit"]; diff --git a/src/errors.js b/src/errors.js index e68bc567da..5c1bede335 100644 --- a/src/errors.js +++ b/src/errors.js @@ -32,6 +32,9 @@ Sk.builtin.BaseException = function (args) { this.traceback = []; // TODO: Hack, this exception isn't guaranteed to be thrown! this.err = Sk.err; + this.__cause__ = Sk.builtin.none.none$; + this.__context__ = Sk.builtin.none.none$; + this.__suppress_context__ = Sk.builtin.none.none$; //Sk.err = this; // For errors occurring during normal execution, the line/col/etc @@ -58,7 +61,14 @@ Sk.builtin.BaseException.prototype.tp$str = function () { ret += ": " + (this.args.v.length > 0 ? this.args.v[0].v : ""); } if (this.traceback.length !== 0) { - ret += " on line " + this.traceback[0].lineno; + var lineno = this.traceback[0].lineno; + ret += " on line "; + console.log(lineno); + if (Sk.builtin.checkInt(lineno)) { + ret += lineno.v !== undefined ? lineno.v : lineno; + } else { + ret += lineno; + } } else { ret += " at "; } @@ -91,6 +101,36 @@ Sk.builtin.BaseException.prototype.toString = function () { Sk.builtin.BaseException.prototype.args = { "tp$descr_get": function(self, clstype) { return self.args; + }, + "tp$descr_set": function(self, value) { + self.args = value; + } +}; + +Sk.builtin.BaseException.prototype.__cause__ = { + "tp$descr_get": function(self, clstype) { + return self.__cause__; + }, + "tp$descr_set": function(self, value) { + self.__cause__ = value; + } +}; + +Sk.builtin.BaseException.prototype.__context__ = { + "tp$descr_get": function(self, clstype) { + return self.__context__; + }, + "tp$descr_set": function(self, value) { + self.__context__ = value; + } +}; + +Sk.builtin.BaseException.prototype.__suppress_context__ = { + "tp$descr_get": function(self, clstype) { + return self.__suppress_context__; + }, + "tp$descr_set": function(self, value) { + self.__suppress_context__ = value; } }; @@ -315,6 +355,8 @@ Sk.builtin.SyntaxError.prototype.tp$getattr = function (name) { if (_name === "lineno") { return this[_name]; + } else if (_name == "__name__") { + return Sk.builtin.str("SyntaxError"); } } @@ -340,6 +382,24 @@ Sk.abstr.setUpInheritance("RuntimeError", Sk.builtin.RuntimeError, Sk.builtin.St Sk.exportSymbol("Sk.builtin.RuntimeError", Sk.builtin.RuntimeError); +/** + * @constructor + * @extends Sk.builtin.StandardError + * @param {...*} args + */ +Sk.builtin.OSError = function (args) { + var o; + if (!(this instanceof Sk.builtin.OSError)) { + o = Object.create(Sk.builtin.OSError.prototype); + o.constructor.apply(o, arguments); + return o; + } + Sk.builtin.StandardError.apply(this, arguments); +}; +Sk.abstr.setUpInheritance("OSError", Sk.builtin.OSError, Sk.builtin.StandardError); +Sk.exportSymbol("Sk.builtin.OSError", Sk.builtin.OSError); + + /** * @constructor * @extends Sk.builtin.StandardError @@ -600,11 +660,12 @@ Sk.builtin.frame.prototype.tp$getattr = function (name) { case "f_code": return Sk.builtin.none.none$; case "f_globals": return Sk.builtin.none.none$; case "f_lasti": return Sk.builtin.none.none$; - case "f_lineno": return this.trace.lineno; + case "f_lineno": return Sk.ffi.remapToPy(this.trace.lineno); + case "f_line": return Sk.ffi.remapToPy(this.trace.source); case "f_locals": return Sk.builtin.none.none$; case "f_trace": return Sk.builtin.none.none$; - case "co_filename": return this.trace.filename; - case "co_name": return this.trace.scope; + case "co_filename": return Sk.ffi.remapToPy(this.trace.filename); + case "co_name": return Sk.ffi.remapToPy(this.trace.scope); } } @@ -630,6 +691,7 @@ Sk.builtin.traceback = function(trace) { this.tb_lineno = new Sk.builtin.int_(trace.lineno); // TODO: Hack, you know this isn't right this.tb_frame = new Sk.builtin.frame(trace); + this.tb_source = new Sk.builtin.str(trace.source); //tb_frame, tb_lasti, tb_lineno, tb_next @@ -660,6 +722,7 @@ Sk.builtin.traceback.prototype.tp$getattr = function (name) { switch (_name) { case "tb_lineno": + case "tb_source": case "tb_frame": case "tb_next": return this[_name]; } diff --git a/src/import.js b/src/import.js index 0fb2810296..d22f1540a6 100644 --- a/src/import.js +++ b/src/import.js @@ -7,6 +7,15 @@ Sk.sysmodules = new Sk.builtin.dict([]); Sk.realsyspath = undefined; +Sk.getCurrentSysModules = function() { + var sys = Sk.sysmodules.mp$lookup(Sk.builtin.str("sys")); + if (sys === undefined) { + return Sk.sysmodules; + } else { + return sys.tp$getattr(Sk.builtin.str("modules")); + } +}; + /** * @param {string} name to look for * @param {string} ext extension to use (.py or .js) @@ -115,6 +124,42 @@ Sk.doOneTimeInitialization = function (canSuspend) { } } + // Mock builtin module + var modname = new Sk.builtin.str("builtins"); + Sk.builtins["__builtins__"] = new Sk.builtin.module(); + Sk.builtins["__builtins__"].$d = { + "__name__": modname, + "__doc__":new Sk.builtin.str("Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices."), + "__package__": Sk.builtin.str.$emptystr + }; + Sk.builtins["__builtins__"].overrides = new Sk.builtin.dict([]); + Sk.builtins["__builtins__"].tp$getattr = function(pyName, canSuspend) { + var jsName = Sk.ffi.remapToJs(pyName); + var overrides = this.overrides; + if (jsName === "__dict__") { + return this; + } else if (jsName === "copy") { + return function(k, d) { + return overrides.copy.tp$call([overrides]); + }; + } else if (jsName === "get") { + return function(k, d) { + return overrides.get.tp$call([overrides, k, d]); + }; + } + }; + Sk.builtins["__builtins__"].tp$getitem = function(key) { + var overridden = this.overrides.mp$lookup(key); + if (overridden !== undefined) { + return overridden; + } + var jsName = Sk.ffi.remapToJs(key); + if (Sk.builtins[jsName] !== undefined) { + return Sk.builtins[jsName]; + } + throw new Sk.builtin.NameError("name '" + Sk.unfixReserved(name) + "' is not defined"); + }; + Sk.getCurrentSysModules().mp$ass_subscript(modname, Sk.builtins["__builtins__"]); for (var file in Sk.internalPy.files) { var fileWithoutExtension = file.split(".")[0].split("/")[1]; @@ -198,10 +243,10 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela // if leaf is already in sys.modules, early out try { - prev = Sk.sysmodules.mp$subscript(new Sk.builtin.str(modname)); + prev = Sk.getCurrentSysModules().mp$subscript(new Sk.builtin.str(modname)); // if we're a dotted module, return the top level, otherwise ourselves if (modNameSplit.length > 1) { - return Sk.sysmodules.mp$subscript(new Sk.builtin.str(absolutePackagePrefix + modNameSplit[0])); + return Sk.getCurrentSysModules().mp$subscript(new Sk.builtin.str(absolutePackagePrefix + modNameSplit[0])); } else { return prev; } @@ -230,7 +275,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela if (!topLevelModuleToReturn) { return undefined; } - parentModule = Sk.sysmodules.mp$subscript(new Sk.builtin.str(absolutePackagePrefix + parentModName)); + parentModule = Sk.getCurrentSysModules().mp$subscript(new Sk.builtin.str(absolutePackagePrefix + parentModName)); searchFileName = modNameSplit[modNameSplit.length-1]; searchPath = parentModule.tp$getattr(Sk.builtin.str.$path); } @@ -300,7 +345,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela } // Now we know this module exists, we can add it to the cache - Sk.sysmodules.mp$ass_subscript(new Sk.builtin.str(modname), module); + Sk.getCurrentSysModules().mp$ass_subscript(new Sk.builtin.str(modname), module); module.$js = co.code; // todo; only in DEBUG? finalcode = co.code; @@ -389,6 +434,9 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela } } + // TODO: answer.py.instructor_append + // Somehow adding it into the name of the module + if (topLevelModuleToReturn) { // if we were a dotted name, then we want to return the top-most // package. we store ourselves into our parent as an attribute @@ -468,7 +516,7 @@ Sk.importBuiltinWithBody = function (name, dumpJS, body, canSuspend) { }; Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { - //console.log("Importing: ", JSON.stringify(name), JSON.stringify(fromlist), level); + //if (name == "") { debugger; } // Save the Sk.globals variable importModuleInternal_ may replace it when it compiles @@ -507,7 +555,7 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { relativeToPackageName = relativeToPackageNames.join("."); } try { - relativeToPackage = Sk.sysmodules.mp$subscript(new Sk.builtin.str(relativeToPackageName)); + relativeToPackage = Sk.getCurrentSysModules().mp$subscript(new Sk.builtin.str(relativeToPackageName)); } catch(e) { relativeToPackageName = undefined; } @@ -554,7 +602,7 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { var leafModule; var importChain; - leafModule = Sk.sysmodules.mp$subscript( + leafModule = Sk.getCurrentSysModules().mp$subscript( new Sk.builtin.str((relativeToPackageName || "") + ((relativeToPackageName && name) ? "." : "") + name) @@ -603,3 +651,4 @@ Sk.exportSymbol("Sk.importMainWithBody", Sk.importMainWithBody); Sk.exportSymbol("Sk.importBuiltinWithBody", Sk.importBuiltinWithBody); Sk.exportSymbol("Sk.builtin.__import__", Sk.builtin.__import__); Sk.exportSymbol("Sk.importStar", Sk.importStar); +Sk.exportSymbol("Sk.getCurrentSysModules", Sk.getCurrentSysModules); \ No newline at end of file diff --git a/src/int.js b/src/int.js index c9b132b2b2..6fcbe7deff 100644 --- a/src/int.js +++ b/src/int.js @@ -1082,6 +1082,10 @@ Sk.builtin.int_.prototype.str$ = function (base, sign) { return tmp; }; +Sk.builtin.int_.prototype.bit_length = function () { + return new Sk.builtin.int_(Math.abs(this.v).toString(2).length); +}; + /** * Takes a JavaScript string and returns a number using the parser and negater * functions (for int/long right now) diff --git a/src/lib/functools.py b/src/lib/functools.py index d5dff1e81e..7d4b5f0f02 100644 --- a/src/lib/functools.py +++ b/src/lib/functools.py @@ -1,7 +1,10 @@ from reprlib import recursive_repr -def wraps(wrapper, wrapped): - return wrapper +def wraps(wrapped): + def make_wrapper(wrapper): + wrapper.__name__ = wrapped.__name__ + return wrapper + return make_wrapper # Purely functional, no descriptor behaviour class partial: diff --git a/src/lib/itertools.py b/src/lib/itertools.py index ee1486720b..3ca951dd75 100644 --- a/src/lib/itertools.py +++ b/src/lib/itertools.py @@ -3,12 +3,21 @@ def islice(iterable, limit): # islice('ABCDEFG', 2, 4) --> C D # islice('ABCDEFG', 2, None) --> C D E F G # islice('ABCDEFG', 0, None, 2) --> A C E G - it = iter(range(0, limit, 1)) + start, stop, step = 0, limit, 1 + it = iter(range(start, stop, step)) try: nexti = next(it) except StopIteration: + # Consume *iterable* up to the *start* position. + for i, element in zip(range(start), iterable): + pass return - for i, element in enumerate(iterable): - if i == nexti: - yield element - nexti = next(it) \ No newline at end of file + try: + for i, element in enumerate(iterable): + if i == nexti: + yield element + nexti = next(it) + except StopIteration: + # Consume to *stop*. + for i, element in zip(range(i + 1, stop), iterable): + pass diff --git a/src/lib/parking/__init__.js b/src/lib/parking/__init__.js new file mode 100644 index 0000000000..5232113422 --- /dev/null +++ b/src/lib/parking/__init__.js @@ -0,0 +1,210 @@ +var $builtinmodule = function(name) { + var WEEKDAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]; + var FULL_DAYS = { + "mon": "Monday", "tue": "Tuesday", "wed": "Wednesday", + "thu": "Thursday", "fri": "Friday", "sat": "Saturday", + "sun": "Sunday" + }; + + var convert_day = function(day) { + return WEEKDAYS.indexOf(day.name.v); + }; + var convert_day_string = function(day) { + return WEEKDAYS.indexOf(day.v.toLowerCase().slice(0, 3)); + }; + var convert_time = function(hour, minute, meridian) { + return hour*60 + minute + (meridian.toLowerCase() === "pm" ? 12*60 : 0); + }; + + var mod = {}; + + var time = function($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self, hour, minute, meridian) { + Sk.builtin.pyCheckArgs("__init__", arguments, 4, 4); + Sk.builtin.pyCheckType("hour", "int", Sk.builtin.checkInt(hour)); + Sk.builtin.pyCheckType("minute", "int", Sk.builtin.checkInt(minute)); + Sk.builtin.pyCheckType("meridian", "int", Sk.builtin.checkString(meridian)); + self.hour = hour; + self.minute = minute; + self.meridian = meridian; + self.meridian.v = self.meridian.v.toLowerCase(); + }); + $loc.__str__ = new Sk.builtin.func(function (self) { + return Sk.ffi.remapToPy("<"+(self.hour.v || 12) +":"+ + (self.minute.v < 10 ? "0"+self.minute.v : self.minute.v)+ + self.meridian.v+">"); + }); + $loc.__repr__ = new Sk.builtin.func(function (self) { + return Sk.ffi.remapToPy("<"+(self.hour.v || 12)+":"+ + (self.minute.v < 10 ? "0"+self.minute.v : self.minute.v)+ + self.meridian.v+">"); + }); + var comparison = function (operation, self, other) { + if (Sk.builtin.isinstance(other, mod.Time).v) { + if (operation(convert_time(self.hour.v % 12, self.minute.v, self.meridian.v), + convert_time(other.hour.v % 12, other.minute.v, other.meridian.v))) { + return Sk.ffi.remapToPy(true); + } else { + return Sk.ffi.remapToPy(false); + } + } else { + return Sk.ffi.remapToPy(false); + } + }; + $loc.__eq__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + return comparison(function(l,r) {return l==r;}, self, other); + }); + + $loc.__ne__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + if (!Sk.builtin.isinstance(other, mod.Time).v) { + return Sk.builtin.bool.true$; + } + return comparison(function(l,r) {return l!=r;}, self, other); + }); + + $loc.__lt__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + return comparison(function(l,r) {return l < r;}, self, other); + }); + + $loc.__gt__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + return comparison(function(l,r) {return l > r;}, self, other); + }); + + $loc.__le__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + return comparison(function(l,r) {return l <= r;}, self, other); + }); + + $loc.__ge__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + return comparison(function(l,r) {return l >= r;}, self, other); + }); + }; + + var day = function($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self, name) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name)); + self.name = name; + self.name.v = self.name.v.toLowerCase().slice(0,3); + }); + $loc.__str__ = new Sk.builtin.func(function (self) { + return Sk.ffi.remapToPy("<"+FULL_DAYS[self.name.v]+">"); + }); + $loc.__repr__ = new Sk.builtin.func(function (self) { + return Sk.ffi.remapToPy("<"+FULL_DAYS[self.name.v]+">"); + }); + var comparison = function (operation, self, other) { + if (Sk.builtin.isinstance(other, mod.Day).v) { + if (operation(convert_day(self), convert_day(other))) { + return Sk.ffi.remapToPy(true); + } else { + return Sk.builtin.bool.false$; + } + } else { + return Sk.builtin.bool.false$; + } + }; + $loc.__eq__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + return comparison(function(l,r) {return l==r;}, self, other); + }); + + $loc.__ne__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + if (!Sk.builtin.isinstance(other, mod.Day).v) { + return Sk.builtin.bool.true$; + } + return comparison(function(l,r) {return l!=r;}, self, other); + }); + + $loc.__lt__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + return comparison(function(l,r) {return l < r;}, self, other); + }); + + $loc.__gt__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + return comparison(function(l,r) {return l > r;}, self, other); + }); + + $loc.__le__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + return comparison(function(l,r) {return l <= r;}, self, other); + }); + + $loc.__ge__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); + return comparison(function(l,r) {return l >= r;}, self, other); + }); + }; + + mod.Day = Sk.misceval.buildClass(mod, day, "Day", []); + mod.Time = Sk.misceval.buildClass(mod, time, "Time", []); + + mod._today = undefined; + mod._hour = undefined; + mod._minute = undefined; + mod._meridian = undefined; + + mod.today = new Sk.builtin.func(function() { + var t = ((new Date).getDay() + 6) % 7; // would be -1, but % is broken for negatives in JS + t = Sk.today || mod._today || Sk.ffi.remapToPy(WEEKDAYS[t]); + return Sk.misceval.callsim(mod.Day, t); + }); + + mod.now = new Sk.builtin.func(function() { + var d = new Date(); + var hour = d.getHours() % 12, + minute = d.getMinutes(), + meridian = d.getHours() < 12 ? "am" : "pm"; + hour = Sk._hour || mod._hour || Sk.ffi.remapToPy(hour); + minute = Sk._minute || mod._minute || Sk.ffi.remapToPy(minute); + meridian = Sk._meridian || mod._meridian || Sk.ffi.remapToPy(meridian); + return Sk.misceval.callsim(mod.Time, hour, minute, meridian); + }); + + mod.day_compare = new Sk.builtin.func(function(comparison, value, day) { + Sk.builtin.pyCheckArgs("day_compare", arguments, 3, 3); + Sk.builtin.pyCheckType("comparison", "string", Sk.builtin.checkString(comparison)); + Sk.builtin.pyCheckType("value", "Day", Sk.builtin.isinstance(value, mod.Day).v); + Sk.builtin.pyCheckType("day", "string", Sk.builtin.checkString(day)); + var day_n = convert_day_string(day), + value_n = convert_day(value); + switch (comparison.v) { + case "IS": return Sk.ffi.remapToPy(value_n == day_n); + case "BEFORE_EQUAL": return Sk.ffi.remapToPy(value_n <= day_n); + case "AFTER_EQUAL": return Sk.ffi.remapToPy(value_n >= day_n); + case "BEFORE": return Sk.ffi.remapToPy(value_n < day_n); + case "AFTER": return Sk.ffi.remapToPy(value_n > day_n); + case "IS_NOT": return Sk.ffi.remapToPy(value_n != day_n); + default: throw new Sk.builtins.ValueError("Unknown comparison"); + } + }); + + mod.time_compare = new Sk.builtin.func(function(comparison, left, hour, minute, meridian) { + Sk.builtin.pyCheckArgs("time_compare", arguments, 5, 5); + Sk.builtin.pyCheckType("comparison", "string", Sk.builtin.checkString(comparison)); + Sk.builtin.pyCheckType("left", "Time", Sk.builtin.isinstance(left, mod.Time).v); + Sk.builtin.pyCheckType("hour", "int", Sk.builtin.checkInt(hour)); + Sk.builtin.pyCheckType("minute", "int", Sk.builtin.checkInt(hour)); + Sk.builtin.pyCheckType("meridian", "string", Sk.builtin.checkString(meridian)); + var right_time = convert_time(hour.v % 12, minute.v, meridian.v), + left_time = convert_time(left.hour.v % 12, left.minute.v, left.meridian.v); + switch (comparison.v) { + case "IS": return Sk.ffi.remapToPy(left_time == right_time); + case "BEFORE_EQUAL": return Sk.ffi.remapToPy(left_time <= right_time); + case "AFTER_EQUAL": return Sk.ffi.remapToPy(left_time >= right_time); + case "BEFORE": return Sk.ffi.remapToPy(left_time < right_time); + case "AFTER": return Sk.ffi.remapToPy(left_time > right_time); + case "IS_NOT": return Sk.ffi.remapToPy(left_time != right_time); + default: throw new Sk.builtins.ValueError("Unknown comparison"); + } + }); + + return mod; +}; \ No newline at end of file diff --git a/src/lib/pedal/assertions/assertions.py b/src/lib/pedal/assertions/assertions.py index 734a0c60af..5e185a6bc8 100644 --- a/src/lib/pedal/assertions/assertions.py +++ b/src/lib/pedal/assertions/assertions.py @@ -7,6 +7,8 @@ from pedal.sandbox.sandbox import DataSandbox from pedal.assertions.setup import _setup_assertions, AssertionException +# TODO: Allow bundling of assertions to make a table + iterable = lambda obj: hasattr(obj,'__iter__') or hasattr(obj,'__getitem__') _MAX_LENGTH = 80 @@ -565,7 +567,11 @@ def assertHasFunction(obj, function, args=None, returns=None, if isinstance(obj, DataSandbox): comparison = lambda o, f: f in o.data else: - comparison = lambda o, f: f in hasattr(o, f) + def comparison(o, f): + try: + return f in o + except: + return hasattr(o, f) if not _basic_assertion(obj, function, comparison, "Could not find function {}{}", @@ -576,7 +582,10 @@ def assertHasFunction(obj, function, args=None, returns=None, if isinstance(obj, DataSandbox): student_function = obj.data[function] else: - student_function = getattr(obj, function) + try: + student_function = obj[function] + except: + student_function = getattr(obj, function) if _basic_assertion(student_function, function, lambda l, r: callable(l), "The value {} is in the variable {}, and that value is not a callable function.", diff --git a/src/lib/pedal/assertions/setup.py b/src/lib/pedal/assertions/setup.py index 609fbffaa1..e282034c22 100644 --- a/src/lib/pedal/assertions/setup.py +++ b/src/lib/pedal/assertions/setup.py @@ -4,7 +4,8 @@ from pedal.sandbox.exceptions import SandboxStudentCodeException class AssertionException(Exception): - pass + def __str__(self): + return self.args[0] def _topological_sort(names, orderings): visited = set() diff --git a/src/lib/pedal/sandbox/__init__.py b/src/lib/pedal/sandbox/__init__.py index 11e4843641..212322d314 100644 --- a/src/lib/pedal/sandbox/__init__.py +++ b/src/lib/pedal/sandbox/__init__.py @@ -1,5 +1,5 @@ from pedal.report import MAIN_REPORT -from pedal.sandbox.sandbox import Sandbox +from pedal.sandbox.sandbox import Sandbox, DataSandbox # Compatibility API ''' diff --git a/src/lib/pedal/sandbox/compatibility.py b/src/lib/pedal/sandbox/compatibility.py index 3fb94f9807..2ad0866414 100644 --- a/src/lib/pedal/sandbox/compatibility.py +++ b/src/lib/pedal/sandbox/compatibility.py @@ -17,11 +17,11 @@ def run_student(raise_exceptions=False, report=None, old_style_messages=False): report = MAIN_REPORT sandbox = _check_sandbox(report) source_code = report['source']['code'] - sandbox.run(source_code, report_exceptions=not raise_exceptions) + filename = report['source']['filename'] + sandbox.run(source_code, as_filename=filename, report_exceptions=not raise_exceptions) if raise_exceptions: raise_exception(sandbox.exception, sandbox.exception_position, - report=report, message=None if old_style_messages else - sandbox.exception_formatted) + report=report, message=None if old_style_messages else sandbox.exception_formatted) return sandbox.exception diff --git a/src/lib/pedal/sandbox/exceptions.py b/src/lib/pedal/sandbox/exceptions.py index 5de27ce608..042ecc032a 100644 --- a/src/lib/pedal/sandbox/exceptions.py +++ b/src/lib/pedal/sandbox/exceptions.py @@ -5,7 +5,8 @@ try: TimeoutError except NameError: - TimeoutError = Exception + class TimeoutError(Exception): + pass class SandboxException(Exception): """ @@ -154,6 +155,9 @@ def _is_relevant_tb_level(self, tb): if self.full_traceback: return False filename, a_, b_, _ = traceback.extract_tb(tb, limit=1)[0] + # Is the error in the student file? + if filename == self.student_filename: + return False # Is the error in the instructor file? if filename == self.instructor_filename: return True diff --git a/src/lib/pedal/sandbox/mocked.py b/src/lib/pedal/sandbox/mocked.py index 61cb9144f8..a0b0df7017 100644 --- a/src/lib/pedal/sandbox/mocked.py +++ b/src/lib/pedal/sandbox/mocked.py @@ -46,9 +46,11 @@ def _disabled_globals(): """ raise RuntimeError("You are not allowed to call 'globals'.") + class FunctionNotAllowed(Exception): pass + def disabled_builtin(name): def _disabled_version(*args, **kwargs): raise FunctionNotAllowed("You are not allowed to call '{}'.".format(name)) @@ -60,6 +62,8 @@ def _disabled_version(*args, **kwargs): # TODO: Turn this into a function that lets us more elegantly specify valid and # invalid filenames/paths + + def _restricted_open(name, mode='r', buffering=-1): if _OPEN_FORBIDDEN_NAMES.search(name): raise RuntimeError("The filename you passed to 'open' is restricted.") @@ -69,11 +73,14 @@ def _restricted_open(name, mode='r', buffering=-1): return _original_builtins['open'](name, mode, buffering) # TODO: Allow this to be flexible + + def _restricted_import(name, globals=None, locals=None, fromlist=(), level=0): if name == 'pedal' or name.startswith('pedal.'): raise RuntimeError("You cannot import pedal!") return _original_builtins['__import__'](name, globals, locals, fromlist, level) + try: __builtins__ except NameError: @@ -100,7 +107,7 @@ def _restricted_import(name, globals=None, locals=None, fromlist=(), level=0): } -def _make_inputs(*input_list, **kwargs): +def make_inputs(input_list, repeat=None): """ Helper function for creating mock user input. @@ -111,10 +118,6 @@ def _make_inputs(*input_list, **kwargs): will return the next element of input_list each time it is called. """ - if 'repeat' in kwargs: - repeat = kwargs['repeat'] - else: - repeat = None generator = iter(input_list) def mock_input(prompt=''): diff --git a/src/lib/pedal/sandbox/sandbox.py b/src/lib/pedal/sandbox/sandbox.py index 3d13474c9d..8e29eb3316 100644 --- a/src/lib/pedal/sandbox/sandbox.py +++ b/src/lib/pedal/sandbox/sandbox.py @@ -89,6 +89,8 @@ def functions(self): def var(self): return {k: SandboxVariable(k, v) for k, v in self.data.items()} + def __repr__(self): + return "" class Sandbox(DataSandbox): """ @@ -189,7 +191,8 @@ def __init__(self, initial_data=None, self.report_exceptions_mode = False self.raise_exceptions_mode = False # Input - self.inputs = None + self.set_input(None) + self._input_tracker = self._track_inputs() # Modules if modules is None: modules = {'matplotlib': True, @@ -302,22 +305,34 @@ def append_output(self, raw_output): self.output.extend(lines) self.output_contexts[self.call_id].extend(lines) - def set_input(self, inputs): + def set_input(self, inputs, clear=True): """ Queues the given value as the next arguments to the `input` function. """ - if isinstance(inputs, tuple): - self.inputs = mocked._make_inputs(*inputs) - else: + if inputs is None: + self.inputs = [] + if clear: + self.inputs.clear() + if isinstance(inputs, str): + self.inputs.append(inputs) + elif isinstance(inputs, (list, tuple)): + self.inputs.extend(inputs) + elif inputs is not None: + # TODO: intelligently handle custom generator self.inputs = inputs - def _track_inputs(self, inputs): + def _track_inputs(self): """ Wraps an input function with a tracker. """ - def _input_tracker(*args, **kwargs): - value_entered = inputs(*args, **kwargs) + def _input_tracker(prompt, *args, **kwargs): + print(prompt) + if self.inputs: + value_entered = self.inputs.pop(0) + else: + # TODO: Make this smarter, more elegant in choosing IF we should repeat 0 + value_entered = '0' self.input_contexts[self.call_id].append(value_entered) return value_entered @@ -369,7 +384,7 @@ def _make_temporary(self, category, name, value, context): self.call_contexts[self.call_id].append("{} = {}".format(key, value)) return key - def run_file(filename, as_filename=None, modules=None, inputs=None, + def run_file(self, filename, as_filename=None, modules=None, inputs=None, threaded=None, context=None, report_exceptions=None, raise_exceptions=None): """ @@ -381,8 +396,8 @@ def run_file(filename, as_filename=None, modules=None, inputs=None, a string, tracebacks will be shown with the given context. If False, no context will be given. """ - if _as_filename is None: - _as_filename = filename + if as_filename is None: + as_filename = filename with open(filename, 'r') as code_file: code = code_file.read() + '\n' self.run(code, as_filename, modules, inputs, threaded, @@ -438,7 +453,7 @@ def call(self, function, *args, **kwargs): as_filename = kwargs.pop('as_filename', self.instructor_filename) target = kwargs.pop('target', '_') modules = kwargs.pop('modules', {}) - inputs = kwargs.pop('inputs', self.inputs) + inputs = kwargs.pop('inputs', None) threaded = kwargs.pop('threaded', self.threaded) context = kwargs.pop('context', self.context) keep_context = kwargs.pop('keep_context', self.keep_context) @@ -460,8 +475,8 @@ def call(self, function, *args, **kwargs): # self.call_contexts[self.call_id].append(student_call) # if context is not False: # self.call_contexts[self.call_id] = context - self.run(actual, as_filename, modules, inputs, - threaded=threaded, + self.run(actual, as_filename=as_filename, modules=modules, + inputs=inputs, threaded=threaded, context=context, keep_context=keep_context, report_exceptions=report_exceptions, raise_exceptions=raise_exceptions) @@ -610,20 +625,13 @@ def run(self, code, as_filename=None, modules=None, inputs=None, if as_filename is None: as_filename = os.path.basename(self.report['source']['filename']) - if inputs is None: - if self.inputs is None: - inputs = mocked._make_inputs('0', repeat='0') - else: - inputs = self.inputs - if isinstance(inputs, (tuple, list)): - inputs = mocked._make_inputs(*inputs) - elif isinstance(inputs, str): - inputs = mocked._make_inputs(inputs) - inputs = self._track_inputs(inputs) + # todo: earlier version of inputs being made? + if inputs is not None: + self.set_input(inputs) # Override builtins and mock stuff out mocked_functions = self.mocked_functions.copy() - mocked_functions['input'] = inputs - mocked_functions['raw_input'] = inputs + mocked_functions['input'] = self._input_tracker + mocked_functions['raw_input'] = self._input_tracker mocked_functions['sys'] = sys mocked_functions['os'] = os mocked._override_builtins(self.data, mocked_functions) @@ -636,9 +644,9 @@ def run(self, code, as_filename=None, modules=None, inputs=None, x = sys.stdout capture_stdout = io.StringIO() self._start_patches( + patch.dict('sys.modules', self.mocked_modules), patch('sys.stdout', capture_stdout), patch('time.sleep', return_value=None), - patch.dict('sys.modules', self.mocked_modules) ) # TODO: Hack, add more flexibile way to specify unusable modules for module in list(sys.modules.keys()): @@ -649,7 +657,6 @@ def run(self, code, as_filename=None, modules=None, inputs=None, with self.trace._as_filename(as_filename, code): exec(compiled_code, self.data) except Exception as user_exception: - print(user_exception) self._stop_patches() info = sys.exc_info() self._capture_exception(user_exception, info, diff --git a/src/lib/pedal/tifa/builtin_definitions.py b/src/lib/pedal/tifa/builtin_definitions.py index a35e56be03..5c84510fa1 100644 --- a/src/lib/pedal/tifa/builtin_definitions.py +++ b/src/lib/pedal/tifa/builtin_definitions.py @@ -147,45 +147,21 @@ def _builtin_zip(tifa, function_type, callee, args, position): return ListType(empty=True) +# TODO: Exceptions + def get_builtin_function(name): # Void Functions if name == "print": return FunctionType(name="print", returns=NoneType()) # Math Functions - elif name == "int": - return FunctionType(name="int", returns=NumType()) - elif name == "abs": - return FunctionType(name="abs", returns=NumType()) - elif name == "float": - return FunctionType(name="float", returns=NumType()) - elif name == "len": - return FunctionType(name="len", returns=NumType()) - elif name == "ord": - return FunctionType(name="ord", returns=NumType()) - elif name == "pow": - return FunctionType(name="pow", returns=NumType()) - elif name == "round": - return FunctionType(name="round", returns=NumType()) - elif name == "sum": - return FunctionType(name="sum", returns=NumType()) + elif name in ("int", "abs", "float", "len", "ord", "pow", "round", "sum"): + return FunctionType(name=name, returns=NumType()) # Boolean Functions - elif name == "bool": - return FunctionType(name="bool", returns=BoolType()) - elif name == "all": - return FunctionType(name="all", returns=BoolType()) - elif name == "any": - return FunctionType(name="any", returns=BoolType()) - elif name == "isinstance": - return FunctionType(name="isinstance", returns=BoolType()) + elif name in ("bool", "all", "any", "isinstance"): + return FunctionType(name=name, returns=BoolType()) # String Functions - elif name == "input": - return FunctionType(name="input", returns=StrType()) - elif name == "str": - return FunctionType(name="str", returns=StrType()) - elif name == "chr": - return FunctionType(name="chr", returns=StrType()) - elif name == "repr": - return FunctionType(name="repr", returns=StrType()) + elif name in ("str", 'chr', 'bin', 'repr', 'input'): + return FunctionType(name=name, returns=StrType()) # File Functions elif name == "open": return FunctionType(name="open", returns=FileType()) @@ -229,3 +205,5 @@ def get_builtin_function(name): returns=DictType(keys=StrType(), values=UnknownType(), empty=False)) + elif name in ("classmethod", "staticmethod"): + return FunctionType(name=name, returns='identity') \ No newline at end of file diff --git a/src/lib/pedal/tifa/messages.py b/src/lib/pedal/tifa/messages.py index 7ba869c3fe..8bc2cde2c7 100644 --- a/src/lib/pedal/tifa/messages.py +++ b/src/lib/pedal/tifa/messages.py @@ -39,6 +39,10 @@ def _format_message(issue, data): return ("You attempted to return outside of a function on line {line}." " But you can only return from within a function." ).format(line=data['position']['line']) + elif issue == "Multiple Return Types": + return ("Your function returned {actual} on line {line}, even though you defined it to" + " return {expected}. Your function should return values consistently." + ).format(expected=data['expected'], actual=data['actual'], line=data['position']['line']) elif issue == 'Write out of scope': # DEPRECATED # Attempted to modify a variable in a higher scope @@ -125,6 +129,15 @@ def _format_message(issue, data): "{line}. But you can't do that with that operator. Make " "sure both sides of the operator are the right type." ).format(op=op, left=left, right=right, line=line) + elif issue == "Parameter Type Mismatch": + name = data['parameter_name'] + parameter = data['parameter'].singular_name + argument = data['argument'].singular_name + line = data['position']['line'] + return ("You defined the parameter {name} on line {line} " + "as {parameter}. However, the argument passed to that parameter " + "was {argument}. The formal parameter type must match the argument's type." + ).format(name=name, argument=argument, parameter=parameter, line=line) elif issue == 'Read out of scope': return ("You attempted to read a variable from a different scope on " "line {line}. You should only use variables inside the " diff --git a/src/lib/pedal/tifa/tifa.py b/src/lib/pedal/tifa/tifa.py index 0d723fd6f0..5bb9d206d6 100644 --- a/src/lib/pedal/tifa/tifa.py +++ b/src/lib/pedal/tifa/tifa.py @@ -9,7 +9,7 @@ ListType, StrType, GeneratorType, DictType, ModuleType, SetType, # FileType, DayType, TimeType, - type_from_json, type_to_literal, + type_from_json, type_to_literal, get_tifa_type, LiteralNum, LiteralBool, LiteralNone, LiteralStr, LiteralTuple) @@ -617,6 +617,14 @@ def definition(tifa, call_type, call_name, parameters, call_position): # TODO: Handle special types of parameters for arg, parameter in zip(args, parameters): name = arg.arg + if arg.annotation: + self.visit(arg.annotation) + annotation = get_tifa_type(arg.annotation, {}) + # TODO: Check that arg.type and parameter type match! + if not are_types_equal(annotation, parameter): + self.report_issue("Parameter Type Mismatch", + {"parameter": annotation, "parameter_name": name, + "argument": parameter}) if parameter is not None: parameter = parameter.clone_mutably() self.store_variable(name, parameter, position) @@ -626,10 +634,19 @@ def definition(tifa, call_type, call_name, parameters, call_position): self.visit_statements(node.body) return_state = self.find_variable_scope("*return") return_value = NoneType() + # TODO: Figure out if we are not returning something when we should # If the pseudo variable exists, we load it and get its type if return_state.exists and return_state.in_scope: return_state = self.load_variable("*return", call_position) return_value = return_state.type + if node.returns: + #self.visit(node.returns) + returns = get_tifa_type(node.returns, {}) + if not are_types_equal(return_value, returns): + self.report_issue("Multiple Return Types", + {"expected": returns.singular_name, + "actual": return_value.singular_name, + "position": return_state.position}) return return_value function = FunctionType(definition=definition, name=function_name) @@ -929,6 +946,7 @@ def store_iter_variable(self, name, type, position=None): return state def return_variable(self, type): + return self.store_variable("*return", type) def append_variable(self, name, type, position=None): diff --git a/src/lib/pedal/tifa/type_definitions.py b/src/lib/pedal/tifa/type_definitions.py index 0535adfbe2..c8a0e466fa 100644 --- a/src/lib/pedal/tifa/type_definitions.py +++ b/src/lib/pedal/tifa/type_definitions.py @@ -1,3 +1,6 @@ +import ast + + def are_literals_equal(first, second): if first is None or second is None: return False @@ -512,3 +515,38 @@ def type_to_literal(type): else: # TODO: Finish the mapping return LiteralStr("") + + +TYPE_STRINGS = { + "str": StrType, "string": StrType, + "num": NumType, "number": NumType, "int": NumType, "integer": NumType, "float": NumType, + "complex": NumType, + "bool": BoolType, "boolean": BoolType, + "none": NoneType, + "dict": DictType, "dictionary": DictType, + "list": ListType, + "tuple": TupleType, + "set": SetType, + "file": FileType, + "func": FunctionType, "function": FunctionType, + "class": ClassType, +} + + +def get_tifa_type_from_str(value, custom_types): + value = value.lower() + if value in custom_types: + return custom_types[value] + if value in TYPE_STRINGS: + return TYPE_STRINGS[value]() + else: + custom_types.add(value) + return UnknownType() + # TODO: handle custom types + + +def get_tifa_type(v, custom_types): + if isinstance(v, ast.Str): + return get_tifa_type_from_str(v.s, custom_types) + elif isinstance(v, ast.Name): + return get_tifa_type_from_str(v.id, custom_types) diff --git a/src/lib/pedal/toolkit/functions.py b/src/lib/pedal/toolkit/functions.py index a9a4b83a9e..fc5400c62b 100644 --- a/src/lib/pedal/toolkit/functions.py +++ b/src/lib/pedal/toolkit/functions.py @@ -3,6 +3,8 @@ from pedal.sandbox import compatibility import ast +from pedal.toolkit.signatures import type_check, parse_type, normalize_type + DELTA = 0.001 @@ -62,6 +64,34 @@ def match_signature_muted(name, length, *parameters): return None +def find_def_by_name(name, root=None): + if root is None: + root = parse_program() + defs = root.find_all('FunctionDef') + for a_def in defs: + if a_def._name == name: + return a_def + return None + + +def match_parameters(name, *types, root=None): + defn = find_def_by_name(name, root) + if defn: + for expected, actual in zip(types, defn.args.args): + if actual.annotation: + if not isinstance(expected, str): + expected = expected.__name__ + actual_type = parse_type(actual.annotation) + if not type_check(expected, actual_type): + gently_r("Error in definition of function `{}` parameter `{}`. Expected `{}`, " + "instead found `{}`.".format(name, actual.arg, expected, actual_type), + "wrong_parameter_type") + return None + else: + return defn + + + def match_signature(name, length, *parameters): ast = parse_program() defs = ast.find_all('FunctionDef') diff --git a/src/lib/pedal/toolkit/signatures.py b/src/lib/pedal/toolkit/signatures.py index e08c4d9838..4b74ab037f 100644 --- a/src/lib/pedal/toolkit/signatures.py +++ b/src/lib/pedal/toolkit/signatures.py @@ -81,6 +81,33 @@ list[int, str, or bool], dict[int: str], or bool or int ''' +def parse_type_slice(slice): + if slice.ast_name == "Index": + return parse_type(slice.index) + elif slice.ast_name == "Slice": + return "{}:{}".format(parse_type(slice.lower), parse_type(slice.upper)) + elif slice.ast_name == "ExtSlice": + return ", ".join(parse_type_slice(s) for s in slice.dims) + +def parse_type(node): + if node.ast_name == "Str": + return node.s + elif node.ast_name == "Name": + return node.id + elif node.ast_name == "NameConstant": + return node.value + elif node.ast_name == "List": + return "list[{}]".format(", ".join([parse_type(n) for n in node.elts])) + elif node.ast_name == "Dict": + return "dict[{}]".format(", ".join(["{}: {}".format(parse_type(k), parse_type(v)) + for k,v in zip(node.keys, node.values)])) + elif node.ast_name == "Subscript": + return parse_type(node.value) + "[{}]".format(parse_type_slice(node.slice)) + elif node.ast_name == "BoolOp": + if node.op.ast_name == "Or": + return " or ".join(parse_type(v) for v in node.values) + return "?" + class SignatureException(Exception): pass diff --git a/src/lib/traceback.py b/src/lib/traceback.py index a9b4c373ac..756d8f17d1 100644 --- a/src/lib/traceback.py +++ b/src/lib/traceback.py @@ -80,7 +80,7 @@ def extract_tb(tb, limit=None): trace. The line is a string with leading and trailing whitespace stripped; if the source is not available it is None. """ - return extract(walk_tb(tb), limit=limit) + return StackSummary.extract(walk_tb(tb), limit=limit) # # Exception formatting and output. @@ -249,7 +249,7 @@ class FrameSummary: mapping the name to the repr() of the variable. """ - __slots__ = ('filename', 'lineno', 'name', '_line', 'locals') + __slots__ = ('filename', 'lineno', 'name', 'line', 'locals') def __init__(self, filename, lineno, name, *, lookup_line=True, locals=None, line=None): @@ -266,8 +266,8 @@ def __init__(self, filename, lineno, name, *, lookup_line=True, self.lineno = lineno self.name = name self._line = line - if lookup_line: - self.line + #if lookup_line: + # self.line self.locals = {k: repr(v) for k, v in locals.items()} if locals else None def __eq__(self, other): @@ -277,14 +277,14 @@ def __eq__(self, other): self.name == other.name and self.locals == other.locals) if isinstance(other, tuple): - return (self.filename, self.lineno, self.name, self.line) == other + return (self.filename, self.lineno, self.name, self._line) == other return NotImplemented def __getitem__(self, pos): - return (self.filename, self.lineno, self.name, self.line)[pos] + return (self.filename, self.lineno, self.name, self._line)[pos] def __iter__(self): - return iter([self.filename, self.lineno, self.name, self.line]) + return iter([self.filename, self.lineno, self.name, self._line]) def __repr__(self): return "".format( @@ -293,11 +293,11 @@ def __repr__(self): def __len__(self): return 4 - @property - def line(self): - if self._line is None: - self._line = linecache.getline(self.filename, self.lineno).strip() - return self._line + #@property + #def line(self): + # if self._line is None: + # self._line = linecache.getline(self.filename, self.lineno).strip() + # return self._line def walk_stack(f): @@ -327,56 +327,56 @@ def walk_tb(tb): _RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. -def extract(frame_gen, limit=None, lookup_lines=True, capture_locals=False): - """Create a StackSummary from a traceback or stack object. - - :param frame_gen: A generator that yields (frame, lineno) tuples to - include in the stack. - :param limit: None to include all frames or the number of frames to - include. - :param lookup_lines: If True, lookup lines for each frame immediately, - otherwise lookup is deferred until the frame is rendered. - :param capture_locals: If True, the local variables from each frame will - be captured as object representations into the FrameSummary. - """ - if limit is None: - limit = getattr(sys, 'tracebacklimit', None) - if limit is not None and limit < 0: - limit = 0 - if limit is not None: - if limit >= 0: - frame_gen = islice(frame_gen, limit) - else: - frame_gen = collections.deque(frame_gen, maxlen=-limit) - - result = StackSummary() - fnames = set() - for f, lineno in frame_gen: - co = f.f_code - filename = f.co_filename #co.co_filename - name = f.co_name #co.co_name - - fnames.add(filename) - linecache.lazycache(filename, f.f_globals) - # Must defer line lookups until we have called checkcache. - if capture_locals: - f_locals = f.f_locals - else: - f_locals = None - result.append(FrameSummary( - filename, lineno, name, lookup_line=False, locals=f_locals)) - for filename in fnames: - linecache.checkcache(filename) - # If immediate lookup was desired, trigger lookups now. - if lookup_lines: - for f in result: - f.line - return result - class StackSummary(list): """A stack of frames.""" + @staticmethod + def extract(frame_gen, limit=None, lookup_lines=True, capture_locals=False): + """Create a StackSummary from a traceback or stack object. + + :param frame_gen: A generator that yields (frame, lineno) tuples to + include in the stack. + :param limit: None to include all frames or the number of frames to + include. + :param lookup_lines: If True, lookup lines for each frame immediately, + otherwise lookup is deferred until the frame is rendered. + :param capture_locals: If True, the local variables from each frame will + be captured as object representations into the FrameSummary. + """ + if limit is None: + limit = getattr(sys, 'tracebacklimit', None) + if limit is not None and limit < 0: + limit = 0 + if limit is not None: + if limit >= 0: + frame_gen = list(frame_gen)[:limit] + else: + frame_gen = collections.deque(frame_gen, maxlen=-limit) + result = StackSummary() + fnames = set() + for f, lineno in frame_gen: + co = f.f_code + filename = f.co_filename # co.co_filename + name = f.co_name # co.co_name + line = f.f_line + + fnames.add(filename) + # linecache.lazycache(filename, f.f_globals) + # Must defer line lookups until we have called checkcache. + if capture_locals: + f_locals = f.f_locals + else: + f_locals = None + result.append(FrameSummary( + filename, lineno, name, line=line, lookup_line=False, locals=f_locals)) + # for filename in fnames: + # linecache.checkcache(filename) + # If immediate lookup was desired, trigger lookups now. + # if lookup_lines: + # for f in result: + # f.line + return result @classmethod def from_list(klass, a_list): @@ -434,8 +434,8 @@ def format(self): row = [] row.append(' File "{}", line {}, in {}\n'.format( frame.filename, frame.lineno, frame.name)) - if frame.line: - row.append(' {}\n'.format(frame.line.strip())) + if frame._line: + row.append(' {}\n'.format(frame._line.strip())) if frame.locals: for name, value in sorted(frame.locals.items()): row.append(' {name} = {value}\n'.format(name=name, value=value)) @@ -532,22 +532,22 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, self.text = exc_value.text self.offset = exc_value.offset self.msg = exc_value.msg - if lookup_lines: - self._load_lines() + #if lookup_lines: + # self._load_lines() @classmethod def from_exception(cls, exc, *args, **kwargs): """Create a TracebackException from an exception.""" return cls(type(exc), exc, exc.__traceback__, *args, **kwargs) - def _load_lines(self): - """Private API. force all lines in the stack to be loaded.""" - for frame in self.stack: - frame.line - if self.__context__: - self.__context__._load_lines() - if self.__cause__: - self.__cause__._load_lines() + #def _load_lines(self): + # """Private API. force all lines in the stack to be loaded.""" + # for frame in self.stack: + # frame.line + # if self.__context__: + # self.__context__._load_lines() + # if self.__cause__: + # self.__cause__._load_lines() def __eq__(self, other): return self.__dict__ == other.__dict__ @@ -572,8 +572,11 @@ def format_exception_only(self): yield _format_final_exc_line(None, self._str) return - stype = self.exc_type.__qualname__ - smod = self.exc_type.__module__ + stype = self.exc_type.__name__ + if hasattr(self.exc_type, "__module__"): + smod = self.exc_type.__module__ + else: + smod = "builtins" if smod not in ("__main__", "builtins"): stype = smod + '.' + stype @@ -600,7 +603,7 @@ def format_exception_only(self): msg = self.msg or "" yield "{}: {}\n".format(stype, msg) - def format(self, *, chain=True): + def format(self, chain=True): """Format the exception. If chain is not *True*, *__cause__* and *__context__* will not be formatted. diff --git a/src/lib/unittest/mock.py b/src/lib/unittest/mock.py index 2d0bb54246..9d0462265d 100644 --- a/src/lib/unittest/mock.py +++ b/src/lib/unittest/mock.py @@ -10,12 +10,12 @@ def _importer(target): components = target.split('.') import_path = components.pop(0) thing = __import__(import_path) - for comp in components: import_path += ".%s" % comp thing = _dot_lookup(thing, comp, import_path) return thing + def rsplit(a_str, sep, howmany): broken = a_str.split(sep) where = len(broken) - howmany @@ -25,6 +25,7 @@ def rsplit(a_str, sep, howmany): back.insert(0, sep.join(front)) return back + def _get_target(target): try: target, attribute = rsplit(target, '.', 1) @@ -34,6 +35,7 @@ def _get_target(target): getter = lambda: _importer(target) return getter, attribute + class Patch: def __init__(self, target, new, return_value): self.target = target @@ -59,12 +61,15 @@ def start(self): new_attr = self.return_value setattr(self.getter(), self.attribute, new_attr) - def stop(self): setattr(self.getter(), self.attribute, self.backup) + if self.target == 'sys.modules': + self.getter().modules['sys'].modules = self.backup + def pass_through(target, new=None, return_value=None): return Patch(target, new, return_value) + patch = pass_through patch.dict = pass_through \ No newline at end of file diff --git a/src/misceval.js b/src/misceval.js index 364e4eea66..a93695f30c 100644 --- a/src/misceval.js +++ b/src/misceval.js @@ -694,7 +694,7 @@ Sk.exportSymbol("Sk.misceval.print_", Sk.misceval.print_); * @param {Object=} other generally globals */ Sk.misceval.loadname = function (name, other) { - var bi; + var builtinModule, bi; var v = other[name]; if (v !== undefined) { if (typeof v === "function" && v["$d"] === undefined && v["tp$name"] === undefined) { @@ -703,6 +703,14 @@ Sk.misceval.loadname = function (name, other) { return v; } + // Check if we've overridden the builtin via the builtin's module + if (other["__builtins__"] !== undefined) { + builtinModule = other["__builtins__"].mp$lookup(new Sk.builtin.str(name)); + if (builtinModule !== undefined) { + return builtinModule; + } + } + bi = Sk.builtins[name]; if (bi !== undefined) { return bi; diff --git a/src/parser.js b/src/parser.js index 8b594b54cd..28d4813937 100644 --- a/src/parser.js +++ b/src/parser.js @@ -313,14 +313,14 @@ Sk.parse = function parse (filename, input) { * @returns {function(): string} */ function readline(input) { - var lines = input.split("\n").reverse().map(function (l) { return l + "\n"; }); + var lines = input.split("\n").reverse();//.map(function (l) { return l + "\n"; }); return function() { if (lines.length === 0) { throw new Sk.builtin.Exception("EOF"); } - return lines.pop(); + return lines.pop()+"\n"; }; } diff --git a/test/test_matplotlib_capture.py b/test/test_matplotlib_capture.py new file mode 100644 index 0000000000..4442e35c5d --- /dev/null +++ b/test/test_matplotlib_capture.py @@ -0,0 +1,27 @@ +import sys + +from pedal.report import * +from pedal.source import set_source +set_source(""" +import matplotlib.pyplot as plt + +print("Hello world") +plt.hist([1,2,3]) +plt.xlabel("Hello") +plt.show() +""", "answer.py") + +from pedal.sandbox.sandbox import Sandbox +from pedal.sandbox import compatibility + +student = MAIN_REPORT['sandbox']['run'] = Sandbox() + +student.report_exceptions_mode = True +compatibility.run_student(raise_exceptions=False) + +print(compatibility.get_output()) +print(compatibility.get_plots()) + +from pedal.resolvers import simple +SUCCESS, SCORE, CATEGORY, LABEL, MESSAGE, DATA, HIDE = simple.resolve() +print(CATEGORY, LABEL, MESSAGE) \ No newline at end of file diff --git a/test/test_pedal.py b/test/test_pedal.py index 8257f1d985..1fc9b35726 100644 --- a/test/test_pedal.py +++ b/test/test_pedal.py @@ -37,6 +37,7 @@ def click(phase): student = run() print(student) +click("Ran sandbox") from pedal.resolvers import simple click("Imported resolver") diff --git a/test/test_sandbox.py b/test/test_sandbox.py index 2da04c2f61..92c7d5344a 100644 --- a/test/test_sandbox.py +++ b/test/test_sandbox.py @@ -8,18 +8,28 @@ student.run(""" def add_together(a, b): return -7 +print(add_together) """, as_filename='student.py') #pprint(student.data) print(student.data) print(student.output) -from pedal.assertions import assertEqual +from pedal.assertions import assertEqual, phase #assertEqual(student.data['a'], 2) -assertEqual(student.call("add_together", 2, 2), 4) +result = student.call("add_together", 2, 2) +#as_int = str(result) +#print("Result was:", as_int) +assertEqual(result, 4) + +@phase('input_tryit') +def try_it(): + print("Executed") + +from pedal.resolvers import simple + +print(simple.resolve()) -from pedal.report import MAIN_REPORT -print(MAIN_REPORT.feedback[0].mistake) \ No newline at end of file diff --git a/test/test_sandbox2.py b/test/test_sandbox2.py new file mode 100644 index 0000000000..a881a175ef --- /dev/null +++ b/test/test_sandbox2.py @@ -0,0 +1,33 @@ +import sys + +from pedal.report import * +from pedal.source import set_source +set_source("1+''", "answer.py") + +from pedal.sandbox.sandbox import Sandbox +from pedal.sandbox import compatibility + +student = MAIN_REPORT['sandbox']['run'] = Sandbox() + +student.report_exceptions_mode = True +print(len(sys.modules.keys()), sorted(sys.modules.keys())) +old = set(sys.modules.keys()) +compatibility.run_student(raise_exceptions=True) + +#import pedal.mistakes.instructor_append as ins_app + +#print(ins_app) + +from pedal.mistakes import instructor_append + +new = set(sys.modules.keys()) +print(len(sys.modules.keys()), sorted(sys.modules.keys())) + +print(new-old) + +print(student.output) +print(student.exception_position) + +from pedal.resolvers import simple +SUCCESS, SCORE, CATEGORY, LABEL, MESSAGE, DATA, HIDE = simple.resolve() +print(CATEGORY, LABEL, MESSAGE) \ No newline at end of file diff --git a/test/test_slowdowns.py b/test/test_slowdowns.py new file mode 100644 index 0000000000..918c51d717 --- /dev/null +++ b/test/test_slowdowns.py @@ -0,0 +1 @@ +import pedal.sandbox.mocked \ No newline at end of file From b8dee6c3cb1f88780e8ea1283153576da749a488 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Fri, 20 Sep 2019 08:14:43 -0400 Subject: [PATCH 24/68] MD5, Pedal, various constants --- package.json | 2 +- src/builtin.js | 41 +- src/constants.js | 19 +- src/errors.js | 17 +- src/lib/hashlib.js | 425 ++++++++++++++++++++ src/lib/pedal/assertions/assertions.py | 306 +++++++------- src/lib/pedal/cait/cait_api.py | 2 +- src/lib/pedal/cait/cait_node.py | 16 +- src/lib/pedal/mistakes/instructor_filter.py | 3 +- src/lib/pedal/mistakes/iteration_context.py | 47 +-- src/lib/pedal/questions/__init__.py | 14 +- src/lib/pedal/sandbox/compatibility.py | 7 +- src/lib/pedal/sandbox/mocked.py | 54 ++- src/lib/pedal/sandbox/sandbox.py | 7 + src/lib/pedal/source/__init__.py | 3 +- src/lib/pedal/tifa/tifa.py | 9 + src/lib/pedal/tifa/type_definitions.py | 9 + src/lib/pedal/toolkit/functions.py | 29 +- src/lib/pedal/toolkit/signatures.py | 2 +- src/lib/pedal/toolkit/upload.py | 18 +- src/lib/posixpath.py | 6 +- src/parser.js | 10 - src/str.js | 5 + src/type.js | 5 +- test/run/t474.py | 49 +-- test/test_hashlib.py | 8 + update_pedal.bat | 1 + 27 files changed, 849 insertions(+), 265 deletions(-) create mode 100644 src/lib/hashlib.js create mode 100644 test/test_hashlib.py create mode 100644 update_pedal.bat diff --git a/package.json b/package.json index c133fbe7dc..b187e32d7b 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "devbuild": "webpack --mode development", "postdevbuild": "node support/build/wrapmodules.js builtin", "watch": "webpack --watch --mode development", - "test": "node test/testwrapper.js && node test/testunit.js && node test/testunit.js --python3", + "test": "node test/testwrapper.js && node test/testunit.js --python3", "start": "node support/run/runfile.js", "profile": "node --prof --no-logfile-per-isolate --log-internal-timer-events support/run/runfile.js -o", "postprofile": "node --prof-process v8.log", diff --git a/src/builtin.js b/src/builtin.js index 8fa2ef738d..f8e18f14ce 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -740,8 +740,18 @@ Sk.builtin.isinstance = function isinstance (obj, type) { } } + var objType; + + // Overridden __class__ + var __class__ = obj.tp$getattr(Sk.builtin.str.$class); + if (__class__ !== undefined) { + objType = __class__; + } else { + objType = obj.ob$type; + } + // Normal case - if (obj.ob$type === type) { + if (objType === type) { return Sk.builtin.bool.true$; } @@ -779,7 +789,7 @@ Sk.builtin.isinstance = function isinstance (obj, type) { return Sk.builtin.bool.false$; }; - return issubclass(obj.ob$type, type); + return issubclass(objType, type); }; Sk.builtin.hash = function hash (value) { @@ -1396,6 +1406,9 @@ Sk.builtin.compile = function(source, filename, mode, flags, dont_inherit, optim var extractDict = function(obj) { var ret = {}; var k, v, kAsJs, iter; + if (obj === undefined) { + return ret; + } for (iter = obj.tp$iter(), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { v = obj.mp$subscript(k); if (v === undefined) { @@ -1430,35 +1443,25 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { new_globals_copy.__package__ = Sk.builtin.none.none$; } var backupGlobals = Sk.globals; - /*var backupSysmodules = new Sk.builtin.dict([]); - Sk.misceval.iterFor(Sk.sysmodules.tp$iter(), function(key) { - var value = Sk.sysmodules.mp$subscript(key); - backupSysmodules.mp$ass_subscript(key, value); - });*/ Sk.globals = new_globals_copy; // Possibly copy over some "default" ones? - //Sk.importMainWithBody(filename, false, python_code, true); var name = filename.endsWith(".py") ? filename.slice(0, -3) : filename; + var pyName = Sk.builtin.str(name); + var sysModules = Sk.getCurrentSysModules(); var modname = name; var caughtError = null; - /*var friendlyKeys = []; - Sk.misceval.iterFor(Sk.sysmodules.tp$iter(), function(key) { - friendlyKeys.push(key.v); - }); - console.log("LOADING", friendlyKeys);*/ try { Sk.importModuleInternal_(name, false, modname, pythonCode, undefined, false, true) } catch (e) { caughtError = e; } Sk.globals = backupGlobals; - /*Sk.misceval.iterFor(backupSysmodules.tp$iter(), function(key) { - var value = backupSysmodules.mp$subscript(key); - Sk.sysmodules.mp$ass_subscript(key, value); - });*/ - Sk.getCurrentSysModules().mp$del_subscript(Sk.builtin.str(name)); + // Only try to delete if we succeeded in creating it! + if (sysModules.mp$lookup(pyName)) { + Sk.getCurrentSysModules().mp$del_subscript(pyName); + } for (var key in new_globals_copy) { if (new_globals_copy.hasOwnProperty(key)) { - var pykey = Sk.ffi.remapToPy(key); + var pykey = Sk.ffi.remapToPy(Sk.unfixReserved(key)); Sk.builtin.dict.prototype.mp$ass_subscript.call(new_globals, pykey, new_globals_copy[key]); } } diff --git a/src/constants.js b/src/constants.js index ed2e427a47..8cb5faf65f 100644 --- a/src/constants.js +++ b/src/constants.js @@ -41,6 +41,7 @@ Sk.builtin.str.$real = new Sk.builtin.str("real"); Sk.builtin.str.$abs = new Sk.builtin.str("__abs__"); Sk.builtin.str.$call = new Sk.builtin.str("__call__"); +Sk.builtin.str.$class = new Sk.builtin.str("__class__"); Sk.builtin.str.$cmp = new Sk.builtin.str("__cmp__"); Sk.builtin.str.$complex = new Sk.builtin.str("__complex__"); Sk.builtin.str.$contains = new Sk.builtin.str("__contains__"); @@ -181,4 +182,20 @@ Sk.builtin.str.prototype["split"].$defaults = [Sk.builtin.none.none$, Sk.builtin Sk.builtin.str.prototype["split"].co_kwargs = true; Sk.builtin.str.prototype["rsplit"].co_varnames = ["sep", "maxsplit"]; Sk.builtin.str.prototype["rsplit"].$defaults = [Sk.builtin.none.none$, Sk.builtin.int_(-1)]; -Sk.builtin.str.prototype["rsplit"].co_kwargs = true; \ No newline at end of file +Sk.builtin.str.prototype["rsplit"].co_kwargs = true; + +var builtinStringMethods = [ + "capitalize", "center", "count", "encode", "endswith", "expandtabs", + "find", "format", "index", "isalnum", "isalpha", + "isdigit", "islower", "isnumeric", "isspace", + "istitle", "isupper", "join", "ljust", "lower", "lstrip", + "partition", "replace", "rfind", "rindex", "rjust", "rpartition", "rsplit", + "rstrip", "split", "splitlines", "startswith", "strip", "swapcase", "title", + "upper", "zfill" +]; +for (i = 0; i < builtinStringMethods.length; i++) { + renamed = builtinStringMethods[i]; + Sk.builtin.str.prototype[renamed].co_name = new Sk.builtin.str(renamed); + Sk.builtin.str.prototype[renamed].__name__ = new Sk.builtin.str(renamed); + Sk.builtin.str.prototype[renamed].tp$name = renamed; +} \ No newline at end of file diff --git a/src/errors.js b/src/errors.js index 5c1bede335..32ae71571b 100644 --- a/src/errors.js +++ b/src/errors.js @@ -337,11 +337,11 @@ Sk.builtin.SyntaxError = function (args) { return o; } Sk.builtin.StandardError.apply(this, arguments); - if (arguments.length >= 3) { - this.lineno = Sk.ffi.remapToPy(arguments[2]); - } else { - this.lineno = Sk.ffi.remapToPy(null); - } + this.text = arguments.length >= 1 ? Sk.ffi.remapToPy(arguments[0]) : Sk.builtin.none.none$; + this.msg = this.text; + this.filename = arguments.length >= 2 ? Sk.ffi.remapToPy(arguments[1]) : Sk.builtin.none.none$; + this.lineno = arguments.length >= 3 ? Sk.ffi.remapToPy(arguments[2]) : Sk.builtin.none.none$; + this.offset = arguments.length >= 4 ? Sk.ffi.remapToPy(arguments[3]) : Sk.builtin.none.none$; }; Sk.abstr.setUpInheritance("SyntaxError", Sk.builtin.SyntaxError, Sk.builtin.StandardError); Sk.builtin.SyntaxError.prototype.tp$getattr = function (name) { @@ -353,10 +353,13 @@ Sk.builtin.SyntaxError.prototype.tp$getattr = function (name) { _name = Sk.ffi.remapToJs(name); } - if (_name === "lineno") { + if (_name === "lineno" || _name === "msg" || _name === "filename" || _name==="offset" || + _name === "text") { return this[_name]; - } else if (_name == "__name__") { + } else if (_name === "__name__") { return Sk.builtin.str("SyntaxError"); + } else if (_name === "__cause__" || _name === "__context__" || _name==="__suppress_context__") { + return Sk.builtin.none.none$; } } diff --git a/src/lib/hashlib.js b/src/lib/hashlib.js new file mode 100644 index 0000000000..039435e65d --- /dev/null +++ b/src/lib/hashlib.js @@ -0,0 +1,425 @@ +/* + * JavaScript MD5 + * https://github.com/blueimp/JavaScript-MD5 + * + * Copyright 2011, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * https://opensource.org/licenses/MIT + * + * Based on + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for more info. + */ + +var $builtinmodule = function (name) { + var mod = {}; + "use strict"; + + /** + * Add integers, wrapping at 2^32. + * This uses 16-bit operations internally to work around bugs in interpreters. + * + * @param {number} x First integer + * @param {number} y Second integer + * @returns {number} Sum + */ + function safeAdd(x, y) { + var lsw = (x & 0xffff) + (y & 0xffff); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xffff); + } + + /** + * Bitwise rotate a 32-bit number to the left. + * + * @param {number} num 32-bit number + * @param {number} cnt Rotation count + * @returns {number} Rotated number + */ + function bitRotateLeft(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); + } + + /** + * Basic operation the algorithm uses. + * + * @param {number} q q + * @param {number} a a + * @param {number} b b + * @param {number} x x + * @param {number} s s + * @param {number} t t + * @returns {number} Result + */ + function md5cmn(q, a, b, x, s, t) { + return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b); + } + + /** + * Basic operation the algorithm uses. + * + * @param {number} a a + * @param {number} b b + * @param {number} c c + * @param {number} d d + * @param {number} x x + * @param {number} s s + * @param {number} t t + * @returns {number} Result + */ + function md5ff(a, b, c, d, x, s, t) { + return md5cmn((b & c) | (~b & d), a, b, x, s, t); + } + + /** + * Basic operation the algorithm uses. + * + * @param {number} a a + * @param {number} b b + * @param {number} c c + * @param {number} d d + * @param {number} x x + * @param {number} s s + * @param {number} t t + * @returns {number} Result + */ + function md5gg(a, b, c, d, x, s, t) { + return md5cmn((b & d) | (c & ~d), a, b, x, s, t); + } + + /** + * Basic operation the algorithm uses. + * + * @param {number} a a + * @param {number} b b + * @param {number} c c + * @param {number} d d + * @param {number} x x + * @param {number} s s + * @param {number} t t + * @returns {number} Result + */ + function md5hh(a, b, c, d, x, s, t) { + return md5cmn(b ^ c ^ d, a, b, x, s, t); + } + + /** + * Basic operation the algorithm uses. + * + * @param {number} a a + * @param {number} b b + * @param {number} c c + * @param {number} d d + * @param {number} x x + * @param {number} s s + * @param {number} t t + * @returns {number} Result + */ + function md5ii(a, b, c, d, x, s, t) { + return md5cmn(c ^ (b | ~d), a, b, x, s, t); + } + + /** + * Calculate the MD5 of an array of little-endian words, and a bit length. + * + * @param {Array} x Array of little-endian words + * @param {number} len Bit length + * @returns {Array} MD5 Array + */ + function binlMD5(x, len) { + /* append padding */ + x[len >> 5] |= 0x80 << len % 32; + x[(((len + 64) >>> 9) << 4) + 14] = len; + + var i; + var olda; + var oldb; + var oldc; + var oldd; + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + + for (i = 0; i < x.length; i += 16) { + olda = a; + oldb = b; + oldc = c; + oldd = d; + + a = md5ff(a, b, c, d, x[i], 7, -680876936); + d = md5ff(d, a, b, c, x[i + 1], 12, -389564586); + c = md5ff(c, d, a, b, x[i + 2], 17, 606105819); + b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330); + a = md5ff(a, b, c, d, x[i + 4], 7, -176418897); + d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426); + c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341); + b = md5ff(b, c, d, a, x[i + 7], 22, -45705983); + a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416); + d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417); + c = md5ff(c, d, a, b, x[i + 10], 17, -42063); + b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162); + a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682); + d = md5ff(d, a, b, c, x[i + 13], 12, -40341101); + c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290); + b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329); + + a = md5gg(a, b, c, d, x[i + 1], 5, -165796510); + d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632); + c = md5gg(c, d, a, b, x[i + 11], 14, 643717713); + b = md5gg(b, c, d, a, x[i], 20, -373897302); + a = md5gg(a, b, c, d, x[i + 5], 5, -701558691); + d = md5gg(d, a, b, c, x[i + 10], 9, 38016083); + c = md5gg(c, d, a, b, x[i + 15], 14, -660478335); + b = md5gg(b, c, d, a, x[i + 4], 20, -405537848); + a = md5gg(a, b, c, d, x[i + 9], 5, 568446438); + d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690); + c = md5gg(c, d, a, b, x[i + 3], 14, -187363961); + b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501); + a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467); + d = md5gg(d, a, b, c, x[i + 2], 9, -51403784); + c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473); + b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734); + + a = md5hh(a, b, c, d, x[i + 5], 4, -378558); + d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463); + c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562); + b = md5hh(b, c, d, a, x[i + 14], 23, -35309556); + a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060); + d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353); + c = md5hh(c, d, a, b, x[i + 7], 16, -155497632); + b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640); + a = md5hh(a, b, c, d, x[i + 13], 4, 681279174); + d = md5hh(d, a, b, c, x[i], 11, -358537222); + c = md5hh(c, d, a, b, x[i + 3], 16, -722521979); + b = md5hh(b, c, d, a, x[i + 6], 23, 76029189); + a = md5hh(a, b, c, d, x[i + 9], 4, -640364487); + d = md5hh(d, a, b, c, x[i + 12], 11, -421815835); + c = md5hh(c, d, a, b, x[i + 15], 16, 530742520); + b = md5hh(b, c, d, a, x[i + 2], 23, -995338651); + + a = md5ii(a, b, c, d, x[i], 6, -198630844); + d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415); + c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905); + b = md5ii(b, c, d, a, x[i + 5], 21, -57434055); + a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571); + d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606); + c = md5ii(c, d, a, b, x[i + 10], 15, -1051523); + b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799); + a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359); + d = md5ii(d, a, b, c, x[i + 15], 10, -30611744); + c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380); + b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649); + a = md5ii(a, b, c, d, x[i + 4], 6, -145523070); + d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379); + c = md5ii(c, d, a, b, x[i + 2], 15, 718787259); + b = md5ii(b, c, d, a, x[i + 9], 21, -343485551); + + a = safeAdd(a, olda); + b = safeAdd(b, oldb); + c = safeAdd(c, oldc); + d = safeAdd(d, oldd); + } + return [a, b, c, d]; + } + + /** + * Convert an array of little-endian words to a string + * + * @param {Array} input MD5 Array + * @returns {string} MD5 string + */ + function binl2rstr(input) { + var i; + var output = ""; + var length32 = input.length * 32; + for (i = 0; i < length32; i += 8) { + output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff); + } + return output; + } + + /** + * Convert a raw string to an array of little-endian words + * Characters >255 have their high-byte silently ignored. + * + * @param {string} input Raw input string + * @returns {Array} Array of little-endian words + */ + function rstr2binl(input) { + var i; + var output = []; + output[(input.length >> 2) - 1] = undefined; + for (i = 0; i < output.length; i += 1) { + output[i] = 0; + } + var length8 = input.length * 8; + for (i = 0; i < length8; i += 8) { + output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32; + } + return output; + } + + /** + * Calculate the MD5 of a raw string + * + * @param {string} s Input string + * @returns {string} Raw MD5 string + */ + function rstrMD5(s) { + return binl2rstr(binlMD5(rstr2binl(s), s.length * 8)); + } + + /** + * Calculates the HMAC-MD5 of a key and some data (raw strings) + * + * @param {string} key HMAC key + * @param {string} data Raw input string + * @returns {string} Raw MD5 string + */ + function rstrHMACMD5(key, data) { + var i; + var bkey = rstr2binl(key); + var ipad = []; + var opad = []; + var hash; + ipad[15] = opad[15] = undefined; + if (bkey.length > 16) { + bkey = binlMD5(bkey, key.length * 8); + } + for (i = 0; i < 16; i += 1) { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5c5c5c5c; + } + hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); + return binl2rstr(binlMD5(opad.concat(hash), 512 + 128)); + } + + /** + * Convert a raw string to a hex string + * + * @param {string} input Raw input string + * @returns {string} Hex encoded string + */ + function rstr2hex(input) { + var hexTab = "0123456789abcdef"; + var output = ""; + var x; + var i; + for (i = 0; i < input.length; i += 1) { + x = input.charCodeAt(i); + output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f); + } + return output; + } + + /** + * Encode a string as UTF-8 + * + * @param {string} input Input string + * @returns {string} UTF8 string + */ + function str2rstrUTF8(input) { + return unescape(encodeURIComponent(input)); + } + + /** + * Encodes input string as raw MD5 string + * + * @param {string} s Input string + * @returns {string} Raw MD5 string + */ + function rawMD5(s) { + return rstrMD5(str2rstrUTF8(s)); + } + + /** + * Encodes input string as Hex encoded string + * + * @param {string} s Input string + * @returns {string} Hex encoded string + */ + function hexMD5(s) { + return rstr2hex(rawMD5(s)); + } + + /** + * Calculates the raw HMAC-MD5 for the given key and data + * + * @param {string} k HMAC key + * @param {string} d Input string + * @returns {string} Raw MD5 string + */ + function rawHMACMD5(k, d) { + return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d)); + } + + /** + * Calculates the Hex encoded HMAC-MD5 for the given key and data + * + * @param {string} k HMAC key + * @param {string} d Input string + * @returns {string} Raw MD5 string + */ + function hexHMACMD5(k, d) { + return rstr2hex(rawHMACMD5(k, d)); + } + + /** + * Calculates MD5 value for a given string. + * If a key is provided, calculates the HMAC-MD5 value. + * Returns a Hex encoded string unless the raw argument is given. + * + * @param {string} string Input string + * @param {string} [key] HMAC key + * @param {boolean} [raw] Raw output switch + * @returns {string} MD5 output + */ + function md5(string, key, raw) { + if (!key) { + if (!raw) { + return hexMD5(string); + } + return rawMD5(string); + } + if (!raw) { + return hexHMACMD5(key, string); + } + return rawHMACMD5(key, string); + } + + var HASH = function ($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self, data) { + self.data$ = data.v; + }); + $loc.__str__ = new Sk.builtin.func(function (self) { + return Sk.ffi.remapToPy("<_hashlib.HASH>"); + }); + + $loc.__repr__ = $loc.__str__; + + $loc.digest = new Sk.builtin.func(function (self) { + var codes = []; + for (var i=0; i < self.data$.length; i++) { + codes.push(new Sk.builtin.int_(self.data$.charCodeAt(i))); + } + return new Sk.builtin.list(codes); + }); + }; + + mod.HASH = Sk.misceval.buildClass(mod, HASH, "HASH", []); + + mod.md5 = new Sk.builtin.func(function (value) { + var hashed = md5(Sk.ffi.remapToJs(value), undefined, true); + var result = Sk.ffi.remapToPy(hashed); + return Sk.misceval.callsim(mod.HASH, result); + }); + + return mod; +}; \ No newline at end of file diff --git a/src/lib/pedal/assertions/assertions.py b/src/lib/pedal/assertions/assertions.py index 5e185a6bc8..41823f333e 100644 --- a/src/lib/pedal/assertions/assertions.py +++ b/src/lib/pedal/assertions/assertions.py @@ -9,20 +9,22 @@ # TODO: Allow bundling of assertions to make a table -iterable = lambda obj: hasattr(obj,'__iter__') or hasattr(obj,'__getitem__') +iterable = lambda obj: hasattr(obj, '__iter__') or hasattr(obj, '__getitem__') _MAX_LENGTH = 80 + def _escape_curly_braces(result): return result.replace("{", "{{").replace("}", "}}") + def safe_repr(obj, short=False): try: result = repr(obj) except Exception: result = object.__repr__(obj) if short and len(result) >= _MAX_LENGTH: - result = result[:_MAX_LENGTH] + ' [truncated]...' + result = result[:_MAX_LENGTH] + ' [truncated]...' result = result return result @@ -62,7 +64,7 @@ def _normalize_string(a_string, numeric_endings=False): def equality_test(actual, expected, _exact_strings, _delta, _test_output): # Float comparison if (isinstance(expected, float) and - isinstance(actual, (float, int)) and + isinstance(actual, (float, int)) and abs(actual - expected) < _delta): return True # Exact Comparison @@ -70,20 +72,20 @@ def equality_test(actual, expected, _exact_strings, _delta, _test_output): return True # Inexact string comparison if (_exact_strings and isinstance(expected, str) and - isinstance(actual, str) and - _normalize_string(actual) == _normalize_string(expected)): + isinstance(actual, str) and + _normalize_string(actual) == _normalize_string(expected)): return True # Output comparison if _test_output: # Inexact output comparison normalized_actual = [_normalize_string(line) for line in actual] if (isinstance(expected, str) and - _normalize_string(expected) in normalized_actual): + _normalize_string(expected) in normalized_actual): return True # Exact output comparison normalized_expected = [_normalize_string(line) for line in expected] if (isinstance(expected, list) and - normalized_expected == normalized_actual): + normalized_expected == normalized_actual): return True # Else return False @@ -99,7 +101,7 @@ def _fail(code_message, actual_message, expected_message, sandboxed_values = [] sandboxed_results = [] if modify_right and values: - values = values[:-1] + (modify_right(values[-1]), ) + values = values[:-1] + (modify_right(values[-1]),) for value in values: if is_sandbox_result(value): sandboxed_results.append(value) @@ -121,7 +123,8 @@ def _build_result_from_target(target, index, quantity): return "the first result" else: return "the second result" - return ""+target+"" + return "" + target + "" + def _build_context(sandboxed_results, actual_message, expected_message, show_expected_value): @@ -141,21 +144,21 @@ def _build_context(sandboxed_results, actual_message, expected_message, # Actual rendering of text if calls: calls = [_escape_curly_braces(str(call)) for call in calls] - context.append("I ran:
"+ "\n".join(calls)+ "
") + context.append("I ran:
" + "\n".join(calls) + "
") if inputs: inputs = [_escape_curly_braces(str(inp)) for inp in inputs] - context.append("I entered as input:
"+ "\n".join(inputs)+ "
") + context.append("I entered as input:
" + "\n".join(inputs) + "
") actual_message += ":
{}
" for i, target in enumerate(targets): named_target = _build_result_from_target(target, i, len(targets)) if target == '_': - context.append(named_target.capitalize() + " "+actual_message) + context.append(named_target.capitalize() + " " + actual_message) else: - context.append("The value of "+named_target+" "+actual_message) + context.append("The value of " + named_target + " " + actual_message) expected_context = "But I expected " if len(targets) == 2: expected_context += _build_result_from_target(targets[0], 0, 2) - expected_context += " " +expected_message + " " + expected_context += " " + expected_message + " " expected_context += _build_result_from_target(targets[1], 1, 2) else: expected_context += _build_result_from_target(targets[0], 0, 1) @@ -181,11 +184,11 @@ def _basic_assertion(left, right, operator, code_comparison_message, _setup_assertions(report) context = "" if message: - message = "\n"+message + message = "\n" + message else: message = "" # TODO: Handle right-side sandbox result - #if is_sandbox_result(right): + # if is_sandbox_result(right): # right = right._actual_value if isinstance(left, Exception): return False @@ -196,8 +199,8 @@ def _basic_assertion(left, right, operator, code_comparison_message, show_expected_value, modify_right, left, right) report['assertions']['collected'].append(failure) report.attach('Instructor Test', category='student', tool='Assertions', - mistake={'message': "Student code failed instructor test.
\n"+ - context+str(failure)+message}) + mistake={'message': "Student code failed instructor test.
\n" + + context + str(failure) + message}) report['assertions']['failures'] += 1 if report['assertions']['exceptions']: raise failure @@ -214,15 +217,15 @@ def assertEqual(left, right, score=None, message=None, report=None, if compare_lengths is None: compare_lengths = (iterable(left) and isinstance(right, (int, float))) if _basic_assertion(left, right, - lambda l, r: - equality_test(len(l), r, False, DELTA, False) if - compare_lengths else - equality_test(l, r, False, DELTA, False), - "len({}) != {}" if compare_lengths else "{} != {}", - "was"+PRE_VAL, - "to have its length equal to" - if compare_lengths else "to be equal to", - message, report, contextualize): + lambda l, r: + equality_test(len(l), r, False, DELTA, False) if + compare_lengths else + equality_test(l, r, False, DELTA, False), + "len({}) != {}" if compare_lengths else "{} != {}", + "was" + PRE_VAL, + "to have its length equal to" + if compare_lengths else "to be equal to", + message, report, contextualize): if report is None: report = MAIN_REPORT report.give_partial(score) @@ -234,13 +237,13 @@ def assertEqual(left, right, score=None, message=None, report=None, def assertNotEqual(left, right, score=None, message=None, report=None, - contextualize=True, exact=False): + contextualize=True, exact=False): if _basic_assertion(left, right, - lambda l, r: not equality_test(l, r, False, DELTA, False), - "{} == {}", - "was"+PRE_VAL, - "to not be equal to", - message, report, contextualize): + lambda l, r: not equality_test(l, r, False, DELTA, False), + "{} == {}", + "was" + PRE_VAL, + "to not be equal to", + message, report, contextualize): if report is None: report = MAIN_REPORT report.give_partial(score) @@ -249,14 +252,14 @@ def assertNotEqual(left, right, score=None, message=None, report=None, def assertTrue(something, score=None, message=None, report=None, - contextualize=True): + contextualize=True): if _basic_assertion(something, True, - lambda l, r: bool(l), - "{} is true", - "was false"+PRE_VAL, - "to be true", - message, report, contextualize, - show_expected_value=False): + lambda l, r: bool(l), + "{} is true", + "was false" + PRE_VAL, + "to be true", + message, report, contextualize, + show_expected_value=False): if report is None: report = MAIN_REPORT report.give_partial(score) @@ -267,12 +270,12 @@ def assertTrue(something, score=None, message=None, report=None, def assertFalse(something, score=None, message=None, report=None, contextualize=True): if _basic_assertion(something, False, - lambda l, r: not bool(l), - "{} is false", - "was true"+PRE_VAL, - "to be false", - message, report, contextualize, - show_expected_value=False): + lambda l, r: not bool(l), + "{} is false", + "was true" + PRE_VAL, + "to be false", + message, report, contextualize, + show_expected_value=False): if report is None: report = MAIN_REPORT report.give_partial(score) @@ -287,40 +290,44 @@ def assertIs(left, right, score=None, message=None): def assertIsNot(left, right, score=None, message=None): pass + def _actually_is_none(l, r): if is_sandbox_result(l): return l._actual_value is None return l is None + def assertIsNone(something, score=None, message=None, report=None, - contextualize=True): + contextualize=True): if _basic_assertion(something, None, - _actually_is_none, - "{} is none", - "was"+PRE_VAL, - "to be none", - message, report, contextualize, - show_expected_value=False): + _actually_is_none, + "{} is none", + "was" + PRE_VAL, + "to be none", + message, report, contextualize, + show_expected_value=False): if report is None: report = MAIN_REPORT report.give_partial(score) return True return False + def _actually_is_not_none(l, r): if is_sandbox_result(l): return l._actual_value is not None return l is not None + def assertIsNotNone(something, score=None, message=None, report=None, - contextualize=True): + contextualize=True): if _basic_assertion(something, None, - _actually_is_not_none, - "{} is not none", - "was"+PRE_VAL, - "to not be none", - message, report, contextualize, - show_expected_value=False): + _actually_is_not_none, + "{} is not none", + "was" + PRE_VAL, + "to not be none", + message, report, contextualize, + show_expected_value=False): if report is None: report = MAIN_REPORT report.give_partial(score) @@ -334,11 +341,11 @@ def assertIn(needle, haystack, score=None, message=None, report=None, if not is_sandbox_result(needle) and is_sandbox_result(haystack): expected_message = "to contain" if _basic_assertion(needle, haystack, - lambda n, h: n in h, - "{} not in {}", - "was"+PRE_VAL, - expected_message, - message, report, contextualize): + lambda n, h: n in h, + "{} not in {}", + "was" + PRE_VAL, + expected_message, + message, report, contextualize): if report is None: report = MAIN_REPORT report.give_partial(score) @@ -352,31 +359,33 @@ def assertNotIn(needle, haystack, score=None, message=None, report=None, if not is_sandbox_result(needle) and is_sandbox_result(haystack): expected_message = "to not contain" if _basic_assertion(needle, haystack, - lambda n, h: n not in h, - "{} in {}", - "was"+PRE_VAL, - expected_message, - message, report, contextualize): + lambda n, h: n not in h, + "{} in {}", + "was" + PRE_VAL, + expected_message, + message, report, contextualize): if report is None: report = MAIN_REPORT report.give_partial(score) return True return False + def _humanize_types(types): if isinstance(types, tuple): return ', '.join([t.__name__ for t in types]) return types.__name__ + def assertIsInstance(value, types, score=None, message=None, report=None, - contextualize=True): + contextualize=True): if _basic_assertion(value, types, - lambda v, t: isinstance(v, t), - "isinstance({}, {})", - "was"+PRE_VAL, - "to be of type", - message, report, contextualize, - modify_right=_humanize_types): + lambda v, t: isinstance(v, t), + "isinstance({}, {})", + "was" + PRE_VAL, + "to be of type", + message, report, contextualize, + modify_right=_humanize_types): if report is None: report = MAIN_REPORT report.give_partial(score) @@ -409,16 +418,16 @@ def assertGreater(left, right, score=None, message=None, report=None, if compare_lengths is None: compare_lengths = (iterable(left) and isinstance(right, (int, float))) if _basic_assertion(left, right, - lambda l, r: - len(l) > r if - compare_lengths else - l > r, - "len({}) <= {}" if compare_lengths else "{} <= {}", - "was"+PRE_VAL, - "to have its length greater than" - if compare_lengths else - "to be greater than", - message, report, contextualize): + lambda l, r: + len(l) > r if + compare_lengths else + l > r, + "len({}) <= {}" if compare_lengths else "{} <= {}", + "was" + PRE_VAL, + "to have its length greater than" + if compare_lengths else + "to be greater than", + message, report, contextualize): if report is None: report = MAIN_REPORT report.give_partial(score) @@ -431,15 +440,15 @@ def assertGreaterEqual(left, right, score=None, message=None, report=None, if compare_lengths is None: compare_lengths = (iterable(left) and isinstance(right, (int, float))) if _basic_assertion(left, right, - lambda l, r: - len(l) >= r if - compare_lengths else - l >= r, - "len({}) < {}" if compare_lengths else "{} < {}", - "was"+PRE_VAL, - "to have its length greater than or equal to" if compare_lengths else - "to be greater than or equal to", - message, report, contextualize): + lambda l, r: + len(l) >= r if + compare_lengths else + l >= r, + "len({}) < {}" if compare_lengths else "{} < {}", + "was" + PRE_VAL, + "to have its length greater than or equal to" if compare_lengths else + "to be greater than or equal to", + message, report, contextualize): if report is None: report = MAIN_REPORT report.give_partial(score) @@ -448,20 +457,20 @@ def assertGreaterEqual(left, right, score=None, message=None, report=None, def assertLess(left, right, score=None, message=None, report=None, - contextualize=True, compare_lengths=None): + contextualize=True, compare_lengths=None): if compare_lengths is None: compare_lengths = (iterable(left) and isinstance(right, (int, float))) if _basic_assertion(left, right, - lambda l, r: - len(l) < r if - compare_lengths else - l < r, - "len({}) >= {}" if compare_lengths else "{} >= {}", - "was"+PRE_VAL, - "to have its length less than" - if compare_lengths else - "to be less than", - message, report, contextualize): + lambda l, r: + len(l) < r if + compare_lengths else + l < r, + "len({}) >= {}" if compare_lengths else "{} >= {}", + "was" + PRE_VAL, + "to have its length less than" + if compare_lengths else + "to be less than", + message, report, contextualize): if report is None: report = MAIN_REPORT report.give_partial(score) @@ -474,15 +483,15 @@ def assertLessEqual(left, right, score=None, message=None, report=None, if compare_lengths is None: compare_lengths = (iterable(left) and isinstance(right, (int, float))) if _basic_assertion(left, right, - lambda l, r: - len(l) <= r if - compare_lengths else - l <= r, - "len({}) > {}" if compare_lengths else "{} > {}", - "was"+PRE_VAL, - "to have its length less than or equal to" if compare_lengths else - "to be less than or equal to", - message, report, contextualize): + lambda l, r: + len(l) <= r if + compare_lengths else + l <= r, + "len({}) > {}" if compare_lengths else "{} > {}", + "was" + PRE_VAL, + "to have its length less than or equal to" if compare_lengths else + "to be less than or equal to", + message, report, contextualize): if report is None: report = MAIN_REPORT report.give_partial(score) @@ -516,8 +525,8 @@ def assertSequenceEqual(left, right): # Speciality Asserts def assertPrints(result, expected_output, args=None, returns=None, - score=None, message=None, report=None, - contextualize=True, exact=False): + score=None, message=None, report=None, + contextualize=True, exact=False): if not isinstance(result, SandboxResult): return False raise TypeError("You must pass in a SandboxResult (e.g., using `call`) to assertPrints") @@ -530,26 +539,26 @@ def assertPrints(result, expected_output, args=None, returns=None, inputs = sandbox.input_contexts[call_id] actual_output = sandbox.output_contexts[call_id] if not equality_test(actual_output, expected_output, exact, DELTA, True): - context= [] + context = [] if calls: - context.append("I ran:
"+
-                           "\n".join(map(str, calls))+
+            context.append("I ran:
" +
+                           "\n".join(map(str, calls)) +
                            "
") if inputs: - context.append("I entered as input:
"+
-                           "\n".join(map(str, inputs))+
+            context.append("I entered as input:
" +
+                           "\n".join(map(str, inputs)) +
                            "
") if actual_output: - context.append("The function printed:
"+
-                           "\n".join(map(str, actual_output))+
+            context.append("The function printed:
" +
+                           "\n".join(map(str, actual_output)) +
                            "
") else: context.append("The function printed nothing.") - context.append("But I expected the output:
"+ "\n".join(map(str, expected_output))+ "
") + context.append("But I expected the output:
" + "\n".join(map(str, expected_output)) + "
") failure = AssertionException("\n".join(context)) report['assertions']['collected'].append(failure) report.attach('Instructor Test', category='student', tool='Assertions', - mistake={'message': "Student code failed instructor test.
\n"+ + mistake={'message': "Student code failed instructor test.
\n" + str(failure)}) report['assertions']['failures'] += 1 if report['assertions']['exceptions']: @@ -559,6 +568,7 @@ def assertPrints(result, expected_output, args=None, returns=None, report.give_partial(score) return True + def assertHasFunction(obj, function, args=None, returns=None, score=None, message=None, report=None, contextualize=True, exact=False): @@ -575,7 +585,7 @@ def comparison(o, f): if not _basic_assertion(obj, function, comparison, "Could not find function {}{}", - "was"+PRE_VAL, + "was" + PRE_VAL, "to have the function", message, report, contextualize): return False @@ -587,18 +597,19 @@ def comparison(o, f): except: student_function = getattr(obj, function) if _basic_assertion(student_function, function, - lambda l, r: callable(l), - "The value {} is in the variable {}, and that value is not a callable function.", - "was callable"+PRE_VAL, - "to be callable", - message, report, contextualize, - show_expected_value=False): + lambda l, r: callable(l), + "The value {} is in the variable {}, and that value is not a callable function.", + "was callable" + PRE_VAL, + "to be callable", + message, report, contextualize, + show_expected_value=False): if report is None: report = MAIN_REPORT report.give_partial(score) return True return False + def assertHasClass(sandbox, class_name, attrs=None): pass @@ -614,7 +625,7 @@ def assertHas(obj, variable, types=None, value=None, score=None, if not _basic_assertion(obj, variable, comparison, "Could not find variable {}{}", - "was"+PRE_VAL, + "was" + PRE_VAL, "to have the variable", message, report, contextualize): return False @@ -624,27 +635,28 @@ def assertHas(obj, variable, types=None, value=None, score=None, student_variable = getattr(obj, variable) if types is not None: if not _basic_assertion(student_variable, types, - lambda v, t: isinstance(v, t), - "isinstance({}, {})", - "was"+PRE_VAL, - "to be of type", - message, report, contextualize, - modify_right=_humanize_types): + lambda v, t: isinstance(v, t), + "isinstance({}, {})", + "was" + PRE_VAL, + "to be of type", + message, report, contextualize, + modify_right=_humanize_types): return False if value is not None: if not _basic_assertion(student_variable, value, - lambda l, r: equality_test(l, r, False, DELTA, False), - "{} != {}", - "was"+PRE_VAL, - "to be equal to", - message, report, contextualize, - show_expected_value=False): + lambda l, r: equality_test(l, r, False, DELTA, False), + "{} != {}", + "was" + PRE_VAL, + "to be equal to", + message, report, contextualize, + show_expected_value=False): return False if report is None: report = MAIN_REPORT report.give_partial(score) return True + def assertGenerally(expression, score=None, message=None, report=None, contextualize=True): if report is None: diff --git a/src/lib/pedal/cait/cait_api.py b/src/lib/pedal/cait/cait_api.py index 115c770e02..8dff0a0308 100644 --- a/src/lib/pedal/cait/cait_api.py +++ b/src/lib/pedal/cait/cait_api.py @@ -73,7 +73,7 @@ def _load_cait(student_code, report): else: student_ast = report['source']['ast'] else: - report.attach("No source code found", tool='cait', + report.attach("Unparsable Source", tool='cait', category='analyzer') cait['success'] = False cait['ast'] = CaitNode(ast.parse(""), report=report) diff --git a/src/lib/pedal/cait/cait_node.py b/src/lib/pedal/cait/cait_node.py index b64d970af9..e64509fb0d 100644 --- a/src/lib/pedal/cait/cait_node.py +++ b/src/lib/pedal/cait/cait_node.py @@ -328,6 +328,18 @@ def find_matches(self, pattern, is_mod=False, check_meta=True, use_previous=True """ Retrieves any patterns that match against this CaitNode. Expected to be used for subpattern matching. + Args: + pattern (CaitNode/str): The pattern searched for in this cait_node + is_mod (bool): Assumes that this node is a module node + check_meta (bool): Checks if meta information of the nodes also matches (e.g. instead of just checking children, + also checking whether the children have the same field name in both the pattern and the + source + use_previous (bool): If True, the match will be searched while inheriting the symbol table of the parent. This + means that variable consistency must be maintained between parent and child matches. + + Returns: + :obj: 'list' of :'obj': AstMap: a list of matches + """ # Avoid circular import import pedal.cait.stretchy_tree_matching as stm @@ -340,8 +352,8 @@ def find_matches(self, pattern, is_mod=False, check_meta=True, use_previous=True prev_match = self.map if use_previous else None return matcher.find_matches(self, check_meta=check_meta, pre_match=prev_match) - def find_match(self, pattern, is_mod=False): - matches = self.find_matches(pattern, is_mod) + def find_match(self, pattern, is_mod=False, check_meta=True, use_previous=True): + matches = self.find_matches(pattern, is_mod, check_meta=check_meta, use_previous=use_previous) if len(matches) != 0: return matches[0] return None diff --git a/src/lib/pedal/mistakes/instructor_filter.py b/src/lib/pedal/mistakes/instructor_filter.py index 026b28497e..7362ae24ef 100644 --- a/src/lib/pedal/mistakes/instructor_filter.py +++ b/src/lib/pedal/mistakes/instructor_filter.py @@ -19,7 +19,8 @@ def missing_if_in_for(): Returns: """ - message = "The arrangement of decision and iteration is not correct for the filter pattern." + message = ("The arrangement of decision and iteration is not correct for the filter pattern. " + "You need to evaluate the decision for each element of the list.") code = "missing_if_in_for" tldr = "Missing if In For" matches = find_matches("for _item_ in ___:\n" diff --git a/src/lib/pedal/mistakes/iteration_context.py b/src/lib/pedal/mistakes/iteration_context.py index bb4c7df6c5..f11fdd5d99 100644 --- a/src/lib/pedal/mistakes/iteration_context.py +++ b/src/lib/pedal/mistakes/iteration_context.py @@ -4,7 +4,7 @@ from pedal.report.imperative import explain, gently import pedal.mistakes.instructor_append as append_api from pedal.toolkit.utilities import * -from pedal.sandbox.compatibility import get_output +from pedal.sandbox.compatibility import get_output, get_plots from pedal.report.imperative import gently_r, explain_r @@ -51,21 +51,20 @@ def list_all_zeros_8_2(): message = 'Try seeing what happens when you change the numbers in the list.' code = 'default_list_8.2' tldr = 'Use different numbers' - std_ast = parse_program() - lists = std_ast.find_all('List') - is_all_zero = True - for init_list in lists: - for node in init_list.elts: - if node.ast_name == 'Num' and node.n != 0: - is_all_zero = False + matches = find_matches("_var_ = [__list__]") + for match in matches: + __list__ = match['__list__'] + list_node = __list__.parent + all_num = list_node.find_all("Num") + all_zeros = True + for num in all_num: + if num.n != 0: + all_zeros = False break - if is_all_zero: - break - if is_all_zero: - return explain_r(message, code, label=tldr) + if all_zeros: + return explain_r(message, code, label=tldr) return False - # ################8.2 End####################### @@ -642,7 +641,7 @@ def wrong_list_initialization_9_1(): message = "The list of rainfall amounts (rainfall_list) is not initialized properly." code = "list_init_9.1" tldr = "Incorrect List Initialization" - match = find_match('rainfall_list = weather.get("Precipitation","Location","Blacksburg, VA")') + match = find_match('rainfall_list = weather.get("Data.Precipitation","Station.Location","Blacksburg, VA")') if not match: return explain_r(message, code, label=tldr) return False @@ -733,7 +732,7 @@ def wrong_list_initialization_9_2(): message = "The list of rainfall amounts (rainfall_list) is not initialized properly." code = "list_init_9.2" tldr = "Incorrect List Initialization" - matches = find_matches('rainfall_list = weather.get("Precipitation","Location","Blacksburg, VA")') + matches = find_matches('rainfall_list = weather.get("Data.Precipitation","Station.Location","Blacksburg, VA")') if not matches: return explain_r(message, code, label=tldr) return False @@ -1061,7 +1060,7 @@ def wrong_debug_10_6(): message = "This is not one of the two changes needed. Undo the change and try again." code = "debug_10.6" tldr = "At least one unnecessary change" - matches = find_matches('quakes = earthquakes.get("depth","(None)","")\n' + matches = find_matches('quakes = earthquakes.get("location.depth","(None)","")\n' 'quakes_in_miles = []\n' 'for quake in _list1_:\n' ' _list2_.append(quake * 0.62)\n' @@ -1086,7 +1085,7 @@ def wrong_debug_10_7(): code = "debug_10.7" tldr = "At least one unnecessary change" match = find_match("filtered_sentence_counts = []\n" - "book_sentence_counts = classics.get('sentences','(None)','')\n" + "book_sentence_counts = classics.get('metrics.statistics.sentences','(None)','')\n" "for book in book_sentence_counts:\n" " if book >= 5000:\n" " filtered_sentence_counts.append(book)\n" @@ -1133,19 +1132,21 @@ def wrong_duplicate_var_in_add(): # ########################PLOTTING############################### -def plot_group_error(output=None): +def plot_group_error(output=None, plots=None): if output is None: output = get_output() - if len(output) > 1: - explain_r('You should only be printing/plotting one thing!', "print_one", "Multiple Calls to print or plot") + if plots is None: + plots = get_plots() + if len(plots) > 1: + explain_r('You should only be plotting one thing!', "print_one", "Multiple Calls to plot") return True - elif len(output) == 0: + elif len(plots) == 0: explain_r('The algorithm is plotting an empty list. Check your logic.', 'blank_plot', "Blank Plot") return True - elif not isinstance(output[0], list): + elif output: explain('You should be plotting, not printing!', 'printing', "Printing instead of Plotting") return True - elif len(output[0]) != 1: + elif len(plots[0]['data']) != 1: explain('You should only be plotting one thing!', 'one_plot', "Too Many Plots") return True diff --git a/src/lib/pedal/questions/__init__.py b/src/lib/pedal/questions/__init__.py index 95a7db77c8..e7531163f6 100644 --- a/src/lib/pedal/questions/__init__.py +++ b/src/lib/pedal/questions/__init__.py @@ -47,13 +47,19 @@ def ask(self): for test in self.tests: test(self) if not self.answered: - self.report.attach('Question', category='Instructions', tool='Questions', - group=self.report.group, - priority='instructions', - hint=self.instructions) + show_question(self.instructions, self.report) + + +def show_question(instructions, report=None): + if report is None: + report = MAIN_REPORT + report.attach('Question', category='Instructions', tool='Questions', + group=report.group, priority='instructions', hint=instructions) + class Pool: _POOL_TRACKER = 0 + def __init__(self, name, choices, seed=None, report=None, position=None): self.name = name self.choices = choices diff --git a/src/lib/pedal/sandbox/compatibility.py b/src/lib/pedal/sandbox/compatibility.py index 2ad0866414..a7c1a07b31 100644 --- a/src/lib/pedal/sandbox/compatibility.py +++ b/src/lib/pedal/sandbox/compatibility.py @@ -52,8 +52,11 @@ def get_plots(report=None): if report is None: report = MAIN_REPORT sandbox = _check_sandbox(report) - mock_plt = sandbox.modules['matplotlib.pyplot'] - return mock_plt.plots + if 'matplotlib.pyplot' in sandbox.modules: + mock_plt = sandbox.modules['matplotlib.pyplot'] + if hasattr(mock_plt, 'plots'): + return mock_plt.plots + return [] def capture_output(function, *args, **kwargs): diff --git a/src/lib/pedal/sandbox/mocked.py b/src/lib/pedal/sandbox/mocked.py index a0b0df7017..930aa101e4 100644 --- a/src/lib/pedal/sandbox/mocked.py +++ b/src/lib/pedal/sandbox/mocked.py @@ -45,11 +45,11 @@ def _disabled_globals(): error. """ raise RuntimeError("You are not allowed to call 'globals'.") - + class FunctionNotAllowed(Exception): pass - + def disabled_builtin(name): def _disabled_version(*args, **kwargs): @@ -192,6 +192,56 @@ class MockPedal(BlockedModule): MODULE_NAME = "pedal" +class MockTurtle(MockModule): + """ + Mock Turtle Module that can be used to trace turtle calls. + + Attributes: + calls (list of dict): The traced list of calls + # TODO: it'd be awesome to have a way to construct a representation + # of the drawing result that we could autograde! + """ + def __init__(self): + super().__init__() + + def _reset_turtles(self): + self.calls = [] + + def __repr__(self): + return repr(self.plots) + + ''' + def _generate_patches(self): + def dummy(**kwargs): + pass + + return dict(Canvas=dummy, Pen=dummy, RawPen=dummy, RawTurtle=dummy, Screen=dummy, ScrolledCanvas=dummy, + Shape=dummy, TK=dummy, TNavigator=dummy, TPen=dummy, Tbuffer=dummy, Terminator=dummy, + Turtle=dummy, TurtleGraphicsError=dummy, TurtleScreen=dummy, TurtleScreenBase=dummy, Vec2D=dummy, + addshape=dummy, back=dummy, backward=dummy, begin_fill=dummy, begin_poly=dummy, bgcolor=dummy, + bgpic=dummy, bk=dummy, bye=dummy, circle=dummy, clear=dummy, clearscreen=dummy, clearstamp=dummy, + clearstamps=dummy, clone=dummy, color=dummy, colormode=dummy, config_dict=dummy, deepcopy=dummy, + degrees=dummy, delay=dummy, distance=dummy, done=dummy, dot=dummy, down=dummy, end_fill=dummy, + end_poly=dummy, exitonclick=dummy, fd=dummy, fillcolor=dummy, filling=dummy, forward=dummy, + get_poly=dummy, get_shapepoly=dummy, getcanvas=dummy, getmethparlist=dummy, getpen=dummy, + getscreen=dummy, getshapes=dummy, getturtle=dummy, goto=dummy, heading=dummy, hideturtle=dummy, + home=dummy, ht=dummy, inspect=dummy, isdown=dummy, isfile=dummy, isvisible=dummy, join=dummy, + left=dummy, listen=dummy, lt=dummy, mainloop=dummy, math=dummy, mode=dummy, numinput=dummy, + onclick=dummy, ondrag=dummy, onkey=dummy, onkeypress=dummy, onkeyrelease=dummy, onrelease=dummy, + onscreenclick=dummy, ontimer=dummy, pd=dummy, pen=dummy, pencolor=dummy, pendown=dummy, + pensize=dummy, penup=dummy, pos=dummy, position=dummy, pu=dummy, radians=dummy, + read_docstrings=dummy, readconfig=dummy, register_shape=dummy, reset=dummy, resetscreen=dummy, + resizemode=dummy, right=dummy, rt=dummy, screensize=dummy, seth=dummy, setheading=dummy, + setpos=dummy, setposition=dummy, settiltangle=dummy, setundobuffer=dummy, setup=dummy, + setworldcoordinates=dummy, setx=dummy, sety=dummy, shape=dummy, shapesize=dummy, + shapetransform=dummy, shearfactor=dummy, showturtle=dummy, simpledialog=dummy, speed=dummy, + split=dummy, st=dummy, stamp=dummy, sys=dummy, textinput=dummy, tilt=dummy, tiltangle=dummy, + time=dummy, title=dummy, towards=dummy, tracer=dummy, turtles=dummy, turtlesize=dummy, types=dummy, + undo=dummy, undobufferentries=dummy, up=dummy, update=dummy, width=dummy, window_height=dummy, + window_width=dummy, write=dummy, write_docstringdict=dummy, xcor=dummy, ycor=dummy) + ''' + + class MockPlt(MockModule): """ Mock MatPlotLib library that can be used to capture plot data. diff --git a/src/lib/pedal/sandbox/sandbox.py b/src/lib/pedal/sandbox/sandbox.py index 8e29eb3316..dd611d4d58 100644 --- a/src/lib/pedal/sandbox/sandbox.py +++ b/src/lib/pedal/sandbox/sandbox.py @@ -122,6 +122,9 @@ class Sandbox(DataSandbox): FILE_CONTEXT_MESSAGE = ( "\n\nThe error above occurred when I ran your file: {filename}" ) + INPUT_CONTEXT_MESSAGE = ( + "And entered the inputs:\n```\n{inputs}\n```" + ) TRACER_STYLES = { 'coverage': SandboxCoverageTracer, 'calls': SandboxCallTracer, @@ -550,6 +553,10 @@ def _capture_exception(self, exception, exc_info, report_exceptions, context = '\n'.join(contexts)#[1:]) if context.strip(): context = self.CONTEXT_MESSAGE.format(context=context) + inputs = self.input_contexts[self.call_id] + if inputs is not None and inputs: + inputs = "\n".join(inputs) + context += "\n"+self.INPUT_CONTEXT_MESSAGE.format(inputs=inputs) else: context = self.FILE_CONTEXT_MESSAGE.format(filename=self.report['source']['filename']) self.exception = _add_context_to_error(self.exception, context) diff --git a/src/lib/pedal/source/__init__.py b/src/lib/pedal/source/__init__.py index 7eeaf4b358..54a9f902b5 100644 --- a/src/lib/pedal/source/__init__.py +++ b/src/lib/pedal/source/__init__.py @@ -79,8 +79,7 @@ def _check_issues(code, report): report.attach('Syntax error', category='Syntax', tool='Source', group=report['source']['section'], mistake={'message': "Invalid syntax on line " - + str(e.lineno) - + "\n\n"+str(e), + + str(e.lineno), 'error': e, 'position': {"line": e.lineno}}) report['source']['success'] = False diff --git a/src/lib/pedal/tifa/tifa.py b/src/lib/pedal/tifa/tifa.py index 5bb9d206d6..a67c4f8b23 100644 --- a/src/lib/pedal/tifa/tifa.py +++ b/src/lib/pedal/tifa/tifa.py @@ -764,6 +764,15 @@ def visit_ListComp(self, node): self.visit(generator) return ListType(self.visit(node.elt)) + + def visit_NameConstant(self, node): + value = node.value + if isinstance(value, bool): + return BoolType() + else: + return NoneType() + + def visit_Name(self, node): name = node.id if name == "___": diff --git a/src/lib/pedal/tifa/type_definitions.py b/src/lib/pedal/tifa/type_definitions.py index c8a0e466fa..83d020673d 100644 --- a/src/lib/pedal/tifa/type_definitions.py +++ b/src/lib/pedal/tifa/type_definitions.py @@ -550,3 +550,12 @@ def get_tifa_type(v, custom_types): return get_tifa_type_from_str(v.s, custom_types) elif isinstance(v, ast.Name): return get_tifa_type_from_str(v.id, custom_types) + elif isinstance(v, ast.List): + elements = v.elts + if elements: + return ListType(subtype=get_tifa_type(elements[0], custom_types)) + else: + return ListType(empty=True) + # TODO: Finish filling in static type system + else: + return UnknownType() \ No newline at end of file diff --git a/src/lib/pedal/toolkit/functions.py b/src/lib/pedal/toolkit/functions.py index fc5400c62b..5938ac890f 100644 --- a/src/lib/pedal/toolkit/functions.py +++ b/src/lib/pedal/toolkit/functions.py @@ -74,7 +74,7 @@ def find_def_by_name(name, root=None): return None -def match_parameters(name, *types, root=None): +def match_parameters(name, *types, returns=None, root=None): defn = find_def_by_name(name, root) if defn: for expected, actual in zip(types, defn.args.args): @@ -88,6 +88,21 @@ def match_parameters(name, *types, root=None): "wrong_parameter_type") return None else: + if returns is not None: + if not isinstance(returns, str): + returns = returns.__name__ + if defn.returns: + actual_type = parse_type(defn.returns) + if not type_check(returns, actual_type): + gently_r("Error in definition of function `{}` return type. Expected `{}`, " + "instead found {}.".format(name, returns, actual_type), + "wrong_return_type") + return None + else: + gently_r("Error in definition of function `{}` return type. Expected `{}`, " + "but there was no return type specified.".format(name, returns), + "missing_return_type") + return None return defn @@ -222,7 +237,17 @@ def unit_test(name, *tests): tip = out[1] out = out[0] message = ("{}" * 3) - test_out = the_function(*inp) + ran = True + try: + test_out = the_function(*inp) + except Exception as e: + message = message.format(inputs, str(e), repr(out)) + message = "" + RED_X + message + "" + success = False + ran = False + if not ran: + result += message + continue message = message.format(inputs, repr(test_out), repr(out)) if (isinstance(out, float) and isinstance(test_out, (float, int)) and diff --git a/src/lib/pedal/toolkit/signatures.py b/src/lib/pedal/toolkit/signatures.py index 4b74ab037f..5ba4844650 100644 --- a/src/lib/pedal/toolkit/signatures.py +++ b/src/lib/pedal/toolkit/signatures.py @@ -83,7 +83,7 @@ def parse_type_slice(slice): if slice.ast_name == "Index": - return parse_type(slice.index) + return parse_type(slice.value) elif slice.ast_name == "Slice": return "{}:{}".format(parse_type(slice.lower), parse_type(slice.upper)) elif slice.ast_name == "ExtSlice": diff --git a/src/lib/pedal/toolkit/upload.py b/src/lib/pedal/toolkit/upload.py index 9cefd148ba..54993edd59 100644 --- a/src/lib/pedal/toolkit/upload.py +++ b/src/lib/pedal/toolkit/upload.py @@ -1,7 +1,7 @@ import re from pedal.source import get_program from pedal.sandbox.compatibility import get_output -from pedal.report.imperative import gently, explain +from pedal.report.imperative import gently_r, explain_r # Feedback for author's name @@ -9,8 +9,8 @@ def check_author_name_on_header(): code = get_program() m_author = re.search('Author: \\w+', code) if not m_author: - gently("You need to add your name to the author field at the top of the file." - "

(name_missing)

") + gently_r("You need to add your name to the author field at the top of the file.", "name_missing", + label="Missing Name") def get_plots(output): @@ -29,23 +29,23 @@ def check_output_on_header(expected_output): between_stars = code.split("*****")[2].strip() between_stars = "\\n".join([x.strip() for x in between_stars.split("\\n")]) if 'REPLACE THIS TEXT WITH THE OUTPUT OF THIS PROGRAM' in between_stars: - gently("In your code, you need to 'REPLACE THIS TEXT WITH THE OUTPUT OF THIS PROGRAM'" - "

(wrong_output_blank)

") + gently_r("In your code, you need to 'REPLACE THIS TEXT WITH THE OUTPUT OF THIS PROGRAM'", "wrong_output_blank", + label="Blank Output") elif expected_output not in between_stars: - gently("The output you copied between the *****, seems to be incorrect. You may have copied it into the wrong " - "location, or it is incomplete.

(wrong_output_fill)

") + gently_r("The output you copied between the *****, seems to be incorrect. " + "You may have copied it into the wrong location, or it is incomplete.", "wrong_output_fill", label="") def check_problem_submission(prob_id): if prob_id not in get_program(): - explain("Make sure that you are turning in {}

(wrong_problem)

".format(prob_id)) + explain_r("Make sure that you are turning in {}
".format(prob_id), "wrong_problem", label="Wrong Problem") return True def check_print_output(multiple_lines): for line in multiple_lines: if line not in get_output(): - gently("You are not doing the correct calculation

(catch_all)

") + gently_r("You are not doing the correct calculation
", "catch_all", label="Wrong Output") return True diff --git a/src/lib/posixpath.py b/src/lib/posixpath.py index 5fd5b6f9cd..91bfc77ce1 100644 --- a/src/lib/posixpath.py +++ b/src/lib/posixpath.py @@ -172,11 +172,7 @@ def dirname(p): def islink(path): """Test whether a path is a symbolic link""" - try: - st = os.lstat(path) - except (OSError, ValueError, AttributeError): - return False - return stat.S_ISLNK(st.st_mode) + return False # Being true for dangling symbolic links is also useful. diff --git a/src/parser.js b/src/parser.js index 28d4813937..f93712e48b 100644 --- a/src/parser.js +++ b/src/parser.js @@ -287,16 +287,8 @@ function makeParser (filename, style) { return p; } -Sk.parseCache = { - "lastInput": null, - "lastParse": null, - "lastUnit": null -}; Sk.parse = function parse (filename, input) { - if (Sk.parseCache.lastInput == input) { - return Sk.parseCache.lastUnit; - } var T_COMMENT = Sk.token.tokens.T_COMMENT; var T_NL = Sk.token.tokens.T_NL; @@ -370,8 +362,6 @@ Sk.parse = function parse (filename, input) { * Small adjustments here in order to return th flags and the cst */ var result = {"cst": parser.rootnode, "flags": parser.p_flags, "comments": parser.comments}; - Sk.parseCache.lastUnit = result; - Sk.parseCache.lastInput = input; return result; }; diff --git a/src/str.js b/src/str.js index d718e4a9a8..797c753087 100755 --- a/src/str.js +++ b/src/str.js @@ -238,6 +238,11 @@ Sk.builtin.str.re_escape_ = function (s) { return ret.join(""); }; +Sk.builtin.str.prototype["encode"] = new Sk.builtin.func(function (self) { + //Sk.builtin.pyCheckArgsLen("encode", arguments.length, 1, 1); + return self; +}); + Sk.builtin.str.prototype["lower"] = new Sk.builtin.func(function (self) { Sk.builtin.pyCheckArgsLen("lower", arguments.length, 1, 1); return new Sk.builtin.str(self.v.toLowerCase()); diff --git a/src/type.js b/src/type.js index 13d74571d9..5bc7f2bd60 100644 --- a/src/type.js +++ b/src/type.js @@ -250,8 +250,8 @@ Sk.builtin.type = function (name, bases, dict) { klass[k.v] = v; } - klass["__class__"] = klass; - klass["__name__"] = name; + klass.prototype.__class__ = klass; + klass.prototype.__name__ = name; klass.sk$klass = true; klass.prototype["$r"] = function () { var cname; @@ -478,6 +478,7 @@ Sk.builtin.type.makeIntoTypeObj = function (name, t) { Sk.asserts.assert(name !== undefined); Sk.asserts.assert(t !== undefined); t.ob$type = Sk.builtin.type; + t.__class__ = Sk.builtin.type; t.tp$name = name; t["$r"] = function () { var ctype; diff --git a/test/run/t474.py b/test/run/t474.py index 7c607c192c..99988a9da3 100644 --- a/test/run/t474.py +++ b/test/run/t474.py @@ -1,29 +1,30 @@ class A: pass -print isinstance(4, int) -print isinstance(4, (float,int)) -print isinstance(A(), A) -print isinstance(4, (int, float, 5)) -print isinstance(4, (int, float, A())) -print isinstance(A, A) -print isinstance(4, type(4)) -print isinstance(True, type(False)) -print isinstance(5.4, type(1.2)) -print isinstance(3L, type(8L)) -print isinstance([1,2,3], type([5,6])) -print isinstance({1:2}, type({3:4})) -print isinstance((1,2), type((3,4))) -print isinstance(set([1,2]), type(set([3,4]))) -print isinstance(A(), type(A())) -print isinstance(None, type(None)) +print(isinstance(4, int)) +print(isinstance(4, (float,int))) +print(isinstance(A(), A)) +print(isinstance(4, (int, float, 5))) +print(isinstance(4, (int, float, A()))) +print(isinstance(A, A)) + +print(isinstance(4, type(4))) +print(isinstance(True, type(False))) +print(isinstance(5.4, type(1.2))) +print(isinstance(3, type(8))) +print(isinstance([1,2,3], type([5,6]))) +print(isinstance({1:2}, type({3:4}))) +print(isinstance((1,2), type((3,4)))) +print(isinstance(set([1,2]), type(set([3,4])))) +print(isinstance(A(), type(A()))) +print(isinstance(None, type(None))) # for error testing -- all of these should throw a TypeError -# print isinstance(4, 4) -# print isinstance(A(), 4) -# print isinstance(A(), True) -# print isinstance(4, A()) -# print isinstance(4, (5, 6, 7)) -# print isinstance(4, (5, 6, float)) -# print isinstance(4, (5, 6, float, int)) -# print isinstance(4, (float, 5, 6)) +# print(isinstance(4, 4)) +# print(isinstance(A(), 4)) +# print(isinstance(A(), True)) +# print(isinstance(4, A())) +# print(isinstance(4, (5, 6, 7))) +# print(isinstance(4, (5, 6, float))) +# print(isinstance(4, (5, 6, float, int))) +# print(isinstance(4, (float, 5, 6))) diff --git a/test/test_hashlib.py b/test/test_hashlib.py new file mode 100644 index 0000000000..8daadfd11c --- /dev/null +++ b/test/test_hashlib.py @@ -0,0 +1,8 @@ +import hashlib + + +assert hashlib.md5("abc".encode('utf-8')).digest()[0] == 144 + +assert hashlib.md5("cory bart".encode('utf-8')).digest()[0] == 135 + +assert hashlib.md5("Dr. m'banana".encode('utf-8')).digest()[0] == 136 \ No newline at end of file diff --git a/update_pedal.bat b/update_pedal.bat new file mode 100644 index 0000000000..0c68c43057 --- /dev/null +++ b/update_pedal.bat @@ -0,0 +1 @@ +cp -R C:/Users/acbart/Projects/pedal/pedal/ C:/Users/acbart/Projects/blockpy-edu/skulpt/src/lib/ \ No newline at end of file From 1dedea8480f02c24bfb3949c0ad5399f9eaf6c97 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 3 Oct 2019 22:21:37 -0400 Subject: [PATCH 25/68] Interesting getattr bug for metaclasses --- src/builtin.js | 6 ++-- src/int.js | 8 ++--- src/lib/pedal/assertions/assertions.py | 16 ++++----- src/lib/pedal/questions/loader.py | 49 ++++++++++++++++++++++++++ src/lib/pedal/toolkit/functions.py | 12 +++---- src/str.js | 4 +-- src/type.js | 8 ++--- test/test_pedal_bugs.py | 47 ++++++++++++++++++++++++ 8 files changed, 123 insertions(+), 27 deletions(-) create mode 100644 src/lib/pedal/questions/loader.py create mode 100644 test/test_pedal_bugs.py diff --git a/src/builtin.js b/src/builtin.js index f8e18f14ce..2e3a56c4ba 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -474,9 +474,9 @@ Sk.builtin.abs = function abs (x) { } // call custom __abs__ methods - if (x.tp$getattr) { - var f = x.tp$getattr(Sk.builtin.str.$abs); - return Sk.misceval.callsimArray(f); + if (x.__class__.tp$getattr) { + var f = x.__class__.tp$getattr(Sk.builtin.str.$abs); + return Sk.misceval.callsimArray(f, [x]); } throw new TypeError("bad operand type for abs(): '" + Sk.abstr.typeName(x) + "'"); diff --git a/src/int.js b/src/int.js index 6fcbe7deff..623d4f62f5 100644 --- a/src/int.js +++ b/src/int.js @@ -89,18 +89,18 @@ Sk.builtin.int_ = function (x, base) { * 1. __int__ * 2. __trunc__ */ - if(x !== undefined && (x.tp$getattr && (func = x.tp$getattr(Sk.builtin.str.$int_)))) { + if(x !== undefined && (x.__class__ && x.__class__.tp$getattr && (func = x.__class__.tp$getattr(Sk.builtin.str.$int_)))) { // calling a method which contains im_self and im_func // causes skulpt to automatically map the im_self as first argument - ret = Sk.misceval.callsimArray(func); + ret = Sk.misceval.callsimArray(func, [x]); magicName = "__int__"; } else if(x !== undefined && x.__int__) { // required for internal types // __int__ method is on prototype ret = Sk.misceval.callsimArray(x.__int__, [x]); magicName = "__int__"; - } else if(x !== undefined && (x.tp$getattr && (func = x.tp$getattr(Sk.builtin.str.$trunc)))) { - ret = Sk.misceval.callsimArray(func); + } else if(x !== undefined && (x.__class__ && x.__class__.tp$getattr && (func = x.__class__.tp$getattr(Sk.builtin.str.$trunc)))) { + ret = Sk.misceval.callsimArray(func, [x]); magicName = "__trunc__"; } else if(x !== undefined && x.__trunc__) { ret = Sk.misceval.callsimArray(x.__trunc__, [x]); diff --git a/src/lib/pedal/assertions/assertions.py b/src/lib/pedal/assertions/assertions.py index 41823f333e..e0d63d5474 100644 --- a/src/lib/pedal/assertions/assertions.py +++ b/src/lib/pedal/assertions/assertions.py @@ -144,11 +144,11 @@ def _build_context(sandboxed_results, actual_message, expected_message, # Actual rendering of text if calls: calls = [_escape_curly_braces(str(call)) for call in calls] - context.append("I ran:
" + "\n".join(calls) + "
") + context.append("I ran:\n
" + "\n".join(calls) + "
") if inputs: inputs = [_escape_curly_braces(str(inp)) for inp in inputs] - context.append("I entered as input:
" + "\n".join(inputs) + "
") - actual_message += ":
{}
" + context.append("I entered as input:\n
" + "\n".join(inputs) + "
") + actual_message += ":\n
{}
" for i, target in enumerate(targets): named_target = _build_result_from_target(target, i, len(targets)) if target == '_': @@ -164,7 +164,7 @@ def _build_context(sandboxed_results, actual_message, expected_message, expected_context += _build_result_from_target(targets[0], 0, 1) expected_context += " " + expected_message if show_expected_value: - expected_context += ":
{}
" + expected_context += ":\n
{}
" context.append(expected_context) return "\n".join(context) @@ -541,20 +541,20 @@ def assertPrints(result, expected_output, args=None, returns=None, if not equality_test(actual_output, expected_output, exact, DELTA, True): context = [] if calls: - context.append("I ran:
" +
+            context.append("I ran:\n
" +
                            "\n".join(map(str, calls)) +
                            "
") if inputs: - context.append("I entered as input:
" +
+            context.append("I entered as input:\n
" +
                            "\n".join(map(str, inputs)) +
                            "
") if actual_output: - context.append("The function printed:
" +
+            context.append("The function printed:\n
" +
                            "\n".join(map(str, actual_output)) +
                            "
") else: context.append("The function printed nothing.") - context.append("But I expected the output:
" + "\n".join(map(str, expected_output)) + "
") + context.append("But I expected the output:\n
" + "\n".join(map(str, expected_output)) + "
") failure = AssertionException("\n".join(context)) report['assertions']['collected'].append(failure) report.attach('Instructor Test', category='student', tool='Assertions', diff --git a/src/lib/pedal/questions/loader.py b/src/lib/pedal/questions/loader.py new file mode 100644 index 0000000000..75ba08ba3e --- /dev/null +++ b/src/lib/pedal/questions/loader.py @@ -0,0 +1,49 @@ +""" +instructions: blah blah blah + +settings: + tifa: + enabled: True + unit testing: + by function (bool): Whether to test each function entirely before moving onto the + next one, or to first check that all functions have been defined, and then + checking their parameters, etc. Defaults to True. +global: + variables: + name: + type: + value: + inputs: + prints: +functions: + function: do_complicated_stuff + signature: int, int, list[int], (int->str), dict[str:list[int]] -> list[int] + cases: + arguments: + inputs: + returns: + prints: +""" + +from pedal.toolkit.printing import * +from pedal.toolkit.utilities import * +from pedal.toolkit.functions import * + +EXAMPLE_DATA = { + 'functions': [{ + 'function': 'do_complicated_stuff', + 'signature': 'int, int, [int] -> list[int]', + 'cases': [ + {'arguments': "5, 4, 3", 'returns': "12"}, + ] + }] +} + +def load(data): + if 'functions' in data: + for function in data['functions']: + pass + + +def load_file(filename): + pass \ No newline at end of file diff --git a/src/lib/pedal/toolkit/functions.py b/src/lib/pedal/toolkit/functions.py index 5938ac890f..ee33113454 100644 --- a/src/lib/pedal/toolkit/functions.py +++ b/src/lib/pedal/toolkit/functions.py @@ -168,32 +168,32 @@ def output_test(name, *tests): message += "" + tip + "" success = False elif len(test_out) > 1: - message = message.format(inputs, repr(out), "Too many outputs", tip) + message = message.format(inputs, "\n".join(out), "Too many outputs", tip) message = "" + RED_X + message + "" if tip: message += "" + tip + "" success = False elif out not in test_out: - message = message.format(inputs, repr(out), repr(test_out[0]), tip) + message = message.format(inputs, "\n".join(out), "\n".join(test_out), tip) message = "" + RED_X + message + "" if tip: message += "" + tip + "" success = False else: - message = message.format(inputs, repr(out), repr(test_out[0]), tip) + message = message.format(inputs, "\n".join(out), "\n".join(test_out), tip) message = "" + GREEN_CHECK + message + "" success_count += 1 elif out != test_out: if len(test_out) < 1: - message = message.format(inputs, repr(out), "No output", tip) + message = message.format(inputs, "\n".join(out), "No output", tip) else: - message = message.format(inputs, repr(out), repr(test_out[0]), tip) + message = message.format(inputs, "\n".join(out), "\n".join(test_out), tip) message = "" + RED_X + message + "" if tip: message += "" + tip + "" success = False else: - message = message.format(inputs, repr(out), repr(test_out[0]), tip) + message = message.format(inputs, "\n".join(out), "\n".join(test_out), tip) message = "" + GREEN_CHECK + message + "" success_count += 1 result += message diff --git a/src/str.js b/src/str.js index 797c753087..002f690830 100755 --- a/src/str.js +++ b/src/str.js @@ -1128,10 +1128,10 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { if (Sk.__future__.python3) { precision = 6; } else { - precision = 7; + precision = 7; + } } } - } result = (convValue)[convName](precision); // possible loose of negative zero sign // apply sign to negative zeros, floats only! diff --git a/src/type.js b/src/type.js index 5bc7f2bd60..3f666ffb4c 100644 --- a/src/type.js +++ b/src/type.js @@ -256,9 +256,9 @@ Sk.builtin.type = function (name, bases, dict) { klass.prototype["$r"] = function () { var cname; var mod; - var reprf = this.tp$getattr(Sk.builtin.str.$repr); + var reprf = this.__class__.tp$getattr(Sk.builtin.str.$repr); if (reprf !== undefined && reprf.im_func !== Sk.builtin.object.prototype["__repr__"]) { - return Sk.misceval.apply(reprf, undefined, undefined, undefined, []); + return Sk.misceval.apply(reprf, undefined, undefined, undefined, [this]); } if ((klass.prototype.tp$base !== undefined) && @@ -317,9 +317,9 @@ Sk.builtin.type = function (name, bases, dict) { }; klass.prototype.tp$str = function () { - var strf = this.tp$getattr(Sk.builtin.str.$str); + var strf = this.__class__.tp$getattr(Sk.builtin.str.$str); if (strf !== undefined && strf.im_func !== Sk.builtin.object.prototype["__str__"]) { - return Sk.misceval.apply(strf, undefined, undefined, undefined, []); + return Sk.misceval.apply(strf, undefined, undefined, undefined, [this]); } if ((klass.prototype.tp$base !== undefined) && (klass.prototype.tp$base !== Sk.builtin.object) && diff --git a/test/test_pedal_bugs.py b/test/test_pedal_bugs.py new file mode 100644 index 0000000000..04d4563fad --- /dev/null +++ b/test/test_pedal_bugs.py @@ -0,0 +1,47 @@ +class C(object): + def __abs__(self): + return 10 + def __getattribute__(*args): + print("Class getattribute invoked", (args[1])) + return object.__getattribute__(*args) + +c = C() + +print("Implicit lookup") +print(abs(c)) +#10 + + +#--------------------------------------- + + + +from pedal.report import * + +from pedal.source import set_source + +CODE = """ +def x(): + return -5 +""" + +set_source(CODE, "answer.py") + +from pedal.assertions import assert_equal +from pedal.sandbox.sandbox import Sandbox +from pedal.sandbox import compatibility + +student = MAIN_REPORT['sandbox']['run'] = Sandbox() + +compatibility.run_student(raise_exceptions=False) + +#assert_equal(student.call('x'), 1.5) +y=student.call('x') +#print("AAAA") +print(abs(y)) +#print("BBB") +#print(abs(y)) +#print("CCCC") + + + From 5c7187227a4bdd99be9b391a1a851b712d88ccba Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 6 Oct 2019 09:35:07 -0400 Subject: [PATCH 26/68] Pedal upgrades --- src/lib/pedal/plugins/grade_magic.py | 2 +- src/lib/pedal/questions/graders.py | 2 +- src/lib/pedal/toolkit/functions.py | 7 ++-- src/lib/pedal/toolkit/signatures.py | 57 +++++++++++++++++++++++++--- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/lib/pedal/plugins/grade_magic.py b/src/lib/pedal/plugins/grade_magic.py index 56dca6d845..695f45f72e 100644 --- a/src/lib/pedal/plugins/grade_magic.py +++ b/src/lib/pedal/plugins/grade_magic.py @@ -21,7 +21,7 @@ # installs per user. # This really should come in as a configuration setting somewhere. -BLOCKPY_URL = 'https://think.cs.vt.edu/blockpy/load_assignment_give_feedback' +BLOCKPY_URL = 'https://think.cs.vt.edu/blockpy/blockpy/load_assignment_give_feedback' def get_response_error(response): diff --git a/src/lib/pedal/questions/graders.py b/src/lib/pedal/questions/graders.py index 20efa494e6..b720226100 100644 --- a/src/lib/pedal/questions/graders.py +++ b/src/lib/pedal/questions/graders.py @@ -101,5 +101,5 @@ def grade_unit_tests(self, question): if all_good: self.points += self.UNIT_TEST_COMPLETION_POINTS else: - gently("Failing unit tests") + gently("Failing instructor unit tests") return all_good diff --git a/src/lib/pedal/toolkit/functions.py b/src/lib/pedal/toolkit/functions.py index ee33113454..e11e187543 100644 --- a/src/lib/pedal/toolkit/functions.py +++ b/src/lib/pedal/toolkit/functions.py @@ -3,7 +3,7 @@ from pedal.sandbox import compatibility import ast -from pedal.toolkit.signatures import type_check, parse_type, normalize_type +from pedal.toolkit.signatures import type_check, parse_type, normalize_type, parse_type_value, test_type_equality DELTA = 0.001 @@ -79,10 +79,9 @@ def match_parameters(name, *types, returns=None, root=None): if defn: for expected, actual in zip(types, defn.args.args): if actual.annotation: - if not isinstance(expected, str): - expected = expected.__name__ + expected = parse_type_value(expected, True) actual_type = parse_type(actual.annotation) - if not type_check(expected, actual_type): + if not test_type_equality(expected, actual_type): gently_r("Error in definition of function `{}` parameter `{}`. Expected `{}`, " "instead found `{}`.".format(name, actual.arg, expected, actual_type), "wrong_parameter_type") diff --git a/src/lib/pedal/toolkit/signatures.py b/src/lib/pedal/toolkit/signatures.py index 5ba4844650..b2a9a3de33 100644 --- a/src/lib/pedal/toolkit/signatures.py +++ b/src/lib/pedal/toolkit/signatures.py @@ -1,6 +1,8 @@ +import ast import re from pedal.cait.cait_api import parse_program +from pedal.cait.cait_node import CaitNode from pedal.report.imperative import gently, explain """ @@ -91,16 +93,19 @@ def parse_type_slice(slice): def parse_type(node): if node.ast_name == "Str": - return node.s + try: + return parse_type(ast.parse(node.s).body[0].value) + except: + return node.s elif node.ast_name == "Name": return node.id elif node.ast_name == "NameConstant": return node.value elif node.ast_name == "List": - return "list[{}]".format(", ".join([parse_type(n) for n in node.elts])) + return "[{}]".format(", ".join([parse_type(n) for n in node.elts])) elif node.ast_name == "Dict": - return "dict[{}]".format(", ".join(["{}: {}".format(parse_type(k), parse_type(v)) - for k,v in zip(node.keys, node.values)])) + return "{"+(", ".join(["{}: {}".format(parse_type(k), parse_type(v)) + for k,v in zip(node.keys, node.values)]))+"}" elif node.ast_name == "Subscript": return parse_type(node.value) + "[{}]".format(parse_type_slice(node.slice)) elif node.ast_name == "BoolOp": @@ -109,6 +114,39 @@ def parse_type(node): return "?" +def parse_type_value(value, parse_strings=False): + if isinstance(value, str): + if parse_strings: + return parse_type(CaitNode(ast.parse(value).body[0].value)) + else: + return repr(value) + elif value in (int, str, bool, float, list, dict, object): + return value.__name__ + elif value is None: + return "None" + elif isinstance(value, list): + if value == []: + return "[]" + else: + return "[{}]".format(parse_type_value(value[0])) + elif isinstance(value, tuple): + if value == (): + return "()" + else: + return "({})".format("".join(["{}, ".format(parse_type_value(v)) + for v in value])) + elif isinstance(value, dict): + if value == {}: + return "{}" + else: + return "{" + (", ".join(["{}: {}".format(parse_type_value(k), parse_type_value(v)) + for k, v in value.items()])) + "}" + + +def test_type_equality(left, right): + return left == right + + class SignatureException(Exception): pass @@ -152,7 +190,7 @@ def _normalize_identifier(identifier): return identifier -SPECIAL_SYMBOLS = r"\s*(->|\s*[\[\],\(\)\:]|or)\s*" +SPECIAL_SYMBOLS = r"\s*(->|\s*[\[\],\(\)\:\{\}]|or)\s*" def _parse_tokens(tokens): @@ -168,6 +206,10 @@ def _parse_tokens(tokens): elif current == "]": subexpression = result_stack.pop() result_stack[-1].append(subexpression) + # Ending a curly bracket, better stop here. + elif current == "}": + subexpression = result_stack.pop() + result_stack[-1].append(subexpression) # We've reached the last token! elif not tokens: # And had no tokens before this one @@ -176,6 +218,10 @@ def _parse_tokens(tokens): # Starting a parentheized expression elif current == "(": result_stack.append(Stack()) + elif current == "[": + result_stack.append(Stack("list")) + elif current == "{": + result_stack.append(Stack("dict")) # Nullary function elif current == "->": result_stack[-1].append(Stack("callable")) @@ -234,6 +280,7 @@ def check_piece(left, right, indent=1): def type_check(left, right): left = normalize_type(left) + print(left, "||||", right) right = normalize_type(right) return check_piece(left, right) From bb6ff875875cf70c8362e922413ff3ddd0ed560f Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 6 Oct 2019 18:23:31 -0400 Subject: [PATCH 27/68] Make luke's cs1014 library available --- src/lib/cs1014/__init__.py | 0 src/lib/cs1014/dictionaries.py | 656 ++++++++++++++++ src/lib/cs1014/input_mistakes.py | 25 + src/lib/cs1014/tests/__init__.py | 0 src/lib/cs1014/tests/test_dictionary.py | 977 ++++++++++++++++++++++++ update_pedal.bat | 3 +- 6 files changed, 1660 insertions(+), 1 deletion(-) create mode 100644 src/lib/cs1014/__init__.py create mode 100644 src/lib/cs1014/dictionaries.py create mode 100644 src/lib/cs1014/input_mistakes.py create mode 100644 src/lib/cs1014/tests/__init__.py create mode 100644 src/lib/cs1014/tests/test_dictionary.py diff --git a/src/lib/cs1014/__init__.py b/src/lib/cs1014/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/lib/cs1014/dictionaries.py b/src/lib/cs1014/dictionaries.py new file mode 100644 index 0000000000..7104680d05 --- /dev/null +++ b/src/lib/cs1014/dictionaries.py @@ -0,0 +1,656 @@ +from pedal.report.imperative import gently_r, explain_r +from pedal.cait.cait_api import * +from pedal.mistakes.instructor_append import app_assign + + +# dict_acc_group +def dict_acc_group(all_keys, unused_keys, used_keys): + print_dict_key(all_keys) + var_instead_of_key(all_keys) + parens_in_dict(all_keys) + missing_key(used_keys) + str_list(all_keys) + dict_parens_brack() + comma_dict_acc() + var_key(all_keys) + miss_dict_acc() + comp_in_dict_acc() + key_comp(all_keys) + col_dict() + wrong_keys(unused_keys) + + +# dict_list_group +def dict_list_group(all_keys): + list_str_dict(all_keys) + list_var_dict_acc() + list_str_as_list_var(all_keys) + fetch_acc_dict(all_keys) + list_as_dict() + iter_as_key(all_keys) + iter_prop_dict_acc() + + append_and_sum() + + dict_out_of_loop(all_keys) + dict_access_not_in_loop() + no_dict_in_loop() + app_assign() + + +# dict_decision +def dict_decision_group(all_keys, c_value, num_slices): + func_filter(all_keys) + filt_key(c_value, num_slices) + compare_key(c_value) + str_equality() + fetch_acc_dict([c_value]) + + +# dict_plot +def dict_plot_group(): + show_args() + dict_plot() + + +# dict_chain +def dict_chain_group(key_sets): + for key_set in key_sets: + key_order(key_set) + key_order_unchained(key_set) + + +# dict_hard_codes +def dict_hard_codes_group(print_vals, list_vals): + hard_coding(print_vals) + hard_coded_list(list_vals) + + +# dict_hard_codes +def hard_coding(val_list): + message = ("Please show code that makes the computer extract " + "the value from the dictionary.") + code = "hard_code" + tldr = "Printing raw value" + # Pattern 1 possibility + matches = find_matches("print(__exp__)") + for match in matches: + __exp__ = match["__exp__"] + value = __exp__.value + if value in val_list: + return explain_r(message, code, label=tldr) + + # Pattern 2 possibility + matches = find_matches("__exp__\n" + "print(_var_)") + for match in matches: + __exp__ = match["__exp__"] + _var_ = match["_var_"] + submatches = __exp__.find_matches("_var_ = __exp2__") + for submatch in submatches: + __exp2__ = submatch["__exp2__"] + value = __exp2__.value + if value in val_list: + return explain_r(message, code, label=tldr) + return False + + +# dict_acc_group +def print_dict_key(keys): + message = ('You\'ve printed the dictionary key "{}" instead of using an extracted value and ' + 'printing it. Use the Dictionary access syntax to print the value associated with a key') + code = "dict_k_print" + tldr = "Printing key, not value" + matches = find_matches("print(__str__)") + matches += find_matches("print([__str__])") + + for match in matches: + __str__ = match["__str__"] + if __str__.is_ast("Str") and __str__.value in keys: + return explain_r(message.format(__str__.value), code, label=tldr) + return False + + +# dict_acc_group +def var_instead_of_key(keys): + message = ("It looks like you are trying to use ({}) as a dictionary key. " + "Use the dictionary access syntax to get values from a dictionary") + code = "var_as_k" + tldr = "Using Variable instead of key" + matches = find_matches("_var_") + matches += find_matches("[_var_]") + for match in matches: + _var_ = match["_var_"] + if _var_.id in keys: + submatch = find_match("_dict_['{}']".format(_var_.id)) + if submatch is None: + return explain_r(message.format(_var_.id), code, label=tldr) + return False + + +# dict_acc_group +def parens_in_dict(keys): + message = ('It seems like you are having trouble with dictionary syntax. The dictionary key "{}"' + "should use brackets.") + code = "par_dict" + tldr = "Not Using Dictionary Brackets" + matches = find_matches("_var_(__str__)") + for match in matches: + __str__ = match['__str__'] + if __str__.is_ast("Str") and __str__.value in keys: + return explain_r(message.format(__str__.value), code, label=tldr) + return False + + +# dict_list_group +def list_as_dict(): + message = ("A list of Dictionaries like {} is not itself a dictionary. " + "To access key-value pairs of the dictionaries in the list, " + "you need to access each dictionary in the list one at a time.") + code = "list_dict" + tldr = "List is not a dictionary" + matches = find_matches("_list_[__exp__]") + for match in matches: + _list_ = match['_list_'] + if (_list_.was_type("ListType") and _list_.get_data_state() + and str(_list_.get_data_state().type.subtype) == "DictType"): + return explain_r(message.format(_list_.id), code, label=tldr) + return False + + +# dict_list_group +def dict_out_of_loop(keys): + message = ("Remember that a list of dictionaries, like {}, " + "is still a list of individual items. Each dictionary needs to be accessed with " + "the appropriate key-value pair one at a time.") + code = "dict_out_loop" + tldr = "Dictionary Access Outside of Loop" + matches = find_matches("__exp__\n" + "for ___ in _var_:\n" + " pass") + matches += find_matches("for ___ in _var_:\n" + " pass\n" + "__exp__\n") + for match in matches: + __exp__ = match['__exp__'] + _var_ = match['_var_'] + submatches = __exp__.find_matches("{var}[__str__]".format(var=_var_.id)) + for submatch in submatches: + __str__ = submatch['__str__'] + if __str__.is_ast("Str") and __str__.value in keys: + return explain_r(message.format(_var_.id), code, label=tldr) + return False + + +# dict_acc_group +def wrong_keys(unused_keys): + message = 'This problem does not require the key "{}".\n' + code = "unused_key" + tldr = "Unnecessary Key Usage" + + matches = find_matches("_var_[__str__]") + for match in matches: + __str__ = match["__str__"] + if __str__.is_ast("Str") and __str__.value in unused_keys: + return explain_r(message.format(__str__.value), code, label=tldr) + return False + + +# dict_list_group +def dict_access_not_in_loop(): + message = ("You haven't used the dictionary access syntax in a for loop. " + "Remember that a list of dictionaries is still a list of individual items. " + "Each dictionary needs to be accessed with the appropriate key-value pair one at a time.") + code = "dict_acc_loop" + tldr = "Dictionary access not in loop" + + matches = find_matches("for ___ in ___:\n" + " __exp__") + for match in matches: + submatches = match["__exp__"].find_matches("_var_[__str__]") + if submatches: + return False + return explain_r(message, code, label=tldr) + + +def hard_coded_list(val_list): + message = ("In later abstractions, it's not possible to view the values of a specific key in a list." + "You should use a dictionary key-value pair to access values in the list of dictionaries.") + code = "hard_list" + tldr = "Don't use raw list" + matches = find_matches("[__exp__]") + for match in matches: + __exp__ = match['__exp__'].parent + if __exp__.ast_name == "List": + try: + vals = sum([x.value for x in __exp__.elts]) + if sum(val_list) == vals: + return explain_r(message, code, label=tldr) + except TypeError: + pass # This should be the only error + return False + + +# dict_list_group +def iter_as_key(keys): + message = ("It looks like you are using the iteration variable {}" + " to access a value of a specific key in a dictionary. " + "To access a key-value from a list of dictionaries, use ") + code = "iter_key" + tldr = "Iteration variable is not key" + matches = find_matches("for _var_ in ___:\n" + " pass") + for match in matches: + _var_ = match['_var_'] + submatches = find_matches("_var2_[__str__]") + missing = True + for submatch in submatches: + __str__ = submatch["__str__"] + if __str__.is_ast("Str") and __str__.value == _var_.id: + missing = False + break + if missing and _var_.id in keys: + return explain_r(message.format(_var_.id), code, label=tldr) + return False + + +# dict_list_group +def list_str_as_list_var(keys): + message = ("The list variable in an iteration can only take lists. " + "To grab individual values in a list of dictionaries, " + "you need to use the appropriate key for each dictionary.") + code = "list_var_dict" + tldr = "List variable cannot filter" + matches = find_matches("for ___ in [__str__]:\n" + " pass") + for match in matches: + __str__ = match["__str__"] + if __str__.is_ast("Str") and __str__.value in keys: + return explain_r(message, code, label=tldr) + return False + + +# dict_list_group +def append_and_sum(): + message = ("It looks like you're trying to build a list and " + "then calculate a value. While this will get you a " + "correct answer, you can calculate the value directly instead of first building a list.") + code = "app_sum" + tldr = "Unnecessary append and sum" + matches = find_match("for ___ in ___:\n" + " _var_.append()\n" + "for ___ in _var_:\n" + " ___ = ___ + ___") + if matches: + return explain_r(message, code, label=tldr) + return False + + +# dict_list_group +def iter_prop_dict_acc(): + message = ("Improper usage of iteration variable." + "The for statement gives the iteration variable a value, " + "in this case, a dictionary. That dictionary can only be accessed in the body of the iteration.") + code = "iter_dict_acc" + tldr = "Iteration variable only initializes" + match = find_match("for _var_[__str__] in ___:\n" + " pass") + if match: + return explain_r(message, code, label=tldr) + return False + + +# dict_list_group +def list_str_dict(keys): + message = ("When using dictionaries with iteration, the list cannot just be a key " + 'value like "{}", it must be the list of dictionaries.') + code = "list_str" + tldr = "List variable is string" + matches = find_matches("for ___ in __str__:\n" + " pass") + for match in matches: + __str__ = match['__str__'] + if __str__.is_ast("Str") and __str__.value in keys: + return explain_r(message.format(__str__.value), code, label=tldr) + return False + + +# dict_acc_group +def missing_key(keys): + message = "You seem to be missing the following dictionary key(s):
    {}
" + code = "miss_key" + tldr = "Missing necessary keys" + key_list = "" + for key in keys: + matches = find_matches("\"{}\"".format(key)) + if not matches: + key_list += '
  • "' + key + '"
  • ' + if key_list != "": + return explain_r(message.format(key_list), code, label=tldr) + return False + + +def blank_key(keys): + message = "You seem to be missing the following dictionary keys:
      {}
    " + code = "blank_key" + tldr = "Missing Key" + key_list = "" + + for key in keys: + if not find_match("_var_['{}']".format(key)): + key_list += '
  • "' + key + '"
  • ' + + if key_list != "": + return explain_r(message.format(key_list), code, label=tldr) + + +# dict_acc_group +def dict_parens_brack(): + message = ("It looks like you are trying to dictionary access {}. " + "The dictionary access syntax does not require parenthesis.") + code = "dict_parbrack" + tldr = "Improper dictionary access" + matches = find_matches("_var_([__str1__][__str2__])") + matches += find_matches("_var_([__str1__])") + for match in matches: + _var_ = match['_var_'] + __str1__ = match["__str1__"] + __str2__ = __str1__ + try: + __str2__ = match["__str2__"] + except KeyError: + pass + if __str1__.is_ast("Str") and __str2__.is_ast("Str") and data_state(_var_.id): + return explain_r(message.format(_var_.id), code, label=tldr) + return False + + +# dict_acc_group +def comma_dict_acc(): + message = ("It looks like you are trying to dictionary access {}. " + "Unlike with initializing dictionaries, keys don't need to be separated with commas " + "when accessing dictionary contents.") + code = "comma_dict" + tldr = "Improper dictionary access" + matches = find_matches("__exp__,[__str2__]") + for match in matches: + submatch = match['__exp__'].find_match("_dict_[__str1__]") + if submatch: + return explain_r(message.format(submatch['_dict_'].id), code, label=tldr) + return False + + +# dict_list_group +def no_dict_in_loop(): + message = "When working with a list of dictionaries, you need to use a dictionary access in your iteration." + code = "no_dict_loop" + tldr = "Missing dictionary access loop" + + matches = find_matches("for _item_ in _list_:\n" + " __expr__") + for match in matches: + _item_ = match['_item_'] + submatches = match['__expr__'].find_matches("_item_[__str__]") + for submatch in submatches: + if submatch["__str__"].is_ast("Str"): + return False + return explain_r(message, code, label=tldr) + + +# dict_decision +def func_filter(keys): + message = "Please do not modify the function call to retrieve the data." + code = "func_filt" + tldr = "Attempting to filter using fetch" + matches = find_matches("_var_.get_weather(__str__)") + for match in matches: + __str__ = match["__str__"] + if __str__.value in keys: # TODO: Relies on .value returning id for Name nodes + return explain_r(message, code, label=tldr) + return False + + +# dict_acc_group +def str_list(keys): + message = ('If you are trying to use a string such as "{}" as a dictionary key, ' + 'it needs to be prefaced with a dictionary') + code = "str_list" + tldr = "String list used instead of Dictionary" + + for key in keys: + if find_match("['{}']".format(key)): + return explain_r(message.format(key), code, label=tldr) + return False + + +# dict_list_group +def list_var_dict_acc(): + message = ("The for statement only specifies a list target, in this case, a list of dictionaries. " + "It does not operate on the entire list. Keys should be used on the individual dictionaries of the list.") + code = "l_var_dacc" + tldr = "List variable cannot be dictionary accessed" + + matches = find_matches("for ___ in _var_[__str__]:\n" + " pass") + if matches: + return explain_r(message, code, label=tldr) + return False + + +# dict_acc_group +def key_comp(keys): + message = ('The strings "{}" and "{}" are keys. ' + 'Dictionary keys do not need to be compared to anything as they ' + 'are not filtering data. Dictionary keys are only used to access existing data.') + code = "key_comp" + tldr = "Comparing Keys" + + matches = find_matches("for _var_ in ___:\n" + " if _var_[__str1__] == __str2__:\n" + " pass") + for match in matches: + __str1__ = match["__str1__"] + __str2__ = match["__str2__"] + if __str1__.is_ast("Str") and __str1__.value in keys and __str2__.is_ast("Str") and __str2__.value in keys: + return explain_r(message.format(__str1__.value, __str2__.value), code, label=tldr) + return False + + +# dict_acc_group +def col_dict(): + message = "When using multiple keys, each key should have it's own set of brackets." + code = "col_dict" + tldr = "Improper Dictionary Access" + + matches = find_matches("_var_[__str1__: __str2__]") + if matches: + return explain_r(message, code, label=tldr) + return False + + +# dict_acc_group +def var_key(keys): + message = ("It looks like you are trying to use {} as a key. Dictionary keys are string values. " + "Variable names don't have a meaning to a computer.") + code = "var_key" + tldr = "Variables are not keys" + + matches = find_matches("_var_[_key_]") + for match in matches: + _key_ = match['_key_'] + if _key_.id in keys: + return explain_r(message.format(_key_.id), code, label=tldr) + return False + + +# dict_plot +def key_order(keys): + message = "It looks like you aren't using the correct keys, or the correct key order. Double check your data map." + code = "key_order_c" + tldr = "Wrong key order" + + construct = None + find_chain = "_var_" + for a_slice in range(len(keys)): + find_chain += "[__str{}__]".format(a_slice) + if find_match(find_chain): + construct = "_var_" + for key in keys: + construct += "['{}']".format(key) + + if construct: + matches = find_matches(construct) + if not matches: + return explain_r(message, code, label=tldr) + return False + + +# dict_plot +def key_order_unchained(keys): + message = "It looks like you aren't using the correct keys, or the correct key order. Double check your data map." + code = "key_order_u" + tldr = "Wrong key order" + + construct = None + find_chain = "" + for a_slice in range(len(keys)): + find_chain += "_var{a2}_ = _var{a1}_[__str{a1}__]\n".format(a2=a_slice+1, a1=a_slice) + if find_match(find_chain): + construct = "" + count = 0 + for key in keys: + construct += "_var{a2}_ = _var{a1}_['{key}']\n".format(a2=count+1, a1=count, key=key) + count += 1 + + if construct: + matches = find_matches(construct) + if not matches: + return explain_r(message, code, label=tldr) + return False + + +# dict_decision +def filt_key(c_value, num_slices): + message = ('It looks like you\'re using "{c_value}" as a dictionary key to filter data. ' + "Dictionary keys don't filter data, they only access data that's already there. " + "You should be comparing data retrieved from the dictionary to '{c_value}'") + code = "filt_key" + tldr = "Attempting filter as Key" + + construct = "_var_" + for a_slice in range(num_slices): + construct += "[__str{}__]".format(a_slice) + matches = find_matches(construct) + for match in matches: + for num in range(a_slice + 1): + value = match["__str{}__".format(num)] + if value.is_ast("Str") and value.value == c_value: + return explain_r(message.format(c_value=value), code, label=tldr) + return False + + +# dict_acc_group +def miss_dict_acc(): + message = ("You are missing something that looks like a dictionary access. " + "In this unit, you should be using dictionary access") + code = "miss_acc" + tldr = "Missing Dictionary Access" + + if not find_matches("_var_[__str1__]"): + return explain_r(message, code, label=tldr) + return False + + +# dict_decision +def compare_key(c_value): + message = ('In this problem, "{}" is not a key, ' + 'but something you should compare against.'.format(c_value)) + code = "comp_key" + tldr = "Using filter value as key" + + matches = find_matches("__exp0__ == __exp1__") + for match in matches: + for num in range(2): + __exp__ = match["__exp{}__".format(num)] + submatches = __exp__.find_matches("[__str__]") + for submatch in submatches: + __str__ = submatch["__str__"] + if __str__.is_ast("Str") and __str__.value == c_value: + return explain_r(message, code, label=tldr) + return False + + +# dict_decision +def str_equality(): + message = ('You are comparing two different string values, "{}" and "{}". While dictionary keys are strings, ' + "they are only interpreted by the computer as keys when used with the dictionary access syntax") + code = "str_eq" + tldr = "Comparing equality of raw strings" + + matches = find_matches("__str1__ == __str2__") + for match in matches: + __str1__ = match["__str1__"] + __str2__ = match["__str2__"] + if __str1__.is_ast("Str") and __str2__.is_ast("Str"): + return explain_r(message.format(__str1__.value, __str2__.value), code, label=tldr) + return False + + +# dict_list_group and dict_decision_group +def fetch_acc_dict(values): + message = ("The code to fetch the list of dictionaries, {}.{}, cannot be used to select data. " + "Selection of data should be done with an if statement") + code = "fetch_acc" + tldr = "Malformed Dictionary List Fetch" + + matches = find_matches("_var_._func_[__str__]") + for match in matches: + _var_ = match["_var_"].id + _func_ = match["_func_"].id + __str__ = match["__str__"] + if __str__.is_ast("Str") and __str__.value in values: + return explain_r(message.format(_var_, _func_), code, label=tldr) + return False + + +# dict_plot +def show_args(): + # TODO: Add this to plotting mistakes? + message = ("The plt.show function only tells the computer to display the plot. " + "If you want to modify the plot, use other available plotting functions.") + code = "show_args" + tldr = "Show takes no arguments" + + matches = find_matches("plt.show(__exp__)") + if matches: + return explain_r(message, code, label=tldr) + return False + + +# dict_plot +def dict_plot(): + message = ("The list {} is a list of dictionaries. plt.plot only accepts a list" + " of numbers. You need to extract the numbers from the list of dictionaries first.") + code = "dict_plot" + tldr = "Plotting list of Dictionaries" + + matches = find_matches("plt._func_(_var_)") + for match in matches: + _var_ = match["_var_"] + var_state = _var_.get_data_state() + if var_state and str(var_state.type) == "ListType" and str(var_state.type.subtype) == "DictType": + return explain_r(message.format(_var_.id), code, label=tldr) + return False + + +# dict_acc_group +def comp_in_dict_acc(): + message = ("You are using a boolean expression in a dictionary access. Remember that the dictionary " + "access takes a key and provides a value. The comparison should be made with the value, not the key.") + code = "comp_acc" + tldr = "Comparison in key access" + + matches = find_matches("_var_[__exp__][__exp2__ == __exp3__]") + if matches: + return explain_r(message, code, label=tldr) + return False diff --git a/src/lib/cs1014/input_mistakes.py b/src/lib/cs1014/input_mistakes.py new file mode 100644 index 0000000000..fbe19884b6 --- /dev/null +++ b/src/lib/cs1014/input_mistakes.py @@ -0,0 +1,25 @@ +from pedal.report.imperative import gently_r, explain_r +from pedal.cait.cait_api import * + + +def unnecessary_cast(needed_casts): + """ + + Args: + needed_casts: List of casts that are necessary to this problem + + Returns: + + """ + message = "Converting to {} is unnecessary in this problem" + code = "ex_cast" + tldr = "Unnecessary Conversion" + + known_casts = ["float", "int", "str"] + matches = find_matches("_cast_(___)") + for match in matches: + user_cast = match["_cast_"].id + if user_cast not in needed_casts and user_cast in known_casts: + return explain_r(message.format(user_cast), code, label=tldr) + return False + diff --git a/src/lib/cs1014/tests/__init__.py b/src/lib/cs1014/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/lib/cs1014/tests/test_dictionary.py b/src/lib/cs1014/tests/test_dictionary.py new file mode 100644 index 0000000000..3ebc41e157 --- /dev/null +++ b/src/lib/cs1014/tests/test_dictionary.py @@ -0,0 +1,977 @@ +import sys +import os + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) + +from tests.mistake_test_template import * +from cs1014.dictionaries import * +from cs1014.input_mistakes import * +from pedal.mistakes.iteration_context import all_labels_present + + +class DictionaryMistakeTest(MistakeTest): + def setUp(self): + self._dict_str = ("[{'City': 'Birmingham', 'Precipitation': 0.0, 'Temperature': 46}," + "{'City': 'Fairbanks' , 'Precipitation': 1.37, 'Temperature': 57}," + "{'City': 'Miami', 'Precipitation': 1.86, 'Temperature': 80}," + "{'City': 'Los Angeles', 'Precipitation': 0.5, 'Temperature': 73}," + "{'City': 'Denver', 'Precipitation': 0.0, 'Temperature': 49}," + "{'City': 'Chicago', 'Precipitation': 0.23, 'Temperature': 40}]") + + def test_hard_coding(self): + constants = [99.23, "99.23"] + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'print ("99.23")') + ret = hard_coding(constants) + self.assertTrue(ret, "Expected feedback message, got {} instead".format(ret)) + + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'price = "99.23"\n' + 'print (price)') + ret = hard_coding(constants) + self.assertTrue(ret, "Expected feedback message, got {} instead".format(ret)) + + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'print (book["price"])') + ret = hard_coding(constants) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_print_dict_key(self): + # TODO: Check output string + key_list = ['price', 'number_of_pages', 'discount'] + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'how_much= book["price"]\n' + 'print("price")') + ret = print_dict_key(key_list) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'how_much= book["price"]\n' + 'print(["price"])') + ret = print_dict_key(key_list) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'print (book["price"])') + ret = print_dict_key(key_list) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_var_instead_of_key(self): + # TODO: Check output string + key_list = ['price', 'number_of_pages', 'discount'] + + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'print (price)') + ret = var_instead_of_key(key_list) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'print (book["price"])') + ret = var_instead_of_key(key_list) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_parens_in_dict(self): + # TODO: Check output string + key_list = ['price', 'number_of_pages', 'discount'] + + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'print (book("price"))') + ret = parens_in_dict(key_list) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'print (book["price"])') + ret = parens_in_dict(key_list) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_list_as_dict(self): + # TODO: Check output string + self.to_source("total = 0\n" + "weather_reports = {}\n" + "for precipitation in weather_reports:\n" + " total = total + weather_reports['Precipitation']\n" + "print (total)".format(self._dict_str)) + ret = list_as_dict() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source("total = 0\n" + "weather_reports = {}\n" + "for precipitation in weather_reports:\n" + " total = total + precipitation['Precipitation']\n" + "print (total)\n".format(self._dict_str)) + ret = list_as_dict() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_dict_out_of_loop(self): + # TODO: Check output string + keys = ['Precipitation'] + self.to_source('rain = weather_reports["Precipitation"]\n' + 'total = 0\n' + 'for report in weather_reports:\n' + ' total = total + rain\n' + 'print(total)\n') + ret = dict_out_of_loop(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('weather_reports = {}\n' + 'total = 0\n' + 'for report in weather_reports:\n' + ' total = total + report["Precipitation"]\n' + 'print(total)\n'.format(self._dict_str)) + ret = dict_out_of_loop(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source('import matplotlib.pyplot as plt\n' + 'import weather\n' + 'weather_reports = weather.get_weather()\n' + 'BB_min = []\n' + 'BB_max = []\n' + 'for weather in weather_reports: \n' + ' if ("Blacksburg" in weather["Station"]["City"]): \n' + ' BB_min.append(weather["Data"]["Temperature"]["Min Temp"])\n' + ' \n' + 'for weather in weather_reports: \n' + ' if ("Blacksburg" in weather["Station"]["City"]):\n' + ' BB_max.append(weather["Data"]["Temperature"]["Max Temp"])\n' + 'plt.scatter(BB_min,BB_max)\n' + 'plt.xlabel("Trend")\n' + 'plt.ylabel("Temperatures")\n' + 'plt.title("Relationship between Minimum and Maximum Temperatures in Blacksburg")\n' + 'plt.show()\n') + all_labels_1 = all_labels_present() + ret = dict_out_of_loop(keys) + self.assertFalse(all_labels_1, "false negative") + all_labels_2 = all_labels_present() + self.assertFalse(ret, "...") + self.assertTrue(all_labels_1 == all_labels_2, "Side effects aren't undoing themselves") + + def test_wrong_keys(self): + # TODO: Check output string + keys = ['Date', "Temperature", "Wind", "Min Temp", "Max Temp", "Avg Temp", "Direction", "Speed", "Month", "Year", + "Week of", "Full", "State", "Code", "City", "Location"] + self.to_source("total = 0\n" + "for reports in weather_reports:\n" + " total = total + reports['Temperature']\n" + "print(total)\n") + ret = wrong_keys(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source("total = 0\n" + "for reports in weather_reports:\n" + " total = total + reports['Precipitation']\n" + "print(total)\n") + ret = wrong_keys(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_dict_access_not_in_loop(self): + self.to_source('weatherPrecipitation = weather_reports["Precipitation"]\n' + 'for report in weather_reports:\n' + ' total_precipitation = weatherPrecipitation + total_precipitation\n' + 'print(total_precipitation)\n') + ret = dict_access_not_in_loop() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('for weather_report in weather_reports:\n' + ' total = total + precipitations[Precipitation]\n' + 'print(total)\n') + ret = dict_access_not_in_loop() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source('for weather_report in weather_reports:\n' + ' total = total + precipitations["Precipitation"]\n' + 'print(total)\n') + ret = dict_access_not_in_loop() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source('for weather in weather_reports:\n' + ' if ("San Diego" in weather["Station"]["City"]):\n' + ' sandiego_list.append(weather["Data"]["Temperature"]["Avg Temp"])\n' + 'for weather in weather_reports:\n' + ' if ("Blacksburg" in weather["Station"]["City"]):\n' + ' blacksburg_list.append(weather["Data"]["Temperature"]["Avg Temp"])\n' + 'for temp in sandiego_list:\n' + ' sandiego_temp = sandiego_temp + 1\n' + ' sandiego_number = sandiego_number + temp\n' + 'sandiego_average = sandiego_number / sandiego_temp\n' + 'for temp in blacksburg_list:\n' + ' blacksburg_temp = blacksburg_temp + 1\n' + ' blacksburg_number = blacksburg_number + temp\n' + 'blacksburg_average = blacksburg_number / blacksburg_temp\n' + 'plt.scatter(BB_min,BB_max)\n' + 'plt.xlabel("Trend")\n' + 'plt.ylabel("Temperatures")\n' + 'plt.title("Relationship between Minimum and Maximum Temperatures in Blacksburg")\n' + 'plt.show()\n') + ret = dict_access_not_in_loop() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + ret2 = all_labels_present() + self.assertFalse(ret2, "Expected False, got message instead") + + def test_hard_coded_list(self): + val_list = [0.0, 1.37, 1.86, 0.5, 0.0, 0.23] + self.to_source('total_rain = 0\n' + 'weather_reports = [0.0,1.37,1.86,0.5,0.0,0.23]\n' + 'for rain in weather_reports:\n' + ' total_rain = rain + total_rain\n' + 'print(total_rain)\n') + ret = hard_coded_list(val_list) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_rain = 0\n' + 'weather_reports = {}\n' + 'for rain in weather_reports:\n' + ' total_rain = rain + total_rain\n' + 'print(total_rain)\n'.format(self._dict_str)) + ret = hard_coded_list(val_list) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_iter_as_key(self): + # TODO: Check output string + keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", + "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] + self.to_source('total_precipitation = 0\n' + 'for Precipitation in weather_reports:\n' + ' total_precipitation = total_precipitation + "Precipitation"\n' + 'print(total_precipitation)\n') + ret = iter_as_key(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for Precipitation in weather_reports:\n' + ' total_precipitation = total_precipitation + Precipitation["Precipitation"]\n' + 'print(total_precipitation)\n') + ret = iter_as_key(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip\n' + 'print(total_precipitation)\n') + ret = iter_as_key(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_dict_acc_as_lis_var(self): + keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", + "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] + self.to_source('precipitation_total=0\n' + 'precipitation_list=[]\n' + 'for precipitation in ["Precipitation"]:\n' + ' precipitation_list.append("Precipitation")\n' + 'for precipitation in precipitation_list:\n' + ' precipitation_total=precipitation_total + precipitation\n' + 'print(precipitation_total)\n') + ret = list_str_as_list_var(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip\n' + 'print(total_precipitation)\n') + ret = list_str_as_list_var(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_append_and_sum(self): + self.to_source('precipitation_total=0\n' + 'precipitation_list=[]\n' + 'for precipitation in weather_reports["Precipitation"]:\n' + ' precipitation_list.append("Precipitation")\n' + 'for precipitation in precipitation_list:\n' + ' precipitation_total= precipitation_total + 1\n' + 'print(precipitation_total)\n') + ret = append_and_sum() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip\n' + 'print(total_precipitation)\n') + ret = append_and_sum() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_iter_prop_dict_acc(self): + self.to_source('for weather_reports["Precipitation"] in weather_reports:\n' + ' total = weather_reports[Precipitation] + total\n' + 'print(total)\n') + ret = iter_prop_dict_acc() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip\n' + 'print(total_precipitation)\n') + ret = iter_prop_dict_acc() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_list_str_dict(self): + # TODO: Check output string + keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", + "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] + self.to_source('total=0\n' + 'number=0\n' + 'for precipitation1 in "Precipitation":\n' + ' total= total+ precipitation1["Precipitation"]\n' + ' number= number + 1\n' + 'average= total/ total\n' + 'print(average)\n') + ret = list_str_dict(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip\n' + 'print(total_precipitation)\n') + ret = list_str_dict(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_missing_key(self): + # TODO: Check output string + keys = ["Precipitation", "Data"] + self.to_source('total=0\n' + 'number=0\n' + 'for precipitation1 in "Precipitation":\n' + ' total= total+ precipitation1["Precipitation"]\n' + ' number= number + 1\n' + 'average= total/ total\n' + 'print(average)\n') + ret = missing_key(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' + 'print(total_precipitation)\n') + ret = missing_key(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_blank_key(self): + keys = ["distance", "time"] + self.to_source('distance_in_kilometers = trip_data["____"]/1000\n' + 'trip_data = {"distance":123000.0, "time":14000.0}\n' + 'print(average_speed_in_mph) \n' + 'average_speed_in_mph = ____ / time_in_hours\n' + 'time_in_hours = trip_data["____"]/____\n' + '____ = distance_in_kilometers / 1.6\n') + ret = blank_key(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('trip_data = {"distance":123000.0, "time":14000.0}\n' + 'distance_in_kilometers = trip_data["distance"]/1000\n' + 'distance_in_miles = distance_in_kilometers / 1.6\n' + 'time_in_hours = trip_data["time"]/3600\n' + 'average_speed_in_mph = distance_in_miles / time_in_hours\n' + 'print(average_speed_in_mph) \n') + ret = blank_key(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_dict_parens_brack(self): + # TODO: Check output string + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for rain in weather_reports:\n' + ' sum = sum + weather_reports(["Data"]["Precipitation"])\n' + 'print(sum)\n') + ret = dict_parens_brack() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'print(["price"])') + ret = dict_parens_brack() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' + 'print(total_precipitation)\n') + ret = dict_parens_brack() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_comma_dict_acc(self): + # TODO: Check output string + self.to_source("import weather\n" + "weather_reports = weather.get_weather()\n" + "total = 0\n" + "for report in weather_reports:\n" + " total = total + report['Data'],['Precipitation']\n" + "print(total)\n") + ret = comma_dict_acc() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' + 'print(total_precipitation)\n') + ret = comma_dict_acc() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_no_dict_in_loop(self): + # TODO: Check output values + self.to_source("import weather\n" + "weather_reports = weather.get_weather()\n" + "total = 0\n" + "for precip in weather_reports:\n" + " total = total + precip\n" + "print(total)\n") + ret = no_dict_in_loop() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip2["Data"]["Precipitation"]\n' + 'print(total_precipitation)\n') + ret = no_dict_in_loop() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' + 'print(total_precipitation)\n') + ret = no_dict_in_loop() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def func_filter(self): + keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", + "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'total_precipitation = 0\n' + 'for report in weather_reports:\n' + ' total_pecipitation = total_precipitation + weather.get_weather("Data")\n' + 'print(total_precipitation)\n') + ret = func_filter(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'precipitation_total=0\n' + 'weather_reports = weather.get_weather("Precipitation")\n' + 'for report in weather_reports:\n' + ' precipitation_total = precipitation_total + 1\n') + ret = func_filter(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' + 'print(total_precipitation)\n') + ret = func_filter(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source("report_list = classics.get_books(test=True)") + ret = func_filter(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_str_list(self): + # TODO: check output values + keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", + "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'totalPrecip = 0\n' + 'for weather in weather_reports:\n' + ' totalPrecip = totalPrecip + ["Precipitation"]\n' + 'print(totalPrecip)\n') + ret = str_list(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' + 'print(total_precipitation)\n') + ret = str_list(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_list_var_dict_acc(self): + # TODO: Check output values + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'pt = 0\n' + 'for precipitation in weather_reports["Precipitation"]:\n' + ' pt = pt + precipiation\n' + 'print(pt)\n') + ret = list_var_dict_acc() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('total_precipitation = 0\n' + 'for precip in weather_reports:\n' + ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' + 'print(total_precipitation)\n') + ret = list_var_dict_acc() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_key_comp(self): + # TODO: Check output values + keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", + "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' if weather_instance["Data"] == "Precipitation":\n' + ' sum = sum + weather_instance["Data"]\n' + 'print(sum)\n') + ret = key_comp(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' if weather_instance["Station"]["City"] == "Chicago":\n' + ' sum = sum + weather_instance["Data"]["Precipitation"]\n' + 'print(sum)\n') + ret = key_comp(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_col_dict(self): + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'precipitation = 0\n' + 'for weather in weather_reports:\n' + ' preciptation = precipitaion + weather["Data":"Precipitation"]\n' + 'print(precipitation)\n') + ret = col_dict() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' if weather_instance["Station"]["City"] == "Chicago":\n' + ' sum = sum + weather_instance["Data"]["Precipitation"]\n' + 'print(sum)\n') + ret = col_dict() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_var_key(self): + # TODO: Check output value + keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", + "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] + self.to_source("import weather\n" + "weather_reports = weather.get_weather()\n" + "sum = 0\n" + "for rain in weather_reports:\n" + " if rain[Station][City] == Chicago:\n" + " sum = sum + rain[Data][Precipitation]\n" + "print(sum)\n") + ret = var_key(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' if weather_instance["Station"]["City"] == "Chicago":\n' + ' sum = sum + weather_instance["Data"]["Precipitation"]\n' + 'print(sum)\n') + ret = var_key(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_key_order(self): + keys1 = ["Station", "City"] + keys2 = ["Data", "Precipitation"] + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather in weather_reports:\n' + ' if weather["Station"]["City"] == "Chicago":\n' + ' sum = sum + weather_instance["Chicago"]["Precipitation"]\n' + 'print(sum)\n') + ret = key_order(keys1) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + ret = key_order(keys2) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' if weather_instance["Station"]["City"] == "Chicago":\n' + ' sum = sum + weather_instance["Data"]["Precipitation"]\n' + 'print(sum)\n') + ret = key_order(keys1) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + ret = key_order(keys2) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' station = weather_instance["Station"]\n' + ' city = station["City"]\n' + ' if city == "Chicago":\n' + ' data = weather_instance["Data"]\n' + ' precipitation = data["Precipitation"]\n' + ' sum = sum + precipitation\n' + 'print(sum)\n') + ret = key_order(keys1) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + ret = key_order(keys2) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_key_order_unchained(self): + keys1 = ["Station", "City"] + keys2 = ["Data", "Precipitation"] + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' station = weather_instance["City"]\n' + ' city = station["Station"]\n' + ' if city == "Chicago":\n' + ' data = weather_instance["Precipitation"]\n' + ' precipitation = data["Data"]\n' + ' sum = sum + precipitation\n' + 'print(sum)\n') + ret = key_order_unchained(keys1) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + ret = key_order_unchained(keys2) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' station = weather_instance["Station"]\n' + ' city = station["City"]\n' + ' if city == "Chicago":\n' + ' data = weather_instance["Data"]\n' + ' precipitation = data["Precipitation"]\n' + ' sum = sum + precipitation\n' + 'print(sum)\n') + ret = key_order_unchained(keys1) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + ret = key_order_unchained(keys2) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' if weather_instance["Station"]["City"] == "Chicago":\n' + ' sum = sum + weather_instance["Data"]["Precipitation"]\n' + 'print(sum)\n') + ret = key_order_unchained(keys1) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + ret = key_order_unchained(keys2) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_filt_key(self): + # TODO: Check output values + c_value = "Chicago" + num_slices = 3 + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'total_precipitation_Chicago = 0\n' + 'total_precipitation_Chicago\n' + 'for report in weather_reports:\n' + ' total_precipitation = total_precipitation + report["Data"]["Precipitation"]["Chicago"]\n' + 'print (total_precipitation)\n') + ret = filt_key(c_value, num_slices) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'total_precipitation_Chicago = 0\n' + 'for report in weather_reports:\n' + ' precip = report["Data"]["Precipitation"]\n' + ' chicago_precip = precip["Chicago"]\n' + ' total_precipitation = total_precipitation + chicago_recip\n' + 'print (total_precipitation)\n') + ret = filt_key(c_value, num_slices) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' if weather_instance["Station"]["City"] == "Chicago":\n' + ' sum = sum + weather_instance["Data"]["Precipitation"]\n' + 'print(sum)\n') + ret = filt_key(c_value, num_slices) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_miss_dict_acc(self): + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' if City == "Chicago":\n' + ' sum = sum + "Precipitation"\n' + 'print(sum)\n') + ret = miss_dict_acc() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'total_precipitation_Chicago = 0\n' + 'total_precipitation_Chicago\n' + 'for report in weather_reports:\n' + ' precip = report["Data"]["Precipitation"]\n' + ' chicago_precip = precip["Chicago"]\n' + ' total_precipitation = total_precipitation + chicago_recip\n' + 'print (total_precipitation)\n') + ret = miss_dict_acc() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_compare_key(self): + c_value = "Chicago" + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' if weather_instance["Station"]["City"] == ["Chicago"]:\n' + ' sum = sum + weather_instance["Data"]["Precipitation"]\n' + 'print(sum)\n') + ret = compare_key(c_value) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'total_precipitation_Chicago = 0\n' + 'total_precipitation_Chicago\n' + 'for report in weather_reports:\n' + ' precip = report["Data"]["Precipitation"]\n' + ' chicago_precip = precip["Chicago"]\n' + ' total_precipitation = total_precipitation + chicago_recip\n' + 'print (total_precipitation)\n') + ret = compare_key(c_value) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_str_equality(self): + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather in weather_reports:\n' + ' if("City" == "Chichago"):\n' + ' sum = sum + weather["Data"]["Precipitation"]\n' + 'print(sum)\n') + ret = str_equality() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'total_precipitation = 0\n' + 'for report in weather_reports:\n' + ' if report["Station"]["City" == "Chicago"]:\n' + ' total_precipitation = total_precipitation + report["Data"]["Precipitation"]\n' + 'print(total_precipitation)\n') + ret = str_equality() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'total_precipitation_Chicago = 0\n' + 'total_precipitation_Chicago\n' + 'for report in weather_reports:\n' + ' precip = report["Data"]["Precipitation"]\n' + ' chicago_precip = precip["Chicago"]\n' + ' total_precipitation = total_precipitation + chicago_recip\n' + 'print (total_precipitation)\n') + ret = str_equality() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_unnecessary_cast(self): + cast_list = ["float"] + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'total_precipitation_Chicago = 0\n' + 'total_precipitation_Chicago\n' + 'for report in weather_reports:\n' + ' precip = report["Data"]["Precipitation"]\n' + ' chicago_precip = int(precip["Chicago"])\n' + ' total_precipitation = total_precipitation + chicago_recip\n' + 'print (total_precipitation)\n') + ret = unnecessary_cast(cast_list) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'total_precipitation_Chicago = 0\n' + 'total_precipitation_Chicago\n' + 'for report in weather_reports:\n' + ' precip = report["Data"]["Precipitation"]\n' + ' chicago_precip = precip["Chicago"]\n' + ' total_precipitation = total_precipitation + chicago_recip\n' + 'print (total_precipitation)\n') + ret = unnecessary_cast(cast_list) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_fetch_acc_dict(self): + keys = ["Data", "Precipitation", "Station", "Chicago"] + self.to_source('import weather\n' + 'precipitation = 0\n' + 'weather_reports = weather.get_weather("Chicago")\n' + 'where = weather.get_weather["Chicago"]\n' + 'for weather in weather_reports:\n' + ' precipitation = precipitation + weather["Data"]["Precipitation"]\n' + 'print(precipitation)\n') + ret = fetch_acc_dict(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'total_precipitation_Chicago = 0\n' + 'total_precipitation_Chicago\n' + 'for report in weather_reports:\n' + ' precip = report["Data"]["Precipitation"]\n' + ' chicago_precip = precip["Chicago"]\n' + ' total_precipitation = total_precipitation + chicago_recip\n' + 'print (total_precipitation)\n') + ret = fetch_acc_dict(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_app_assign(self): + self.to_source('import weather\n' + 'import matplotlib.pyplot as plt\n' + 'weather_reports = weather.get_weather()\n' + 'sum = []\n' + 'for rain in weather_reports:\n' + ' if rain["Station"]["City"] == "Chicago":\n' + ' sum = sum.append(rain["Data"]["Precipitation"])\n' + 'plt.plot(sum)\n' + 'plt.xlabel("Years")\n' + 'plt.ylabel("Precipitation")\n' + 'plt.title("Chicago Rain")\n' + 'plt.show(sum)\n') + ret = app_assign() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'total_precipitation_Chicago = 0\n' + 'total_precipitation_Chicago\n' + 'for report in weather_reports:\n' + ' precip = report["Data"]["Precipitation"]\n' + ' chicago_precip = precip["Chicago"]\n' + ' total_precipitation = total_precipitation + chicago_recip\n' + 'print (total_precipitation)\n') + ret = app_assign() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_show_args(self): + self.to_source('import weather\n' + 'import matplotlib.pyplot as plt\n' + 'weather_reports = weather.get_weather()\n' + 'sum = []\n' + 'for rain in weather_reports:\n' + ' if rain["Station"]["City"] == "Chicago":\n' + ' sum.append(rain["Data"]["Precipitation"])\n' + 'plt.plot(sum)\n' + 'plt.xlabel("Years")\n' + 'plt.ylabel("Precipitation")\n' + 'plt.title("Chicago Rain")\n' + 'plt.show(sum)\n') + ret = show_args() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'import matplotlib.pyplot as plt\n' + 'weather_reports = weather.get_weather()\n' + 'sum = []\n' + 'for rain in weather_reports:\n' + ' if rain["Station"]["City"] == "Chicago":\n' + ' sum.append(rain["Data"]["Precipitation"])\n' + 'plt.plot(sum)\n' + 'plt.xlabel("Years")\n' + 'plt.ylabel("Precipitation")\n' + 'plt.title("Chicago Rain")\n' + 'plt.show()\n') + ret = show_args() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_dict_plot(self): + self.to_source('import weather\n' + 'import matplotlib.pyplot as plt\n' + 'weather_reports = {}\n' + 'sum = []\n' + 'for rain in weather_reports:\n' + ' if rain["Station"]["City"] == "Chicago":\n' + ' sum.append(rain["Data"]["Precipitation"])\n' + 'plt.plot(weather_reports)\n' + 'plt.xlabel("Years")\n' + 'plt.ylabel("Precipitation")\n' + 'plt.title("Chicago Rain")\n' + 'plt.show()\n'.format(self._dict_str)) + ret = dict_plot() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'import matplotlib.pyplot as plt\n' + 'weather_reports = weather.get_weather()\n' + 'cityWeather = input("Choose a city: ")\n' + 'cityPrecip = []\n' + '# add other input and variable initializations here\n' + '# Put here the code to create the list of data to be plotted.\n' + 'for weather in weather_reports:\n' + ' if weather["Station"]["City"] == cityWeather:\n' + ' cityPrecip.append(weather["Data"]["Precipitation"])\n' + '# Put here the code to display a properly labelled line plot of the list of data.\n' + 'plt.plot(cityPrecip)\n' + 'plt.title(cityWeather)\n' + 'plt.xlabel("Trend")\n' + 'plt.ylabel("Amount of Precipitation")\n' + 'plt.show()\n') + ret = dict_plot() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source('import weather\n' + 'import matplotlib.pyplot as plt\n' + 'weather_reports = weather.get_weather()\n' + 'sum = []\n' + 'for rain in weather_reports:\n' + ' if rain["Station"]["City"] == "Chicago":\n' + ' sum.append(rain["Data"]["Precipitation"])\n' + 'plt.plot(sum)\n' + 'plt.xlabel("Years")\n' + 'plt.ylabel("Precipitation")\n' + 'plt.title("Chicago Rain")\n' + 'plt.show()\n') + ret = dict_plot() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source('import classics\n' + 'report_list = classics.get_books(test=True)\n' + 'for report in report_list:\n' + ' hist = report["bibliography"]["type"]\n' + ' if hist == "Text":\n' + ' list.append("Text")\n' + 'plt.hist(list)\n' + 'plt.x("test")\n' + 'plt.y("test")\n' + 'plt.title(list)\n') + ret = dict_plot() + self.assertFalse(ret, "Didn't give message returned {} instead".format(ret)) + + def test_comp_in_dict_acc(self): + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'total_precipitation_Chicago = 0\n' + 'for report in weather_reports:\n' + ' if report["Station"]["City" == "Chicago"]:\n' + ' total_precipitation_Chicago = total_precipitation_Chicago + ' + 'report["Data"]["Precipitation"]\n' + 'print(total_precipitation_Chicago)\n') + ret = comp_in_dict_acc() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + + self.to_source('import weather\n' + 'import matplotlib.pyplot as plt\n' + 'weather_reports = weather.get_weather()\n' + 'sum = []\n' + 'for rain in weather_reports:\n' + ' if rain["Station"]["City"] == "Chicago":\n' + ' sum.append(rain["Data"]["Precipitation"])\n' + 'plt.plot(sum)\n' + 'plt.xlabel("Years")\n' + 'plt.ylabel("Precipitation")\n' + 'plt.title("Chicago Rain")\n' + 'plt.show()\n') + ret = dict_plot() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + def test_general_testing(self): + self.to_source('print("fun")') + matches = find_matches("_var_") + var = matches[0]["_var_"] + self.assertTrue(var.ast_name == "Name", "is: {}".format(var.ast_name)) diff --git a/update_pedal.bat b/update_pedal.bat index 0c68c43057..5707cf82de 100644 --- a/update_pedal.bat +++ b/update_pedal.bat @@ -1 +1,2 @@ -cp -R C:/Users/acbart/Projects/pedal/pedal/ C:/Users/acbart/Projects/blockpy-edu/skulpt/src/lib/ \ No newline at end of file +cp -R C:/Users/acbart/Projects/pedal/pedal/ C:/Users/acbart/Projects/blockpy-edu/skulpt/src/lib/ +cp -R C:/Users/acbart/Projects/pedal/cs1014/ C:/Users/acbart/Projects/blockpy-edu/skulpt/src/lib/ \ No newline at end of file From 0c80a96492720f3eb5c7f1d112190a20d0913670 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 16 Oct 2019 21:12:40 -0400 Subject: [PATCH 28/68] various --- loose_compile.py | 16 +++++++------- src/builtin.js | 4 ++-- src/lib/cisc108/assertions.py | 12 +++++----- src/lib/cs1014/dictionaries.py | 12 +++++++++- src/lib/cs1014/tests/test_dictionary.py | 16 ++++++++++++-- src/lib/pedal/assertions/assertions.py | 2 ++ src/lib/pedal/questions/loader.py | 15 ++++++++++--- src/lib/pedal/sandbox/exceptions.py | 7 ++++-- src/lib/pedal/sandbox/sandbox.py | 6 ++++- src/lib/pedal/tifa/builtin_definitions.py | 4 +++- src/lib/pedal/tifa/tifa.py | 21 +++++++++++------- src/lib/pedal/tifa/type_definitions.py | 27 +++++++++++++---------- src/lib/pedal/tifa/type_operations.py | 18 +++++++++++++-- src/type.js | 13 ++++++----- test/test_pedal_bugs.py | 22 +++++++++++------- 15 files changed, 133 insertions(+), 62 deletions(-) diff --git a/loose_compile.py b/loose_compile.py index 1a3635001b..2101bf9082 100644 --- a/loose_compile.py +++ b/loose_compile.py @@ -3,6 +3,7 @@ from ast import NodeVisitor, parse + class SkulptCompiler(NodeVisitor): def __init__(self): self.indent = 0 @@ -16,7 +17,7 @@ def generic_visit(self, node): return NodeVisitor.generic_visit(self, node) def add_statement(self, line): - self.result.append(" "*self.indent + line) + self.result.append(" " * self.indent + line) def enter(self, name): self.indent += 1 @@ -61,7 +62,7 @@ def visit_Name(self, node): return "var {name}".format(node.id) elif isinstance(node.ctx, ast.Delete): pass - #return "Sk.misceval.loadname('{}', $gbl)".format(node.id) + # return "Sk.misceval.loadname('{}', $gbl)".format(node.id) def visit_FunctionDef(self, node): owner = self.context @@ -115,17 +116,16 @@ def visit_Attribute(self, node): attr = "Sk.builtins.str('{}')".format(node.attr) if type(node.ctx).__name__ == "Store": return "Sk.abstr.sattr({owner}, {attr}, {{value}}, true);".format( - owner=owner, attr=attr - ) + owner=owner, attr=attr + ) elif type(node.ctx).__name__ == "Load": return "Sk.abstr.gattr({owner}, {attr}, true)".format(owner=owner, attr=attr) - else: # Del + else: # Del pass - def visit_Return(self, node): self.add_statement("return {};".format(self.visit(node.value))); - + def visit_ImportFrom(self, node): module = node.module names = node.names @@ -151,4 +151,4 @@ def visit_Call(self, node): compiler = SkulptCompiler() compiled = compiler.visit(parsed) print("\n".join(compiler.result)) - print(compiler.unhandled) \ No newline at end of file + print(compiler.unhandled) diff --git a/src/builtin.js b/src/builtin.js index 2e3a56c4ba..8a02eebf44 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -274,9 +274,9 @@ Sk.builtin.len = function len (item) { if (item.tp$length) { if (Sk.builtin.checkFunction(item)) { - special = Sk.abstr.lookupSpecial(item, Sk.builtin.str.$len); + special = Sk.abstr.lookupSpecial(item.__class__, Sk.builtin.str.$len); if (special != null) { - return Sk.misceval.callsimArray(special, [item]); + return Sk.misceval.callsimArray(special, [this, item]); } else { if (Sk.__future__.exceptions) { throw new Sk.builtin.TypeError("object of type '" + Sk.abstr.typeName(item) + "' has no len()"); diff --git a/src/lib/cisc108/assertions.py b/src/lib/cisc108/assertions.py index 94e18da73a..8ff53366b2 100644 --- a/src/lib/cisc108/assertions.py +++ b/src/lib/cisc108/assertions.py @@ -199,14 +199,14 @@ def _is_equal(x, y, precision, exact_strings, *args): """ # Check if generators - if isinstance(x, SET_GENERATOR_TYPES): - x = set(x) - elif isinstance(x, LIST_GENERATOR_TYPES): + if isinstance(x, LIST_GENERATOR_TYPES): x = list(x) - if isinstance(y, SET_GENERATOR_TYPES): - y = set(y) - elif isinstance(y, LIST_GENERATOR_TYPES): + elif isinstance(x, SET_GENERATOR_TYPES): + x = set(x) + if isinstance(y, LIST_GENERATOR_TYPES): y = list(y) + elif isinstance(y, SET_GENERATOR_TYPES): + y = set(y) if isinstance(x, float) and isinstance(y, float): error = 10 ** (-precision) diff --git a/src/lib/cs1014/dictionaries.py b/src/lib/cs1014/dictionaries.py index 7104680d05..d1520d346e 100644 --- a/src/lib/cs1014/dictionaries.py +++ b/src/lib/cs1014/dictionaries.py @@ -321,9 +321,12 @@ def missing_key(keys): code = "miss_key" tldr = "Missing necessary keys" key_list = "" + first = False for key in keys: matches = find_matches("\"{}\"".format(key)) if not matches: + if not first: + key_list += ", " key_list += '
  • "' + key + '"
  • ' if key_list != "": return explain_r(message.format(key_list), code, label=tldr) @@ -336,8 +339,11 @@ def blank_key(keys): tldr = "Missing Key" key_list = "" + first = False for key in keys: if not find_match("_var_['{}']".format(key)): + if not first: + key_list += ", " key_list += '
  • "' + key + '"
  • ' if key_list != "": @@ -478,7 +484,7 @@ def var_key(keys): matches = find_matches("_var_[_key_]") for match in matches: _key_ = match['_key_'] - if _key_.id in keys: + if _key_.id in keys and not _key_.was_type("StrType"): return explain_r(message.format(_key_.id), code, label=tldr) return False @@ -490,15 +496,19 @@ def key_order(keys): tldr = "Wrong key order" construct = None + # Assemble chain of dictionary slicing find_chain = "_var_" for a_slice in range(len(keys)): find_chain += "[__str{}__]".format(a_slice) + # If we find a chain of dictionary accesses if find_match(find_chain): + # Assemble a new match pattern using the provided key order construct = "_var_" for key in keys: construct += "['{}']".format(key) if construct: + # check if we have a set of keys of the proper order matches = find_matches(construct) if not matches: return explain_r(message, code, label=tldr) diff --git a/src/lib/cs1014/tests/test_dictionary.py b/src/lib/cs1014/tests/test_dictionary.py index 3ebc41e157..20761afefa 100644 --- a/src/lib/cs1014/tests/test_dictionary.py +++ b/src/lib/cs1014/tests/test_dictionary.py @@ -4,8 +4,8 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) from tests.mistake_test_template import * -from cs1014.dictionaries import * -from cs1014.input_mistakes import * +from CS1014.dictionaries import * +from CS1014.input_mistakes import * from pedal.mistakes.iteration_context import all_labels_present @@ -566,6 +566,18 @@ def test_var_key(self): ret = var_key(keys) self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + self.to_source('import weather\n' + 'Station = "Station"\n' + 'City = "City"\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'for weather_instance in weather_reports:\n' + ' if weather_instance[Station][City] == "Chicago":\n' + ' sum = sum + weather_instance["Data"]["Precipitation"]\n' + 'print(sum)\n') + ret = var_key(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + def test_key_order(self): keys1 = ["Station", "City"] keys2 = ["Data", "Precipitation"] diff --git a/src/lib/pedal/assertions/assertions.py b/src/lib/pedal/assertions/assertions.py index e0d63d5474..391632be51 100644 --- a/src/lib/pedal/assertions/assertions.py +++ b/src/lib/pedal/assertions/assertions.py @@ -523,6 +523,8 @@ def assertSequenceEqual(left, right): pass +# TODO: assertPrintIncludes + # Speciality Asserts def assertPrints(result, expected_output, args=None, returns=None, score=None, message=None, report=None, diff --git a/src/lib/pedal/questions/loader.py b/src/lib/pedal/questions/loader.py index 75ba08ba3e..83be5d604a 100644 --- a/src/lib/pedal/questions/loader.py +++ b/src/lib/pedal/questions/loader.py @@ -17,12 +17,21 @@ prints: functions: function: do_complicated_stuff - signature: int, int, list[int], (int->str), dict[str:list[int]] -> list[int] + signature: int, int -> float cases: - arguments: + - arguments: 5, 4 inputs: - returns: + returns: 27.3 prints: +syntax: + prevent: + ___ + ___ + + + + + + signature: int, int, list[int], (int->str), dict[str:list[int]] -> list[int] """ from pedal.toolkit.printing import * diff --git a/src/lib/pedal/sandbox/exceptions.py b/src/lib/pedal/sandbox/exceptions.py index 042ecc032a..7094de2653 100644 --- a/src/lib/pedal/sandbox/exceptions.py +++ b/src/lib/pedal/sandbox/exceptions.py @@ -81,7 +81,7 @@ def _add_context_to_error(e, message): # Might be weird. e.args = tuple([e.args[0] + message]) return e - elif e.args: + elif hasattr(e, 'args') and e.args: e.args = tuple([e.args[0] + message]) return e x=sys.stdout @@ -131,7 +131,10 @@ def format_exception(self, preamble=""): for frame in tb_e.stack: if frame.filename == os.path.basename(self.student_filename): frame.lineno += self.line_offset - frame._line = self.original_code_lines[frame.lineno-1] + if frame.lineno-1 < len(self.original_code_lines): + frame._line = self.original_code_lines[frame.lineno-1] + else: + frame._line = "*line missing*" lines = [self._clean_traceback_line(line) for line in tb_e.format()] lines[0] = "Traceback:\n" diff --git a/src/lib/pedal/sandbox/sandbox.py b/src/lib/pedal/sandbox/sandbox.py index dd611d4d58..d27587634c 100644 --- a/src/lib/pedal/sandbox/sandbox.py +++ b/src/lib/pedal/sandbox/sandbox.py @@ -329,7 +329,11 @@ def _track_inputs(self): Wraps an input function with a tracker. """ - def _input_tracker(prompt, *args, **kwargs): + def _input_tracker(*args, **kwargs): + if args: + prompt = args[0] + else: + prompt = "" print(prompt) if self.inputs: value_entered = self.inputs.pop(0) diff --git a/src/lib/pedal/tifa/builtin_definitions.py b/src/lib/pedal/tifa/builtin_definitions.py index 5c84510fa1..78aee1588b 100644 --- a/src/lib/pedal/tifa/builtin_definitions.py +++ b/src/lib/pedal/tifa/builtin_definitions.py @@ -206,4 +206,6 @@ def get_builtin_function(name): values=UnknownType(), empty=False)) elif name in ("classmethod", "staticmethod"): - return FunctionType(name=name, returns='identity') \ No newline at end of file + return FunctionType(name=name, returns='identity') + elif name in ("__name__",): + return StrType() \ No newline at end of file diff --git a/src/lib/pedal/tifa/tifa.py b/src/lib/pedal/tifa/tifa.py index a67c4f8b23..06245a08e1 100644 --- a/src/lib/pedal/tifa/tifa.py +++ b/src/lib/pedal/tifa/tifa.py @@ -129,7 +129,7 @@ def process_ast(self, ast_tree): report. Args: - ast (Ast): The AST object + ast_tree (Ast): The AST object Returns: Report: The final report object created (also available as a field). """ @@ -529,9 +529,12 @@ def _visit_collection_loop(self, node): iter_type = self.visit(iter) if iter_type.is_empty(): - self.report_issue("Iterating over empty list", - {"name": iter_list_name, - "position": self.locate(iter)}) + # TODO: It should check if its ONLY ever iterating over an empty list. + # For now, only reports if we are NOT in a function + if len(self.scope_chain) == 1: + self.report_issue("Iterating over empty list", + {"name": iter_list_name, + "position": self.locate(iter)}) if not isinstance(iter_type, INDEXABLE_TYPES): self.report_issue("Iterating over Non-list", @@ -561,6 +564,7 @@ def visit_Dict(self, node): - empty - uniform type - record + TODO: Handle records appropriately """ type = DictType() if not node.keys: @@ -619,9 +623,9 @@ def definition(tifa, call_type, call_name, parameters, call_position): name = arg.arg if arg.annotation: self.visit(arg.annotation) - annotation = get_tifa_type(arg.annotation, {}) + annotation = get_tifa_type(arg.annotation, self) # TODO: Check that arg.type and parameter type match! - if not are_types_equal(annotation, parameter): + if not are_types_equal(annotation, parameter, True): self.report_issue("Parameter Type Mismatch", {"parameter": annotation, "parameter_name": name, "argument": parameter}) @@ -641,8 +645,8 @@ def definition(tifa, call_type, call_name, parameters, call_position): return_value = return_state.type if node.returns: #self.visit(node.returns) - returns = get_tifa_type(node.returns, {}) - if not are_types_equal(return_value, returns): + returns = get_tifa_type(node.returns, self) + if not are_types_equal(return_value, returns, True): self.report_issue("Multiple Return Types", {"expected": returns.singular_name, "actual": return_value.singular_name, @@ -803,6 +807,7 @@ def visit_Num(self, node): def visit_Return(self, node): if len(self.scope_chain) == 1: self.report_issue("Return outside function") + # TODO: Unconditional return inside loop if node.value is not None: self.return_variable(self.visit(node.value)) else: diff --git a/src/lib/pedal/tifa/type_definitions.py b/src/lib/pedal/tifa/type_definitions.py index 83d020673d..7b36572d53 100644 --- a/src/lib/pedal/tifa/type_definitions.py +++ b/src/lib/pedal/tifa/type_definitions.py @@ -533,29 +533,32 @@ def type_to_literal(type): } -def get_tifa_type_from_str(value, custom_types): - value = value.lower() - if value in custom_types: - return custom_types[value] - if value in TYPE_STRINGS: - return TYPE_STRINGS[value]() +def get_tifa_type_from_str(value, self): + #if value in custom_types: + # return custom_types[value] + if value.lower() in TYPE_STRINGS: + return TYPE_STRINGS[value.lower()]() else: - custom_types.add(value) + variable = self.find_variable_scope(value) + if variable.exists: + state = self.load_variable(value) + return state.type + #custom_types.add(value) return UnknownType() # TODO: handle custom types -def get_tifa_type(v, custom_types): +def get_tifa_type(v, self): if isinstance(v, ast.Str): - return get_tifa_type_from_str(v.s, custom_types) + return get_tifa_type_from_str(v.s, self) elif isinstance(v, ast.Name): - return get_tifa_type_from_str(v.id, custom_types) + return get_tifa_type_from_str(v.id, self) elif isinstance(v, ast.List): elements = v.elts if elements: - return ListType(subtype=get_tifa_type(elements[0], custom_types)) + return ListType(subtype=get_tifa_type(elements[0], self)) else: return ListType(empty=True) # TODO: Finish filling in static type system else: - return UnknownType() \ No newline at end of file + return UnknownType() diff --git a/src/lib/pedal/tifa/type_operations.py b/src/lib/pedal/tifa/type_operations.py index 21021e13c2..b51bddae3c 100644 --- a/src/lib/pedal/tifa/type_operations.py +++ b/src/lib/pedal/tifa/type_operations.py @@ -3,7 +3,7 @@ from pedal.tifa.type_definitions import (UnknownType, NumType, BoolType, TupleType, ListType, StrType, DictType, SetType, GeneratorType, - DayType, TimeType) + DayType, TimeType, FunctionType, TYPE_STRINGS) def merge_types(left, right): @@ -79,12 +79,16 @@ def keep_right(left, right): } -def are_types_equal(left, right): +def are_types_equal(left, right, formal=False): """ Determine if two types are equal. This could be more Polymorphic - move the code for each type into its respective class instead. + + Args: + formal (bool): Whether the left argument is formal, indicating that it can accept + type names. """ if left is None or right is None: return False @@ -108,6 +112,11 @@ def are_types_equal(left, right): return False return True elif isinstance(left, DictType): + #print("L", [literal.value for literal in left.literals], [v.singular_name + # if not formal and not isinstance(v, FunctionType) + # else TYPE_STRINGS[v.name]().singular_name + # for v in left.values]) + #print("R", [literal.value for literal in right.literals], [v.singular_name for v in right.values]) if left.empty or right.empty: return True elif left.literals is not None and right.literals is not None: @@ -118,6 +127,11 @@ def are_types_equal(left, right): if not are_types_equal(l, r): return False for l, r in zip(left.values, right.values): + if formal: + if isinstance(l, FunctionType) and l.name in TYPE_STRINGS: + l = TYPE_STRINGS[l.name]() + if isinstance(r, FunctionType) and r.name in TYPE_STRINGS: + r = TYPE_STRINGS[r.name]() if not are_types_equal(l, r): return False return True diff --git a/src/type.js b/src/type.js index 3f666ffb4c..527117325e 100644 --- a/src/type.js +++ b/src/type.js @@ -336,11 +336,12 @@ Sk.builtin.type = function (name, bases, dict) { return canSuspend ? r : Sk.misceval.retryOptionalSuspensionOrThrow(r); }; klass.prototype.tp$call = function (args, kw) { - return Sk.misceval.chain(this.tp$getattr(Sk.builtin.str.$call, true), function(callf) { + var that = this; + return Sk.misceval.chain(this.__class__.tp$getattr(Sk.builtin.str.$call, true), function(callf) { if (callf === undefined) { throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(this) + "' object is not callable"); } - return Sk.misceval.applyOrSuspend(callf, undefined, undefined, kw, args); + return Sk.misceval.applyOrSuspend(callf, undefined, undefined, kw, [that].concat(args)); }); }; klass.prototype.tp$iter = function () { @@ -379,17 +380,17 @@ Sk.builtin.type = function (name, bases, dict) { }; klass.prototype.tp$getitem = function (key, canSuspend) { - var getf = this.tp$getattr(Sk.builtin.str.$getitem, canSuspend), r; + var getf = this.__class__.tp$getattr(Sk.builtin.str.$getitem, canSuspend), r; if (getf !== undefined) { - r = Sk.misceval.applyOrSuspend(getf, undefined, undefined, undefined, [key]); + r = Sk.misceval.applyOrSuspend(getf, undefined, undefined, undefined, [this, key]); return canSuspend ? r : Sk.misceval.retryOptionalSuspensionOrThrow(r); } throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(this) + "' object does not support indexing"); }; klass.prototype.tp$setitem = function (key, value, canSuspend) { - var setf = this.tp$getattr(Sk.builtin.str.$setitem, canSuspend), r; + var setf = this.__class__.tp$getattr(Sk.builtin.str.$setitem, canSuspend), r; if (setf !== undefined) { - r = Sk.misceval.applyOrSuspend(setf, undefined, undefined, undefined, [key, value]); + r = Sk.misceval.applyOrSuspend(setf, undefined, undefined, undefined, [this, key, value]); return canSuspend ? r : Sk.misceval.retryOptionalSuspensionOrThrow(r); } throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(this) + "' object does not support item assignment"); diff --git a/test/test_pedal_bugs.py b/test/test_pedal_bugs.py index 04d4563fad..fc18c3273b 100644 --- a/test/test_pedal_bugs.py +++ b/test/test_pedal_bugs.py @@ -20,9 +20,12 @@ def __getattribute__(*args): from pedal.source import set_source +print("TEST") + CODE = """ def x(): - return -5 + print("ALPHA") + return {'x': 5} """ set_source(CODE, "answer.py") @@ -35,13 +38,16 @@ def x(): compatibility.run_student(raise_exceptions=False) +print("FIRE") + #assert_equal(student.call('x'), 1.5) y=student.call('x') -#print("AAAA") -print(abs(y)) -#print("BBB") +print("AAAA") +print(y.keys()) +print(y['x']) +print(y['x']+7) +print(7) +print("BBB") #print(abs(y)) -#print("CCCC") - - - +print("CCCC") +print(len(y)) \ No newline at end of file From 943962e2f1b52228bbb7b644e6d04dd374f5ebc9 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 28 Oct 2019 15:28:25 -0400 Subject: [PATCH 29/68] Update pedal --- src/lib/cs1014/dictionaries.py | 138 ++++++++++-- src/lib/cs1014/tests/test_dictionary.py | 220 ++++++++++++++++++- src/lib/pedal/cait/stretchy_tree_matching.py | 2 +- src/lib/pedal/sandbox/exceptions.py | 8 +- src/lib/pedal/sandbox/sandbox.py | 3 + src/lib/pedal/tifa/tifa.py | 66 ++++-- src/lib/pedal/tifa/type_definitions.py | 36 +++ src/lib/pedal/tifa/type_operations.py | 3 + src/lib/pedal/toolkit/records.py | 32 +++ src/lib/pedal/toolkit/signatures.py | 1 - 10 files changed, 463 insertions(+), 46 deletions(-) create mode 100644 src/lib/pedal/toolkit/records.py diff --git a/src/lib/cs1014/dictionaries.py b/src/lib/cs1014/dictionaries.py index d1520d346e..8cad9ca288 100644 --- a/src/lib/cs1014/dictionaries.py +++ b/src/lib/cs1014/dictionaries.py @@ -60,6 +60,68 @@ def dict_chain_group(key_sets): key_order_unchained(key_set) +def var_check(expr, keys=None): + """ + + :param expr: Expression to be evaluated + :type expr: CaitNode + :param keys: List of keys + :type keys: list of Str + :return: Key value if expression was a name node assigned a key value or if the value is a string key value + :rtype: bool/Str + """ + if expr.is_ast('Str') and (keys is None or expr.value in keys): + return expr.value + elif expr.is_ast("Name"): + matches = find_matches("{} = __key__".format(expr.value)) # TODO: Relies on .value returning id for Name nodes + for match in matches: + __key__ = match["__key__"] + if __key__.is_ast('Str') and (keys is None or __key__.value in keys): + return __key__.value + return False + + +def list_dict_indices(expr): + """ + Takes the first key slice of a dictionary and returns a list of all the slices. + :param expr: a single key slice value at the one level of slicing + :type expr: CaitNode + :return: A list of index ast nodes (each slice), .value should get the ast_node unit that was used for the slice + :rtype: CaitNode(Index) + """ + return expr.parent.parent.parent.find_all('Index') # TODO: Relies on AST Structure + + +def uncover_type(name, tifa_type): + """ + + :param name: A Name Ast Node whose type we are looking through + :type name: CaitNode (Name) + :param tifa_type: The data type of the item + :type tifa_type: str + :return: the data_state object representing when name was of the specified type + :rtype: Tifa State or None + """ + state = name.get_data_state() + if name.was_type(tifa_type) and state: + while state and str(state.type) != tifa_type: + state = state.trace[0] + return state + return None + + +def dict_detect(expr): + if expr.find_match("_var_[__expr__]", use_previous=False): + return expr + elif expr.is_ast("Name"): + matches = find_matches("{} = _var_[__expr__]".format(expr.id)) + if matches: + return matches[-1]["_var_"].ast_node.parent.parent + # TODO: Rework to chase down all indirect accesses part of the same chain as a string (and then CaitNode) + # fall through return None + return None + + # dict_hard_codes def dict_hard_codes_group(print_vals, list_vals): hard_coding(print_vals) @@ -106,8 +168,9 @@ def print_dict_key(keys): for match in matches: __str__ = match["__str__"] - if __str__.is_ast("Str") and __str__.value in keys: - return explain_r(message.format(__str__.value), code, label=tldr) + key = var_check(__str__, keys) + if key: + return explain_r(message.format(key), code, label=tldr) return False @@ -123,13 +186,23 @@ def var_instead_of_key(keys): _var_ = match["_var_"] if _var_.id in keys: submatch = find_match("_dict_['{}']".format(_var_.id)) - if submatch is None: + submatch2 = find_match("{} = ___".format(_var_.id)) + if submatch is None and submatch2 is None: + # If we don't find a dictionary access using this key and + # we don't see that this variable is assigned to a value... return explain_r(message.format(_var_.id), code, label=tldr) return False # dict_acc_group def parens_in_dict(keys): + """ + Checks fr the mistsake of using parenthesis as a dictionary access + :param keys: List of keys + :type keys: list of Str + :return: Feedback String + :rtype: Str + """ message = ('It seems like you are having trouble with dictionary syntax. The dictionary key "{}"' "should use brackets.") code = "par_dict" @@ -137,14 +210,16 @@ def parens_in_dict(keys): matches = find_matches("_var_(__str__)") for match in matches: __str__ = match['__str__'] - if __str__.is_ast("Str") and __str__.value in keys: - return explain_r(message.format(__str__.value), code, label=tldr) + _var_ = match['_var_'] + key = var_check(__str__, keys) + if key and data_state(_var_.id): + return explain_r(message.format(key), code, label=tldr) return False # dict_list_group def list_as_dict(): - message = ("A list of Dictionaries like {} is not itself a dictionary. " + message = ("The list of Dictionaries {} is not itself a dictionary. " "To access key-value pairs of the dictionaries in the list, " "you need to access each dictionary in the list one at a time.") code = "list_dict" @@ -152,8 +227,8 @@ def list_as_dict(): matches = find_matches("_list_[__exp__]") for match in matches: _list_ = match['_list_'] - if (_list_.was_type("ListType") and _list_.get_data_state() - and str(_list_.get_data_state().type.subtype) == "DictType"): + type_check = uncover_type(_list_, "ListType") + if type_check and str(type_check.type.subtype) == "DictType": return explain_r(message.format(_list_.id), code, label=tldr) return False @@ -191,8 +266,12 @@ def wrong_keys(unused_keys): matches = find_matches("_var_[__str__]") for match in matches: __str__ = match["__str__"] - if __str__.is_ast("Str") and __str__.value in unused_keys: - return explain_r(message.format(__str__.value), code, label=tldr) + indices = list_dict_indices(__str__) + for index in indices: + __str__ = index.value + key = var_check(__str__, unused_keys) + if key: + return explain_r(message.format(key), code, label=tldr) return False @@ -317,6 +396,16 @@ def list_str_dict(keys): # dict_acc_group def missing_key(keys): + """ + Checks if student is missing a key + + TODO: Should be good if run AFTER the var_instead_of_key check, although it doesn't appear to catch a key that's + been assigned as the value of an unused variable. + :param keys: list of keys + :type keys: list of Str + :return: Feedback String + :rtype: Str + """ message = "You seem to be missing the following dictionary key(s):
      {}
    " code = "miss_key" tldr = "Missing necessary keys" @@ -398,7 +487,8 @@ def no_dict_in_loop(): _item_ = match['_item_'] submatches = match['__expr__'].find_matches("_item_[__str__]") for submatch in submatches: - if submatch["__str__"].is_ast("Str"): + key = var_check(submatch["__str__"]) + if key: return False return explain_r(message, code, label=tldr) @@ -431,8 +521,8 @@ def str_list(keys): # dict_list_group def list_var_dict_acc(): - message = ("The for statement only specifies a list target, in this case, a list of dictionaries. " - "It does not operate on the entire list. Keys should be used on the individual dictionaries of the list.") + message = ("The for statement only specifies a list target, in this case, a list of dictionaries. It does not " + "operate on the entire list. Keys should be used on the individual dictionaries of the list.") code = "l_var_dacc" tldr = "List variable cannot be dictionary accessed" @@ -450,14 +540,24 @@ def key_comp(keys): 'are not filtering data. Dictionary keys are only used to access existing data.') code = "key_comp" tldr = "Comparing Keys" - + """ matches = find_matches("for _var_ in ___:\n" " if _var_[__str1__] == __str2__:\n" " pass") + """ + matches = find_matches("for _var_ in ___:\n" + " if __expr__ == __str2__:\n" + " pass") for match in matches: - __str1__ = match["__str1__"] + __expr__ = match["__expr__"] + submatch = dict_detect(__expr__) + # __str1__ = match["__str1__"] + # if submatch: + __str1__ = submatch.find_match("_var_[__str1__]", use_previous=False)["__str1__"] __str2__ = match["__str2__"] - if __str1__.is_ast("Str") and __str1__.value in keys and __str2__.is_ast("Str") and __str2__.value in keys: + value1 = var_check(__str1__, keys) + value2 = var_check(__str2__, keys) + if value1 and value2: return explain_r(message.format(__str1__.value, __str2__.value), code, label=tldr) return False @@ -476,6 +576,9 @@ def col_dict(): # dict_acc_group def var_key(keys): + # TODO: Could use this method for other methods to check if the code needs to use the value of the variable. + # In other words, if we have a variable in place of a key AND this test fails, it means that they have an + # initialized variable whose assigned value we should check as we are able to (statically). message = ("It looks like you are trying to use {} as a key. Dictionary keys are string values. " "Variable names don't have a meaning to a computer.") code = "var_key" @@ -491,6 +594,9 @@ def var_key(keys): # dict_plot def key_order(keys): + # TODO: Is it possible to run this test after confirming (through other tests) that there are no unused keys and + # that all keys used are the correct keys, such that the feedback message can explicitly address JUST the case of + # wrong order? message = "It looks like you aren't using the correct keys, or the correct key order. Double check your data map." code = "key_order_c" tldr = "Wrong key order" diff --git a/src/lib/cs1014/tests/test_dictionary.py b/src/lib/cs1014/tests/test_dictionary.py index 20761afefa..38af09d863 100644 --- a/src/lib/cs1014/tests/test_dictionary.py +++ b/src/lib/cs1014/tests/test_dictionary.py @@ -7,6 +7,10 @@ from CS1014.dictionaries import * from CS1014.input_mistakes import * from pedal.mistakes.iteration_context import all_labels_present +from pedal.resolvers import simple +# import pedal.sandbox.compatibility as compatibility +# from tests.execution_helper import Execution +from pedal.toolkit.utilities import * class DictionaryMistakeTest(MistakeTest): @@ -45,6 +49,13 @@ def test_print_dict_key(self): ret = print_dict_key(key_list) self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + self.to_source('price = "price"\n' + 'book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'how_much= book[price]\n' + 'print(price)') + ret = print_dict_key(key_list) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' 'how_much= book["price"]\n' 'print(["price"])') @@ -56,34 +67,81 @@ def test_print_dict_key(self): ret = print_dict_key(key_list) self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + self.to_source('price = "price"\n' + 'book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'print (book[price])') + ret = print_dict_key(key_list) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + def test_var_instead_of_key(self): # TODO: Check output string - key_list = ['price', 'number_of_pages', 'discount'] + key_list = ['price', 'number_of_pages', 'discount', 'title'] self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print (price)') + 'print(price)') ret = var_instead_of_key(key_list) self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print (book["price"])') + 'print(book["price"])') + ret = var_instead_of_key(key_list) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'price = book["price"]\n' + 'print(price)') ret = var_instead_of_key(key_list) self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + self.to_source("import weather\n" + "import matplotlib.pyplot as plt\n" + "weather_reports = weather.get_report()\n" + "list = []\n" + "City = input('City')\n" + "for report in weather_reports:\n" + " if City == report['Station']['City']:\n" + " list.append(report[\"Data\"][\"Precipitation\"])\n") + ret = var_instead_of_key(['City', 'Data', 'Precipitation']) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + def test_parens_in_dict(self): # TODO: Check output string key_list = ['price', 'number_of_pages', 'discount'] self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print (book("price"))') + 'print(book("price"))') ret = parens_in_dict(key_list) self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + self.to_source('price = "price"\n' + 'book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'print(book(price))') + ret = parens_in_dict(key_list) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + self.assertTrue("price" in ret, "Message '{}' didn't include correct key".format(ret)) + self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print (book["price"])') + 'print(book["price"])') + ret = parens_in_dict(key_list) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source('price = "price"' + 'book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'print(book[price])') + ret = parens_in_dict(key_list) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source('price = input("price")\n' + 'book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' + 'print(book[price])') ret = parens_in_dict(key_list) self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + self.to_source('for item in _list:\n' + ' print(item("price"))') + ret = parens_in_dict(key_list) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + def test_list_as_dict(self): # TODO: Check output string self.to_source("total = 0\n" @@ -102,6 +160,24 @@ def test_list_as_dict(self): ret = list_as_dict() self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + self.to_source("earthquake_report = [{\"Location\" : \"California\", \"Magnitude\" : 2.3, \"Depth\" : 7.66},\n" + " {\"Location\" : \"Japan\", \"Magnitude\" : 5.3, \"Depth\" : 3.34},\n" + " {\"Location\" : \"Burma\", \"Magnitude\" : 4.9, \"Depth\" :97.07},\n" + " {\"Location\" : \"Alaska\", \"Magnitude\" : 4.6, \"Depth\" : 35.0},\n" + " {\"Location\" : \"Washington\", \"Magnitude\" : 2.19, \"Depth\" : 15.28},\n" + " {\"Location\" : \"China\", \"Magnitude\" : 4.3, \"Depth\" : 10.0}\n" + " ]\n" + "total = 0\n" + "number = 0\n" + "for earthquake_report in earthquake_reports:\n" + " total = total + earthquake_report['Magnitude']\n" + " number = 1 + number\n" + "average = total / number\n" + "print(average)" + ) + ret = list_as_dict() + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + def test_dict_out_of_loop(self): # TODO: Check output string keys = ['Precipitation'] @@ -156,6 +232,15 @@ def test_wrong_keys(self): ret = wrong_keys(keys) self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + self.to_source("temperature = 'Temperature'\n" + "total = 0\n" + "for reports in weather_reports:\n" + " total = total + reports[temperature]\n" + "print(total)\n") + ret = wrong_keys(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + self.assertTrue("Temperature" in ret, "Message '{}' didn't include correct key".format(ret)) + self.to_source("total = 0\n" "for reports in weather_reports:\n" " total = total + reports['Precipitation']\n" @@ -163,6 +248,14 @@ def test_wrong_keys(self): ret = wrong_keys(keys) self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + self.to_source("precip = 'Precipitation'\n" + "total = 0\n" + "for reports in weather_reports:\n" + " total = total + reports[precip]\n" + "print(total)\n") + ret = wrong_keys(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + def test_dict_access_not_in_loop(self): self.to_source('weatherPrecipitation = weather_reports["Precipitation"]\n' 'for report in weather_reports:\n' @@ -429,6 +522,14 @@ def test_no_dict_in_loop(self): ret = no_dict_in_loop() self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + self.to_source('total_precipitation = 0\n' + 'key = "Precipitation"\n' + 'for city in weather_reports:\n' + ' total_precipitation = total_precipitation + city[key]\n' + 'print(total_precipitation)\n') + ret = no_dict_in_loop() + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + def func_filter(self): keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] @@ -512,6 +613,18 @@ def test_key_comp(self): ret = key_comp(keys) self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'data = "Data"\n' + 'precip = "Precipitation"\n' + 'for weather_instance in weather_reports:\n' + ' if weather_instance[data] == precip:\n' + ' sum = sum + weather_instance[data]\n' + 'print(sum)\n') + ret = key_comp(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + self.to_source('import weather\n' 'weather_reports = weather.get_weather()\n' 'sum = 0\n' @@ -522,6 +635,57 @@ def test_key_comp(self): ret = key_comp(keys) self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'loc1 = "Station"\n' + 'loc2 = "City"\n' + 'data = "Data"\n' + 'precip = "Precipitation"\n' + 'for weather_instance in weather_reports:\n' + ' if weather_instance[loc1][loc2] == "Chicago":\n' + ' sum = sum + weather_instance[data][precip]\n' + 'print(sum)\n') + ret = key_comp(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + # TODO: Get this to work + ''' + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'data = "Data"\n' + 'precip = "Precipitation"\n' + 'for weather_instance in weather_reports:\n' + ' loc1 = weather_instance["Station"]["City"]\n' + ' if loc1 == "Chicago":\n' + ' sum = sum + weather_instance[data][precip]\n' + 'print(sum)\n') + ret = key_comp(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + ''' + # TODO: Get this to work + """ + self.to_source('import weather\n' + 'weather_reports = weather.get_weather()\n' + 'sum = 0\n' + 'precip = "Precipitation"\n' + 'for weather_instance in weather_reports:\n' + ' data = weather_instance["Data"]\n' + ' if data == precip:\n' + ' sum = sum + weather_instance[data]\n' + 'print(sum)\n') + ret = key_comp(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + """ + + # TODO: Fix this bug (An if statement bypasses this for now, real bug is in CAIT) + self.to_source('for reports in weather_reports:\n' + ' if report["Station"]["City"] == "Chicago":\n' + ' trend.append(reports["Data"]["Precipitation"])') + ret = key_comp(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + def test_col_dict(self): self.to_source('import weather\n' 'weather_reports = weather.get_weather()\n' @@ -987,3 +1151,49 @@ def test_general_testing(self): matches = find_matches("_var_") var = matches[0]["_var_"] self.assertTrue(var.ast_name == "Name", "is: {}".format(var.ast_name)) + + def test_group(self): + self.to_source("earthquake_report = [{'Location' : 'California', 'Magnitude' : 2.3, 'Depth' : 7.66},\n" + " {'Location' : 'Japan', 'Magnitude' : 5.3, 'Depth' : 3.34},\n" + " {'Location' : 'Burma', 'Magnitude' : 4.9, 'Depth' :97.07},\n" + " {'Location' : 'Alaska', 'Magnitude' : 4.6, 'Depth' : 35.0},\n" + " {'Location' : 'Washington', 'Magnitude' : 2.19, 'Depth' : 15.28},\n" + " {'Location' : 'China', 'Magnitude' : 4.3, 'Depth' : 10.0}\n" + " ]\n" + "total = 0\n" + "number = 0\n" + "for earthquake_report in earthquake_reports:\n" + " total = total + earthquake_report['Magnitude']\n" + " number = 1 + number\n" + "average = total / number\n" + "print(average)" + ) + target_dict = ('_quake_dict_list_ = [{"Location": "California", "Magnitude": 2.3, "Depth": 7.66},' + '{"Location": "Japan", "Magnitude": 5.3, "Depth": 3.34},' + '{"Location": "Burma", "Magnitude": 4.9, "Depth": 97.07},' + '{"Location": "Alaska", "Magnitude": 4.6, "Depth": 35.0},' + '{"Location": "Washington", "Magnitude": 2.19, "Depth": 15.28},' + '{"Location": "China", "Magnitude": 4.3, "Depth": 10.0}]') + matches = find_matches(target_dict) + if not matches: + explain_r("You need to properly define a dictionary for the abstraction first", "dict_def_err", + label="Dictionary Definition Incorrect") + + all_keys = ["Location", "Magnitude", "Depth"] + unused_keys = ["Location", "Depth"] + used_keys = ["Magnitude"] + dict_acc_group(all_keys, unused_keys, used_keys) + dict_list_group(all_keys) + + target_list = [2.3, 5.3, 4.9, 4.6, 2.19, 4.3] + ___target_avg = sum(target_list) / len(target_list) + + prevent_literal(___target_avg, str(___target_avg)) + + (success, score, category, label, + message, data, hide) = simple.resolve() + # self.assertFalse(success) + # self.assertEqual(message, 'You should always create unit tests.') + self.assertEqual(message, "The list of Dictionaries earthquake_report is not itself a dictionary. " + "To access key-value pairs of the dictionaries in the list, you need to access each " + "dictionary in the list one at a time.

    (list_dict)

    ") diff --git a/src/lib/pedal/cait/stretchy_tree_matching.py b/src/lib/pedal/cait/stretchy_tree_matching.py index 639aedd19f..e18cae37af 100644 --- a/src/lib/pedal/cait/stretchy_tree_matching.py +++ b/src/lib/pedal/cait/stretchy_tree_matching.py @@ -169,7 +169,7 @@ def deep_find_match_Name(self, ins_node, std_node, check_meta=True, pre_match=No if match[_VAR] and meta_matched: # if variable if type(std_node.astNode).__name__ == "Name": return self.deep_find_match_generic(ins_node, std_node, - check_meta=check_meta, ignores=["ctx"],pre_match=pre_match) + check_meta=check_meta, ignores=["ctx"], pre_match=pre_match) # could else return False, but shallow_match_generic should do this as well elif match[_EXP]: # and meta_matched: # if expression # terminate recursion, the whole subtree should match since expression nodes match to anything diff --git a/src/lib/pedal/sandbox/exceptions.py b/src/lib/pedal/sandbox/exceptions.py index 7094de2653..57aab77239 100644 --- a/src/lib/pedal/sandbox/exceptions.py +++ b/src/lib/pedal/sandbox/exceptions.py @@ -62,9 +62,11 @@ class KeyError(BuiltinKeyError): __module__ = "builtins" def __init__(self, original, message): - self.__cause__ = original.__cause__ - self.__traceback__ = original.__traceback__ - self.__context__ = original.__context__ + for field in ['__cause__', '__traceback__', '__context__']: + if hasattr(original, field): + setattr(self, field, getattr(original, field)) + else: + setattr(self, field, None) self.message = message def __str__(self): diff --git a/src/lib/pedal/sandbox/sandbox.py b/src/lib/pedal/sandbox/sandbox.py index d27587634c..17ac77f7a3 100644 --- a/src/lib/pedal/sandbox/sandbox.py +++ b/src/lib/pedal/sandbox/sandbox.py @@ -541,6 +541,8 @@ def _start_patches(self, *patches): patch.start() def _stop_patches(self): + if not self._current_patches: + return patches = self._current_patches.pop() for patch in patches: patch.stop() @@ -628,6 +630,7 @@ def run(self, code, as_filename=None, modules=None, inputs=None, report_exceptions, raise_exceptions, context, keep_context) except TimeoutError as timeout_exception: + self._stop_patches() self._capture_exception(timeout_exception, sys.exc_info(), report_exceptions, raise_exceptions, context, keep_context, as_filename, diff --git a/src/lib/pedal/tifa/tifa.py b/src/lib/pedal/tifa/tifa.py index 06245a08e1..8afec2bc73 100644 --- a/src/lib/pedal/tifa/tifa.py +++ b/src/lib/pedal/tifa/tifa.py @@ -354,24 +354,46 @@ def visit_Assign(self, node): # Handle targets self._visit_nodes(node.targets) - # TODO: Properly handle assignments with subscripts - def action(target, type): - if isinstance(target, ast.Name): - self.store_variable(target.id, type) - elif isinstance(target, (ast.Tuple, ast.List)): - for i, elt in enumerate(target.elts): - eltType = type.index(LiteralNum(i)) - action(elt, eltType) - elif isinstance(target, ast.Subscript): - pass - elif isinstance(target, ast.Attribute): - left_hand_type = self.visit(target.value) - if isinstance(left_hand_type, InstanceType): - left_hand_type.add_attr(target.attr, type) - # TODO: Otherwise we attempted to assign to a non-instance - # TODO: Handle minor type changes (e.g., appending to an inner list) + self.walk_targets(node.targets, value_type, self.assign_target) - self.walk_targets(node.targets, value_type, action) + # TODO: Properly handle assignments with subscripts + def assign_target(self, target, type): + if isinstance(target, ast.Name): + self.store_variable(target.id, type) + elif isinstance(target, (ast.Tuple, ast.List)): + for i, elt in enumerate(target.elts): + eltType = type.index(LiteralNum(i)) + self.assign_target(elt, eltType) + elif isinstance(target, ast.Subscript): + left_hand_type = self.visit(target.value) + if isinstance(left_hand_type, ListType): + # TODO: Handle updating value in list + pass + elif isinstance(left_hand_type, DictType): + if not isinstance(target.slice, ast.Index): + # TODO: Can't subscript a dictionary assignment + return None + literal = self.get_literal(target.slice.value) + if not literal: + key_type = self.visit(target.slice.value) + left_hand_type.empty = False + left_hand_type.keys = key_type.clone() + left_hand_type.values = type.clone() + elif left_hand_type.literals: + original_type = left_hand_type.has_literal(literal) + if not original_type: + left_hand_type.update_key(literal, type.clone()) + elif not are_types_equal(original_type, type): + # TODO: Fix "Dictionary" to be the name of the variable + self.report_issue("Type changes", + {'name': "Dictionary", 'old': original_type, + 'new': type}) + elif isinstance(target, ast.Attribute): + left_hand_type = self.visit(target.value) + if isinstance(left_hand_type, InstanceType): + left_hand_type.add_attr(target.attr, type) + # TODO: Otherwise we attempted to assign to a non-instance + # TODO: Handle minor type changes (e.g., appending to an inner list) def visit_AugAssign(self, node): # Handle value @@ -392,7 +414,7 @@ def visit_AugAssign(self, node): if type(right) in op_lookup: op_lookup = op_lookup[type(right)] result_type = op_lookup(left, right) - self.store_variable(name, result_type) + self.assign_target(node.target, result_type) return result_type self.report_issue("Incompatible types", @@ -624,6 +646,10 @@ def definition(tifa, call_type, call_name, parameters, call_position): if arg.annotation: self.visit(arg.annotation) annotation = get_tifa_type(arg.annotation, self) + # TODO: Use parameter information to "fill in" empty lists + if isinstance(parameter, ListType) and isinstance(annotation, ListType): + if isinstance(parameter.subtype, UnknownType): + parameter.subtype = annotation.subtype # TODO: Check that arg.type and parameter type match! if not are_types_equal(annotation, parameter, True): self.report_issue("Parameter Type Mismatch", @@ -648,8 +674,8 @@ def definition(tifa, call_type, call_name, parameters, call_position): returns = get_tifa_type(node.returns, self) if not are_types_equal(return_value, returns, True): self.report_issue("Multiple Return Types", - {"expected": returns.singular_name, - "actual": return_value.singular_name, + {"expected": returns.precise_description(), + "actual": return_value.precise_description(), "position": return_state.position}) return return_value diff --git a/src/lib/pedal/tifa/type_definitions.py b/src/lib/pedal/tifa/type_definitions.py index 7b36572d53..9b5756b839 100644 --- a/src/lib/pedal/tifa/type_definitions.py +++ b/src/lib/pedal/tifa/type_definitions.py @@ -105,6 +105,9 @@ def clone(self): def __str__(self): return str(self.__class__.__name__) + def precise_description(self): + return self.singular_name + def clone_mutably(self): if self.immutable: return self.clone() @@ -368,6 +371,20 @@ def is_empty(self): class DictType(Type): singular_name = 'a dictionary' + def precise_description(self): + base = "a dictionary" + if self.literals: + base += " mapping " + # TODO: Handle recursive precise names more correctly + base += ", ".join("{!r} to {}".format(l.value, r.precise_description()) + for l, r in zip(self.literals, self.values)) + elif self.keys: + keys = self.keys[0] if isinstance(self.keys, list) else self.keys + values = self.values[0] if isinstance(self.values, list) else self.values + base += " mapping {}".format(keys.precise_description()) + base += " to {}".format(values.precise_description()) + return base + def __init__(self, empty=False, literals=None, keys=None, values=None): self.empty = empty self.literals = literals @@ -380,6 +397,12 @@ def clone(self): def is_empty(self): return self.empty + def has_literal(self, l): + for literal, value in zip(self.literals, self.values): + if are_literals_equal(literal, l): + return value + return None + def index(self, i): if self.empty: return UnknownType() @@ -391,6 +414,11 @@ def index(self, i): else: return self.keys.clone() + def update_key(self, literal_key, type): + self.literals.append(literal_key) + self.values.append(type) + + def load_attr(self, attr, tifa, callee=None, callee_position=None): if attr == 'items': def _items(tifa, function_type, callee, args, position): @@ -559,6 +587,14 @@ def get_tifa_type(v, self): return ListType(subtype=get_tifa_type(elements[0], self)) else: return ListType(empty=True) + elif isinstance(v, ast.Dict): + if not v.keys: + return DictType(empty=True) + if all(isinstance(k, ast.Str) for k in v.keys): + return DictType(literals=[LiteralStr(s.s) for s in v.keys], + values=[get_tifa_type(vv, self) for vv in v.values]) + return DictType(keys=[get_tifa_type(k, self) for k in v.keys], + values=[get_tifa_type(vv, self) for vv in v.values]) # TODO: Finish filling in static type system else: return UnknownType() diff --git a/src/lib/pedal/tifa/type_operations.py b/src/lib/pedal/tifa/type_operations.py index b51bddae3c..2faa7d0d83 100644 --- a/src/lib/pedal/tifa/type_operations.py +++ b/src/lib/pedal/tifa/type_operations.py @@ -112,6 +112,9 @@ def are_types_equal(left, right, formal=False): return False return True elif isinstance(left, DictType): + #print(left.empty, left.keys, left.literals, right) + if not left.keys and not left.literals: + return isinstance(right, DictType) #print("L", [literal.value for literal in left.literals], [v.singular_name # if not formal and not isinstance(v, FunctionType) # else TYPE_STRINGS[v.name]().singular_name diff --git a/src/lib/pedal/toolkit/records.py b/src/lib/pedal/toolkit/records.py new file mode 100644 index 0000000000..1f076f832d --- /dev/null +++ b/src/lib/pedal/toolkit/records.py @@ -0,0 +1,32 @@ +from pedal.report.imperative import gently, explain, gently_r, explain_r, MAIN_REPORT +from pedal.sandbox import compatibility + + +def check_record_instance(record_instance, record_type, instance_identifier, type_identifier): + if not isinstance(record_instance, dict): + explain("{} was not a {} because it is not a dictionary.".format(instance_identifier, type_identifier)) + return False + for expected_key, expected_value_type in record_type.items(): + if expected_key not in record_instance: + explain("{} was supposed to have the key `{}`, but it did not.".format(instance_identifier, expected_key)) + return False + actual_value = record_instance[expected_key] + # Handle nested record types + if isinstance(expected_value_type, list): + if not isinstance(actual_value, list): + explain("{} was not a {} because its key `{}` did not have a list.".format( + instance_identifier, type_identifier, expected_key + )) + return False + elif actual_value: + actual_value = actual_value[0] + expected_value_type = expected_value_type[0] + if not isinstance(actual_value, expected_value_type): + explain("{} was not a {} because its key `{}` did not have a `{}` value".format( + instance_identifier, type_identifier, expected_key, expected_value_type.__name__ + )) + return False + if len(record_type) != len(record_instance): + explain("{} had extra keys that it should not have.".format(instance_identifier)) + return False + return True \ No newline at end of file diff --git a/src/lib/pedal/toolkit/signatures.py b/src/lib/pedal/toolkit/signatures.py index b2a9a3de33..d0d7df615b 100644 --- a/src/lib/pedal/toolkit/signatures.py +++ b/src/lib/pedal/toolkit/signatures.py @@ -280,7 +280,6 @@ def check_piece(left, right, indent=1): def type_check(left, right): left = normalize_type(left) - print(left, "||||", right) right = normalize_type(right) return check_piece(left, right) From c4967af8d02c280e4fe0a88a4b5d4a229c158d43 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 30 Oct 2019 15:51:35 -0400 Subject: [PATCH 30/68] Readme updates --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index afd6e5a462..d928969605 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,15 @@ This is a BlockPy fork of the Skulpt project. We add a few features: * MatPlotLib module * Exec support +Our instructions for installation + +* `git clone ` +* `cd ./skulpt` +* `npm install` +* `npm run devbuild` + +Now you've built skulpt! You can use it in BlockPy-server, or the BlockPy-client, depending on what you want to work on. + # Welcome to Skulpt [![Join the chat at https://gitter.im/skulpt/skulpt](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/skulpt/skulpt?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) From 669a5bf2602c8165ee6493762e1e5aa0a8b9cd06 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 4 Nov 2019 23:11:51 -0500 Subject: [PATCH 31/68] Prior to exec modifications --- src/builtin.js | 4 ++- src/errors.js | 1 - src/import.js | 1 + src/lib/cs1014/dictionaries.py | 40 +++++++++++++-------- src/lib/cs1014/tests/test_dictionary.py | 24 +++++++++---- src/lib/pedal/assertions/assertions.py | 11 ++++-- src/lib/pedal/mistakes/iteration_context.py | 9 +++++ src/lib/requests/__init__.js | 16 +++++---- src/module.js | 1 + 9 files changed, 75 insertions(+), 32 deletions(-) diff --git a/src/builtin.js b/src/builtin.js index 8a02eebf44..6c6bb8d05b 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -1450,10 +1450,12 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { var modname = name; var caughtError = null; try { - Sk.importModuleInternal_(name, false, modname, pythonCode, undefined, false, true) + Sk.importModuleInternal_(name, false, modname, pythonCode, undefined, false, true); } catch (e) { + console.log("SYSTEMATIC ERROR"); caughtError = e; } + console.log("FINISHED EVAL"); Sk.globals = backupGlobals; // Only try to delete if we succeeded in creating it! if (sysModules.mp$lookup(pyName)) { diff --git a/src/errors.js b/src/errors.js index 32ae71571b..bdf56cd597 100644 --- a/src/errors.js +++ b/src/errors.js @@ -63,7 +63,6 @@ Sk.builtin.BaseException.prototype.tp$str = function () { if (this.traceback.length !== 0) { var lineno = this.traceback[0].lineno; ret += " on line "; - console.log(lineno); if (Sk.builtin.checkInt(lineno)) { ret += lineno.v !== undefined ? lineno.v : lineno; } else { diff --git a/src/import.js b/src/import.js index d22f1540a6..ce50df5b92 100644 --- a/src/import.js +++ b/src/import.js @@ -395,6 +395,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela parentModName ? new Sk.builtin.str(absolutePackagePrefix + parentModName) : relativePackageName ? relativePackageName : Sk.builtin.none.none$ }; + //module["$d"]["__dict__"] = module["$d"]; if (co.packagePath) { module["$d"]["__path__"] = new Sk.builtin.tuple([new Sk.builtin.str(co.packagePath)]); } diff --git a/src/lib/cs1014/dictionaries.py b/src/lib/cs1014/dictionaries.py index 8cad9ca288..d1b747dbfb 100644 --- a/src/lib/cs1014/dictionaries.py +++ b/src/lib/cs1014/dictionaries.py @@ -67,7 +67,8 @@ def var_check(expr, keys=None): :type expr: CaitNode :param keys: List of keys :type keys: list of Str - :return: Key value if expression was a name node assigned a key value or if the value is a string key value + :return: Key value if expression was a name node assigned a key value or if the value is a string key value, + otherwise returns False :rtype: bool/Str """ if expr.is_ast('Str') and (keys is None or expr.value in keys): @@ -114,10 +115,15 @@ def dict_detect(expr): if expr.find_match("_var_[__expr__]", use_previous=False): return expr elif expr.is_ast("Name"): - matches = find_matches("{} = _var_[__expr__]".format(expr.id)) - if matches: - return matches[-1]["_var_"].ast_node.parent.parent - # TODO: Rework to chase down all indirect accesses part of the same chain as a string (and then CaitNode) + # TODO: This match is buggy because in the case slicing, the dictionary dives deeper instead of making siblings + matches = find_matches("{} = __expr__".format(expr.id)) + submatch = None + for match in matches: + __expr__ = match["__expr__"] + submatch = __expr__.find_match("__expr__[__expr2__]", use_previous=False) + if submatch: + return __expr__ + # TODO: Rework to chase down all indirect accesses part of the same chain as a string (and then CaitNode) # fall through return None return None @@ -546,19 +552,23 @@ def key_comp(keys): " pass") """ matches = find_matches("for _var_ in ___:\n" - " if __expr__ == __str2__:\n" + " if __expr1__ == __expr2__:\n" " pass") for match in matches: - __expr__ = match["__expr__"] - submatch = dict_detect(__expr__) + __str1__ = match["__expr1__"] + __str2__ = match["__expr2__"] + submatch1 = dict_detect(__str1__) + submatch2 = dict_detect(__str2__) # __str1__ = match["__str1__"] - # if submatch: - __str1__ = submatch.find_match("_var_[__str1__]", use_previous=False)["__str1__"] - __str2__ = match["__str2__"] - value1 = var_check(__str1__, keys) - value2 = var_check(__str2__, keys) - if value1 and value2: - return explain_r(message.format(__str1__.value, __str2__.value), code, label=tldr) + if submatch1: + __str1__ = submatch1.find_match("_var_[__str1__]", use_previous=False)["__str1__"] + elif submatch2: + __str2__ = submatch2.find_match("_var_[__str2__]", use_previous=False)["__str2__"] + if submatch1 or submatch2: + value1 = var_check(__str1__, keys) + value2 = var_check(__str2__, keys) + if value1 and value2: + return explain_r(message.format(__str1__.value, __str2__.value), code, label=tldr) return False diff --git a/src/lib/cs1014/tests/test_dictionary.py b/src/lib/cs1014/tests/test_dictionary.py index 38af09d863..82594715b4 100644 --- a/src/lib/cs1014/tests/test_dictionary.py +++ b/src/lib/cs1014/tests/test_dictionary.py @@ -649,8 +649,6 @@ def test_key_comp(self): ret = key_comp(keys) self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - # TODO: Get this to work - ''' self.to_source('import weather\n' 'weather_reports = weather.get_weather()\n' 'sum = 0\n' @@ -663,9 +661,7 @@ def test_key_comp(self): 'print(sum)\n') ret = key_comp(keys) self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - ''' - # TODO: Get this to work - """ + self.to_source('import weather\n' 'weather_reports = weather.get_weather()\n' 'sum = 0\n' @@ -677,15 +673,29 @@ def test_key_comp(self): 'print(sum)\n') ret = key_comp(keys) self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - """ - # TODO: Fix this bug (An if statement bypasses this for now, real bug is in CAIT) self.to_source('for reports in weather_reports:\n' ' if report["Station"]["City"] == "Chicago":\n' ' trend.append(reports["Data"]["Precipitation"])') ret = key_comp(keys) self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + self.to_source("weather_reports = weather.get_weather()\n" + "for report in weather_reports:\n" + " City = report['Station']['City']\n" + " if City == 'Blacksburg':\n" + " pass\n") + ret = key_comp(keys) + self.assertFalse(ret, "Expected False, got {} instead".format(ret)) + + self.to_source("weather_reports = weather.get_weather()\n" + "for report in weather_reports:\n" + " City = report['Station']\n" + " if City == 'City':\n" + " pass\n") + ret = key_comp(keys) + self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) + def test_col_dict(self): self.to_source('import weather\n' 'weather_reports = weather.get_weather()\n' diff --git a/src/lib/pedal/assertions/assertions.py b/src/lib/pedal/assertions/assertions.py index 391632be51..4597478e85 100644 --- a/src/lib/pedal/assertions/assertions.py +++ b/src/lib/pedal/assertions/assertions.py @@ -371,10 +371,17 @@ def assertNotIn(needle, haystack, score=None, message=None, report=None, return False +def _humanize_type(t): + if hasattr(t, '__name__'): + return t.__name__ + else: + return str(t) + + def _humanize_types(types): if isinstance(types, tuple): - return ', '.join([t.__name__ for t in types]) - return types.__name__ + return ', '.join(_humanize_type(t) for t in types) + return _humanize_type(types) def assertIsInstance(value, types, score=None, message=None, report=None, diff --git a/src/lib/pedal/mistakes/iteration_context.py b/src/lib/pedal/mistakes/iteration_context.py index f11fdd5d99..d06626bc39 100644 --- a/src/lib/pedal/mistakes/iteration_context.py +++ b/src/lib/pedal/mistakes/iteration_context.py @@ -1171,6 +1171,15 @@ def all_labels_present(): # TODO: make sure it's before the show, maybe check f return False +def show_parens(): + message = "Make sure you add parenthesis to plt.show" + code = "show_parens" + tldr = "Incorrect Show" + if not find_match("plt.show"): + return gently_r() + return False + + def hard_code_8_5(): # TODO: This one's weird message = "Use iteration to calculate the sum." code = "hard_code_8.5" diff --git a/src/lib/requests/__init__.js b/src/lib/requests/__init__.js index 89cef1c41c..f06987c325 100644 --- a/src/lib/requests/__init__.js +++ b/src/lib/requests/__init__.js @@ -1,5 +1,5 @@ var $builtinmodule = function (name) { - var request = {}; + var request = {__name__: Sk.builtin.str("requests")}; //~ Classes ................................................................. @@ -19,17 +19,17 @@ var $builtinmodule = function (name) { self.lineList = self.data$.split("\n"); self.lineList = self.lineList.slice(0, -1); for (var i = 0; i < self.lineList.length; i++) { - self.lineList[i] = self.lineList[i] + '\n'; + self.lineList[i] = self.lineList[i] + "\n"; } self.currentLine = 0; self.pos$ = 0; - Sk.abstr.sattr(self, 'text', Sk.ffi.remapToPy(self.data$), true); + Sk.abstr.sattr(self, Sk.builtin.str("text"), Sk.ffi.remapToPy(self.data$), true); }); // ------------------------------------------------------------ $loc.__str__ = new Sk.builtin.func(function (self) { - return Sk.ffi.remapToPy(''); + return Sk.ffi.remapToPy(""); }); $loc.__repr__ = $loc.__str__; @@ -97,7 +97,7 @@ var $builtinmodule = function (name) { }; request.Response = - Sk.misceval.buildClass(request, response, 'Response', []); + Sk.misceval.buildClass(request, response, "Response", []); //~ Module functions ........................................................ @@ -118,6 +118,7 @@ var $builtinmodule = function (name) { Sk.requestsGet(Sk.ffi.remapToJs(url), data, timeout).then(function(result) { resolve(Sk.misceval.callsim(request.Response, result)); }, function(err) { + console.log("Err1"); reject(err); //resolve(Sk.misceval.callsim(request.Response, err)); }); @@ -143,6 +144,7 @@ var $builtinmodule = function (name) { var susp = new Sk.misceval.Suspension(); susp.resume = function() { + console.log("err2", susp); if (susp.data["error"]) { //throw new Sk.builtin.IOError(susp.data["error"].message); throw susp.data["error"]; @@ -157,8 +159,10 @@ var $builtinmodule = function (name) { resolution = value; return value; }, function(err) { + console.log("err3", err); resolution = ""; - throw err; + //throw err; + return Promise.reject(err); //return err; }) }; diff --git a/src/module.js b/src/module.js index cebece9f9e..d9022ef6b0 100644 --- a/src/module.js +++ b/src/module.js @@ -6,6 +6,7 @@ Sk.builtin.module = function module (name) { return new Sk.builtin.module(name); } this["$d"] = {__name__: name}; + this["$d"]["__dict__"] = this["$d"]; return this; }; Sk.exportSymbol("Sk.builtin.module", Sk.builtin.module); From 0a617ea4e015267ef8fd8bb918c6dcce28c35d29 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 2 Dec 2019 08:40:09 -0500 Subject: [PATCH 32/68] Pedal updates, playing around with compiler optimizations --- dumb_tifa.js | 99 ++++++ loose_compile.py | 112 ++++-- src/builtin.js | 100 +++--- src/lib/pedal/assertions/assertions.py | 84 +---- src/lib/pedal/assertions/tests.py | 147 ++++++++ src/lib/pedal/questions/__init__.py | 29 +- src/lib/pedal/questions/loader.py | 468 ++++++++++++++++++++++++- src/lib/pedal/toolkit/functions.py | 27 +- src/lib/pedal/toolkit/signatures.py | 4 + src/lib/requests/__init__.js | 3 + src/misceval.js | 2 +- support/build/wrapmodules.js | 21 +- 12 files changed, 916 insertions(+), 180 deletions(-) create mode 100644 dumb_tifa.js create mode 100644 src/lib/pedal/assertions/tests.py diff --git a/dumb_tifa.js b/dumb_tifa.js new file mode 100644 index 0000000000..b26ed46180 --- /dev/null +++ b/dumb_tifa.js @@ -0,0 +1,99 @@ + Import + alias + Compare + Is +var $builtinmodule = function (name) { + let mod = {__name__: dumb_tifa }; + + var pprint = Sk.builtin.__import__('pprint', $gbl, $loc, ['pprint'], -1); + var pprint = Sk.abstr.gattr(pprint, new Sk.builtin.str('pprint')) + + var pedal.report = Sk.builtin.__import__('pedal.report', $gbl, $loc, ['MAIN_REPORT'], -1); + var MAIN_REPORT = Sk.abstr.gattr(pedal.report, new Sk.builtin.str('MAIN_REPORT')) + + var pedal.tifa.type_definitions = Sk.builtin.__import__('pedal.tifa.type_definitions', $gbl, $loc, ['UnknownType', 'RecursedType', 'FunctionType', 'ClassType', 'InstanceType', 'NumType', 'NoneType', 'BoolType', 'TupleType', 'ListType', 'StrType', 'GeneratorType', 'DictType', 'ModuleType', 'SetType', 'type_from_json', 'type_to_literal', 'get_tifa_type', 'LiteralNum', 'LiteralBool', 'LiteralNone', 'LiteralStr', 'LiteralTuple'], -1); + var UnknownType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('UnknownType')) + var RecursedType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('RecursedType')) + var FunctionType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('FunctionType')) + var ClassType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('ClassType')) + var InstanceType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('InstanceType')) + var NumType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('NumType')) + var NoneType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('NoneType')) + var BoolType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('BoolType')) + var TupleType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('TupleType')) + var ListType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('ListType')) + var StrType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('StrType')) + var GeneratorType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('GeneratorType')) + var DictType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('DictType')) + var ModuleType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('ModuleType')) + var SetType = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('SetType')) + var type_from_json = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('type_from_json')) + var type_to_literal = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('type_to_literal')) + var get_tifa_type = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('get_tifa_type')) + var LiteralNum = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('LiteralNum')) + var LiteralBool = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('LiteralBool')) + var LiteralNone = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('LiteralNone')) + var LiteralStr = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('LiteralStr')) + var LiteralTuple = Sk.abstr.gattr(pedal.tifa.type_definitions, new Sk.builtin.str('LiteralTuple')) + + var pedal.tifa.builtin_definitions = Sk.builtin.__import__('pedal.tifa.builtin_definitions', $gbl, $loc, ['get_builtin_module', 'get_builtin_function'], -1); + var get_builtin_module = Sk.abstr.gattr(pedal.tifa.builtin_definitions, new Sk.builtin.str('get_builtin_module')) + var get_builtin_function = Sk.abstr.gattr(pedal.tifa.builtin_definitions, new Sk.builtin.str('get_builtin_function')) + + var pedal.tifa.type_operations = Sk.builtin.__import__('pedal.tifa.type_operations', $gbl, $loc, ['merge_types', 'are_types_equal', 'VALID_UNARYOP_TYPES', 'VALID_BINOP_TYPES', 'ORDERABLE_TYPES', 'INDEXABLE_TYPES'], -1); + var merge_types = Sk.abstr.gattr(pedal.tifa.type_operations, new Sk.builtin.str('merge_types')) + var are_types_equal = Sk.abstr.gattr(pedal.tifa.type_operations, new Sk.builtin.str('are_types_equal')) + var VALID_UNARYOP_TYPES = Sk.abstr.gattr(pedal.tifa.type_operations, new Sk.builtin.str('VALID_UNARYOP_TYPES')) + var VALID_BINOP_TYPES = Sk.abstr.gattr(pedal.tifa.type_operations, new Sk.builtin.str('VALID_BINOP_TYPES')) + var ORDERABLE_TYPES = Sk.abstr.gattr(pedal.tifa.type_operations, new Sk.builtin.str('ORDERABLE_TYPES')) + var INDEXABLE_TYPES = Sk.abstr.gattr(pedal.tifa.type_operations, new Sk.builtin.str('INDEXABLE_TYPES')) + + var pedal.tifa.identifier = Sk.builtin.__import__('pedal.tifa.identifier', $gbl, $loc, ['Identifier'], -1); + var Identifier = Sk.abstr.gattr(pedal.tifa.identifier, new Sk.builtin.str('Identifier')) + + var pedal.tifa.state = Sk.builtin.__import__('pedal.tifa.state', $gbl, $loc, ['State'], -1); + var State = Sk.abstr.gattr(pedal.tifa.state, new Sk.builtin.str('State')) + + var pedal.tifa.messages = Sk.builtin.__import__('pedal.tifa.messages', $gbl, $loc, ['_format_message'], -1); + var _format_message = Sk.abstr.gattr(pedal.tifa.messages, new Sk.builtin.str('_format_message')) + + var __all__ = ['Tifa']; + + mod.Tifa = Sk.misceval.buildClass(mod, function($gbl, $loc) { + /** + TIFA Class for traversing an AST and finding common issues. + + Args: + python_3 (bool): Whether to parse the code in regular PYTHON_3 mode or + the modified AST that Skulpt uses. + report (Report): The report object to store data and feedback in. If + left None, defaults to the global MAIN_REPORT. + + **/ + + var __init__ = new Sk.builtin.func(function(self, python_3, report) { + if (None) { + var report = MAIN_REPORT; + } + this.report = report; + this._initialize_report() + }}); + __init__.co_varnames = ['self', 'python_3', 'report'] + __init__.$defaults = [true, null] + $loc.__init__ = ___init__ + + var _initialize_report = new Sk.builtin.func(function(self) { + /** + Initialize a successful report with possible set of issues. + + **/ + this.report['tifa'] = {'success': true, 'variables': {}, 'top_level_variables': {}, 'issues': {}};; + }}); + _initialize_report.co_varnames = ['self'] + $loc._initialize_report = __initialize_report + + }, 'Tifa', [Sk.abstr.gattr(ast, Sk.builtins.str('NodeVisitor'), true)], {}); + + return mod; +}; +Did not handle: {'Compare', 'Import', 'alias', 'Is'} diff --git a/loose_compile.py b/loose_compile.py index 2101bf9082..a211ca87dc 100644 --- a/loose_compile.py +++ b/loose_compile.py @@ -2,21 +2,26 @@ import os from ast import NodeVisitor, parse +import ast +from textwrap import indent, dedent class SkulptCompiler(NodeVisitor): - def __init__(self): + def __init__(self, module_name, original_lines): + self.module_name = module_name + self.original_lines = original_lines self.indent = 0 self.unhandled = set() self.result = [] self.stack = [] def generic_visit(self, node): - print(type(node).__name__) + print(self.indent*" ", type(node).__name__) self.unhandled.add(type(node).__name__) - return NodeVisitor.generic_visit(self, node) + result = NodeVisitor.generic_visit(self, node) + return result - def add_statement(self, line): + def add_statement(self, line=""): self.result.append(" " * self.indent + line) def enter(self, name): @@ -30,9 +35,14 @@ def exit(self): def visit_Module(self, node): self.add_statement("var $builtinmodule = function (name) {") self.enter("mod") - self.add_statement("let mod = {{__name__: {name} }};".format(name=name)) + self.add_statement("let mod = {{__name__: {name} }};".format(name=self.module_name)) for statement in node.body: - self.visit(statement) + try: + self.visit(statement) + self.add_statement() + except Exception as e: + print("ERROR line {}: {}".format(statement.lineno, self.original_lines[statement.lineno-1])) + raise e self.add_statement("return mod;") self.exit() self.add_statement("};") @@ -41,6 +51,7 @@ def visit_Module(self, node): def context(self): return self.stack[-1] + def visit_ClassDef(self, node): owner = self.context self.add_statement( @@ -49,6 +60,7 @@ def visit_ClassDef(self, node): self.enter("$loc") for statement in node.body: self.visit(statement) + self.add_statement() self.exit() bases = ", ".join(self.visit(base) for base in node.bases) self.add_statement("}}, '{name}', [{bases}], {{}});".format( @@ -59,9 +71,9 @@ def visit_Name(self, node): if isinstance(node.ctx, ast.Load): return node.id elif isinstance(node.ctx, ast.Store): - return "var {name}".format(node.id) - elif isinstance(node.ctx, ast.Delete): - pass + return "var {name} = {{value}}".format(name=node.id) + elif isinstance(node.ctx, ast.Del): + return "delete {name};".format(name=node.id) # return "Sk.misceval.loadname('{}', $gbl)".format(node.id) def visit_FunctionDef(self, node): @@ -80,39 +92,95 @@ def visit_FunctionDef(self, node): self.add_statement("{name}.co_varnames = [{args}]".format( name=node.name, args=str_args )) - self.add_statement("{name}.$defaults = [{defaults}]".format( - name=node.name, defaults=defaults - )) + if defaults: + self.add_statement("{name}.$defaults = [{defaults}]".format( + name=node.name, defaults=defaults + )) self.add_statement("{owner}.{name} = _{name}".format( owner=owner, name=node.name )) + def visit_If(self, node): + condition = self.visit(node.test) + self.add_statement("if ({condition}) {{".format(condition=condition)) + self.enter("if") + for statement in node.body: + self.visit(statement) + self.exit() + self.add_statement("}") + def visit_Assign(self, node): # handle multiple targets target = node.targets[0] value = self.visit(node.value) if type(target).__name__ == 'Attribute': self.add_statement(self.visit_Attribute(target).format(value=value)) + elif isinstance(target, ast.Name): + self.add_statement(self.visit_Name(target).format(value=value)+";") + elif isinstance(target, ast.Subscript): + self.add_statement(self.visit_Subscript(target).format(value=value)+";") def visit_Str(self, node): - return "new Sk.builtins.str('{}')".format(node.s) + return repr(node.s) + #return "new Sk.builtins.str('{}')".format(node.s) def visit_Num(self, node): return "new Sk.builtin.int_({})".format(node.n) + def visit_List(self, node): + return "[" + ", ".join(str(self.visit(elt)) + for elt in node.elts) + "]" + + def visit_Dict(self, node): + return "{"+", ".join("{}: {}".format(self.visit(k), self.visit(v)) + for k,v in zip(node.keys, node.values))+"}" + def visit_NameConstant(self, node): if node.value == True: - return "Sk.builtin.bool.true$" + return "true"#"Sk.builtin.bool.true$" elif node.value == False: - return "Sk.builtin.bool.false$" + return "false"#"Sk.builtin.bool.false$" elif node.value == None: - return "Sk.builtin.none.none$" + return "null"#"Sk.builtin.none.none$" def visit_Expr(self, node): - self.add_statement(self.visit(node.value)) + if isinstance(node.value, ast.Str): + self.add_commented_block(node.value.s) + else: + self.add_statement(self.visit(node.value)) + + def add_commented_block(self, value): + self.add_statement("/**"+indent(dedent(value), (self.indent)*" ")) + self.add_statement("**/") + + def visit_Subscript(self, node): + if isinstance(node.slice, ast.Index): + value = self.visit(node.value) + index = self.visit(node.slice.value) + if isinstance(node.ctx, ast.Store): + return "{value}[{index}] = {{value}};".format(value=value, index=index) + elif isinstance(node.ctx, ast.Load): + return "{value}[{index}]".format(value=value, index=index) + elif isinstance(node.ctx, ast.Del): + return "delete {value}[{index}];".format(value=value, index=index) + else: + pass + # TODO: Slices + return "BANANA" + + + def visit_InternalAttribute(self, node, attr): + if isinstance(node.ctx, ast.Store): + return "this.{attr} = {{value}};".format(attr=attr) + elif isinstance(node.ctx, ast.Load): + return "this.{attr}".format(attr=attr) + elif isinstance(node.ctx, ast.Del): + return "delete this.{attr};".format(attr=attr) def visit_Attribute(self, node): owner = self.visit(node.value) + if owner == "self": + return self.visit_InternalAttribute(node, node.attr) attr = "Sk.builtins.str('{}')".format(node.attr) if type(node.ctx).__name__ == "Store": return "Sk.abstr.sattr({owner}, {attr}, {{value}}, true);".format( @@ -131,16 +199,18 @@ def visit_ImportFrom(self, node): names = node.names self.add_statement("var {module} = Sk.builtin.__import__('{module}', $gbl, $loc, [{names}], -1);".format( module=module, - names=", ".join(repr(name) for name in names), + names=", ".join(repr(name.name) for name in names), )) for name in names: self.add_statement("var {name} = Sk.abstr.gattr({module}, new Sk.builtin.str({name!r}))".format( - name=name, + name=name.asname if name.asname else name.name, module=module )) def visit_Call(self, node): func = node.func + # TODO: args + return "{func}()".format(func=self.visit(func)) if __name__ == '__main__': @@ -148,7 +218,7 @@ def visit_Call(self, node): with open(target, 'r') as target_file: code = target_file.read() parsed = parse(code) - compiler = SkulptCompiler() + compiler = SkulptCompiler(sys.argv[2], code.split("\n")) compiled = compiler.visit(parsed) print("\n".join(compiler.result)) - print(compiler.unhandled) + print("Did not handle:", compiler.unhandled) diff --git a/src/builtin.js b/src/builtin.js index 6c6bb8d05b..124abf3cd5 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -1423,54 +1423,60 @@ var extractDict = function(obj) { Sk.builtin.exec = function execf(pythonCode, new_globals) { Sk.builtin.pyCheckArgs("exec", arguments, 1, 2); - var backupRG = Sk.retainGlobals; - Sk.retainGlobals = true; - var filename = ""; - if (pythonCode instanceof Sk.builtin.code) { - filename = pythonCode.filename; - pythonCode = pythonCode.source; - } else { - pythonCode = Sk.ffi.remapToJs(pythonCode); - } - var new_globals_copy = extractDict(new_globals); - if (!new_globals_copy.__file__) { - new_globals_copy.__file__ = Sk.ffi.remapToPy(filename); - } - if (!new_globals_copy.__name__) { - new_globals_copy.__name__ = Sk.ffi.remapToPy(filename); - } - if (!new_globals_copy.__package__) { - new_globals_copy.__package__ = Sk.builtin.none.none$; - } - var backupGlobals = Sk.globals; - Sk.globals = new_globals_copy; // Possibly copy over some "default" ones? - var name = filename.endsWith(".py") ? filename.slice(0, -3) : filename; - var pyName = Sk.builtin.str(name); - var sysModules = Sk.getCurrentSysModules(); - var modname = name; - var caughtError = null; - try { - Sk.importModuleInternal_(name, false, modname, pythonCode, undefined, false, true); - } catch (e) { - console.log("SYSTEMATIC ERROR"); - caughtError = e; - } - console.log("FINISHED EVAL"); - Sk.globals = backupGlobals; - // Only try to delete if we succeeded in creating it! - if (sysModules.mp$lookup(pyName)) { - Sk.getCurrentSysModules().mp$del_subscript(pyName); - } - for (var key in new_globals_copy) { - if (new_globals_copy.hasOwnProperty(key)) { - var pykey = Sk.ffi.remapToPy(Sk.unfixReserved(key)); - Sk.builtin.dict.prototype.mp$ass_subscript.call(new_globals, pykey, new_globals_copy[key]); + + var prom = new Promise(function(resolve, reject) { + var backupRG = Sk.retainGlobals; + Sk.retainGlobals = true; + var filename = ""; + if (pythonCode instanceof Sk.builtin.code) { + filename = pythonCode.filename; + pythonCode = pythonCode.source; + } else { + pythonCode = Sk.ffi.remapToJs(pythonCode); + } + var new_globals_copy = extractDict(new_globals); + if (!new_globals_copy.__file__) { + new_globals_copy.__file__ = Sk.ffi.remapToPy(filename); + } + if (!new_globals_copy.__name__) { + new_globals_copy.__name__ = Sk.ffi.remapToPy(filename); + } + if (!new_globals_copy.__package__) { + new_globals_copy.__package__ = Sk.builtin.none.none$; + } + var backupGlobals = Sk.globals; + Sk.globals = new_globals_copy; // Possibly copy over some "default" ones? + var name = filename.endsWith(".py") ? filename.slice(0, -3) : filename; + var pyName = Sk.builtin.str(name); + var sysModules = Sk.getCurrentSysModules(); + var modname = name; + var caughtError = null; + try { + Sk.importModuleInternal_(name, false, modname, pythonCode, undefined, false, true); + } catch (e) { + console.log("SYSTEMATIC ERROR"); + caughtError = e; + } + console.log("FINISHED EVAL"); + Sk.globals = backupGlobals; + // Only try to delete if we succeeded in creating it! + if (sysModules.mp$lookup(pyName)) { + Sk.getCurrentSysModules().mp$del_subscript(pyName); + } + for (var key in new_globals_copy) { + if (new_globals_copy.hasOwnProperty(key)) { + var pykey = Sk.ffi.remapToPy(Sk.unfixReserved(key)); + Sk.builtin.dict.prototype.mp$ass_subscript.call(new_globals, pykey, new_globals_copy[key]); + } } - } - Sk.retainGlobals = backupRG; - if (caughtError !== null) { - throw caughtError; - } + Sk.retainGlobals = backupRG; + if (caughtError !== null) { + throw caughtError; + } + resolve(); + }); + + return Sk.misceval.promiseToSuspension(prom); }; Sk.builtin.frozenset = function frozenset () { diff --git a/src/lib/pedal/assertions/assertions.py b/src/lib/pedal/assertions/assertions.py index 4597478e85..5d7d3c0c33 100644 --- a/src/lib/pedal/assertions/assertions.py +++ b/src/lib/pedal/assertions/assertions.py @@ -6,11 +6,17 @@ from pedal.sandbox.exceptions import SandboxException from pedal.sandbox.sandbox import DataSandbox from pedal.assertions.setup import _setup_assertions, AssertionException +from pedal.assertions.tests import _normalize_string, strip_punctuation, equality_test, output_test + # TODO: Allow bundling of assertions to make a table -iterable = lambda obj: hasattr(obj, '__iter__') or hasattr(obj, '__getitem__') +def iterable(obj): + return hasattr(obj, '__iter__') or hasattr(obj, '__getitem__') + + +DELTA = .001 _MAX_LENGTH = 80 @@ -29,72 +35,6 @@ def safe_repr(obj, short=False): return result -try: - punctuation_table = str.maketrans(string.punctuation, ' ' * len(string.punctuation)) -except AttributeError: - punctuation_table = None - -if punctuation_table is None: - def strip_punctuation(a_string): - return ''.join(ch for ch in a_string if ch not in set(string.punctuation)) -else: - def strip_punctuation(a_string): - return a_string.translate(punctuation_table) - - -def _normalize_string(a_string, numeric_endings=False): - # Lower case - a_string = a_string.lower() - # Remove trailing decimals (TODO: How awful!) - if numeric_endings: - a_string = re.sub(r"(\s*[0-9]+)\.[0-9]+(\s*)", r"\1\2", a_string) - # Remove punctuation - a_string = strip_punctuation(a_string) - # Split lines - lines = a_string.split("\n") - normalized = [[piece - for piece in line.split()] - for line in lines] - normalized = [[piece for piece in line if piece] - for line in normalized - if line] - return sorted(normalized) - - -def equality_test(actual, expected, _exact_strings, _delta, _test_output): - # Float comparison - if (isinstance(expected, float) and - isinstance(actual, (float, int)) and - abs(actual - expected) < _delta): - return True - # Exact Comparison - if actual == expected: - return True - # Inexact string comparison - if (_exact_strings and isinstance(expected, str) and - isinstance(actual, str) and - _normalize_string(actual) == _normalize_string(expected)): - return True - # Output comparison - if _test_output: - # Inexact output comparison - normalized_actual = [_normalize_string(line) for line in actual] - if (isinstance(expected, str) and - _normalize_string(expected) in normalized_actual): - return True - # Exact output comparison - normalized_expected = [_normalize_string(line) for line in expected] - if (isinstance(expected, list) and - normalized_expected == normalized_actual): - return True - # Else - return False - - -# Unittest Asserts -DELTA = .001 - - def _fail(code_message, actual_message, expected_message, show_expected_value, modify_right, *values): normal_values = [] @@ -218,9 +158,9 @@ def assertEqual(left, right, score=None, message=None, report=None, compare_lengths = (iterable(left) and isinstance(right, (int, float))) if _basic_assertion(left, right, lambda l, r: - equality_test(len(l), r, False, DELTA, False) if + equality_test(len(l), r, exact, DELTA) if compare_lengths else - equality_test(l, r, False, DELTA, False), + equality_test(l, r, exact, DELTA), "len({}) != {}" if compare_lengths else "{} != {}", "was" + PRE_VAL, "to have its length equal to" @@ -239,7 +179,7 @@ def assertEqual(left, right, score=None, message=None, report=None, def assertNotEqual(left, right, score=None, message=None, report=None, contextualize=True, exact=False): if _basic_assertion(left, right, - lambda l, r: not equality_test(l, r, False, DELTA, False), + lambda l, r: not equality_test(l, r, exact, DELTA), "{} == {}", "was" + PRE_VAL, "to not be equal to", @@ -547,7 +487,7 @@ def assertPrints(result, expected_output, args=None, returns=None, calls = sandbox.call_contexts[call_id] inputs = sandbox.input_contexts[call_id] actual_output = sandbox.output_contexts[call_id] - if not equality_test(actual_output, expected_output, exact, DELTA, True): + if not output_test(actual_output, expected_output, exact): context = [] if calls: context.append("I ran:\n
    " +
    @@ -653,7 +593,7 @@ def assertHas(obj, variable, types=None, value=None, score=None,
                 return False
         if value is not None:
             if not _basic_assertion(student_variable, value,
    -                                lambda l, r: equality_test(l, r, False, DELTA, False),
    +                                lambda l, r: equality_test(l, r, False, DELTA),
                                     "{} != {}",
                                     "was" + PRE_VAL,
                                     "to be equal to",
    diff --git a/src/lib/pedal/assertions/tests.py b/src/lib/pedal/assertions/tests.py
    new file mode 100644
    index 0000000000..aa8bfef6b5
    --- /dev/null
    +++ b/src/lib/pedal/assertions/tests.py
    @@ -0,0 +1,147 @@
    +import string
    +import re
    +
    +# Number encapsulates bool, int, float, complex, decimal.Decimal, etc.
    +try:
    +    from numbers import Number
    +except:
    +    Number = (bool, int, float, complex)
    +
    +try:
    +    bytes
    +except NameError:
    +    bytes = str
    +
    +try:
    +    frozenset()
    +except:
    +    frozenset = tuple()
    +
    +try:
    +    punctuation_table = str.maketrans(string.punctuation, ' ' * len(string.punctuation))
    +except AttributeError:
    +    punctuation_table = None
    +
    +if punctuation_table is None:
    +    def strip_punctuation(a_string):
    +        return ''.join(ch for ch in a_string if ch not in set(string.punctuation))
    +else:
    +    def strip_punctuation(a_string):
    +        return a_string.translate(punctuation_table)
    +
    +SET_GENERATOR_TYPES = (type({}.keys()), type({}.values()), type({}.items()))
    +
    +LIST_GENERATOR_TYPES = (type(map(bool, [])), type(filter(bool, [])),
    +                        type(range(0)), type(reversed([])), type(zip()),
    +                        type(enumerate([])))
    +
    +
    +def _normalize_string(a_string, numeric_endings=False):
    +    # Lower case
    +    a_string = a_string.lower()
    +    # Remove trailing decimals (TODO: How awful!)
    +    if numeric_endings:
    +        a_string = re.sub(r"(\s*[0-9]+)\.[0-9]+(\s*)", r"\1\2", a_string)
    +    # Remove punctuation
    +    a_string = strip_punctuation(a_string)
    +    # Split lines
    +    lines = a_string.split("\n")
    +    normalized = [[piece for piece in line.split()]
    +                  for line in lines]
    +    normalized = [[piece for piece in line if piece]
    +                  for line in normalized if line]
    +    return sorted(normalized)
    +
    +
    +def output_test(actual, expected, _exact_strings):
    +    normalized_actual = [_normalize_string(line) for line in actual]
    +    if isinstance(expected, (str, bytes)):
    +        return _normalize_string(expected) in normalized_actual
    +    else:
    +        normalized_expected = [_normalize_string(line) for line in expected]
    +        return all(each_actual in normalized_expected for each_actual in normalized_actual)
    +
    +
    +def equality_test(actual, expected, _exact_strings, _delta):
    +    # Check if generators
    +    if isinstance(expected, LIST_GENERATOR_TYPES):
    +        expected = list(expected)
    +    elif isinstance(expected, SET_GENERATOR_TYPES):
    +        expected = set(expected)
    +    if isinstance(actual, LIST_GENERATOR_TYPES):
    +        actual = list(actual)
    +    elif isinstance(actual, SET_GENERATOR_TYPES):
    +        actual = set(actual)
    +
    +    # Float comparison
    +    if isinstance(expected, float) and isinstance(actual, (float, int)):
    +        error = 10 ** (-_delta)
    +        return abs(expected - actual) < error
    +    # Other numerics
    +    elif isinstance(expected, Number) and isinstance(actual, Number) and isinstance(expected, type(actual)):
    +        return expected == actual
    +    # String comparisons
    +    elif ((isinstance(expected, str) and isinstance(actual, str)) or
    +          (isinstance(expected, bytes) and isinstance(actual, bytes))):
    +        if _exact_strings:
    +            return expected == actual
    +        else:
    +            return _normalize_string(expected) == _normalize_string(actual)
    +    # Exact Comparison
    +    elif actual == expected:
    +        return True
    +    # Sequence comparisons
    +    elif isinstance(expected, list) and isinstance(actual, list):
    +        return _are_sequences_equal(actual, expected, _exact_strings, _delta)
    +    elif isinstance(expected, tuple) and isinstance(actual, tuple):
    +        return _are_sequences_equal(actual, expected, _exact_strings, _delta)
    +    elif isinstance(expected, set) and isinstance(actual, set):
    +        return _are_sets_equal(actual, expected, _exact_strings, _delta)
    +    elif isinstance(expected, frozenset) and isinstance(actual, frozenset):
    +        return _are_sets_equal(actual, expected, _exact_strings, _delta)
    +    elif isinstance(expected, dict) and isinstance(actual, dict):
    +        primary_keys = set(expected.keys())
    +        if not _are_sets_equal(primary_keys, set(actual.keys()), _exact_strings, _delta):
    +            return False
    +        for key in primary_keys:
    +            if not equality_test(expected[key], actual[key], _exact_strings, _delta):
    +                return False
    +        return True
    +    # Else
    +    return False
    +
    +
    +def _are_sequences_equal(x, y, _exact_strings, _delta):
    +    """
    +    For sequences that support __len__, __iter__, and should have the same
    +    order.
    +    """
    +    if len(x) != len(y):
    +        return False
    +    for x_element, y_element in zip(x, y):
    +        if not equality_test(x_element, y_element, _exact_strings, _delta):
    +            return False
    +    return True
    +
    +
    +def _set_contains(needle, haystack, _exact_strings, _delta):
    +    """
    +    Tests if the given needle is one of the elements of haystack, using
    +    the _is_equal function.
    +    """
    +    for element in haystack:
    +        if equality_test(element, needle, _exact_strings, _delta):
    +            return True
    +    return False
    +
    +
    +def _are_sets_equal(x, y, _exact_strings, _delta):
    +    """
    +    For sequences that support __len__, __iter__, but order does not matter.
    +    """
    +    if len(x) != len(y):
    +        return False
    +    for x_element in x:
    +        if not _set_contains(x_element, y, _exact_strings, _delta):
    +            return False
    +    return True
    diff --git a/src/lib/pedal/questions/__init__.py b/src/lib/pedal/questions/__init__.py
    index e7531163f6..cd64bd039b 100644
    --- a/src/lib/pedal/questions/__init__.py
    +++ b/src/lib/pedal/questions/__init__.py
    @@ -13,8 +13,10 @@
     __all__ = ['NAME', 'DESCRIPTION', 'SHORT_DESCRIPTION', 'REQUIRES', 'OPTIONALS',
                'Question', 'Pool', 'set_seed']
     
    -from pedal.report.imperative import MAIN_REPORT
    +from pedal.report.imperative import MAIN_REPORT, gently
     from pedal.questions.setup import _setup_questions, set_seed, _name_hash
    +from pedal.questions.loader import load_question, SETTING_SHOW_CASE_DETAILS
    +
     
     class QuestionGrader:
         def _get_functions_with_filter(self, filter='grade_'):
    @@ -92,3 +94,28 @@ def answered(self):
                 if choice.answered:
                     return True
             return False
    +
    +
    +def check_pool_exam(name, questions, force=None, seed=None):
    +    _setup_questions(MAIN_REPORT)
    +    # Choose a question
    +    if force is None:
    +        if seed is None:
    +            force = MAIN_REPORT['questions']['seed']
    +            if isinstance(force, str):
    +                force = _name_hash(force + name)
    +        else:
    +            force = seed
    +    elif isinstance(force, str):
    +        force = _name_hash(force + name)
    +    question = questions[force % len(questions)]
    +    # Ask it
    +    show_question(question['instructions'])
    +    # Check if they're done
    +    if 'settings' not in question:
    +        question['settings'] = {}
    +    question['settings'][SETTING_SHOW_CASE_DETAILS] = False
    +    results = list(load_question(question))
    +    if results:
    +        message, label = results[0]
    +        gently(message, label=label)
    diff --git a/src/lib/pedal/questions/loader.py b/src/lib/pedal/questions/loader.py
    index 83be5d604a..b502a6c6a4 100644
    --- a/src/lib/pedal/questions/loader.py
    +++ b/src/lib/pedal/questions/loader.py
    @@ -4,10 +4,17 @@
     settings:
         tifa:
             enabled: True
    -    unit testing:
    -        by function (bool): Whether to test each function entirely before moving onto the
    +    unit test by function (bool): Whether to test each function entirely before moving onto the
                 next one, or to first check that all functions have been defined, and then
                 checking their parameters, etc. Defaults to True.
    +    show case details (bool): Whether to show the specific args/inputs that caused a test case
    +            to fail.
    +rubric:
    +    functions:
    +        total: 100
    +        definition: 10
    +        signature: 10
    +        cases: 80
     global:
         variables:
             name:
    @@ -15,32 +22,61 @@
             value:
         inputs:
         prints:
    +# Sandbox, type checking
     functions:
    -    function: do_complicated_stuff
    +    documentation: "any" or "google"
    +    coverage: 100%
    +    tests: int
    +    name: do_complicated_stuff
    +    arity: int
         signature: int, int -> float
    +    signature: int, int, list[int], (int->str), dict[str:list[int]] -> list[int]
    +    parameters:
    +        name: banana
    +            exactly:
    +            regex:
    +            includes:
    +            within:
    +        type: int
         cases:
    -      - arguments: 5, 4
    -        inputs:
    -        returns: 27.3
    +      - arguments (list): 5, 4
    +        inputs (list):
    +        returns (Any):
    +            equals: 27.3
    +            is:
    +            is not: _1
    +        name (str): Meaningful name for tracking purposes? Or possibly separate into label/id/code
    +        hint (str): Message to display to user
             prints:
    +            exactly:
    +            regex:
    +            startswith:
    +            endswith:
    +        plots:
    +# Cait
     syntax:
         prevent:
             ___ + ___
    -
    -
    -
    -
    -
    -        signature: int, int, list[int], (int->str), dict[str:list[int]] -> list[int]
    +# Override any of our default feedback messages
    +messages:
    +    FUNCTION_NOT_DEFINED: "Oops you missed a function"
     """
    +from pedal.report.imperative import set_success, give_partial
     
    +from pedal.sandbox.compatibility import _check_sandbox
     from pedal.toolkit.printing import *
     from pedal.toolkit.utilities import *
     from pedal.toolkit.functions import *
    +from pedal.assertions.tests import equality_test
    +
    +SETTING_SHOW_CASE_DETAILS = "show case details"
    +DEFAULT_SETTINGS = {
    +    SETTING_SHOW_CASE_DETAILS: True
    +}
     
     EXAMPLE_DATA = {
         'functions': [{
    -        'function': 'do_complicated_stuff',
    +        'name': 'do_complicated_stuff',
             'signature': 'int, int, [int] -> list[int]',
             'cases': [
                 {'arguments': "5, 4, 3", 'returns': "12"},
    @@ -48,11 +84,411 @@
         }]
     }
     
    -def load(data):
    +
    +class FeedbackException(Exception):
    +    def __init__(self, category, label, **fields):
    +        self.category = category
    +        self.label = label
    +        self.fields = fields
    +
    +    def as_message(self):
    +        return FEEDBACK_MESSAGES[self.category][self.label].format(**self.fields)
    +
    +
    +def check_function_defined(function, function_definitions, settings=None):
    +    # 1. Is the function defined syntactically?
    +    # 1.1. With the right name?
    +    function_name = function['name']
    +    if function_name not in function_definitions:
    +        raise FeedbackException('toolkit', 'missing_function', function_name=function_name)
    +    definition = function_definitions[function_name]
    +    return definition
    +
    +
    +def check_function_signature(function, definition, settings=None):
    +    function_name = function['name']
    +    # 1.2. With the right parameters and return type?
    +    # 1.2.1 'arity' style - simply checks number of parameters
    +    if 'arity' in function or 'parameters' in function:
    +        expected_arity = function['arity'] if 'arity' in function else len(function['parameters'])
    +        actual_arity = len(definition.args.args)
    +        if actual_arity < expected_arity:
    +            raise FeedbackException('toolkit', 'insufficient_args',
    +                                    function_name=function_name, expected_arity=expected_arity,
    +                                    actual_arity=actual_arity)
    +        elif actual_arity > expected_arity:
    +            raise FeedbackException('toolkit', 'excessive_args',
    +                                    function_name=function_name, expected_arity=expected_arity,
    +                                    actual_arity=actual_arity)
    +    # 1.2.2 'parameters' style - checks each parameter's name and type
    +    if 'parameters' in function:
    +        expected_parameters = function['parameters']
    +        actual_parameters = definition.args.args
    +        for expected_parameter, actual_parameter in zip(expected_parameters, actual_parameters):
    +            actual_parameter_name = get_arg_name(actual_parameter)
    +            if 'name' in expected_parameter:
    +                if actual_parameter_name != expected_parameter['name']:
    +                    raise FeedbackException('toolkit', 'wrong_parameter_name',
    +                                            function_name=function_name,
    +                                            expected_parameter_name=expected_parameter['name'],
    +                                            actual_parameter_name=actual_parameter_name
    +                                            )
    +            if 'type' in expected_parameter:
    +                actual_parameter_type = parse_type(actual_parameter)
    +                # TODO: Handle non-string expected_parameter types (dict)
    +                expected_parameter_type = parse_type_value(expected_parameter['type'], True)
    +                if not type_check(expected_parameter_type, actual_parameter_type):
    +                    raise FeedbackException('toolkit', 'wrong_parameter_type',
    +                                            function_name=function_name,
    +                                            parameter_name=actual_parameter_name,
    +                                            expected_parameter_type=expected_parameter_type,
    +                                            actual_parameter_type=actual_parameter_type)
    +    # 1.2.3. 'returns' style - checks the return type explicitly
    +    if 'returns' in function:
    +        expected_returns = parse_type_value(function['returns'], True)
    +        actual_returns = parse_type(definition.returns)
    +        if actual_returns != "None":
    +            if not type_check(expected_returns, actual_returns):
    +                raise FeedbackException("toolkit", "wrong_returns",
    +                                        function_name=function_name, expected_returns=expected_returns,
    +                                        actual_returns=actual_returns)
    +        elif expected_returns != "None":
    +            raise FeedbackException("toolkit", "missing_returns",
    +                                    function_name=function_name, expected_returns=expected_returns)
    +    # 1.2.4. 'signature' style - shortcut for specifying the types
    +    if 'signature' in function:
    +        expected_signature = function['signature']
    +        actual_returns = parse_type(definition.returns)
    +        actual_parameters = ", ".join(parse_type(actual_parameter.annotation)
    +                                      for actual_parameter in definition.args.args)
    +        actual_signature = "{} -> {}".format(actual_parameters, actual_returns)
    +        if not type_check(expected_signature, actual_signature):
    +            raise FeedbackException("toolkit", "wrong_signature",
    +                                    function_name=function_name, expected_signature=expected_signature,
    +                                    actual_signature=actual_signature)
    +    # All good here!
    +    return True
    +
    +
    +def check_function_value(function, values, settings):
    +    """
    +    2. Does the function exist in the data?
    +
    +    :param function:
    +    :param values:
    +    :param settings:
    +    :return:
    +    """
    +    function_name = function['name']
    +    # 2.1. Does the name exist in the values?
    +    if function_name not in values:
    +        raise FeedbackException("toolkit", "function_not_available", function_name=function_name)
    +    function_value = values[function_name]
    +    # 2.2. Is the name bound to a callable?
    +    if not callable(function_value):
    +        raise FeedbackException("toolkit", "name_is_not_function", function_name=function_name)
    +    # All good here
    +    return function_value
    +
    +
    +class TestCase:
    +    CASE_COUNT = 0
    +    def __init__(self, function_name, case_name):
    +        self.function_name = function_name
    +        if case_name is None:
    +            self.case_name = str(TestCase.CASE_COUNT)
    +            TestCase.CASE_COUNT += 1
    +        else:
    +            self.case_name = case_name
    +        self.arguments, self.has_arguments = [], False
    +        self.inputs, self.has_inputs = [], False
    +        self.error, self.has_error = None, False
    +        self.message, self.has_message = None, False
    +        self.expected_prints, self.has_expected_prints = None, False
    +        self.expected_returns, self.has_expected_returns = None, False
    +        self.prints = []
    +        self.returns = None
    +        self.success = True
    +
    +    def add_message(self, message):
    +        self.message = message
    +        self.has_message = True
    +
    +    def add_inputs(self, inputs):
    +        if not isinstance(inputs, list):
    +            inputs = [inputs]
    +        self.inputs = inputs
    +        self.has_inputs = True
    +
    +    def add_arguments(self, arguments):
    +        if not isinstance(arguments, list):
    +            arguments = [arguments]
    +        self.arguments = arguments
    +        self.has_arguments = True
    +
    +    def add_error(self, error):
    +        self.error = error
    +        self.has_error = True
    +        self.success = False
    +
    +    def add_expected_prints(self, prints):
    +        self.expected_prints = prints
    +        self.has_expected_prints = True
    +
    +    def add_expected_returns(self, returns):
    +        self.expected_returns = returns
    +        self.has_expected_returns = True
    +
    +    def add_prints_returns(self, prints, returns):
    +        self.prints = prints
    +        self.returns = returns
    +
    +    def fail(self):
    +        self.success = False
    +
    +def check_case(function, case, student_function):
    +    """
    +
    +    :param function:
    +    :param case:
    +    :param student_function:
    +    :return: status, arg, input, error, output, return, message
    +    """
    +    function_name = function['name']
    +    test_case = TestCase(function_name, case.get('name'))
    +    # Get callable
    +    sandbox = _check_sandbox(MAIN_REPORT)
    +    sandbox.set_output(None)
    +    # Potential bonus message
    +    if 'message' in case:
    +        test_case.add_message(case['message'])
    +    # Queue up the the inputs
    +    if 'inputs' in case:
    +        test_case.add_inputs(case['inputs'])
    +        sandbox.set_input(test_case.inputs)
    +    else:
    +        sandbox.set_input(None)
    +    # Pass in the arguments and call the function
    +    if 'arguments' in case:
    +        test_case.add_arguments(case['arguments'])
    +    result = sandbox.call(function_name, *test_case.arguments,
    +                          report_exceptions=False, context=False)
    +    # Store actual values
    +    test_case.add_prints_returns(sandbox.output, result)
    +    # Check for errors
    +    if sandbox.exception:
    +        test_case.add_error(sandbox.exception)
    +    # 4. Check out the output
    +    if 'prints' in case:
    +        test_case.add_expected_prints(case['prints'])
    +        if not output_test(sandbox.output, case['prints'], False, .0001):
    +            test_case.fail()
    +    # 5. Check the return value
    +    if 'returns' in case:
    +        test_case.add_expected_returns(case['returns'])
    +        if not equality_test(result, case['returns'], True, .0001):
    +            test_case.fail()
    +    # TODO: Check the plots
    +    # Return results
    +    return test_case
    +
    +
    +# TODO: blockpy-feedback-unit => pedal-test-cases in BlockPy Client
    +TEST_TABLE_TEMPLATE = """
    +    
    +        
    +        
    +        
    +        
    +    
    +    {body}
    +
    ArgumentsExpectedReturned
    """ +TEST_TABLE_FOOTER = "" +TEST_TABLE_ROW_HEADER = "" +TEST_TABLE_ROW_NORMAL = "" +TEST_TABLE_ROW_FOOTER = "" +TEST_TABLE_ROW_INFO = "" +GREEN_CHECK = " ✔" +RED_X = " ❌" +CODE_CELL = " {}" +COLUMN_TITLES = ["", "Arguments", "Inputs", "Errors", "Expected", "Expected", "Returned", "Printed"] + + +def make_table(cases): + body = [] + for case in cases: + body.append(" ") + body.append(GREEN_CHECK if case.success else RED_X) + body.append(CODE_CELL.format(", ".join(repr(arg) for arg in case.arguments))) + if case.has_error: + body.append(" Error: {}".format(str(case.error))) + else: + body.append(CODE_CELL.format(repr(case.expected_returns))) + body.append(CODE_CELL.format(repr(case.returns))) + if not case.success and case.has_message: + body.append(" {}".format(case.message)) + body.append(" ") + body = "\n".join(body) + return TEST_TABLE_TEMPLATE.format(body=body) + #if ((any(args) and any(inputs)) or + # (any(expected_outputs) and any(expected_returns)) or + # (any(actual_outputs) and any(actual_returns))): + # # Complex cells + # pass + #else: + # Simple table + # Make header + + # row_mask = [True, any(args), any(inputs), False, + # any("returns" in reason for reason in reasons), + # any("prints" in reason for reason in reasons), + # any("returns" in reason for reason in reasons), + # any("prints" in reason for reason in reasons)] + # header_cells = "".join("{}".format(title) for use, title in zip(row_mask, COLUMN_TITLES) if use) + # body = [TEST_TABLE_ROW_HEADER.format(header_cells)] + # for case in zip( + # statuses, args, inputs, errors, actual_outputs, actual_returns, + # expected_outputs, expected_returns): + # status, case = case[0], case[1:] + # print(row_mask[1:], case) + # def make_code(values): + # if values == None: + # return "None" + # elif isinstance(values, int): + # return "{!r}".format(values) + # else: + # return ", ".join("{}".format(repr(value)) for value in values) + # body.append( + # TEST_TABLE_ROW_NORMAL+ + # (GREEN_CHECK if case[0] else RED_X)+ + # "\n".join(" {}".format(make_code(values)) + # for use, values in zip(row_mask[1:], case) if use)+ + # "\n" + # ) + # # Make each row + # table = "{}\n{}\n{}".format(TEST_TABLE_HEADER, "\n ".join(body), TEST_TABLE_FOOTER) + # return table + + +def check_cases(function, student_function, settings): + function_name = function['name'] + if 'cases' in function: + cases = function['cases'] + test_cases = [check_case(function, case, student_function) for case in cases] + success_cases = sum(test.success for test in test_cases) + if success_cases < len(cases): + if settings[SETTING_SHOW_CASE_DETAILS]: + table = make_table(test_cases) + raise FeedbackException("toolkit", "failed_test_cases", + function_name=function_name, + cases_count=len(cases), failure_count=len(cases)-success_cases, + table=table) + else: + raise FeedbackException("toolkit", "failed_test_cases_count", + function_name=function_name, + cases_count=len(cases), failure_count=len(cases) - success_cases) + + +def get_arg_name(node): + name = node.id + if name is None: + return node.arg + else: + return name + + +def load_question(data): + """ + + :param data: + :return: + """ + ast = parse_program() + student = compatibility.get_student_data() + # Check that there aren't any invalid syntactical structures + # Get all of the function ASTs in a dictionary + function_definitions = {definition._name: definition + for definition in ast.find_all("FunctionDef")} + settings = DEFAULT_SETTINGS.copy() + settings.update(data.get('settings', {})) + rubric = settings.get('rubric', {}) + function_points = 0 if 'functions' in data: + function_rubric = rubric.get('functions', {}) + successes = [] for function in data['functions']: - pass + success = False + try: + definition = check_function_defined(function, function_definitions, settings) + function_points += function_rubric.get('definition', 10) + check_function_signature(function, definition, settings) + function_points += function_rubric.get('signature', 10) + student_function = check_function_value(function, student.data, settings) + function_points += function_rubric.get('value', 0) + except FeedbackException as fe: + yield fe.as_message(), fe.label + else: + try: + check_cases(function, student_function, settings) + except FeedbackException as fe: + success_ratio = (1.0 - fe.fields['failure_count'] / fe.fields['cases_count']) + function_points += function_rubric.get('cases', 80*success_ratio) + yield fe.as_message(), fe.label + else: + function_points += function_rubric.get('cases', 80) + success = True + successes.append(success) + function_points /= len(data['functions']) + if all(successes): + set_success() + else: + give_partial(function_points) + + +def check_question(data): + results = list(load_question(data)) + if results: + message, label = results[0] + gently(message, label=label) + + +def check_pool(questions): + pass def load_file(filename): - pass \ No newline at end of file + pass + + +FEEDBACK_MESSAGES = { + "toolkit": { + "missing_function": "No function named `{function_name}` was found.", + "insufficient_args": ("The function named `{function_name}` " + "has fewer parameters ({actual_arity}) " + "than expected ({expected_arity})."), + "excessive_args": ("The function named `{function_name}` " + "has more parameters ({actual_arity}) " + "than expected ({expected_arity})."), + # TODO: missing_parameter that checks if parameter name exists, but is in the wrong place + "wrong_parameter_name": ("Error in definition of `{function_name}`. " + "Expected a parameter named `{expected_parameter_name}`, " + "instead found `{actual_parameter_name}`."), + "wrong_parameter_type": ("Error in definition of function `{function_name}` " + "parameter `{parameter_name}`. Expected `{expected_parameter_type}`, " + "instead found `{actual_parameter_type}`."), + "missing_returns": ("Error in definition of function `{function_name}` return type. " + "Expected `{expected_returns}`, but there was no return type specified."), + "wrong_returns": ("Error in definition of function `{function_name}` return type. " + "Expected `{expected_returns}`, instead found `{actual_returns}`."), + "wrong_signature": ("Error in definition of function `{function_name}` signature. " + "Expected `{expected_signature}`, instead found `{actual_signature}`."), + "name_is_not_function": "You defined `{function_name}`, but did not define it as a function.", + "function_not_available": ("You defined `{function_name}` somewhere in your code, " + "but it was not available in the top-level scope to be called. " + "Perhaps you defined it inside another function or scope?"), + "failed_test_cases": ("I ran your function {function_name} on my own test cases. " + "It failed {failure_count}/{cases_count} of my tests.\n{table}"), + "failed_test_cases_count": ("I ran your function {function_name} on my own test cases. " + "It failed {failure_count}/{cases_count} of my tests."), + } +} diff --git a/src/lib/pedal/toolkit/functions.py b/src/lib/pedal/toolkit/functions.py index e11e187543..5135d9aec7 100644 --- a/src/lib/pedal/toolkit/functions.py +++ b/src/lib/pedal/toolkit/functions.py @@ -43,7 +43,8 @@ def match_function(name, root=None): if a_def._name == name: return a_def return None - + + def match_signature_muted(name, length, *parameters): ast = parse_program() defs = ast.find_all('FunctionDef') @@ -105,7 +106,6 @@ def match_parameters(name, *types, returns=None, root=None): return defn - def match_signature(name, length, *parameters): ast = parse_program() defs = ast.find_all('FunctionDef') @@ -135,6 +135,13 @@ def match_signature(name, length, *parameters): return None +TEST_TABLE_HEADER = "" +TEST_TABLE_OUTPUT = TEST_TABLE_HEADER+( + "" +) +TEST_TABLE_UNITS = TEST_TABLE_HEADER+( + "" +) GREEN_CHECK = "" RED_X = "" @@ -144,9 +151,7 @@ def output_test(name, *tests): if name in student.data: the_function = student.data[name] if callable(the_function): - result = ("
    ArgumentsExpectedActual
    ArgumentsReturnedExpected
    " - "" - ) + result = TEST_TABLE_OUTPUT success = True success_count = 0 for test in tests: @@ -164,7 +169,7 @@ def output_test(name, *tests): message = message.format(inputs, repr(out), "No output", tip) message = "" + RED_X + message + "" if tip: - message += "" + message += "" success = False elif len(test_out) > 1: message = message.format(inputs, "\n".join(out), "Too many outputs", tip) @@ -176,7 +181,7 @@ def output_test(name, *tests): message = message.format(inputs, "\n".join(out), "\n".join(test_out), tip) message = "" + RED_X + message + "" if tip: - message += "" + message += "" success = False else: message = message.format(inputs, "\n".join(out), "\n".join(test_out), tip) @@ -189,7 +194,7 @@ def output_test(name, *tests): message = message.format(inputs, "\n".join(out), "\n".join(test_out), tip) message = "" + RED_X + message + "" if tip: - message += "" + message += "" success = False else: message = message.format(inputs, "\n".join(out), "\n".join(test_out), tip) @@ -222,9 +227,7 @@ def unit_test(name, *tests): if name in student.data: the_function = student.data[name] if callable(the_function): - result = ("
    ArgumentsExpectedActual
    " + tip + "
    " + tip + "
    " + tip + "
    " + tip + "
    " + tip + "
    " + tip + "
    " - "" - ) + result = TEST_TABLE_UNITS success = True success_count = 0 for test in tests: @@ -257,7 +260,7 @@ def unit_test(name, *tests): # gently(message) message = "" + RED_X + message + "" if tip: - message += "" + message += "" success = False else: message = "" + GREEN_CHECK + message + "" diff --git a/src/lib/pedal/toolkit/signatures.py b/src/lib/pedal/toolkit/signatures.py index d0d7df615b..3f814080db 100644 --- a/src/lib/pedal/toolkit/signatures.py +++ b/src/lib/pedal/toolkit/signatures.py @@ -83,6 +83,7 @@ list[int, str, or bool], dict[int: str], or bool or int ''' + def parse_type_slice(slice): if slice.ast_name == "Index": return parse_type(slice.value) @@ -91,7 +92,10 @@ def parse_type_slice(slice): elif slice.ast_name == "ExtSlice": return ", ".join(parse_type_slice(s) for s in slice.dims) + def parse_type(node): + if node == None: + return "Any" if node.ast_name == "Str": try: return parse_type(ast.parse(node.s).body[0].value) diff --git a/src/lib/requests/__init__.js b/src/lib/requests/__init__.js index f06987c325..6ec6431f04 100644 --- a/src/lib/requests/__init__.js +++ b/src/lib/requests/__init__.js @@ -113,6 +113,9 @@ var $builtinmodule = function (name) { * constructs a Response. */ request.get = new Sk.builtin.func(function (url, data, timeout) { + if (Sk.requestsGet) { + return Sk.misceval.callsim(request.Response, Sk.requestsGet(Sk.ffi.remapToJs(url), data, timeout)); + } var prom = new Promise(function(resolve, reject) { if (Sk.requestsGet) { Sk.requestsGet(Sk.ffi.remapToJs(url), data, timeout).then(function(result) { diff --git a/src/misceval.js b/src/misceval.js index a93695f30c..b862cc54f0 100644 --- a/src/misceval.js +++ b/src/misceval.js @@ -1046,7 +1046,7 @@ Sk.exportSymbol("Sk.misceval.applyAsync", Sk.misceval.applyAsync); */ Sk.misceval.chain = function (initialValue, chainedFns) { - // We try to minimse overhead when nothing suspends (the common case) + // We try to minimise overhead when nothing suspends (the common case) var i = 1, value = initialValue, j, fs; while (true) { diff --git a/support/build/wrapmodules.js b/support/build/wrapmodules.js index 6fd73bcbd6..aba87306b0 100644 --- a/support/build/wrapmodules.js +++ b/support/build/wrapmodules.js @@ -2,17 +2,17 @@ const fs = require('fs'); const path = require('path'); const minify = require('babel-minify'); -/* + const reqskulpt = require('../run/require-skulpt').requireSkulpt; var skulpt = reqskulpt(); Sk.configure({__future__: Sk.python3}); -var WHITE_LIST = ['tifa.py', 'builtin_definitions.py', 'type_definitions.py']; +var WHITE_LIST = ['tifa.py', 'sandbox.py', 'stretchy_tree_matching.py']; function endsWithAny(string, suffixes) { return suffixes.some(function (suffix) { return string.endsWith(suffix); }); -}*/ +} /** * If this optional file exists in the top level directory, it will be @@ -50,18 +50,18 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { let ext = path.extname(file); if (exts.includes(ext)) { let contents = fs.readFileSync(fullname, 'utf8'); - if (minifyjs && (ext == ".js")) { + if (minifyjs && (ext === ".js")) { let result = minify(contents); contents = result.code; } // AOT compilation - /* - else if (ext == '.py' && - !endsWithAny(fullname, WHITE_LIST) && - !fullname.startsWith('src/builtin/')) { + + else if (false && ext === ".py" && + endsWithAny(fullname, WHITE_LIST) && + fullname.startsWith("src/lib/pedal/")) { var co; try { - co = Sk.compile(contents, fullname, 'exec', true); + co = Sk.compile(contents, fullname, "exec", true, false); console.log("Compiled: "+fullname); } catch (e) { console.log("Failed to compile: "+fullname); @@ -71,7 +71,8 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { } fullname = fullname.replace(/\.py$/, ".js"); contents = co.code + '\nvar $builtinmodule = ' + co.funcname + ';'; - }*/ + contents = minify(contents).code; + } ret.files[fullname] = contents; } } From 9a94394fa715084d3d2718d72271448099f1a1fe Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Fri, 17 Jan 2020 13:13:49 -0500 Subject: [PATCH 33/68] Various changes? --- src/ast.js | 16 ++++++++++++++-- src/lib/ast.js | 8 +++++++- src/parser.js | 10 +++++++++- src/symtable.js | 6 +++++- src/tokenize.js | 6 ++++-- support/build/wrapmodules.js | 6 +++--- webpack.config.js | 4 ++-- 7 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/ast.js b/src/ast.js index dbdff6266b..81b9287d46 100644 --- a/src/ast.js +++ b/src/ast.js @@ -408,6 +408,10 @@ function astForExceptClause (c, exc, body) { return new Sk.astnodes.ExceptHandler(ast_for_expr(c, CHILD(exc, 1)), null, astForSuite(c, body), exc.lineno, exc.col_offset, exc.end_lineno, exc.end_col_offset); } else if (NCH(exc) === 4) { + if (Sk.__future__.python3 && CHILD(exc, 2).value == ",") { + ast_error(c, exc, "Old-style 'except' clauses are not supported in Python 3"); + } + var expression = ast_for_expr(c, CHILD(exc, 1)); e = ast_for_expr(c, CHILD(exc, 3)); setContext(c, e, Sk.astnodes.Store, CHILD(exc, 3)); @@ -809,10 +813,15 @@ function astForImportStmt (c, n) { idx++; break; } - else if (CHILD(n, idx).type !== TOK.T_DOT) { + else if (CHILD(n, idx).type === TOK.T_DOT) { + ndots++; + } + else if (CHILD(n, idx).type === TOK.T_ELLIPSIS) { + ndots += 3; + } + else { break; } - ndots++; } ++idx; // skip the import keyword switch (CHILD(n, idx).type) { @@ -2157,6 +2166,9 @@ function ast_for_exprStmt (c, n) { return new Sk.astnodes.AugAssign(expr1, astForAugassign(c, CHILD(n, 1)), expr2, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } else if (CHILD(n, 1).type === SYM.annassign) { + if (!Sk.__future__.python3) { + throw new Sk.builtin.SyntaxError("Annotated assignment is not supported in Python 2", c.c_filename, n.lineno); + } // annotated assignment ch = CHILD(n, 0); ann = CHILD(n, 1); diff --git a/src/lib/ast.js b/src/lib/ast.js index 3ccc8ac6a0..ea508dccb1 100644 --- a/src/lib/ast.js +++ b/src/lib/ast.js @@ -368,6 +368,12 @@ var $builtinmodule = function (name) { }*/ function functionName(fun) { + let astname = fun.prototype._astname; + switch (astname) { + case "arguments": return "arguments_"; + default: return astname; + } + /* var ret = fun.toString(); ret = ret.substr("function ".length); ret = ret.substr(0, ret.indexOf("(")); @@ -376,7 +382,7 @@ var $builtinmodule = function (name) { } else if (ret == "Import_") { ret = "Import"; } - return ret; + return ret;*/ } for (var base in Sk.INHERITANCE_MAP) { diff --git a/src/parser.js b/src/parser.js index f93712e48b..b22bd55492 100644 --- a/src/parser.js +++ b/src/parser.js @@ -186,7 +186,15 @@ Parser.prototype.classify = function (type, value, context) { // throw new Sk.builtin.SyntaxError("bad token", type, value, context); // Questionable modification to put line number in position 2 // like everywhere else and filename in position 1. - throw new Sk.builtin.SyntaxError("bad token", this.filename, context[0][0], context); + let descr = "#"+type; + for (let i in Sk.token.tokens) { + if (Sk.token.tokens[i] == type) { + descr = i; + break; + } + } + + throw new Sk.builtin.SyntaxError("bad token " + descr, this.filename, context[0][0], context); } return ilabel; }; diff --git a/src/symtable.js b/src/symtable.js index e9e82029ab..d6f11d7db5 100644 --- a/src/symtable.js +++ b/src/symtable.js @@ -906,7 +906,8 @@ SymbolTable.prototype.analyzeBlock = function (ste, bound, free, global) { if (ste.blockType === FunctionBlock) { this.analyzeCells(scope, newfree); } - this.updateSymbols(ste.symFlags, scope, bound, newfree, ste.blockType === ClassBlock); + let discoveredFree = this.updateSymbols(ste.symFlags, scope, bound, newfree, ste.blockType === ClassBlock); + ste.hasFree = ste.hasFree || discoveredFree; _dictUpdate(free, newfree); }; @@ -953,6 +954,7 @@ SymbolTable.prototype.updateSymbols = function (symbols, scope, bound, free, cla var w; var flags; var name; + var discoveredFree = false; for (name in symbols) { flags = symbols[name]; w = scope[name]; @@ -978,7 +980,9 @@ SymbolTable.prototype.updateSymbols = function (symbols, scope, bound, free, cla continue; } symbols[name] = freeValue; + discoveredFree = true; } + return discoveredFree; }; SymbolTable.prototype.analyzeName = function (ste, dict, name, flags, bound, local, free, global) { diff --git a/src/tokenize.js b/src/tokenize.js index c9bfb5f385..d075bd3346 100644 --- a/src/tokenize.js +++ b/src/tokenize.js @@ -246,6 +246,8 @@ function _tokenize(readline, encoding, yield_, filename) { needcont = 0, contline = null, indents = [0], + spos = [0,0], + epos = [0,0], capos = null, endprog = undefined, strstart = undefined, @@ -379,8 +381,8 @@ function _tokenize(readline, encoding, yield_, filename) { if (pseudomatch) { // scan for tokens var start = pos; var end = start + pseudomatch[1].length; - var spos = [lnum, start]; - var epos = [lnum, end]; + spos = [lnum, start]; + epos = [lnum, end]; var pos = end; if (start == end) { continue; diff --git a/support/build/wrapmodules.js b/support/build/wrapmodules.js index aba87306b0..1489d8ebba 100644 --- a/support/build/wrapmodules.js +++ b/support/build/wrapmodules.js @@ -49,14 +49,14 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { } else if (stat.isFile()) { let ext = path.extname(file); if (exts.includes(ext)) { - let contents = fs.readFileSync(fullname, 'utf8'); + let contents = fs.readFileSync(fullname, "utf8"); if (minifyjs && (ext === ".js")) { let result = minify(contents); contents = result.code; } // AOT compilation - else if (false && ext === ".py" && + else if (ext === ".py" && endsWithAny(fullname, WHITE_LIST) && fullname.startsWith("src/lib/pedal/")) { var co; @@ -70,7 +70,7 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { console.error(e.args); } fullname = fullname.replace(/\.py$/, ".js"); - contents = co.code + '\nvar $builtinmodule = ' + co.funcname + ';'; + contents = co.code + "\nvar $builtinmodule = " + co.funcname + ";"; contents = minify(contents).code; } ret.files[fullname] = contents; diff --git a/webpack.config.js b/webpack.config.js index 3cfcf79fa3..5e111fbed4 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -51,12 +51,12 @@ module.exports = (env, argv) => { assertfile = "./assert-prod.js"; mod = { rules: [ - /*{ + { test: /\.js$/, enforce: 'pre', exclude: styleexcludes, loader: 'eslint-loader' - }*/ + } ] }; } From 05d90e07fb5f53d7b6c1c996ffd89f26dd62ff4f Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 18 Jan 2020 12:38:46 -0500 Subject: [PATCH 34/68] Optimizations: handleTraceback, startTimer, $fname, duplicateSuspensionValues --- src/compile.js | 57 +++++++++++++++++------------------- src/misceval.js | 33 +++++++++++++++++++++ support/build/wrapmodules.js | 4 +++ 3 files changed, 64 insertions(+), 30 deletions(-) diff --git a/src/compile.js b/src/compile.js index abeedb5b28..939bf88412 100644 --- a/src/compile.js +++ b/src/compile.js @@ -382,7 +382,7 @@ Compiler.prototype._checkSuspension = function(e) { e = e || {lineno: "$currLineNo", col_offset: "$currColNo", source: "$currSource"}; - out ("if ($ret && $ret.$isSuspension) { return $saveSuspension($ret,'"+this.filename+"',"+e.lineno+","+e.col_offset+","+e.source+"); }"); + out ("if ($ret && $ret.$isSuspension) { return $saveSuspension($ret,$fname,"+e.lineno+","+e.col_offset+","+e.source+"); }"); this.u.doesSuspend = true; this.u.tempsToSave = this.u.tempsToSave.concat(this.u.localtemps); @@ -1122,9 +1122,20 @@ Compiler.prototype.outputSuspensionHelpers = function (unit) { var localSaveCode = []; var localsToSave = unit.localnames.concat(unit.tempsToSave); var seenTemps = {}; + + var localsToSaveWithoutDuplicates = []; + for (i = 0; i < localsToSave.length; i++) { + t = localsToSave[i]; + if (seenTemps[t]===undefined) { + localsToSaveWithoutDuplicates.push(t); + seenTemps[t] = true; + } + } + localsToSave = localsToSaveWithoutDuplicates; + var hasCell = unit.ste.blockType === Sk.SYMTAB_CONSTS.FunctionBlock && unit.ste.childHasFree; - var output = (localsToSave.length > 0 ? ("var " + localsToSave.join(",") + ";") : "") + - "var $wakeFromSuspension = function() {" + + var output = (localsToSave.length > 0 ? ("var " + localsToSave.join(",") + ";") : ""); + output += "var $wakeFromSuspension = function() {" + "var susp = "+unit.scopename+".$wakingSuspension; "+unit.scopename+".$wakingSuspension = undefined;" + "$blk=susp.$blk; $loc=susp.$loc; $gbl=susp.$gbl; $exc=susp.$exc; $err=susp.$err; $postfinally=susp.$postfinally;" + "$currLineNo=susp.$lineno;$currColNo=susp.$colno;$currSource=susp.$source;Sk.lastYield=Date.now();" + @@ -1132,10 +1143,7 @@ Compiler.prototype.outputSuspensionHelpers = function (unit) { for (i = 0; i < localsToSave.length; i++) { t = localsToSave[i]; - if (seenTemps[t]===undefined) { - output += t + "=susp.$tmps." + t + ";"; - seenTemps[t] = true; - } + output += t + "=susp.$tmps." + t + ";"; } output += ("try {"+ @@ -1152,13 +1160,9 @@ Compiler.prototype.outputSuspensionHelpers = function (unit) { "susp.optional=susp.child.optional;" + (hasCell ? "susp.$cell=$cell;" : ""); - seenTemps = {}; for (i = 0; i < localsToSave.length; i++) { t = localsToSave[i]; - if (seenTemps[t]===undefined) { - localSaveCode.push("\"" + t + "\":" + t); - seenTemps[t]=true; - } + localSaveCode.push("\"" + t + "\":" + t); } output += "susp.$tmps={" + localSaveCode.join(",") + "};" + "return susp;" + @@ -1905,7 +1909,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal // all function invocations in call this.u.varDeclsCode += "var $blk=" + entryBlock + ",$exc=[],$loc=" + locals + cells + ",$gbl=this,$err=undefined,$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;$currSource=undefined;"; if (Sk.execLimit !== null) { - this.u.varDeclsCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now();Sk.execPaused=0}"; + this.u.varDeclsCode += "Sk.misceval.startTimer();"; } if (Sk.yieldLimit !== null && this.u.canSuspend) { this.u.varDeclsCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}"; @@ -2259,7 +2263,7 @@ Compiler.prototype.cclass = function (s) { this.u.switchCode += "var $blk=" + entryBlock + ",$exc=[],$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;$currSource=undefined;"; if (Sk.execLimit !== null) { - this.u.switchCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now();Sk.execPaused=0}"; + this.u.switchCode += "Sk.misceval.startTimer();"; } if (Sk.yieldLimit !== null && this.u.canSuspend) { this.u.switchCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}"; @@ -2534,7 +2538,8 @@ Compiler.prototype.nameop = function (name, ctx, dataToStore) { case Sk.astnodes.Load: case Sk.astnodes.Param: // Need to check that it is bound! - out("if (", mangled, " === undefined) { throw new Sk.builtin.UnboundLocalError('local variable \\\'", mangled, "\\\' referenced before assignment'); }\n"); + //out("if (", mangled, " === undefined) { throw new Sk.builtin.UnboundLocalError('local variable \\\'", mangled, "\\\' referenced before assignment'); }\n"); + out("if (", mangled, " === undefined) { throw Sk.misceval.errorUL('",mangled,"'); }\n"); return mangled; case Sk.astnodes.Store: out(mangled, "=", dataToStore, ";"); @@ -2700,7 +2705,7 @@ Compiler.prototype.cmod = function (mod) { "');var $ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;$currSource=undefined;"; if (Sk.execLimit !== null) { - this.u.varDeclsCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now();Sk.execPaused=0}"; + this.u.varDeclsCode += "Sk.misceval.startTimer();"; } if (Sk.yieldLimit !== null && this.u.canSuspend) { @@ -2759,19 +2764,9 @@ Compiler.prototype.cmod = function (mod) { Compiler.prototype.handleTraceback = function(doContinue, scopeName) { doContinue = doContinue ? "continue" : ""; - return ("}catch(err){ "+ - "if(err instanceof Sk.builtin.TimeLimitError){"+ - "Sk.execStart=Date.now();Sk.execPaused=0"+ - "}if(!(err instanceof Sk.builtin.BaseException)) {"+ - "err=new Sk.builtin.ExternalError(err);" + - "}Sk.err=err;err.traceback.push({"+ - "lineno:$currLineNo,"+ - "colno:$currColNo,"+ - "source:$currSource,"+ - "filename:'"+this.filename+"'," + - "scope:'"+scopeName+"'"+ - "});"+ - "if($exc.length>0){$err=err;$blk=$exc.pop();"+doContinue+"}else{throw err;}}}"); + return "}catch(err){"+ + "err=Sk.misceval.handleTraceback(err,$currLineNo,$currColNo,$currSource,$fname,'" + scopeName + "');"+ + "if($exc.length>0){$err=err;$blk=$exc.pop();"+doContinue+"}else{throw err;}}}"; }; /** @@ -2805,7 +2800,9 @@ Sk.compile = function (source, filename, mode, canSuspend, annotate) { // Restore the global __future__ flags Sk.__future__ = savedFlags; - var ret = "$compiledmod = function() {" + c.result.join("") + "\nreturn " + funcname + ";}();"; + var shortCutConstants = "const $fname='"+filename+"';"; + + var ret = "$compiledmod = function() {" + shortCutConstants +c.result.join("") + "\nreturn " + funcname + ";}();"; return { funcname: "$compiledmod", code : ret diff --git a/src/misceval.js b/src/misceval.js index b862cc54f0..8224a8b428 100644 --- a/src/misceval.js +++ b/src/misceval.js @@ -1309,3 +1309,36 @@ Sk.misceval.buildClass = function (globals, func, name, bases, cell) { return klass; }; Sk.exportSymbol("Sk.misceval.buildClass", Sk.misceval.buildClass); + +Sk.misceval.handleTraceback = function(err,currLineNo,currColNo,currSource,filename, scopeName) { + if(err instanceof Sk.builtin.TimeLimitError){ + Sk.execStart=Date.now(); + Sk.execPaused=0; + } + if(!(err instanceof Sk.builtin.BaseException)) { + err=new Sk.builtin.ExternalError(err); + } + Sk.err=err; + err.traceback.push({ + lineno: currLineNo, + colno: currColNo, + source: currSource, + filename: filename, + scope: scopeName + }); + return err; +}; +Sk.exportSymbol("Sk.misceval.handleTraceback", Sk.misceval.handleTraceback); + +Sk.misceval.startTimer = function() { + if (typeof Sk.execStart === "undefined") { + Sk.execStart = Date.now(); + Sk.execPaused=0; + } +}; +Sk.exportSymbol("Sk.misceval.startTimer", Sk.misceval.startTimer); + +Sk.misceval.errorUL = function(mangled) { + return new Sk.builtin.UnboundLocalError('local variable "'+mangled+ '" referenced before assignment'); +}; +Sk.exportSymbol("Sk.misceval.errorUL", Sk.misceval.errorUL); \ No newline at end of file diff --git a/support/build/wrapmodules.js b/support/build/wrapmodules.js index 1489d8ebba..92c7d4af89 100644 --- a/support/build/wrapmodules.js +++ b/support/build/wrapmodules.js @@ -1,6 +1,7 @@ const fs = require('fs'); const path = require('path'); const minify = require('babel-minify'); +const beautify = require('js-beautify'); const reqskulpt = require('../run/require-skulpt').requireSkulpt; @@ -71,7 +72,10 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { } fullname = fullname.replace(/\.py$/, ".js"); contents = co.code + "\nvar $builtinmodule = " + co.funcname + ";"; + fs.writeFileSync("dist/"+file+".js", beautify(contents), 'utf8'); contents = minify(contents).code; + fs.writeFileSync("dist/"+file+".minified.js", contents, 'utf8'); + fs.writeFileSync("dist/"+file+".minified.beautified.js", beautify(contents), 'utf8'); } ret.files[fullname] = contents; } From f812dc1f0d0c58855478bd8b4afbde70886a9180 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 27 Jan 2020 12:34:43 -0500 Subject: [PATCH 35/68] Compiler optimizations --- src/compile.js | 32 +++++++++++++++++++------------- support/build/wrapmodules.js | 15 ++++++++++----- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/compile.js b/src/compile.js index 939bf88412..a2fb670bd0 100644 --- a/src/compile.js +++ b/src/compile.js @@ -20,6 +20,7 @@ function Compiler (filename, st, flags, canSuspend, sourceCodeForAnnotation) { this.nestlevel = 0; this.u = null; + this.consts = {}; this.stack = []; this.result = []; @@ -61,7 +62,7 @@ function CompilerUnit () { this.blocks = []; this.curblock = 0; - this.consts = {}; + //this.consts = {}; this.scopename = null; @@ -287,9 +288,9 @@ Compiler.prototype.makeConstant = function (rest) { } // Check if we've already defined this exact constant - for (var constant in this.u.consts) { - if (this.u.consts.hasOwnProperty(constant)) { - cval = this.u.consts[constant]; + for (var constant in this.consts) { + if (this.consts.hasOwnProperty(constant)) { + cval = this.consts[constant]; if (cval == val) { // We have, just use it return constant; @@ -298,8 +299,8 @@ Compiler.prototype.makeConstant = function (rest) { } // We have not, build new one - v = this.u.scopename + "." + this.gensym("const"); - this.u.consts[v] = val; + v = "$moduleConstants" + "." + this.gensym("_"); + this.consts[v] = val; return v; }; @@ -2651,11 +2652,6 @@ Compiler.prototype.exitScope = function () { mangled = fixReservedNames(mangled); out(prev.scopename, ".co_name=new Sk.builtins['str']('", mangled, "');"); } - for (var constant in prev.consts) { - if (prev.consts.hasOwnProperty(constant)) { - prev.suffixCode += constant + " = " + prev.consts[constant] + ";"; - } - } }; /** @@ -2800,9 +2796,19 @@ Sk.compile = function (source, filename, mode, canSuspend, annotate) { // Restore the global __future__ flags Sk.__future__ = savedFlags; - var shortCutConstants = "const $fname='"+filename+"';"; + var shortCutConstants = "const $fname='"+filename+"',$moduleConstants={};"; + var constantDefinitions = []; + for (var constant in c.consts) { + if (c.consts.hasOwnProperty(constant)) { + constantDefinitions.push(constant + " = " + c.consts[constant] + ";"); + } + } - var ret = "$compiledmod = function() {" + shortCutConstants +c.result.join("") + "\nreturn " + funcname + ";}();"; + var ret = ("$compiledmod = function() {" + + shortCutConstants + + c.result.join("") + + constantDefinitions.join("") + + "\nreturn " + funcname + ";}();"); return { funcname: "$compiledmod", code : ret diff --git a/support/build/wrapmodules.js b/support/build/wrapmodules.js index 92c7d4af89..ec45e30a62 100644 --- a/support/build/wrapmodules.js +++ b/support/build/wrapmodules.js @@ -58,8 +58,9 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { // AOT compilation else if (ext === ".py" && - endsWithAny(fullname, WHITE_LIST) && + //endsWithAny(fullname, WHITE_LIST) && fullname.startsWith("src/lib/pedal/")) { + return; var co; try { co = Sk.compile(contents, fullname, "exec", true, false); @@ -72,10 +73,10 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { } fullname = fullname.replace(/\.py$/, ".js"); contents = co.code + "\nvar $builtinmodule = " + co.funcname + ";"; - fs.writeFileSync("dist/"+file+".js", beautify(contents), 'utf8'); - contents = minify(contents).code; - fs.writeFileSync("dist/"+file+".minified.js", contents, 'utf8'); - fs.writeFileSync("dist/"+file+".minified.beautified.js", beautify(contents), 'utf8'); + //fs.writeFileSync("dist/parts/"+file+".js", beautify(contents), 'utf8'); + //contents = minify(contents).code; + //fs.writeFileSync("dist/parts/"+file+".minified.js", contents, 'utf8'); + //fs.writeFileSync("dist/parts/"+file+".minified.beautified.js", beautify(contents), 'utf8'); } ret.files[fullname] = contents; } @@ -116,6 +117,10 @@ if (process.argv.includes("internal")) { excludes: excludes }; + if (!fs.existsSync("dist/parts/")){ + fs.mkdirSync("dist/parts/"); + } + buildJsonFile("builtinFiles", ["src/builtin", "src/lib"], [".js", ".py"], "dist/skulpt-stdlib.js", opts) } else if (process.argv.includes("unit2")) { if (!fs.existsSync("support/tmp")) { From dbe45d286df0847c9b0e31263b1b8e2e23228937 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Fri, 26 Jun 2020 14:38:49 -0400 Subject: [PATCH 36/68] Image stuff, other fixes for the semester. --- src/builtin.js | 1 + src/builtin/sys.js | 1 + src/file.js | 3 + src/float.js | 2 +- src/import.js | 2 +- src/lib/cisc108/__init__.py | 4 +- src/lib/cisc108/assertions.py | 271 +++++++++++++++++++++++--- src/lib/cisc108/old_assertions.py | 294 +++++++++++++++++++++++++++++ src/lib/{image_old.js => image.js} | 0 src/misceval.js | 10 + support/run/runfile.js | 19 +- test/test_infinite_loop.py | 9 + webpack.config.js | 8 +- 13 files changed, 588 insertions(+), 36 deletions(-) create mode 100644 src/lib/cisc108/old_assertions.py rename src/lib/{image_old.js => image.js} (100%) create mode 100644 test/test_infinite_loop.py diff --git a/src/builtin.js b/src/builtin.js index 124abf3cd5..2506449401 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -872,6 +872,7 @@ Sk.builtin.raw_input = function (prompt) { var lprompt = prompt ? prompt : ""; return Sk.misceval.chain(Sk.importModule("sys", false, true), function (sys) { + if (Sk.inputfunTakesPrompt) { return Sk.misceval.callsimOrSuspendArray(Sk.builtin.file.$readline, [sys["$d"]["stdin"], null, lprompt]); } else { diff --git a/src/builtin/sys.js b/src/builtin/sys.js index 5c35d49ca6..9fb4b4a773 100644 --- a/src/builtin/sys.js +++ b/src/builtin/sys.js @@ -44,6 +44,7 @@ var $builtinmodule = function (name) { sys.resetTimeout = new Sk.builtin.func(function () { Sk.execStart = new Date(); + Sk.execPaused = 0; }); sys.getYieldLimit = new Sk.builtin.func(function () { diff --git a/src/file.js b/src/file.js index be0fcaeb81..fc9b76c8ed 100644 --- a/src/file.js +++ b/src/file.js @@ -148,6 +148,7 @@ Sk.builtin.file.$readline = function (self, size, prompt) { lprompt = lprompt ? lprompt : ""; + Sk.misceval.pauseTimer(); x = Sk.inputfun(lprompt); if (x instanceof Promise) { @@ -158,6 +159,7 @@ Sk.builtin.file.$readline = function (self, size, prompt) { throw susp.data.error; } + Sk.misceval.unpauseTimer(); return new Sk.builtin.str(susp.data.result); }; @@ -168,6 +170,7 @@ Sk.builtin.file.$readline = function (self, size, prompt) { return susp; } else { + Sk.execPaused = Date.now()-Sk.execPaused; return new Sk.builtin.str(x); } } else { diff --git a/src/float.js b/src/float.js index d60e589230..34d2cfab90 100644 --- a/src/float.js +++ b/src/float.js @@ -886,7 +886,7 @@ Sk.builtin.float_.prototype.str$ = function (base, sign) { if (Sk.__future__.python3) { tmp = work.toPrecision(16); } else { - tmp = work.toPrecision(12); + tmp = work.toPrecision(12); } diff --git a/src/import.js b/src/import.js index ce50df5b92..b57697c476 100644 --- a/src/import.js +++ b/src/import.js @@ -355,7 +355,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela } if (Sk.dateSet == null || !Sk.dateSet) { - finalcode = "Sk.execStart = Sk.lastYield = new Date();\n" + co.code; + finalcode = "Sk.execStart = Sk.lastYield = new Date();Sk.execPaused=0;\n" + co.code; Sk.dateSet = true; } diff --git a/src/lib/cisc108/__init__.py b/src/lib/cisc108/__init__.py index b5f2e5b07e..2123022081 100644 --- a/src/lib/cisc108/__init__.py +++ b/src/lib/cisc108/__init__.py @@ -1 +1,3 @@ -from cisc108.assertions import assert_equal, QUIET, student_tests +from cisc108.assertions import (assert_equal, assert_type, + assert_true, assert_false, + QUIET, student_tests) diff --git a/src/lib/cisc108/assertions.py b/src/lib/cisc108/assertions.py index 8ff53366b2..744ca7ad7d 100644 --- a/src/lib/cisc108/assertions.py +++ b/src/lib/cisc108/assertions.py @@ -1,7 +1,13 @@ ''' -CISC106 Module that includes some basic helper functions such as assert_equal(). +CISC108 Module that includes some basic helper functions such as assert_equal(). Versions: +0.4 - 2020-APR-25, Austin Cory Bart + + Added new assert_type function + + Added tests for assert_type +0.3 - 2019-JUL-??, Eleonor Bart + + Vastly improved unit tests' flexibility + + Started work on assert_false and assert_true 0.2.1 - 2019-JAN-23, Austin Cory Bart + Keep track of tests' counts in student_tests + Improve make_type_name for BlockPy compatibility @@ -72,6 +78,7 @@ def make_type_name(value): except Exception: return str(type(value))[8:-2] + def get_line_code(): # Load in extract_stack, or provide shim for environments without it. try: @@ -85,6 +92,22 @@ def get_line_code(): return None, None +def _normalize_string(text): + ''' + For strings: + - strips whitespace from each line + - lower cases + ''' + # Lowercase + text = text.lower() + # Strip whitespace from each line + lines = text.split("\n") + lines = [line.strip() for line in lines if line.strip()] + text = "\n".join(lines) + # Return result + return text + + # Don't print message from assert_equal on success QUIET = False @@ -96,12 +119,12 @@ def get_line_code(): MESSAGE_LINE_CODE = " - [line {line}] {code}" MESSAGE_UNRELATED_TYPES = ( - "FAILURE{context}, predicted answer was {y!r} ({y_type!r}), " - "computed answer was {x!r} ({x_type!r}). " + "FAILURE{context}, predicted answer was {y} ({y_type!r}), " + "computed answer was {x} ({x_type!r}). " "You attempted to compare unrelated data types.") MESSAGE_GENERIC_FAILURE = ( - "FAILURE{context}, predicted answer was {y!r}, " - "computed answer was {x!r}.") + "FAILURE{context}, predicted answer was {y}, " + "computed answer was {x}.") MESSAGE_GENERIC_SUCCESS = ( "TEST PASSED{context}") @@ -123,10 +146,12 @@ def reset(self): self.successes = 0 self.tests = 0 self.lines = [] - + + student_tests = StudentTestReport() -def assert_equal(x, y, precision=4, exact_strings=False, *args): + +def assert_equal(x, y, precision=4, exact_strings=False, *args) -> bool: """ Checks an expected value using the _is_equal function. Prints a message if the test case passed or failed. @@ -197,7 +222,7 @@ def _is_equal(x, y, precision, exact_strings, *args): >>> _is_equal(12.3456, 12.34568w5) False """ - + # Check if generators if isinstance(x, LIST_GENERATOR_TYPES): x = list(x) @@ -207,7 +232,7 @@ def _is_equal(x, y, precision, exact_strings, *args): y = list(y) elif isinstance(y, SET_GENERATOR_TYPES): y = set(y) - + if isinstance(x, float) and isinstance(y, float): error = 10 ** (-precision) return abs(x - y) < error @@ -242,22 +267,6 @@ def _is_equal(x, y, precision, exact_strings, *args): return x == y -def _normalize_string(text): - ''' - For strings: - - strips whitespace from each line - - lower cases - ''' - # Lowercase - text = text.lower() - # Strip whitespace from each line - lines = text.split("\n") - lines = [line.strip() for line in lines if line.strip()] - text = "\n".join(lines) - # Return result - return text - - def _are_sequences_equal(x, y, precision, exact_strings): ''' For sequences that support __len__, __iter__, and should have the same @@ -292,3 +301,215 @@ def _are_sets_equal(x, y, precision, exact_strings): if not _set_contains(x_element, y, precision, exact_strings): return False return True + +################################################################################ +# Truthiness stuff + +def assert_true(x) -> bool: + """ + Checks an expected value using the _is_true function. + Prints a message if the test case passed or failed. + + Args: + x (Any): Any kind of python value. Should have been computed by + the students' code (their actual answer). + Returns: + bool: Whether or not the assertion passed. + """ + line, code = get_line_code() + if None in (line, code): + context = "" + else: + context = MESSAGE_LINE_CODE.format(line=line, code=code) + + result = _is_true(x) + if result is None: + print(MESSAGE_UNRELATED_TYPES.format(context=context, + x=x, x_type=type(x).__name__, + y=True, y_type=type(True).__name__)) + return False + elif not result: + print(MESSAGE_GENERIC_FAILURE.format(context=context, x=x, y=True)) + return False + elif not QUIET: + print(MESSAGE_GENERIC_SUCCESS.format(context=context)) + return True + + +def assert_false(x) -> bool: + """ + Checks an expected value using the _is_true function. + Prints a message if the test case passed or failed. + + Args: + x (Any): Any kind of python value. Should have been computed by + the students' code (their actual answer). + Returns: + bool: Whether or not the assertion passed. + """ + line, code = get_line_code() + if None in (line, code): + context = "" + else: + context = MESSAGE_LINE_CODE.format(line=line, code=code) + + result = _is_true(x) + if result is None: + print(MESSAGE_UNRELATED_TYPES.format(context=context, + x=x, x_type=type(x).__name__, + y=False, y_type=type(False).__name__)) + return False + elif result: + print(MESSAGE_GENERIC_FAILURE.format(context=context, x=x, y=False)) + return False + elif not QUIET: + print(MESSAGE_GENERIC_SUCCESS.format(context=context)) + return True + + +def _is_true(x): + """ + _is_true : thing -> boolean + _is_true : number -> boolean + Determines whether the argument is true. + Returns None when attempting to assert a non-boolean + + Examples: + >>> _is_true(True) + False + + >>> _is_true("hi") + None + + >>> _is_true(False) + False + """ + + if not isinstance(x, bool): + return None + else: + return x + +################################################################################ +# Type Checking + +BETTER_TYPE_NAMES = { + str: 'string', + int: 'integer', + float: 'float', + bool: 'boolean', + dict: 'dictionary', + list: 'list' +} + +def _get_name(value): + try: + return BETTER_TYPE_NAMES.get(value, value.__name__) + except Exception: + return str(value)[8:-2] + +def _make_key_list(values): + if not values: + return "and there were no keys at all" + elif len(values) == 1: + return "but there was the key {!r}".format(values[0]) + else: + return "but there were the keys "+ (", ".join(sorted(map(repr, values[:-1])))) + " and {!r}".format(values[-1]) + +WRONG_TYPE_MESSAGE = " was the wrong type. Expected type was {y_type!r}, but actual value was {x} ({x_type!r})." +WRONG_KEY_TYPE_MESSAGE = " had a wrong type for a key. Expected type of all keys was {y_type!r}, but there was the key {x} ({x_type!r})." +MISSING_KEY_MESSAGE = " was missing the key {!r}, {}." +EXTRA_KEYS_MESSAGE = " had all the correct keys ({}), but also had these unexpected keys: {}" +NOT_A_TYPE_MESSAGE = " was the value {x!r} ({x_type!r}). However, that's not important because the expected type ({y}) doesn't make sense! The type definition should not have literal values like {y} in it, only types (like {y_type}). The literal values go into instances of the type." + +def _validate_dictionary_type(value, expected_type, path): + if not isinstance(value, dict): + return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y_type="dictionary") + for expected_key, expected_value in expected_type.items(): + if isinstance(expected_key, str): + if expected_key not in value: + return path + MISSING_KEY_MESSAGE.format(expected_key, _make_key_list(list(value.keys()))) + reason = _validate_type(value[expected_key], expected_value, + path+"[{!r}]".format(expected_key)) + if reason: + return reason + elif isinstance(expected_key, type): + for k, v in value.items(): + new_path = path+"[{!r}]".format(k) + if not isinstance(k, expected_key): + return path + WRONG_KEY_TYPE_MESSAGE.format(x=repr(k), x_type=_get_name(type(k)), y_type=_get_name(expected_key)) + reason = _validate_type(v, expected_value, new_path) + if reason: + return reason + break # only support one key/value type in Lookup style + else: + if len(expected_type) != len(value): + unexpected_keys = set(value.keys()) - set(expected_type.keys()) + unexpected_keys = ", ".join(sorted(map(repr, unexpected_keys))) + expected_keys = ", ".join(sorted(map(repr, expected_type))) + return path + EXTRA_KEYS_MESSAGE.format(expected_keys, unexpected_keys) + +SIMPLE_TYPES = (int, float, bool, str) + +def _validate_type(value, expected_type, path="world"): + if isinstance(expected_type, dict): + return _validate_dictionary_type(value, expected_type, path) + elif isinstance(expected_type, list): + if not isinstance(value, list): + return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y_type="list") + if not expected_type and value: + return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y_type="empty list") + for index, element in enumerate(value): + reason = _validate_type(element, expected_type[0], path+"[{}]".format(index)) + if reason: + return reason + elif isinstance(expected_type, SIMPLE_TYPES): + return path + NOT_A_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y=repr(expected_type), y_type=type(expected_type).__name__) + elif expected_type == float: + if not isinstance(value, (int, float)) and value is not None: + return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y_type=_get_name(expected_type)) + elif isinstance(value, bool) and expected_type != bool: + return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y_type=_get_name(expected_type)) + elif not isinstance(value, expected_type) and value is not None: + return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y_type=_get_name(expected_type)) + elif value == None and expected_type != None: + return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), + y_type=_get_name(expected_type)) + +def assert_type(value, expected_type) -> bool: + """ + Checks that the given value is of the expected_type. + + Args: + value (Any): Any kind of python value. Should have been computed by + the students' code (their actual answer). + expected_type (type): Any kind of type value. Should be in the format + used within CISC108. This includes support for literal composite + types (e.g., [int] and {str: int}) and record types. + Returns: + bool: Whether or not the assertion passed. + """ + # Can we add in the line number and code? + line, code = get_line_code() + if None in (line, code): + context = "" + else: + context = MESSAGE_LINE_CODE.format(line=line, code=code) + student_tests.lines.append(line) + + reason = _validate_type(value, expected_type, "value") + student_tests.tests += 1 + if reason is not None: + student_tests.failures += 1 + if isinstance(expected_type, dict): + if isinstance(value, dict): + reason = "the "+reason + else: + reason = "the "+reason + print("FAILURE{context},".format(context=context), reason) + return False + elif not QUIET: + print(MESSAGE_GENERIC_SUCCESS.format(context=context)) + student_tests.successes += 1 + return True + diff --git a/src/lib/cisc108/old_assertions.py b/src/lib/cisc108/old_assertions.py new file mode 100644 index 0000000000..8ff53366b2 --- /dev/null +++ b/src/lib/cisc108/old_assertions.py @@ -0,0 +1,294 @@ +''' +CISC106 Module that includes some basic helper functions such as assert_equal(). + +Versions: +0.2.1 - 2019-JAN-23, Austin Cory Bart + + Keep track of tests' counts in student_tests + + Improve make_type_name for BlockPy compatibility +0.2 - 2019-JAN-02, Modified by Austin Cory Bart + + Renamed functions to be in line with common Python convention + + Packaged into an actual library on PyPI, with tests and stuff. + + Replaced type(X) checks with isinstance + + Changed string interpolation to .format + + Extracted out string messages +0.142 - 2014-APR-23, Modified by Jon Leighton + + Modified success and failure messages to print "SUCCESS" and "FAILURE" at the + beginning of each result. This makes it much easier to quickly discern the + outcome visually. +0.141 - 2014-MAR-26, Modified by Jon Leighton + + Removed unused function print_verbose(). + + Appended text to FAILURE message for incompatible types to indicate that types + involved can't be compared. +0.14 - 2014-MAR-26, Modified by Andrew Roosen + + Modified assert_equal() to return False on failure and True on success. + + Introduced QUITE option to supress output on SUCCESS. + + Modified FAILURE message for incompatible data types to be consistent with + + FAILURE message for unequal values. + + Modified names of internal functions isEqual() and isseqtype() to _is_equal() + and _is_seq_type(), respectively +0.13 - 2014-MAR-25, Modified by Jon Leighton + + added elif clause to _is_equal(), to avoid comparing ints and floats to anything + that is not an int or a float, and to return None in this case. The previous + comparison tried to subtract these quantities from each other, causing a + runtime error. + + Modified assert_equal() to check for _is_equal() returning None, which now + indicates an attempt to compare unrelated data types. Failure message is + modified in this case to report the attempt to compare unrelated types. + + Removed unused global variables fail and success. + + Added version numbers to Paul Amer's modifications, and bumped version number to + reflect my modifications. + + Changed version number to string, to match recommended practice. +0.122 - 2012-APR-17, Modified by Paul Amer + + removed graphics stuff; just kept assert_equal +0.121 - 2011-SEP-08, Modified by Paul Amer + +improved success-failure messages +0.12 + + display can be called multiple times + + assert_equal supports PIL.Image.Image +0.1 + + Initial assert_equal, display, animate, bind +''' +__version__ = '0.2.1' + +# Number encapsulates bool, int, float, complex, decimal.Decimal, etc. +try: + from numbers import Number +except: + Number = (bool, int, float, complex) + +try: + bytes +except NameError: + bytes = str + +try: + frozenset() +except: + frozenset = tuple() + +def make_type_name(value): + try: + return type(value).__name__ + except Exception: + return str(type(value))[8:-2] + +def get_line_code(): + # Load in extract_stack, or provide shim for environments without it. + try: + from traceback import extract_stack + trace = extract_stack() + frame = trace[len(trace) - 3] + line = frame[1] + code = frame[3] + return line, code + except Exception: + return None, None + + +# Don't print message from assert_equal on success +QUIET = False + +SET_GENERATOR_TYPES = (type({}.keys()), type({}.values()), type({}.items())) + +LIST_GENERATOR_TYPES = (type(map(bool, [])), type(filter(bool, [])), + type(range(0)), type(reversed([])), type(zip()), + type(enumerate([]))) + +MESSAGE_LINE_CODE = " - [line {line}] {code}" +MESSAGE_UNRELATED_TYPES = ( + "FAILURE{context}, predicted answer was {y!r} ({y_type!r}), " + "computed answer was {x!r} ({x_type!r}). " + "You attempted to compare unrelated data types.") +MESSAGE_GENERIC_FAILURE = ( + "FAILURE{context}, predicted answer was {y!r}, " + "computed answer was {x!r}.") +MESSAGE_GENERIC_SUCCESS = ( + "TEST PASSED{context}") + +class StudentTestReport: + def __init__(self): + self.reset() + def __repr__(self): + return str(self) + def __str__(self): + return ('' + ).format( + failures=self.failures, successes=self.successes, tests=self.tests, + lines=', '.join(self.lines) + ) + def reset(self): + self.failures = 0 + self.successes = 0 + self.tests = 0 + self.lines = [] + +student_tests = StudentTestReport() + +def assert_equal(x, y, precision=4, exact_strings=False, *args): + """ + Checks an expected value using the _is_equal function. + Prints a message if the test case passed or failed. + + Args: + x (Any): Any kind of python value. Should have been computed by + the students' code (their actual answer). + y (Any): Any kind of python value. The expected value to be produced, + precalculated (their expected answer). + precision (int): Optional. Indicates how many decimal places to use + when comparing floating point values. + exact_strings (bool): Whether or not strings should be matched perfectly + character-by-character, or if you should ignore capitalization, + whitespace, and symbols. + Returns: + bool: Whether or not the assertion passed. + """ + + # Can we add in the line number and code? + line, code = get_line_code() + if None in (line, code): + context = "" + else: + context = MESSAGE_LINE_CODE.format(line=line, code=code) + student_tests.lines.append(line) + + result = _is_equal(x, y, precision, exact_strings, *args) + student_tests.tests += 1 + if result is None: + student_tests.failures += 1 + print(MESSAGE_UNRELATED_TYPES.format(context=context, + x=repr(x), x_type=make_type_name(x), + y=repr(y), y_type=make_type_name(y))) + return False + elif not result: + student_tests.failures += 1 + print(MESSAGE_GENERIC_FAILURE.format(context=context, x=repr(x), y=repr(y))) + return False + elif not QUIET: + print(MESSAGE_GENERIC_SUCCESS.format(context=context)) + student_tests.successes += 1 + return True + +# Hack to allow anyone with an assert_equal reference to get the results +# since they are global across all calls. Weird strategy! +assert_equal.student_tests = student_tests + +def _is_equal(x, y, precision, exact_strings, *args): + """ + _is_equal : thing thing -> boolean + _is_equal : number number number -> boolean + Determines whether the two arguments are equal, or in the case of + floating point numbers, within a specified number of decimal points + precision (by default, checks to with 4 decimal points for floating + point numbers). Returns None when attempting to compare ints and floats + to anything other than ints and floats. + + Examples: + >>> _is_equal('ab', 'a'+'b') + True + + >>> _is_equal(12.34, 12.35) + False + + >>> _is_equal(12.3456, 12.34568, 4) + True + + >>> _is_equal(12.3456, 12.34568w5) + False + """ + + # Check if generators + if isinstance(x, LIST_GENERATOR_TYPES): + x = list(x) + elif isinstance(x, SET_GENERATOR_TYPES): + x = set(x) + if isinstance(y, LIST_GENERATOR_TYPES): + y = list(y) + elif isinstance(y, SET_GENERATOR_TYPES): + y = set(y) + + if isinstance(x, float) and isinstance(y, float): + error = 10 ** (-precision) + return abs(x - y) < error + elif isinstance(x, Number) and isinstance(y, Number) and isinstance(x, type(y)): + return x == y + elif ((isinstance(x, str) and isinstance(y, str)) or + (isinstance(x, bytes) and isinstance(y, bytes))): + if exact_strings: + return x == y + else: + return _normalize_string(x) == _normalize_string(y) + elif isinstance(x, list) and isinstance(y, list): + return _are_sequences_equal(x, y, precision, exact_strings) + elif isinstance(x, tuple) and isinstance(y, tuple): + return _are_sequences_equal(x, y, precision, exact_strings) + elif isinstance(x, set) and isinstance(y, set): + return _are_sets_equal(x, y, precision, exact_strings) + elif isinstance(x, frozenset) and isinstance(y, frozenset): + return _are_sets_equal(x, y, precision, exact_strings) + elif isinstance(x, dict) and isinstance(y, dict): + primary_keys = set(x.keys()) + if not _are_sets_equal(primary_keys, set(y.keys()), + precision, exact_strings): + return False + for key in primary_keys: + if not _is_equal(x[key], y[key], precision, exact_strings): + return False + return True + elif not isinstance(x, type(y)): + return None + else: + return x == y + + +def _normalize_string(text): + ''' + For strings: + - strips whitespace from each line + - lower cases + ''' + # Lowercase + text = text.lower() + # Strip whitespace from each line + lines = text.split("\n") + lines = [line.strip() for line in lines if line.strip()] + text = "\n".join(lines) + # Return result + return text + + +def _are_sequences_equal(x, y, precision, exact_strings): + ''' + For sequences that support __len__, __iter__, and should have the same + order. + ''' + if len(x) != len(y): + return False + for x_element, y_element in zip(x, y): + if not _is_equal(x_element, y_element, precision, exact_strings): + return False + return True + + +def _set_contains(needle, haystack, precision, exact_strings): + ''' + Tests if the given needle is one of the elements of haystack, using + the _is_equal function. + ''' + for element in haystack: + if _is_equal(element, needle, precision, exact_strings): + return True + return False + + +def _are_sets_equal(x, y, precision, exact_strings): + ''' + For sequences that support __len__, __iter__, but order does not matter. + ''' + if len(x) != len(y): + return False + for x_element in x: + if not _set_contains(x_element, y, precision, exact_strings): + return False + return True diff --git a/src/lib/image_old.js b/src/lib/image.js similarity index 100% rename from src/lib/image_old.js rename to src/lib/image.js diff --git a/src/misceval.js b/src/misceval.js index 8224a8b428..9213a547cc 100644 --- a/src/misceval.js +++ b/src/misceval.js @@ -1338,6 +1338,16 @@ Sk.misceval.startTimer = function() { }; Sk.exportSymbol("Sk.misceval.startTimer", Sk.misceval.startTimer); +Sk.misceval.pauseTimer = function() { + Sk.execPaused=Date.now(); +}; +Sk.exportSymbol("Sk.misceval.pauseTimer", Sk.misceval.pauseTimer); + +Sk.misceval.unpauseTimer = function() { + Sk.execPaused=Date.now()-Sk.execPaused; +}; +Sk.exportSymbol("Sk.misceval.unpauseTimer", Sk.misceval.unpauseTimer); + Sk.misceval.errorUL = function(mangled) { return new Sk.builtin.UnboundLocalError('local variable "'+mangled+ '" referenced before assignment'); }; diff --git a/support/run/runfile.js b/support/run/runfile.js index 2f44c8dd5f..cdce8eef1f 100644 --- a/support/run/runfile.js +++ b/support/run/runfile.js @@ -27,10 +27,21 @@ function run (python3, opt, filename) { console.log("-----"); Sk.configure({ - syspath: [path.dirname(filename)], - read: (fname) => { return fs.readFileSync(fname, "utf8"); }, - output: (args) => { process.stdout.write(args); }, - __future__: pyver + syspath: [path.dirname(filename)], + inputfun: (promptMessage) => { + let resolveText; + let submittedPromise = new Promise((resolve) => { + resolveText = resolve; + }); + setTimeout(()=>{resolveText(Sk.builtin.str("quit"));}, 2000); + return submittedPromise; + /*return process.stdin.on("data", function (data) { + return data; + });*/ + }, + read: (fname) => { return fs.readFileSync(fname, "utf8"); }, + output: (args) => { process.stdout.write(args); }, + __future__: pyver }); Sk.misceval.asyncToPromise(function() { diff --git a/test/test_infinite_loop.py b/test/test_infinite_loop.py new file mode 100644 index 0000000000..0ba4f1f53d --- /dev/null +++ b/test/test_infinite_loop.py @@ -0,0 +1,9 @@ +import sys +sys.setExecutionLimit(1000) + +quit = False +while not quit: + quit = "quit" == input("Type 'quit' to quit.").lower() + while True: + print("SECOND EVIL LOOP") +print("Never reached") \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 5e111fbed4..3a47e0e704 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -52,10 +52,10 @@ module.exports = (env, argv) => { mod = { rules: [ { - test: /\.js$/, - enforce: 'pre', - exclude: styleexcludes, - loader: 'eslint-loader' + test: /\.js$/, + enforce: "pre", + exclude: styleexcludes, + loader: "eslint-loader" } ] }; From 2026d377c5dc9a141e6f6eb07387e8946b775b47 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 6 Aug 2020 00:49:55 -0400 Subject: [PATCH 37/68] This is ridiculous. My git discipline is too lax. Oh well, it is what it is, I guess. Committing everything. --- HACKING.md | 518 +++- breakingchanges.md | 153 + doc/ReferenceManual.rst | 8 +- jsdoc.json | 39 +- package.json | 37 +- repl/repl.js | 64 +- src/abstract.js | 1548 ++++++----- src/assert-dev.js | 4 +- src/assert-prod.js | 4 +- src/ast.js | 1020 ++++--- src/biginteger.js | 1800 +----------- src/bool.js | 101 +- src/builtin.js | 1100 +++----- src/builtin/sys.js | 20 +- src/builtin/this.py | 4 +- src/builtindict.js | 619 ++++- src/check.js | 252 ++ src/classmethod.js.disabled | 10 +- src/classmethod.py | 2 + src/compile.js | 554 ++-- src/complex.js | 1315 ++++----- src/constants.js | 154 +- src/descr.js | 378 +++ src/dict.js | 1224 ++++---- src/dictviews.js | 203 ++ src/env.js | 189 +- src/errors.js | 777 ++---- src/ffi.js | 17 +- src/file.js | 36 +- src/filter.js | 2 +- src/float.js | 1030 +++---- src/formatting.js | 601 ++-- src/fromcodepoint.js | 6 +- src/function.js | 447 ++- src/generator.js | 8 +- src/generic.js | 285 ++ src/import.js | 550 ++-- src/int.js | 1765 +++++------- src/iterator.js | 30 +- src/iteratorobjects.js | 290 ++ src/lib/PIL/__init__.js | 32 +- src/lib/StringIO.py | 2 +- src/lib/_thread.py | 2 +- src/lib/array.js | 136 +- src/lib/ast.js | 201 +- src/lib/bisect.py | 36 +- src/lib/cisc108/assertions.py | 60 +- src/lib/cisc108/old_assertions.py | 22 +- src/lib/collections.js | 1628 +++++++---- src/lib/copy.py | 57 +- src/lib/cs1014/dictionaries.py | 4 +- src/lib/cs1014/input_mistakes.py | 1 - src/lib/cs1014/tests/test_dictionary.py | 3 +- src/lib/datetime.py | 98 +- src/lib/document.js | 83 +- src/lib/functools.py | 21 +- src/lib/genericpath.py | 6 +- src/lib/hashlib.js | 3 +- src/lib/hashlib.py | 1 - src/lib/image.js | 40 +- src/lib/io.py | 17 +- src/lib/itertools.js | 877 ++++++ src/lib/itertools.py | 23 - src/lib/json/__init__.js | 226 +- src/lib/keyword.py | 73 +- src/lib/linecache.py | 9 +- src/lib/math.js | 1220 ++++++-- src/lib/matplotlib/__init__.js | 7 +- src/lib/matplotlib/pyplot/__init__.js | 370 +-- src/lib/numpy/__init__.js | 2040 +++++++------- src/lib/operator.js | 20 +- src/lib/os.py | 3 +- src/lib/parking/__init__.js | 174 +- src/lib/pedal/assertions/__init__.py | 3 +- src/lib/pedal/assertions/organizers.py | 16 +- src/lib/pedal/assertions/setup.py | 26 +- src/lib/pedal/cait/ast_map.py | 2 +- src/lib/pedal/mistakes/iteration_context.py | 1 + src/lib/pedal/plugins/__init__.py | 1 - src/lib/pedal/plugins/cmd_line.py | 2 +- .../pedal/plugins/test_reference_solution.py | 16 +- src/lib/pedal/questions/__init__.py | 16 +- src/lib/pedal/questions/graders.py | 43 +- src/lib/pedal/questions/loader.py | 10 +- src/lib/pedal/questions/setup.py | 3 + src/lib/pedal/resolvers/sectional.py | 6 +- src/lib/pedal/sandbox/exceptions.py | 14 +- src/lib/pedal/sandbox/messages.py | 2 +- src/lib/pedal/sandbox/mocked.py | 4 + src/lib/pedal/sandbox/sandbox.py | 7 +- src/lib/pedal/sandbox/timeout.py | 157 +- src/lib/pedal/sandbox/tracer.py | 2 +- src/lib/pedal/source/__init__.py | 1 + src/lib/pedal/source/sections.py | 14 +- src/lib/pedal/tifa/builtin_definitions.py | 3 +- src/lib/pedal/tifa/tifa.py | 10 +- src/lib/pedal/tifa/type_definitions.py | 5 +- src/lib/pedal/tifa/type_operations.py | 6 +- src/lib/pedal/toolkit/functions.py | 6 +- src/lib/pedal/toolkit/plotting.py | 3 +- src/lib/pedal/toolkit/records.py | 2 +- src/lib/pedal/toolkit/signatures.py | 17 +- src/lib/pedal/toolkit/utilities.py | 5 +- src/lib/platform.js | 34 +- src/lib/posixpath.py | 49 +- src/lib/pprint.py | 3 +- src/lib/processing.js | 1772 ++++++------ src/lib/pythonds/basic/__init__.py | 10 +- src/lib/pythonds/basic/deque.py | 48 +- src/lib/pythonds/basic/queue.py | 34 +- src/lib/pythonds/basic/stack.py | 41 +- src/lib/pythonds/graphs/__init__.py | 2 - src/lib/pythonds/graphs/adjGraph.py | 85 +- src/lib/pythonds/graphs/priorityQueue.py | 76 +- src/lib/pythonds/trees/__init__.py | 3 - src/lib/pythonds/trees/balance.py | 24 +- src/lib/pythonds/trees/binaryTree.py | 41 +- src/lib/pythonds/trees/binheap.py | 29 +- src/lib/pythonds/trees/bst.py | 107 +- src/lib/random.js | 77 +- src/lib/re.js | 27 +- src/lib/reprlib.py | 25 +- src/lib/requests/__init__.js | 26 +- src/lib/signal.js | 13 +- src/lib/sound/__init__.js | 4 +- src/lib/sound/sample.js | 12 +- src/lib/sound/sound.js | 108 +- src/lib/stat.py | 114 +- src/lib/string.js | 38 +- src/lib/test/__init__.py | 5 +- src/lib/textwrap.py | 465 +++- src/lib/time.js | 89 +- src/lib/token.js | 36 + src/lib/token.py | 1 - src/lib/tokenize.js | 49 + src/lib/tokenize.py | 1 - src/lib/traceback.py | 62 +- src/lib/turtle.js | 1088 ++++---- src/lib/types.py | 27 +- src/lib/unittest/__init__.py | 179 +- src/lib/unittest/gui.py | 218 +- src/lib/unittest/mock.py | 2 +- src/lib/urllib/request/__init__.js | 16 +- src/lib/webgl/__init__.js | 690 +++-- src/lib/webgl/math.js | 536 ++-- src/lib/webgl/matrix4.js | 501 ++-- src/lib/webgl/models.js | 220 +- src/lib/webgl/primitives.js | 199 +- src/list.js | 1022 +++---- src/long.js | 854 +----- src/main.js | 28 +- src/map.js | 2 +- src/mappingproxy.js | 95 + src/method.js | 219 +- src/misceval.js | 881 +++--- src/module.js | 66 +- src/object.js | 608 ++-- src/parser.js | 78 +- src/pgen/ast/asdl.py | 41 +- src/pgen/ast/asdl_js.py | 49 +- src/pgen/ast/spark.py | 158 +- src/pgen/parser/grammar.py | 5 +- src/pgen/parser/main.py | 6 +- src/pgen/parser/pgen.py | 38 +- src/pgen/parser/token.py | 11 +- src/pgen/parser/tokenize.py | 91 +- src/print.js | 64 +- src/property_class_static.js | 206 ++ src/range.js | 265 ++ src/seqtype.js | 4 +- src/set.js | 912 +++--- src/simple_iterators.js | 261 ++ src/sk_method.js | 138 + src/slice.js | 327 ++- src/slotdefs.js | 2172 +++++++++++++++ src/sorted.js | 2 +- src/str.js | 904 ++++-- src/structseq.js | 141 +- src/super.js | 150 + src/symtable.js | 74 +- src/token.js | 96 +- src/tokenize.js | 242 +- src/tuple.js | 521 ++-- src/type.js | 1069 ++++--- src/typeobject.js | 8 +- src/zip.js | 4 +- support/build/help.js | 2 +- support/build/wrapmodules.js | 4 +- .../jsdoc-toolkit/app/test/event.js | 2 +- .../jsdoc-toolkit/app/test/multi_methods.js | 6 +- .../jsdoc-toolkit/app/test/overview.js | 2 +- .../jsdoc-toolkit/app/test/params_optional.js | 2 +- support/externs/sk.js | 47 + support/precompile/precompile.js | 86 + support/run/require-skulpt.js | 1 + support/run/run_template.ejs | 2 +- support/run/runfile.js | 36 +- support/time-helpers/strptime.js | 10 +- test/exec_test.py | 4 +- test/fix_dunder_class.py | 3 +- test/hello_world.py | 1 + test/run/t158.py | 2 +- test/run/t158.py.real | 1 - test/run/t228.py.real | 2 +- test/run/t228.py.real.force | 2 +- test/run/t231.py.real | 2 +- test/run/t231.py.real.force | 2 +- test/run/t289.py.real | 2 +- test/run/t289.py.real.force | 2 +- test/run/t343.py.disabled | 47 + test/run/t355.py | 2 +- test/run/t355.py.real | 1 - test/run/t355.trans | 8 +- test/run/t376.py.real | 2 +- test/run/t376.py.real.force | 2 +- test/run/t394.py.real | 2 +- test/run/t394.py.real.force | 2 +- test/run/t407.py.real | 4 +- test/run/t407.py.real.force | 4 +- test/run/t421.py.real | 2 +- test/run/t421.py.real.force | 2 +- test/run/t430.py | 2 +- test/run/t430.py.real | 1 - test/run/t444.py.real | 10 +- test/run/t449.py.real | 2 +- test/run/t449.py.real.force | 2 +- test/run/t450.py.real | 2 +- test/run/t450.py.real.force | 2 +- test/run/t463.py.real | 2 +- test/run/t474.py | 49 +- test/run/t483.py | 2 +- test/run/t483.py.real | 2 +- test/run/t483.py.real.force | 2 +- test/run/t484.py | 6 +- test/run/t484.py.real | 4 +- test/run/t484.py.real.force | 4 +- test/run/t498.py | 22 +- test/run/t498.py.real | 22 +- test/run/t498.py.real.force | 22 +- test/run/t509.py | 10 +- test/run/t509.py.real | 10 +- test/run/t509.py.real.force | 10 +- test/run/t514.py | 10 +- test/run/t514.py.real | 10 +- test/run/t514.py.real.force | 10 +- test/run/t518.py | 4 +- test/run/t518.py.real | 4 +- test/run/t518.py.real.force | 4 +- test/run/t519.py | 16 +- test/run/t519.py.real | 16 +- test/run/t519.py.real.force | 16 +- test/run/t522.py | 8 + test/run/t522.py.real | 7 + test/run/t522.trans | 93 +- test/run/t523.py | 8 +- test/run/t523.py.real | 4 +- test/run/t523.py.real.force | 4 +- test/run/t531.py | 2 +- test/run/t531.py.real | 2 +- test/run/t531.trans | 2 +- test/run/t539.py | 2 +- test/run/t539.py.real | 1 - test/run/t539.py.real.force | 1 - test/run/t548.py | 2 +- test/run/t548.py.real | 2 +- test/run/t901.py.real | 8 +- test/run/t901.py.real.alt | 8 +- test/run/t901.py.real.force | 8 +- test/run/t902.py | 14 +- test/run/t902.py.real | 12 +- test/run/t902.py.real.force | 12 +- test/run/t903.py | 28 +- test/run/t903.py.real | 18 +- test/run/t903.py.real.force | 18 +- test/run/t904.py.real | 10 +- test/run/t904.py.real.force | 10 +- test/run/t905.py.real | 1 + test/test.js | 9 +- test/test_hashlib.py | 4 +- test/test_scope.py | 15 + test/test_supering.py | 15 + test/test_syntax_error.py | 7 + test/test_traceback.py | 4 +- test/turtle/SpecRunner.html | 1 - test/turtle/referenceImageCreator.html | 1 - test/unit/subpackage/implicit_import.py | 8 +- test/unit/subpackage/implicit_import_2.py | 3 + test/unit/subpackage/importable_module.py | 6 + test/unit/test_builtin.py | 5 + test/unit/test_complex.py | 5 +- test/unit/test_decorators_2.py | 12 +- test/unit/test_descr.py | 65 +- test/unit/test_descr_skulpt.py | 68 +- test/unit/test_exceptions.py | 2 +- test/unit/test_factorial.py | 2 +- test/unit/test_format.py | 4 +- test/unit/test_inheritance.py | 4 +- test/unit/test_input.py | 2 +- test/unit/test_int.py | 14 +- test/unit/test_list.py | 68 +- test/unit/test_list_sort.py | 2 +- test/unit/test_namedtuple.py | 358 ++- test/unit/test_regressions.py | 16 + test/unit/test_strformat.py | 44 +- test/unit/test_subclass.py | 2 +- test/unit/test_tokenize.py | 50 + test/unit/test_unicode.py | 13 + test/unit3/pkga/pkgb/modc.py | 3 + test/unit3/string_functions.py | 307 ++ test/unit3/t221_sub.py | 3 + test/unit3/test_bitwise_operators.py | 57 + test/unit3/test_bool.py | 120 +- test/unit3/test_builtin.py | 406 ++- test/unit3/test_calling.py | 13 - test/unit3/test_class.py | 310 ++- test/unit3/test_compare.py | 105 +- test/unit3/test_conditionals.py | 63 + test/unit3/test_counter.py | 105 + test/unit3/test_decorators.py | 34 + test/unit3/test_defaultdict.py | 21 + test/unit3/test_deque.py | 521 ++++ test/unit3/test_dict.py | 71 +- test/unit3/test_enumerate.py | 36 +- test/unit3/test_errors.py | 102 + test/unit3/test_filter_map_zip.py | 230 ++ test/unit3/test_float.py | 25 +- test/unit3/test_frozenset.py | 188 ++ test/unit3/test_fstring.py | 1082 ++++++++ test/unit3/test_functions.py | 466 ++++ test/unit3/test_hash.py | 30 + test/unit3/test_imports.py | 22 + test/unit3/test_inheritance.py | 109 + test/unit3/test_int.py | 38 +- test/unit3/test_int_literal.py | 26 +- test/unit3/test_isinstance.py | 139 + test/unit3/test_itertools.py | 2464 +++++++++++++++++ test/unit3/test_list.py | 204 +- test/unit3/test_list_sort.py | 5 + test/unit3/test_logical_operators.py | 72 + test/unit3/test_loops.py | 236 ++ test/unit3/test_magicmethods.py | 34 + test/unit3/test_math.py | 1187 ++++++-- test/unit3/test_matmul.py | 113 + test/unit3/test_none.py | 57 + test/unit3/test_operator.py | 183 ++ test/unit3/test_range.py | 271 +- test/unit3/test_re.py | 289 ++ test/unit3/test_set.py.disabled | 22 +- test/unit3/test_sets2.py | 251 ++ test/unit3/test_strformat.py | 89 +- test/unit3/test_string.py | 1 - test/unit3/test_string_methods.py | 101 + test/unit3/test_textwrap.py | 1021 +++++++ test/unit3/test_time.py | 90 +- test/unit3/test_tuple.py | 64 +- test/unit3/test_type.py | 103 + test/unit3/test_variables.py | 161 ++ webpack.config.js | 74 +- 358 files changed, 36459 insertions(+), 20998 deletions(-) create mode 100644 breakingchanges.md create mode 100644 src/check.js create mode 100644 src/descr.js create mode 100644 src/dictviews.js create mode 100644 src/generic.js create mode 100644 src/iteratorobjects.js delete mode 100644 src/lib/hashlib.py create mode 100644 src/lib/itertools.js delete mode 100644 src/lib/itertools.py create mode 100644 src/lib/token.js delete mode 100644 src/lib/token.py create mode 100644 src/lib/tokenize.js delete mode 100644 src/lib/tokenize.py create mode 100644 src/mappingproxy.js create mode 100644 src/property_class_static.js create mode 100644 src/range.js create mode 100644 src/simple_iterators.js create mode 100644 src/sk_method.js create mode 100644 src/slotdefs.js create mode 100644 src/super.js create mode 100644 support/precompile/precompile.js create mode 100644 test/hello_world.py create mode 100644 test/run/t343.py.disabled create mode 100644 test/test_scope.py create mode 100644 test/test_supering.py create mode 100644 test/test_syntax_error.py create mode 100644 test/unit/subpackage/implicit_import_2.py create mode 100644 test/unit/test_regressions.py create mode 100644 test/unit/test_tokenize.py create mode 100644 test/unit/test_unicode.py create mode 100644 test/unit3/pkga/pkgb/modc.py create mode 100644 test/unit3/t221_sub.py create mode 100644 test/unit3/test_bitwise_operators.py create mode 100644 test/unit3/test_conditionals.py create mode 100644 test/unit3/test_counter.py create mode 100644 test/unit3/test_deque.py create mode 100644 test/unit3/test_errors.py create mode 100644 test/unit3/test_filter_map_zip.py create mode 100644 test/unit3/test_frozenset.py create mode 100644 test/unit3/test_fstring.py create mode 100644 test/unit3/test_functions.py create mode 100644 test/unit3/test_hash.py create mode 100644 test/unit3/test_imports.py create mode 100644 test/unit3/test_inheritance.py create mode 100644 test/unit3/test_isinstance.py create mode 100644 test/unit3/test_itertools.py create mode 100644 test/unit3/test_logical_operators.py create mode 100644 test/unit3/test_loops.py create mode 100644 test/unit3/test_magicmethods.py create mode 100644 test/unit3/test_matmul.py create mode 100644 test/unit3/test_none.py create mode 100644 test/unit3/test_operator.py create mode 100644 test/unit3/test_re.py create mode 100644 test/unit3/test_sets2.py create mode 100644 test/unit3/test_string_methods.py create mode 100644 test/unit3/test_textwrap.py create mode 100644 test/unit3/test_type.py create mode 100644 test/unit3/test_variables.py diff --git a/HACKING.md b/HACKING.md index 285374a353..2f55a369d5 100644 --- a/HACKING.md +++ b/HACKING.md @@ -6,74 +6,198 @@ If you are reading this document, chances are you have used Skulpt in some form What is Skulpt? --------------- -Skulpt is a system that compiles Python (of the 2.6-ish variety) into Javascript. But it's not Javascript that you can paste in to your browser and run. Python and Javascript are very different languanges, their types are different, their scoping rules are different. Python is designed to be run on Linux, or Windows, or Mac OS X, not in the browser! So, to provide a True Python experience Skulpt must provide a runtime environment in which the compiled code executes. This runtime environment is provided by the skulpt.min.js and skulpt-stdlib.js files that you must include in your web page in order to make Skulpt work. +Skulpt is a system that compiles Python (of the 3.7-ish variety) into Javascript. But it's not Javascript that you can paste in to your browser and run. Python and Javascript are very different languages, their types are different, their scoping rules are different. Python is designed to be run on Linux, or Windows, or Mac OS X, not in the browser! So, to provide a True Python experience Skulpt must provide a runtime environment in which the compiled code executes. This runtime environment is provided by the skulpt.min.js and skulpt-stdlib.js files that you must include in your web page in order to make Skulpt work. To give you some idea of what is going on behind the scenes with skulpt lets look at what happens when our friend "hello world" is compiled from Python to Skulpt. We will revisit this program later and go into more detail, so for now, don't get bogged down in the detail, just have a look to see how much is really happening **Python Version** - print "hello world" + print("hello world") **Javascript Translation** - - /* 1 */ var $scope0 = (function($modname) { - /* 2 */ var $blk = 0, - /* 3 */ $exc = [], - /* 4 */ $gbl = {}, - /* 5 */ $loc = $gbl, - /* 6 */ $err = undefined; - /* 7 */ $gbl.__name__ = $modname; - /* 8 */ Sk.globals = $gbl; - /* 9 */ try { - /* 10 */ while (true) { - /* 11 */ try { - /* 12 */ switch ($blk) { - /* 13 */ case 0: - /* 14 */ /* --- module entry --- */ - /* 15 */ // - /* 16 */ // line 1: - /* 17 */ // print "hello world" - /* 18 */ // ^ - /* 19 */ // - /* 20 */ Sk.currLineNo = 1; - /* 21 */ Sk.currColNo = 0 - /* 22 */ - /* 23 */ - /* 24 */ Sk.currFilename = './simple.py'; - /* 25 */ - /* 26 */ var $str1 = new Sk.builtins['str']('hello world'); - /* 27 */ Sk.misceval.print_(new Sk.builtins['str']($str1).v); - /* 28 */ Sk.misceval.print_("\n"); - /* 29 */ return $loc; - /* 30 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); - /* 31 */ } - /* 32 */ } catch (err) { - /* 33 */ if ($exc.length > 0) { - /* 34 */ $err = err; - /* 35 */ $blk = $exc.pop(); - /* 36 */ continue; - /* 37 */ } else { - /* 38 */ throw err; - /* 39 */ } - /* 40 */ } - /* 41 */ } - /* 42 */ } catch (err) { - /* 43 */ if (err instanceof Sk.builtin.SystemExit && !Sk.throwSystemExit) { - /* 44 */ Sk.misceval.print_(err.toString() + '\n'); - /* 45 */ return $loc; - /* 46 */ } else { - /* 47 */ throw err; - /* 48 */ } - /* 49 */ } - /* 50 */ }); - - -So, 50 lines of Javascript for hello world eh? That sounds kind of crazy, but you have to recognize that the environment with global variables, local variables, error handling, etc all has to happen even for the simplest program to run. The parts of the program above that really print "hello world" are lines 26-29. If you have a look at them you will see that we have to construct a string object from the string literal and then pass that off to some print function. - -In the example above `Sk.builtin.str` and `Sk.misceval.print_` are part of the Skulpt runtime. It is usually the case that to extend Skulpt one of these runtime functions must be modified, or a new runtime function must be created and exposed so that it can be used in an ordinary Python program. The rest of this manual will take you through the essential parts of Skulpt so you can feel comfortable working on and extending the runtime environment. +
    +View the code translation + +```javascript + /* 1 */ Sk.execStart = Sk.lastYield = new Date(); + /* 2 */ $compiledmod = function() { + /* 3 */ var $scope0 = (function($forcegbl) { + /* 4 */ var $loadname1; + /* 5 */ var $wakeFromSuspension = function() { + /* 6 */ var susp = $scope0.$wakingSuspension; + /* 7 */ $scope0.$wakingSuspension = undefined; + /* 8 */ $blk = susp.$blk; + /* 9 */ $loc = susp.$loc; + /* 10 */ $gbl = susp.$gbl; + /* 11 */ $exc = susp.$exc; + /* 12 */ $err = susp.$err; + /* 13 */ $postfinally = susp.$postfinally; + /* 14 */ $currLineNo = susp.$lineno; + /* 15 */ $currColNo = susp.$colno; + /* 16 */ Sk.lastYield = Date.now(); + /* 17 */ $loadname1 = susp.$tmps.$loadname1; + /* 18 */ try { + /* 19 */ $ret = susp.child.resume(); + /* 20 */ } catch (err) { + /* 21 */ if (!(err instanceof Sk.builtin.BaseException)) { + /* 22 */ err = new Sk.builtin.ExternalError(err); + /* 23 */ } + /* 24 */ err.traceback.push({ + /* 25 */ lineno: $currLineNo, + /* 26 */ colno: $currColNo, + /* 27 */ filename: '.py' + /* 28 */ }); + /* 29 */ if ($exc.length > 0) { + /* 30 */ $err = err; + /* 31 */ $blk = $exc.pop(); + /* 32 */ } else { + /* 33 */ throw err; + /* 34 */ } + /* 35 */ } + /* 36 */ }; + /* 37 */ var $saveSuspension = function($child, $filename, $lineno, $colno) { + /* 38 */ var susp = new Sk.misceval.Suspension(); + /* 39 */ susp.child = $child; + /* 40 */ susp.resume = function() { + /* 41 */ $scope0.$wakingSuspension = susp; + /* 42 */ return $scope0(); + /* 43 */ }; + /* 44 */ susp.data = susp.child.data; + /* 45 */ susp.$blk = $blk; + /* 46 */ susp.$loc = $loc; + /* 47 */ susp.$gbl = $gbl; + /* 48 */ susp.$exc = $exc; + /* 49 */ susp.$err = $err; + /* 50 */ susp.$postfinally = $postfinally; + /* 51 */ susp.$filename = $filename; + /* 52 */ susp.$lineno = $lineno; + /* 53 */ susp.$colno = $colno; + /* 54 */ susp.optional = susp.child.optional; + /* 55 */ susp.$tmps = { + /* 56 */ "$loadname1": $loadname1 + /* 57 */ }; + /* 58 */ return susp; + /* 59 */ }; + /* 60 */ var $gbl = $forcegbl || {}, + /* 61 */ $blk = 0, + /* 62 */ $exc = [], + /* 63 */ $loc = $gbl, + /* 64 */ $cell = {}, + /* 65 */ $err = undefined; + /* 66 */ $loc.__file__ = new Sk.builtins.str('.py'); + /* 67 */ var $ret = undefined, + /* 68 */ $postfinally = undefined, + /* 69 */ $currLineNo = undefined, + /* 70 */ $currColNo = undefined; + /* 71 */ if (typeof Sk.execStart === 'undefined') { + /* 72 */ Sk.execStart = Date.now() + /* 73 */ } + /* 74 */ if (typeof Sk.lastYield === 'undefined') { + /* 75 */ Sk.lastYield = Date.now() + /* 76 */ } + /* 77 */ if ($scope0.$wakingSuspension !== undefined) { + /* 78 */ $wakeFromSuspension(); + /* 79 */ } + /* 80 */ if (Sk.retainGlobals) { + /* 81 */ if (Sk.globals) { + /* 82 */ $gbl = Sk.globals; + /* 83 */ Sk.globals = $gbl; + /* 84 */ $loc = $gbl; + /* 85 */ } + /* 86 */ if (Sk.globals) { + /* 87 */ $gbl = Sk.globals; + /* 88 */ Sk.globals = $gbl; + /* 89 */ $loc = $gbl; + /* 90 */ $loc.__file__ = new Sk.builtins.str('.py'); + /* 91 */ } else { + /* 92 */ Sk.globals = $gbl; + /* 93 */ } + /* 94 */ } else { + /* 95 */ Sk.globals = $gbl; + /* 96 */ } + /* 97 */ while (true) { + /* 98 */ try { + /* 99 */ var $dateNow = Date.now(); + /* 100 */ if ($dateNow - Sk.execStart > Sk.execLimit) { + /* 101 */ throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg()) + /* 102 */ } + /* 103 */ if ($dateNow - Sk.lastYield > Sk.yieldLimit) { + /* 104 */ var $susp = $saveSuspension({ + /* 105 */ data: { + /* 106 */ type: 'Sk.yield' + /* 107 */ }, + /* 108 */ resume: function() {} + /* 109 */ }, '.py', $currLineNo, $currColNo); + /* 110 */ $susp.$blk = $blk; + /* 111 */ $susp.optional = true; + /* 112 */ return $susp; + /* 113 */ } + /* 114 */ switch ($blk) { + /* 115 */ case 0: + /* 116 */ /* --- module entry --- */ + /* 117 */ // + /* 118 */ // line 1: + /* 119 */ // print("hello world") + /* 120 */ // ^ + /* 121 */ // + /* 122 */ $currLineNo = 1; + /* 123 */ $currColNo = 0; + /* 124 */ + /* 125 */ var $loadname1 = $loc.print !== undefined ? $loc.print : Sk.misceval.loadname('print', $gbl);; + /* 126 */ $ret = ($loadname1.tp$call) ? $loadname1.tp$call([$scope0.$const2], undefined) : Sk.misceval.applyOrSuspend($loadname1, undefined, undefined, undefined, [$scope0.$const2]); + /* 127 */ $blk = 1; /* allowing case fallthrough */ + /* 128 */ case 1: + /* 129 */ /* --- function return or resume suspension --- */ if ($ret && $ret.$isSuspension) { + /* 130 */ return $saveSuspension($ret, '.py', 1, 0); + /* 131 */ } + /* 132 */ var $call3 = $ret; + /* 133 */ // + /* 134 */ // line 1: + /* 135 */ // print("hello world") + /* 136 */ // ^ + /* 137 */ // + /* 138 */ $currLineNo = 1; + /* 139 */ $currColNo = 0; + /* 140 */ + /* 141 */ return $loc; + /* 142 */ throw new Sk.builtin.SystemError('internal error: unterminated block'); + /* 143 */ } + /* 144 */ } catch (err) { + /* 145 */ if (!(err instanceof Sk.builtin.BaseException)) { + /* 146 */ err = new Sk.builtin.ExternalError(err); + /* 147 */ } + /* 148 */ err.traceback.push({ + /* 149 */ lineno: $currLineNo, + /* 150 */ colno: $currColNo, + /* 151 */ filename: '.py' + /* 152 */ }); + /* 153 */ if ($exc.length > 0) { + /* 154 */ $err = err; + /* 155 */ $blk = $exc.pop(); + /* 156 */ continue; + /* 157 */ } else { + /* 158 */ throw err; + /* 159 */ } + /* 160 */ } + /* 161 */ } + /* 162 */ }); + /* 163 */ $scope0.$const2 = new Sk.builtin.str('hello world'); + /* 164 */ return $scope0; + /* 165 */ }(); +``` + +
    + +
    + +So, 165 lines of Javascript for `"hello world"` eh? That sounds kind of crazy, but you have to recognize that the environment with `global variables`, `local variables`, `error handling`, etc all has to happen even for the simplest program to run. The parts of the program above that really `print("hello world")` are lines `163` and `125-126`. If you have a look at them you will see that we have to construct a string object from the string literal, load the `constant`, load the `print` function and then pass the `constant` off to that `print` function. + +In the example above `Sk.builtin.str` and `Sk.misceval.loadname` are part of the Skulpt runtime. It is usually the case that to extend Skulpt one of these runtime functions must be modified, or a new runtime function must be created and exposed so that it can be used in an ordinary Python program. The rest of this manual will take you through the essential parts of Skulpt so you can feel comfortable working on and extending the runtime environment. + +Looking at translation code can be quite unsightly and much of the information is unimportant to add new features. For those new to Skulpt, reading the translation code is useful only as much as to hone in on the relevant parts of the code that do what you need. More suggestions on this later. For now, know that it would be rare to have to change compile code. You can implement a whole missing library in Skulpt without having to touch the compiler. An important thing to keep in mind as you are trying to understand Skulpt is that it is heavily influenced by the implementation of CPython. So although Python and Javascript are both object oriented languages many parts of the skulpt implementation are quite procedural. For example using functions that take an object as their first parameter may seem strange as we should have just created a method on that object. But in order to follow the CPython implementation this decision was made early on. @@ -82,68 +206,67 @@ The Source The `src` directory contains the javascript that implements skulpt as well as parts of the standard library. library modules are in src/lib. The -source files could roughly be divided into two pieces. The compiler and -the runtime. The compiler files are: -`ast.js, parser.js, symtable.js, compile.js, and tokenize.js` The -compiler part of skulpt reads python code and generates a Javascript -program. If you want to change the syntax of Python these are the files -to look at. The syntax used in skulpt is taken right from the Python -2.6.5 distribution. - -When you run the program in the browser the javascript part is 'evaled' -by javascript. The runtime files roughly correspond to all of the major -object types in Python plus builtins: - -- abstract.js -- contains lots of abstract function defs -- biginteger.js -- implements Python's long integer type -- bool.js -- skulpt-stdlib.js -- builtin functions: range, min, max, etc. are - defined here -- builtindict.js -- Provides a mapping from the standard Python name - to the internal name in skulpt-stdlib.js -- dict.js -- enumerate.js -- env.js -- errors.js -- Exceptions are defined here -- file.js -- float.js -- function.js -- generator.js -- import.js -- int.js -- list.js -- long.js -- method.js -- module.js -- native.js -- number.js -- object.js -- most things "inherit" from object -- set.js -- slice.js -- str.js -- timsort.js -- tuple.js -- type.js +source files could roughly be divided into four pieces: +- compiler and runtime +- namespaces +- builtin functions and types +- python modules (`src/lib`) + + +**Compiler** +The compiler and the runtime. The compiler files are: +- `ast.js` +- `parser.js` +- `symtable.js` +- `compile.js` +- `tokenize.js` + +The compiler part of skulpt reads python code and generates a Javascript program. If you want to change the syntax of Python these are the files to look at. The syntax used in skulpt is taken right from the Python 2.6.5 distribution and has since been updated to allow for Python 3.7 syntax. + +When you run the program in the browser the javascript part is 'evaled' by javascript. + + + +**Namespaces** + +The namespaces can be somewhat of a collections of seemingly unconnected functions but as a rough guide: +- `Sk.abstract` (`abstract.js`) + - contains lots of abstract functions that work with Skulpt objects e.g + - `Sk.abstr.iter(obj)` + - `Sk.abstr.numberBinOp(v, w, op)` +- `Sk.misceval` (`misceval.js`) + - contains methods for calling Python callables + - performing an operation on a Skulpt object and converting the result to javascript + - working with suspensions/promises/async + - `Sk.misceval.richCompareBool(v, w, op)` (returns `Boolean`) + - `Sk.misceval.callsimArrayOrSusend(func, args, kwargs)` + - `Sk.misceval.arrayFromIterable(iterable)` +- `Sk.ffi` (`ffi.js`) + - A set of tools for moving between Python and javascript + - `Sk.ffi.remapToJs(pyObject)` + - `Sk.ffi.remapToPy(jsObject)` + + +**Builtins** + +Builtin types can all be found in the `Sk.builtin` namespace. Files that contain these functions can all be found in `src/`. All the expected python objects can be found here. + +In order to extend the functionality of a builtin type find the location of the relevant type/method in `src/`. Most methods can be found in `builtin.js`. And most types have their own file - `bool.js` + + Types and Namespaces -------------------- The `Sk` object contains all of the core Skulpt objects and -functions. It's pretty easy to get from Sk.blah to its source. +functions. It's pretty easy to get from `Sk.blah` to its source. Usually you will see something like `Sk.builtin.foo` which indicates that you will likely find a corresponding file for foo in the src directory. -Similarly `Sk.misceval.callsim` tells you that you should look -in `misceval.js` for the callsim function. - -Perhaps one of the most important concepts to learn when starting to program Skulpt is that you -are always moving back and forth between Python objects and Javascript objects. Much of your job -as a skulpt hacker is to either create Python objects as part of a builtin or module function, -or interact with objects that have been created by the users "regular" Python code. Knowing when -you are working with what is critical. For example a Javascript string is not the same thing as a -python string. A Python string is really an instance of ``Sk.builtin.str`` and a Javscript string is -an instance of ``string``. You can't compare the two directly, and you definitely cannot use them -interchangeably. +Similarly `Sk.misceval.callsimArray` tells you that you should look +in `misceval.js` for the `callsimArray` function. + +Perhaps one of the most important concepts to learn when starting to program Skulpt is that you are always moving back and forth between Python objects and Javascript objects. Much of your job as a skulpt hacker is to either create Python objects as part of a builtin or module function, or interact with objects that have been created by the users "regular" Python code. Knowing when you are working with what is critical. For example a Javascript string is not the same thing as a python string. A Python string is really an instance of ``Sk.builtin.str`` and a Javscript string is an instance of ``string``. You can't compare the two directly, and you definitely cannot use them interchangeably. Python | Skulpt | Javascript --------|----------------------|----------- @@ -154,7 +277,7 @@ complex | Sk.builtin.complex | NA list | Sk.builtin.list | Array dict | Sk.builtin.dict | Object set | Sk.builtin.set | NA -bool | Sk.builtin.bool | bool +bool | Sk.builtin.bool | boolean tuple | Sk.builtin.tuple | NA @@ -162,14 +285,14 @@ So how do I get the equivalent value? How do I work with these Python objects f There are two key functions in Sk.ffi: `Sk.ffi.remapToJs` and `Sk.ffi.remapToPy` These utility functions are smart enough to remap most builtin data types back and forth. So if you have a Python string and want to compare it to a Javascript string literal you just need to do `Sk.ffi.remapToJs(pystring)` to get a Javscript string you can compare. -If the Python object in question is a collection, remapToJs will work recursively and not only remap the top level object but also all of the contained objects. +If the Python object in question is a collection, `remapToJs` will work recursively and not only remap the top level object but also all of the contained objects. When would you want to convert from Javascript to Python? Very often, in your implementation you will calculate a value that you want to return. The returned value needs to be a valid Python type. So lets say you calculate the factorial of a number in a new function you are adding to math. Then the resulting Javascript number must be turned into a Python object using `Sk.ffi.remapToPy(myresult)`. -In many places in the current codebase you will see the use of `somePythonObject.v` Where `v` is the actual -javascript value hidden away inside the Python object. This is not the preferred way to obtain the mapping. Use -the `Sk.ffi` API. +In many places in the current codebase you will see the use of `somePythonObject.v` Where `v` is the actual javascript value hidden away inside the Python object. This is not the preferred way to obtain the mapping. Use the `Sk.ffi` API. + +That said `Sk.ffi.remapToJs` can be slow since it does not know in advance which object will be passed to the function. If you are extending the functionality of `Sk.builtin.list`, say, it is worth being consistent with the design choices inside `list.js`, particularly when performance might be a factor. Skulpt is divided into several namespaces, you have already seen a couple of them, so here is the list @@ -179,14 +302,130 @@ Skulpt is divided into several namespaces, you have already seen a couple of the * Sk.misceval -- To extend skulpt you should know these functions +Extending/Creating a native type +------------------ +A common pull request might be to extend a builtin type, or even to create a missing type. We'll focus on recent example that outlines how to start. + +**mappingproxy** +The `mappingproxy` is a non-writable dictionary like object. To create this object you need to know its makeup. Start by getting the `mappingproxy` type and `pprinting` its `__dict__`. + +```python +mappingproxy = type(object.__dict__) +# mappingproxy are not part of the global namespace +from pprint import pprint +pprint(mappingproxy.__dict__) +``` + +```python +mappingproxy({'__contains__': , + '__doc__': None, + '__eq__': , + '__ge__': , + '__getattribute__': , + '__getitem__': , + '__gt__': , + '__hash__': None, + '__iter__': , + '__le__': , + '__len__': , + '__lt__': , + '__ne__': , + '__new__': , + '__repr__': , + '__str__': , + 'copy': , + 'get': , + 'items': , + 'keys': , + 'values': }) +``` +Notice that there are no actual python functions defined. Unlike when you build a Python class... +```python +class A: + def __init__(self, x): + self.x = x +pprint(A.__dict__) +""" +mappingproxy({... + '__init__': , + ...}) +""" +``` + +There is a difference then in building a skulpt native type compared to building a python class written in python. In Cpython this would be the difference between writing an object in C code vs writing an object in pure python. + +The above `mappingproxy.__dict__` can be outlined into skulpt as follows: + +```javascript +Sk.builtin.mappingproxy = Sk.asbtr.buildNativeClass("mappingproxy",{ + constructor: function mappingproxy(d) { + // the constructor in javascript creates an instance of mappingproxy + }, + slots: { + tp$getattr: function(pyName) {}, // __getattribute__ + tp$as_sequence_or_mapping: true, // required for building a mapping or sequence type + tp$richcompare: function(){}, // __eq__, __ne__, etc + tp$new: function (args, kwargs) {}, // __new__ + tp$hash: Sk.builtin.none.none$, // __hash__ + $r: function(){}, // __repr__ + mp$subscript: function (key) {}, // __getitem__ + sq$contains: function (key) {}, // __contains__ + sq$length: function () {}, //__len__ + tp$iter: function () {}, // __iter__ + }, + methods: { + copy: {}, // an object literal that defines the method + get: {}, + keys: {}, + items: {}, + values: {}, + }, + proto: { + // private methods and attributes use a dollar in the name + // these are accessible by instances of this type + }, + flags: { + // flags added to the type object. Not directly accessible by instances + sk$acceptable_as_base_class: false, + }, +}); + +``` +The translation from the `mappingproxy.__dict__` to the skulpt skeleton is relatively straight forward. You can find the mapping of dunder methods to skulpt slots in the documentation at [skulpt.org/docs](http://skulpt.org/docs/) or in `src/slotdefs.js` + +From this template we can do a little digging... + +```python +>>> iter(mappingproxy) +# +``` +So we now know that a `mappingproxy` reuses some `dict` code. + +```javascript +tp$iter: function() { + return new Sk.builtin.dict_iter_(this); +} +``` + +Then we look at `dict_iter_` and find that `mappingproxy` needs two private methods `get$size` and `sq$asarray` method. + +Since these are private methods we add them to the `proto` in the above object literal. + +`sk$acceptable_as_base_class` is flag that was added since +```python +>>> class A(mappingproxy): pass +# TypeError: type 'mappingproxy' is not an acceptable base type +``` + +We continue down the rabbit hole of exploring the object as well as reading related skulpt source code from `dict.js`. Perhaps looking at some other implementations from `Cpython` and `pypy` before coming up with skulpt implementation. + +_(It is worth noting that the skulpt implementation is a little less complete than the above. At the point someone needs a missing feature that's when it might be added.)_ + The Generated Code ------------------ -Perhaps one of the most instructive things you can do to understand -Skulpt and how the pieces begin to fit together is to look at a simple -Python program, and its translation to Javscript. So lets begin with -Hello World. +Another instructive things you can do to understand Skulpt and how the pieces begin to fit together is to look at a simple Python program, and its translation to Javscript. So lets begin with Hello World. ### Python Version @@ -282,6 +521,7 @@ specifically for our program. That would be lines 26-29 above. takes care of the same issue. Not sure, maybe this is an optimization. +```javascript Sk.misceval.print_ = function(x) // this was function print(x) not sure why... { @@ -299,7 +539,7 @@ specifically for our program. That would be lines 26-29 above. if (s.v.length === 0 || !isspace(s.v[s.v.length - 1]) || s.v[s.v.length - 1] === ' ') Sk.misceval.softspace_ = true; }; - +``` - 28: print always results in a newline. So do it. - 29: done return. This gets us out of the while(true) loop. @@ -410,18 +650,12 @@ Another Example Naming Conventions /* 89 */ }); -So, here we create some local variables. x, y, do some math to create a -third local variable z, and then print it. Line 26 illustrates creating -a local variable `x` (stored as an attribute of $loc) -`new Sk.builtin.nmber(1, 'int');` By now you can probably guess that -`Sk.builtin.nmber` is a constructor that creates a Python number object -that is of type int, and has the value of 1. The same thing happens for -`y`. +So, here we create some local variables. x, y, do some math to create a third local variable z, and then print it. Line 26 illustrates creating a local variable `x` (stored as an attribute of $loc) `new Sk.builtin.nmber(1, 'int');` By now you can probably guess that `Sk.builtin.nmber` is a constructor that creates a Python number object that is of type int, and has the value of 1. The same thing happens for `y`. Next, on lines 40 -- 53 we see what happens in an assignment statement. -first we load the values of x and y into temporary variables $loadname1 -and $loadname2. Why not just use $loc.x ?? Well, we need to use -Python's scoping rules. If $loc.x is undefined then we should check the +first we load the values of x and y into temporary variables `$loadname1` +and `$loadname2`. Why not just use `$loc.x` ? Well, we need to use +Python's scoping rules. If `$loc.x` is undefined then we should check the outer scope to see if it exists there. `Sk.misceval.loadname` If loadname does not find a name `x` or `y` it throws a NameError, and execution would abort. You can see where this works by changing the @@ -433,9 +667,9 @@ On lines 52 and 53 we perform the addition using `Sk.abstr.numberBinOp($loadname1, $loadname2, 'Add');` Note the abstract (see abstract.js) nature of `numberBinOp` -- two parameters for the operands, and one parameter `'Add'` that indicates the operator. Finally -the temporary result returned by numberBinOp is stored in $loc.z. It's -important to note that $loc.z contains a Python number object. Down in -the bowels of numberBinOp, the javascript numeric values for x and y are +the temporary result returned by `numberBinOp` is stored in `$loc.z`. It's +important to note that `$loc.z` contains a Python number object. Down in +the bowels of `numberBinOp`, the javascript numeric values for `x` and `y` are retrieved and result of adding two javascript numbers is converted to the appropriate type of Python object. @@ -446,15 +680,7 @@ understand this we need a bit more complicated example, so lets look at a program that contains an if/else conditional. We'll see that we now have a much more interesting switch statement. -Without showing all of the generated code, lets consider a simple python -program like the one below. There will be two scope functions generated -by the compiler for this example. $scope0 is for the main program where -foo is defined and there is an if statement. The second $scope1 is for -when the foo function is actually called. The $scope1 while/switch -combo contains four cases: 0, 1, 2, and 3. You can imagine this python -code consisting of four blocks. The first block starts at the beginning -and goes through the evaluation of the if condition. The second block is -the if true block of the if. The third block is the else block of the if +Without showing all of the generated code, lets consider a simple python program like the one below. There will be two scope functions generated by the compiler for this example. `$scope0` is for the main program where foo is defined and there is an if statement. The second `$scope1` is for when the foo function is actually called. The `$scope1` while/switch combo contains four cases: 0, 1, 2, and 3. You can imagine this python code consisting of four blocks. The first block starts at the beginning and goes through the evaluation of the if condition. The second block is the if true block of the if. The third block is the else block of the if statement, and the final block is the rest of the program after the if/else is all done. You can verify this for yourself by putting this program into a file `simple.py` and running `./skulpt.py run simple.py` @@ -476,7 +702,7 @@ would probably look a lot different. foo("goodbye") # <---- $blk 2 # <--- $blk 1 end of if -When foo is called, it has its own scope $scope1 created and called using Sk.misceval.callsim. +When foo is called, it has its own scope `$scope1` created and called using Sk.misceval.callsim. How do I add Feature X or Fix bug Y ----------------------------------- @@ -493,7 +719,7 @@ straightforward path. So start as follows: x = [1,2,3] print(sorted(x,reverse=True)) -Now run this using `./skulpt.py run test.py` and you will get a compiled +Now run this using `npm run brun test.py`. Run the program and you will get a compiled program. With a little bit of sleuthing you find: /* 35 */ // line 2: diff --git a/breakingchanges.md b/breakingchanges.md new file mode 100644 index 0000000000..fc8f125bd9 --- /dev/null +++ b/breakingchanges.md @@ -0,0 +1,153 @@ +**breaking changes** + +**python 2 incorrectness:** +- `long` + - when adding two long objects the result is likely to be an `int` +- `method` + - unbound methods are no longer supported +- The `base` class for all type objects will be `object` even if the base class is not specified + + +**skulpt api** +- `Sk.builtin.object.prototype.genericGetAttr` -> `Sk.generic.getAttr` +- `Sk.builtin.object.prototype.genericSetAttr` -> `Sk.generic.setAttr` +- `Sk.builtin.typeLookup` removed +- `biginter.js` replaced by the [jsbi](https://github.com/GoogleChromeLabs/jsbi) library +- `Sk.abstr.inherits` removed - inheritance exclusively dealt with by `Sk.abstr.setUpInheritance` +- `Sk.misceval.objectRepr` returns a js string (previously `Sk.builtin.str`) +- `Sk.__future__.python3` becomes the default. Those wishing to use `python2` must define this in the `Sk.configure` object. + +**slot changes** +- `mp$length` replaced by `sq$length` in the codebase +- `sq$ass_item`/`sq$ass_slice` replaced with `mp$ass_subscript` +- `nb$nonzero` replaced with `nb$bool` and switch version takes care of mapping the appropriate dunder method. +- `mp$del_subscript` replaced by `mp$ass_subscript` (as per Cpython) + - deleting vs setting an item is based on the call signature + - `mp$ass_subscript(key, value)` -> set item + - `mp$ass_subscript(key)` -> delete item +- If a dunder func is defined on a user defined class then the slot function is guaranteed. + - e.g. `__len__` defined guarantees `sq$length`. + - A slot function defined by skulpt in this way throws the appropriate errors and converts the return value to the appropriate object. + - `sq$length`: `__len__` is called using `Sk.misceval.callsim(OrSuspend)Array`. + - The result is checked to be an `int` and then converted to `number` since `sq$length` expects a `number`. +- `tp$str` removed from some builtins as per Python 3.8 changes +- If `tp$richcompare` is defined - wrapper functions `ob$eq` etc are created during - this way `Sk.misceval.richCompareBool` need only check for the existance of an `ob$*` slot. + - in fact - the existance of these slots is guaranteed since they are inherited from `Sk.builtin.object` +- `tp$mro`/`tp$bases` are Js Arrays rather than `Sk.builtin.tuple` +- `tp$str` and `$r` for errors were changed as per Cpython. + +**flags** +- `sk$acceptable_as_base_class` used for some type objects +- `sk$object` every skulpt object inherits this flag from `Sk.builtin.object` +- `hp$type` all instance of `sk$klass` klasses +- `sk$basetype` all native classes that inherit from `object` +- `sk$prototypical` do we need to walk up the MRO or can we just check the `prototype` + +**other internal changes** +- the use of `numPromoteFunc` was removed for performance improvements in the implementation of `Sk.asbtr.numberBinOp` + - It was performance beneficial to leave the promoting to the respective `nb$` slots + - `int` binop slots only deal with instance of `int` + - `float` binop slots deal with instances of `float` and `int` + - `complex` binop slots deal with instances of `complex`, `float` and `int` +- `set` and `frozenset` now share much of their implementation +- `collections` module rewritten using new api +- `itertools` module rewritten using new api - these are now type objects rather than instances of `generator` +- `dict` and `set` throw errors if the objects change size during iteration as per Cpython. +- `Sk.builtin.check*` moved to `src/check.js` +- `enumerate`/`filter_`/`reversed`/`zip_`/`map_` combined into one file `src/iteratorobjects.js` +- `tuple_iter_`/`str_iter_` etc combined into `src/simple_iterators.js` +- `dictviews` like `dict_items` etc moved to `src/dictviews.js` +- `number.js` removed +- `numtype.js` removed +- `seqtype.js` removed +- `Sk.builtin.check*` moved to `src/check.js` +- `mp$subscript` should not be called by a js object (see changes in `random.js`) + + +**call signatures of builtins** +- `new` is required for (almost) all builtin types + - 3 exceptions - `Sk.builtin.bool`, `Sk.builtin.none`, `Sk.builtin.NotImplemented` + - These 3 will always return their respective constant(s) and are thus not required to be used as constructors. +- Restricted parameters for directly accessing a constructor of an `Sk.builtin` type + + + +| type | params | notes | +|---|---|---| +| `Sk.builtin.int_` | `{number| JSBI (bigint)| string| undefined}` | | +| `Sk.builtin.float_` | `{number}` | | +| `Sk.builtin.complex` | `{number, number}` | | +| `Sk.builtin.list` | `{Array=}` | | +| `Sk.builtin.tuple` | `{Array=}` | | +| `Sk.builtin.set` | `{Array=}` | | +| `Sk.builtin.dict` | `{Array=}` | key/value pairs - only pyObjects | +| `Sk.builtin.str` | `{*}` | can be used to convert a pyObject| +| `Sk.builtin.bool` | `{*}` | can be used to convert a pyObject| + + +**Major changes** +- All type objects are now callable using their respective `tp$call` methods inherited from `Sk.builtin.type` +- All native type objects will require a `tp$new` and `tp$init` method (maybe inherited by `Sk.builtin.object`) +- All type objects are javascript instances of `Sk.builtin.type` +- All single inherited objects follow javascript inheritance +- All native type objects now have the following and replaces the use of `Sk.builtin.function` for all dunder function/methods. + - `wrapper_descriptors` aka `slot_wrappers` + - `method_descriptors` + - `classmethod_descriptors` + - `getset_descriptors` aka `attributes`/`member_descriptors` +- `Sk.builtin.sk_method` is an alternative to `Sk.builtin.function` and is used by the above `descriptor` types +- mangled names are never passed to the user but instead are an attribute on `Sk.builtin.str` instances as `$mangled` +- `mappingproxy` added +- `$d` removed on all type objects. +- `attributes` of a type object now only appear on the `prototype`. Previously these appeared on both the `type` object and the `prototype` + + + +**Additions** +- `dict`, `tuple` are suspendable +- `classmethod`, `property`, `staticmethod` have native skulpt implementations +- `super` can now be unbound +- `Sk.builtin.func` objects gain a `qualname` in compile code +- API for building native types + - `Sk.abstr.buildNativeClass` +- `range_iterator` class added +- `reverse` iterators added for `list`, `dict_views`, `range` +- `sk$asarray` used by types to convert to a javascript array. + + +**`Sk.abstr.`** +- `buildNativeClass` +- `buildIteratorClass` +- `setUpBuiltinMro` +- `setUpMethods` +- `setUpGetSets` +- `setUpSlots` +- `setUpClassMethod` +- `setUpBaseInheritance` +- `setUpModuleMethod` +- `checkNoKwargs` +- `checkNoArgs` +- `checkOneArg` +- `checkArgsLen` +- `copyKeywordsToNamedArgs` + + +**`Sk.generic.`** +- `getAttr` +- `setAttr` +- `selfIter` +- `iterator` +- `new` +- `newMethodDef` +- `iterNextWithArray` +- `iterNextWithArrayCheckSize` +- `iterLengthHintWithArrayMethodDef` +- `iterReverseLengthHintMethodDef` +- `getSetDict` + +**`Sk.misceval.`** +- `asIndexOrThrow` +- `arrayFromIterable` - optional canSuspend implementation that returns an array from a python iterator + +**`slotdefs.js`** +- contains all the information about mapping slots to dunders and vice versa. \ No newline at end of file diff --git a/doc/ReferenceManual.rst b/doc/ReferenceManual.rst index f2c78cf02f..8d98548698 100644 --- a/doc/ReferenceManual.rst +++ b/doc/ReferenceManual.rst @@ -161,7 +161,7 @@ Misc Slot("__new__", "tp$new", "new"), Slot("__init__", "tp$init", "init"), Slot("__str__", "tp$print", "print"), - Slot("__repr__", "tp$repr", "repr", + Slot("__repr__", "$r", "repr", opcode="UNARY_CONVERT"), Slot("__hash__", "tp$hash", "hash"), @@ -244,13 +244,13 @@ Misc opcode="UNARY_NEGATIVE"), Slot("__pos__", "nb$positive", "unary", opcode="UNARY_POSITIVE"), - Slot("__abs__", "nb$absolute", "unary"), - Slot("__nonzero__", "nb$nonzero", "inquiry"), # inverse of UNARY_NOT opcode + Slot("__abs__", "nb$abs", "unary"), + Slot("__nonzero__", "nb$bool", "inquiry"), # inverse of UNARY_NOT opcode Slot("__invert__", "nb$invert", "unary", opcode="UNARY_INVERT"), Slot("__coerce__", "nb$coerce", "coercion"), # not needed Slot("__int__", "nb$int", "unary"), # expects exact int as return - Slot("__long__", "nb$long", "unary"), # expects exact long as return + Slot("__long__", "nb$lng", "unary"), # expects exact long as return Slot("__float__", "nb$float", "unary"), # expects exact float as return Slot("__oct__", "nb$oct", "unary"), Slot("__hex__", "nb$hex", "unary"), diff --git a/jsdoc.json b/jsdoc.json index b1ac1b32bb..5448e5130c 100644 --- a/jsdoc.json +++ b/jsdoc.json @@ -1,19 +1,48 @@ { "tags": { "allowUnknownTags": true, - "dictionaries": ["jsdoc","closure"] + "dictionaries": ["jsdoc", "closure"] }, "source": { - "include": ["src/misceval.js", "src/abstract.js", "src/function.js", "src/ffi.js", "src/import.js", - "src/int.js", "src/float.js", "src/bool.js", "src/constants.js", "src/object.js", "src/seqtype.js", "src/numtype.js"] + "include": [ + "src/generic.js", + "src/misceval.js", + "src/abstract.js", + "src/ffi.js", + "src/slotdefs.js", + "src/import.js", + "src/function.js", + "src/complex.js", + "src/check.js", + "src/type.js", + "src/int.js", + "src/set.js", + "src/str.js", + "src/dict.js", + "src/tuple.js", + "src/iteratorobjects.js", + "src/simple_iterators.js", + "src/slice.js", + "src/float.js", + "src/range.js", + "src/bool.js", + "src/constants.js", + "src/object.js" + ] }, "plugins": ["plugins/markdown"], + "markdown": { + "hardwrap": true + }, "templates": { "cleverLinks": false, - "monospaceLinks": false + "monospaceLinks": false, + "default": { + "useLongnameInNav": true + } }, "opts": { "encoding": "utf8", // same as -e utf8 "destination": "./doc/ProgMan" // same as -d ./out/ } -} \ No newline at end of file +} diff --git a/package.json b/package.json index b187e32d7b..886f9f5efe 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "watch": "webpack --watch --mode development", "test": "node test/testwrapper.js && node test/testunit.js --python3", "start": "node support/run/runfile.js", + "precompile": "node support/precompile/precompile.js", "profile": "node --prof --no-logfile-per-isolate --log-internal-timer-events support/run/runfile.js -o", "postprofile": "node --prof-process v8.log", "standaloneparser": "webpack src/standalone_parser.js -o dist/python_parser.js" @@ -44,33 +45,35 @@ "license": "MIT", "private": true, "devDependencies": { - "acorn": "^6.1.1", - "babel-minify": "^0.5.0", + "acorn": "^6.4.1", + "babel-minify": "^0.5.1", "chalk": "^2.4.2", "clean-webpack-plugin": "^2.0.2", - "closure-webpack-plugin": "^2.0.1", - "commander": "^2.20.0", + "closure-webpack-plugin": "^2.3.0", + "commander": "^2.20.3", "compression-webpack-plugin": "^2.0.0", - "copy-webpack-plugin": "^5.0.3", - "ejs": "^2.6.1", + "copy-webpack-plugin": "^5.1.1", + "ejs": "^2.7.4", "eslint": "^5.16.0", - "eslint-loader": "^2.1.2", + "eslint-loader": "^2.2.1", "express": "^4.17.0", - "git-revision-webpack-plugin": "^3.0.3", + "git-revision-webpack-plugin": "^3.0.6", "google-closure-compiler": "^20190415.0.0", - "js-beautify": "^1.10.0", - "jsdoc": "~3.5.5", + "js-beautify": "^1.11.0", + "jsdoc": "^3.6.3", "micro-strptime": "^0.2.3", - "open": "^6.3.0", - "readline-sync": "^1.4.9", + "open": "^6.4.0", + "readline-sync": "^1.4.10", "setimmediate": "^1.0.5", - "shelljs": "^0.8.3", + "shelljs": "^0.8.4", "strftime": "^0.10.0", - "webpack": "^4.35.2", - "webpack-cli": "^3.3.5" + "webpack": "^4.43.0", + "webpack-cli": "^3.3.12" }, "engines": { - "node": ">=8.10" + "node": ">=10.4" }, - "dependencies": {} + "dependencies": { + "jsbi": "^3.1.3" + } } diff --git a/repl/repl.js b/repl/repl.js index 2b23c99559..04eb730692 100644 --- a/repl/repl.js +++ b/repl/repl.js @@ -1,4 +1,6 @@ -const reqskulpt = require('../support/run/require-skulpt').requireSkulpt; +const reqskulpt = require("../support/run/require-skulpt").requireSkulpt; +const program = require("commander"); +const chalk = require("chalk"); // Import Skulpt var skulpt = reqskulpt(false); @@ -6,32 +8,46 @@ if (skulpt === null) { process.exit(1); } -var readlineSync = require('readline-sync'); -var fs = require('fs'); +var readlineSync = require("readline-sync"); +var fs = require("fs"); var readline = function () { return readlineSync.question("", { keepWhitespace: true }); }; +program.parse(process.argv); + +if (program.args.length != 1) { + console.log(chalk.red("error: must specify python version (py2/py3)")); + process.exit(1); +} + +var py3; +if (program.args[0] == "py2") { + py3 = false; +} else if (program.args[0] == "py3") { + py3 = true; +} else { + console.log(chalk.red("error: must specify python version ('py2' or 'py3'), not '" + program.args[0] + "'")); + process.exit(1); +} + Sk.configure({ output: (args) => { process.stdout.write(args); }, read: (fname) => { return fs.readFileSync(fname, "utf8"); }, systemexit: true, retainglobals: true, - inputfun: readline + inputfun: readline, + __future__: py3 ? Sk.python3 : Sk.python2, }); -var compilableLines = [], - //finds lines starting with "print" +var //finds lines starting with "print" re = new RegExp("\\s*print"), //finds import statements importre = new RegExp("\\s*import"), - //finds multuline string constants - mls = new RegExp("'''"), //finds defining statements defre = new RegExp("def.*|class.*"), //test for empty line. - emptyline = new RegExp("^\\s*$"), comment = new RegExp("^#.*"), //a regex to check if a line is an assignment //this regex checks whether or not a line starts with @@ -39,21 +55,28 @@ var compilableLines = [], //it also checks if the identifier is a tuple. assignment = /^((\s*\(\s*(\s*((\s*((\s*[_a-zA-Z]\w*\s*)|(\s*\(\s*(\s*[_a-zA-Z]\w*\s*,)*\s*[_a-zA-Z]\w*\s*\)\s*))\s*)|(\s*\(\s*(\s*((\s*[_a-zA-Z]\w*\s*)|(\s*\(\s*(\s*[_a-zA-Z]\w*\s*,)*\s*[_a-zA-Z]\w*\s*\)\s*))\s*,)*\s*((\s*[_a-zA-Z]\w*\s*)|(\s*\(\s*(\s*[_a-zA-Z]\w*\s*,)*\s*[_a-zA-Z]\w*\s*\)\s*))\s*\)\s*))\s*,)*\s*((\s*((\s*[_a-zA-Z]\w*\s*)|(\s*\(\s*(\s*[_a-zA-Z]\w*\s*,)*\s*[_a-zA-Z]\w*\s*\)\s*))\s*)|(\s*\(\s*(\s*((\s*[_a-zA-Z]\w*\s*)|(\s*\(\s*(\s*[_a-zA-Z]\w*\s*,)*\s*[_a-zA-Z]\w*\s*\)\s*))\s*,)*\s*((\s*[_a-zA-Z]\w*\s*)|(\s*\(\s*(\s*[_a-zA-Z]\w*\s*,)*\s*[_a-zA-Z]\w*\s*\)\s*))\s*\)\s*))\s*\)\s*)|(\s*\s*(\s*((\s*((\s*[_a-zA-Z]\w*\s*)|(\s*\(\s*(\s*[_a-zA-Z]\w*\s*,)*\s*[_a-zA-Z]\w*\s*\)\s*))\s*)|(\s*\(\s*(\s*((\s*[_a-zA-Z]\w*\s*)|(\s*\(\s*(\s*[_a-zA-Z]\w*\s*,)*\s*[_a-zA-Z]\w*\s*\)\s*))\s*,)*\s*((\s*[_a-zA-Z]\w*\s*)|(\s*\(\s*(\s*[_a-zA-Z]\w*\s*,)*\s*[_a-zA-Z]\w*\s*\)\s*))\s*\)\s*))\s*,)*\s*((\s*((\s*[_a-zA-Z]\w*\s*)|(\s*\(\s*(\s*[_a-zA-Z]\w*\s*,)*\s*[_a-zA-Z]\w*\s*\)\s*))\s*)|(\s*\(\s*(\s*((\s*[_a-zA-Z]\w*\s*)|(\s*\(\s*(\s*[_a-zA-Z]\w*\s*,)*\s*[_a-zA-Z]\w*\s*\)\s*))\s*,)*\s*((\s*[_a-zA-Z]\w*\s*)|(\s*\(\s*(\s*[_a-zA-Z]\w*\s*,)*\s*[_a-zA-Z]\w*\s*\)\s*))\s*\)\s*))\s*\s*))([+-/*%&\^\|]?|[/<>*]{2})=/, lines = [], - origLines; - -console.log("Python 2.6(ish) (skulpt, " + new Date() + ")"); + origLines, + printevaluationresult; + +if (Sk.__future__.python3) { + console.log("Python 3.7(ish) (skulpt, " + new Date() + ")"); + printevaluationresult = "if not evaluationresult == None: print(repr(evaluationresult))" +} else { + console.log("Python 2.7(ish) (skulpt, " + new Date() + ")"); + printevaluationresult = "if not evaluationresult == None: print repr(evaluationresult)" +} console.log("[node: " + process.version + "] on a system"); console.log('Don\'t type "help", "copyright", "credits" or "license" unless you\'ve assigned something to them'); function isBalanced(lines) { - 'use strict'; + "use strict"; var depth = 0, mlsopened = false, l; for (l = 0; l < lines.length; l = l + 1) { if (lines[l] !== undefined) { - if (lines[l].match(/'''/) !== null && lines[l].match(/'''/).length === 1) { + if (lines[l].match(/'''|"""/) !== null && lines[l].match(/'''|"""/).length === 1) { mlsopened = !mlsopened; } if (!mlsopened && lines[l].substr(lines[l].length - 1) === ":") { @@ -69,7 +92,7 @@ function isBalanced(lines) { //Loop while (true) { - process.stdout.write(isBalanced(lines) ? '>>> ' : '... '); + process.stdout.write(isBalanced(lines) ? ">>> " : "... "); //Read var l = readline(); @@ -93,7 +116,7 @@ while (true) { //evaluate it if nessecary lines.push("evaluationresult = " + lines.pop()); //print the result if not None - lines.push("if not evaluationresult == None: print repr(evaluationresult)"); + lines.push(printevaluationresult); } } } @@ -101,10 +124,9 @@ while (true) { try { //Evaluate if (!lines || /^\s*$/.test(lines)) { - continue - } - else { - Sk.importMainWithBody("repl", false, lines.join('\n')); + continue; + } else { + Sk.importMainWithBody("repl", false, lines.join("\n")); } } catch (err) { if (err instanceof Sk.builtin.SystemExit) { @@ -123,7 +145,7 @@ while (true) { //Don't add the last statement to the accumulated code console.log(origLines.map(function (str) { return ++line + (index === line ? ">" : " ") + ": " + str; - }).join('\n')); + }).join("\n")); } finally { lines = []; } diff --git a/src/abstract.js b/src/abstract.js index 4f23daa270..34bff274bf 100644 --- a/src/abstract.js +++ b/src/abstract.js @@ -1,26 +1,37 @@ /** * @namespace Sk.abstr * + * @description + * A collection of functions that can be used to interact with Skulpt Python Objects */ Sk.abstr = {}; -// -// Number -// +/**@typedef {Sk.builtin.object}*/var pyObject; +/** @typedef {Sk.builtin.type|Function}*/var typeObject; -Sk.abstr.typeName = function (v) { - var vtypename; - if (v.tp$name !== undefined) { - vtypename = v.tp$name; + +/** + * @function + * @description + * Typically used withing error messages + * + * @example + * throw new Sk.builtin.TypeError("expected an 'int' (got '" + Sk.abstr.typeName(i) + "'"); + * + * @param {*} obj + * @returns {string} - returns the typeName of any pyObject or `` if a JS object was passed + */ +Sk.abstr.typeName = function (obj) { + if (obj != null && obj.tp$name !== undefined) { + return obj.tp$name; } else { - vtypename = ""; + return ""; } - return vtypename; }; Sk.abstr.binop_type_error = function (v, w, name) { - var vtypename = Sk.abstr.typeName(v), - wtypename = Sk.abstr.typeName(w); + const vtypename = Sk.abstr.typeName(v); + const wtypename = Sk.abstr.typeName(w); throw new Sk.builtin.TypeError("unsupported operand type(s) for " + name + ": '" + vtypename + "' and '" + wtypename + "'"); }; @@ -28,154 +39,145 @@ Sk.abstr.binop_type_error = function (v, w, name) { Sk.abstr.unop_type_error = function (v, name) { var vtypename = Sk.abstr.typeName(v), uop = { - "UAdd" : "+", - "USub" : "-", - "Invert": "~" + UAdd: "+", + USub: "-", + Invert: "~", }[name]; throw new Sk.builtin.TypeError("bad operand type for unary " + uop + ": '" + vtypename + "'"); }; /** - * lookup and return the LHS object slot function method. This coudl be either a builtin slot function or a dunder method defined by the user. + * lookup and return the LHS object slot function method. This could be either a builtin slot function or a dunder method defined by the user. + * * @param obj * @param name - * @returns {Object|null|undefined} + * + * @returns {Function|undefined} + * * @private */ Sk.abstr.boNameToSlotFuncLhs_ = function (obj, name) { - if (obj === null) { - return undefined; - } - switch (name) { case "Add": - return obj.nb$add ? obj.nb$add : obj["__add__"]; + return obj.nb$add; case "Sub": - return obj.nb$subtract ? obj.nb$subtract : obj["__sub__"]; + return obj.nb$subtract; case "Mult": - return obj.nb$multiply ? obj.nb$multiply : obj["__mul__"]; + return obj.nb$multiply; case "MatMult": if (Sk.__future__.python3) { - return obj.tp$matmul ? obj.tp$matmul : obj["__matmul__"]; + return obj.nb$matrix_multiply; } case "Div": - return obj.nb$divide ? obj.nb$divide : obj["__div__"]; + return obj.nb$divide; case "FloorDiv": - return obj.nb$floor_divide ? obj.nb$floor_divide : obj["__floordiv__"]; + return obj.nb$floor_divide; case "Mod": - return obj.nb$remainder ? obj.nb$remainder : obj["__mod__"]; + return obj.nb$remainder; case "DivMod": - return obj.nb$divmod ? obj.nb$divmod : obj["__divmod__"]; + return obj.nb$divmod; case "Pow": - return obj.nb$power ? obj.nb$power : obj["__pow__"]; + return obj.nb$power; case "LShift": - return obj.nb$lshift ? obj.nb$lshift : obj["__lshift__"]; + return obj.nb$lshift; case "RShift": - return obj.nb$rshift ? obj.nb$rshift : obj["__rshift__"]; + return obj.nb$rshift; case "BitAnd": - return obj.nb$and ? obj.nb$and : obj["__and__"]; + return obj.nb$and; case "BitXor": - return obj.nb$xor ? obj.nb$xor : obj["__xor__"]; + return obj.nb$xor; case "BitOr": - return obj.nb$or ? obj.nb$or : obj["__or__"]; + return obj.nb$or; } }; - +/**@suppress {checkTypes} */ Sk.abstr.boNameToSlotFuncRhs_ = function (obj, name) { - if (obj === null) { - return undefined; - } - switch (name) { case "Add": - return obj.nb$reflected_add ? obj.nb$reflected_add : obj["__radd__"]; + return obj.nb$reflected_add; case "Sub": - return obj.nb$reflected_subtract ? obj.nb$reflected_subtract : obj["__rsub__"]; + return obj.nb$reflected_subtract; case "Mult": - return obj.nb$reflected_multiply ? obj.nb$reflected_multiply : obj["__rmul__"]; + return obj.nb$reflected_multiply; case "MatMult": if (Sk.__future__.python3) { - return obj.tp$reflected_matmul ? obj.tp$reflected_matmul : obj["__rmatmul__"]; + return obj.nb$reflected_matrix_multiply; } case "Div": - return obj.nb$reflected_divide ? obj.nb$reflected_divide : obj["__rdiv__"]; + return obj.nb$reflected_divide; case "FloorDiv": - return obj.nb$reflected_floor_divide ? obj.nb$reflected_floor_divide : obj["__rfloordiv__"]; + return obj.nb$reflected_floor_divide; case "Mod": - return obj.nb$reflected_remainder ? obj.nb$reflected_remainder : obj["__rmod__"]; + return obj.nb$reflected_remainder; case "DivMod": - return obj.nb$reflected_divmod ? obj.nb$reflected_divmod : obj["__rdivmod__"]; + return obj.nb$reflected_divmod; case "Pow": - return obj.nb$reflected_power ? obj.nb$reflected_power : obj["__rpow__"]; + return obj.nb$reflected_power; case "LShift": - return obj.nb$reflected_lshift ? obj.nb$reflected_lshift : obj["__rlshift__"]; + return obj.nb$reflected_lshift; case "RShift": - return obj.nb$reflected_rshift ? obj.nb$reflected_rshift : obj["__rrshift__"]; + return obj.nb$reflected_rshift; case "BitAnd": - return obj.nb$reflected_and ? obj.nb$reflected_and : obj["__rand__"]; + return obj.nb$reflected_and; case "BitXor": - return obj.nb$reflected_xor ? obj.nb$reflected_xor : obj["__rxor__"]; + return obj.nb$reflected_xor; case "BitOr": - return obj.nb$reflected_or ? obj.nb$reflected_or : obj["__ror__"]; + return obj.nb$reflected_or; } }; - +/**@suppress {checkTypes} */ Sk.abstr.iboNameToSlotFunc_ = function (obj, name) { switch (name) { case "Add": - return obj.nb$inplace_add ? obj.nb$inplace_add : obj["__iadd__"]; + return obj.nb$inplace_add; case "Sub": - return obj.nb$inplace_subtract ? obj.nb$inplace_subtract : obj["__isub__"]; + return obj.nb$inplace_subtract; case "Mult": - return obj.nb$inplace_multiply ? obj.nb$inplace_multiply : obj["__imul__"]; + return obj.nb$inplace_multiply; case "MatMult": if (Sk.__future__.python3) { - return obj.tp$inplace_matmul ? obj.tp$inplace_matmul : obj["__imatmul__"]; + return obj.nb$inplace_matrix_multiply; } case "Div": - return obj.nb$inplace_divide ? obj.nb$inplace_divide : obj["__idiv__"]; + return obj.nb$inplace_divide; case "FloorDiv": - return obj.nb$inplace_floor_divide ? obj.nb$inplace_floor_divide : obj["__ifloordiv__"]; + return obj.nb$inplace_floor_divide; case "Mod": return obj.nb$inplace_remainder; case "Pow": return obj.nb$inplace_power; case "LShift": - return obj.nb$inplace_lshift ? obj.nb$inplace_lshift : obj["__ilshift__"]; + return obj.nb$inplace_lshift; case "RShift": - return obj.nb$inplace_rshift ? obj.nb$inplace_rshift : obj["__irshift__"]; + return obj.nb$inplace_rshift; case "BitAnd": return obj.nb$inplace_and; case "BitOr": return obj.nb$inplace_or; case "BitXor": - return obj.nb$inplace_xor ? obj.nb$inplace_xor : obj["__ixor__"]; + return obj.nb$inplace_xor; } }; + Sk.abstr.uoNameToSlotFunc_ = function (obj, name) { - if (obj === null) { - return undefined; - } switch (name) { case "USub": - return obj.nb$negative ? obj.nb$negative : obj["__neg__"]; + return obj.nb$negative; case "UAdd": - return obj.nb$positive ? obj.nb$positive : obj["__pos__"]; + return obj.nb$positive; case "Invert": - return obj.nb$invert ? obj.nb$invert : obj["__invert__"]; + return obj.nb$invert; } }; Sk.abstr.binary_op_ = function (v, w, opname) { - var wop; - var ret; - var vop; - // All Python inheritance is now enforced with Javascript inheritance // (see Sk.abstr.setUpInheritance). This checks if w's type is a strict // subclass of v's type - var w_is_subclass = w.constructor.prototype instanceof v.constructor; + const w_type = w.constructor; + const v_type = v.constructor; + const w_is_subclass = w_type !== v_type && w_type.sk$basetype === undefined && w instanceof v_type; // From the Python 2.7 docs: // @@ -186,28 +188,22 @@ Sk.abstr.binary_op_ = function (v, w, opname) { // // -- https://docs.python.org/2/reference/datamodel.html#index-92 + let wop; + let ret; if (w_is_subclass) { wop = Sk.abstr.boNameToSlotFuncRhs_(w, opname); if (wop !== undefined) { - if (wop.call) { - ret = wop.call(w, v); - } else { - ret = Sk.misceval.callsimArray(wop, [w, v]); - } - if (ret !== undefined && ret !== Sk.builtin.NotImplemented.NotImplemented$) { + ret = wop.call(w, v); + if (ret !== Sk.builtin.NotImplemented.NotImplemented$) { return ret; } } } - vop = Sk.abstr.boNameToSlotFuncLhs_(v, opname); + const vop = Sk.abstr.boNameToSlotFuncLhs_(v, opname); if (vop !== undefined) { - if (vop.call) { - ret = vop.call(v, w); - } else { - ret = Sk.misceval.callsimArray(vop, [v, w]); - } - if (ret !== undefined && ret !== Sk.builtin.NotImplemented.NotImplemented$) { + ret = vop.call(v, w); + if (ret !== Sk.builtin.NotImplemented.NotImplemented$) { return ret; } } @@ -215,12 +211,8 @@ Sk.abstr.binary_op_ = function (v, w, opname) { if (!w_is_subclass) { wop = Sk.abstr.boNameToSlotFuncRhs_(w, opname); if (wop !== undefined) { - if (wop.call) { - ret = wop.call(w, v); - } else { - ret = Sk.misceval.callsimArray(wop, [w, v]); - } - if (ret !== undefined && ret !== Sk.builtin.NotImplemented.NotImplemented$) { + ret = wop.call(w, v); + if (ret !== Sk.builtin.NotImplemented.NotImplemented$) { return ret; } } @@ -229,242 +221,75 @@ Sk.abstr.binary_op_ = function (v, w, opname) { }; Sk.abstr.binary_iop_ = function (v, w, opname) { - var wop; - var ret; - var vop = Sk.abstr.iboNameToSlotFunc_(v, opname); + const vop = Sk.abstr.iboNameToSlotFunc_(v, opname); if (vop !== undefined) { - if (vop.call) { - ret = vop.call(v, w); - } else { // assume that vop is an __xxx__ type method - ret = Sk.misceval.callsimArray(vop, [v, w]); - } - if (ret !== undefined && ret !== Sk.builtin.NotImplemented.NotImplemented$) { + const ret = vop.call(v, w); + if (ret !== Sk.builtin.NotImplemented.NotImplemented$) { return ret; } } // If there wasn't an in-place operation, fall back to the binop return Sk.abstr.binary_op_(v, w, opname); }; + Sk.abstr.unary_op_ = function (v, opname) { - var ret; - var vop = Sk.abstr.uoNameToSlotFunc_(v, opname); + const vop = Sk.abstr.uoNameToSlotFunc_(v, opname); if (vop !== undefined) { - if (vop.call) { - ret = vop.call(v); - } else { // assume that vop is an __xxx__ type method - ret = Sk.misceval.callsimArray(vop, [v]); // added to be like not-in-place... is this okay? - } - if (ret !== undefined) { - return ret; - } + return vop.call(v); } Sk.abstr.unop_type_error(v, opname); }; -// -// handle upconverting a/b from number to long if op causes too big/small a -// result, or if either of the ops are already longs -Sk.abstr.numOpAndPromote = function (a, b, opfn) { - var tmp; - var ans; - if (a === null || b === null) { - return undefined; - } - - if (typeof a === "number" && typeof b === "number") { - ans = opfn(a, b); - // todo; handle float Removed RNL (bugs in lng, and it should be a question of precision, not magnitude -- this was just wrong) - if ((ans > Sk.builtin.int_.threshold$ || ans < -Sk.builtin.int_.threshold$) && Math.floor(ans) === ans) { - return [Sk.builtin.lng.fromInt$(a), Sk.builtin.lng.fromInt$(b)]; - } else { - return ans; - } - } else if (a === undefined || b === undefined) { - throw new Sk.builtin.NameError("Undefined variable in expression"); - } - - if (a.constructor === Sk.builtin.lng) { - return [a, b]; - } else if ((a.constructor === Sk.builtin.int_ || - a.constructor === Sk.builtin.float_) && - b.constructor === Sk.builtin.complex) { - // special case of upconverting nmber and complex - // can we use here the Sk.builtin.checkComplex() method? - tmp = new Sk.builtin.complex(a); - return [tmp, b]; - } else if (a.constructor === Sk.builtin.int_ || - a.constructor === Sk.builtin.float_) { - return [a, b]; - } else if (typeof a === "number") { - tmp = Sk.builtin.assk$(a); - return [tmp, b]; - } else { - return undefined; - } -}; - -Sk.abstr.boNumPromote_ = { - "Add" : function (a, b) { - return a + b; - }, - "Sub" : function (a, b) { - return a - b; - }, - "Mult" : function (a, b) { - return a * b; - }, - "Mod" : function (a, b) { - var m; - if (b === 0) { - throw new Sk.builtin.ZeroDivisionError("division or modulo by zero"); - } - m = a % b; - return ((m * b) < 0 ? (m + b) : m); - }, - "Div" : function (a, b) { - if (b === 0) { - throw new Sk.builtin.ZeroDivisionError("division or modulo by zero"); - } else { - return a / b; - } - }, - "FloorDiv": function (a, b) { - if (b === 0) { - throw new Sk.builtin.ZeroDivisionError("division or modulo by zero"); - } else { - return Math.floor(a / b); - } // todo; wrong? neg? - }, - "Pow" : Math.pow, - "BitAnd" : function (a, b) { - var m = a & b; - if (m < 0) { - m = m + 4294967296; // convert back to unsigned - } - return m; - }, - "BitOr" : function (a, b) { - var m = a | b; - if (m < 0) { - m = m + 4294967296; // convert back to unsigned - } - return m; - }, - "BitXor" : function (a, b) { - var m = a ^ b; - if (m < 0) { - m = m + 4294967296; // convert back to unsigned - } - return m; - }, - "LShift" : function (a, b) { - var m; - if (b < 0) { - throw new Sk.builtin.ValueError("negative shift count"); - } - m = a << b; - if (m > a) { - return m; - } else { - // Fail, this will get recomputed with longs - return a * Math.pow(2, b); - } - }, - "RShift" : function (a, b) { - var m; - if (b < 0) { - throw new Sk.builtin.ValueError("negative shift count"); - } - m = a >> b; - if ((a > 0) && (m < 0)) { - // fix incorrect sign extension - m = m & (Math.pow(2, 32 - b) - 1); - } - return m; - } -}; - +/** + * @function + * @description + * Perform a binary operation with any pyObjects that support the operation + * @param {pyObject} v + * @param {pyObject} w + * @param {string} op - `Add`, `Sub`, `Mult`, `Divide`, ... + * + * @throws {Sk.builtin.TypeError} + */ Sk.abstr.numberBinOp = function (v, w, op) { - var tmp; - var numPromoteFunc = Sk.abstr.boNumPromote_[op]; - if (numPromoteFunc !== undefined) { - tmp = Sk.abstr.numOpAndPromote(v, w, numPromoteFunc); - if (typeof tmp === "number") { - return tmp; - } else if (tmp !== undefined && tmp.constructor === Sk.builtin.int_) { - return tmp; - } else if (tmp !== undefined && tmp.constructor === Sk.builtin.float_) { - return tmp; - } else if (tmp !== undefined && tmp.constructor === Sk.builtin.lng) { - return tmp; - } else if (tmp !== undefined) { - v = tmp[0]; - w = tmp[1]; - } - } - return Sk.abstr.binary_op_(v, w, op); }; Sk.exportSymbol("Sk.abstr.numberBinOp", Sk.abstr.numberBinOp); +/** + * @function + * @description + * Perform an inplace operation with any pyObjects that support the operation + * @param {pyObject} v + * @param {pyObject} w + * @param {string} op - `Add`, `Sub`, `Mult`, `Divide`, ... + * + * @throws {Sk.builtin.TypeError} + */ Sk.abstr.numberInplaceBinOp = function (v, w, op) { - var tmp; - var numPromoteFunc = Sk.abstr.boNumPromote_[op]; - if (numPromoteFunc !== undefined) { - tmp = Sk.abstr.numOpAndPromote(v, w, numPromoteFunc); - if (typeof tmp === "number") { - return tmp; - } else if (tmp !== undefined && tmp.constructor === Sk.builtin.int_) { - return tmp; - } else if (tmp !== undefined && tmp.constructor === Sk.builtin.float_) { - return tmp; - } else if (tmp !== undefined && tmp.constructor === Sk.builtin.lng) { - return tmp; - } else if (tmp !== undefined) { - v = tmp[0]; - w = tmp[1]; - } - } - return Sk.abstr.binary_iop_(v, w, op); }; Sk.exportSymbol("Sk.abstr.numberInplaceBinOp", Sk.abstr.numberInplaceBinOp); +/** + * @function + * @description + * Perform a unary operation with any pyObjects that support the operation + * @param {pyObject} v + * @param {string} op - `UAdd`, `USub` + * + * @throws {Sk.builtin.TypeError} + */ Sk.abstr.numberUnaryOp = function (v, op) { - var value; if (op === "Not") { return Sk.misceval.isTrue(v) ? Sk.builtin.bool.false$ : Sk.builtin.bool.true$; - } else if (v instanceof Sk.builtin.bool) { - value = Sk.builtin.asnum$(v); - if (op === "USub") { - return new Sk.builtin.int_(-value); - } - if (op === "UAdd") { - return new Sk.builtin.int_(value); - } - if (op === "Invert") { - return new Sk.builtin.int_(~value); - } - } else { - if (op === "USub" && v.nb$negative) { - return v.nb$negative(); - } - if (op === "UAdd" && v.nb$positive) { - return v.nb$positive(); - } - if (op === "Invert" && v.nb$invert) { - return v.nb$invert(); - } } - return Sk.abstr.unary_op_(v, op); }; Sk.exportSymbol("Sk.abstr.numberUnaryOp", Sk.abstr.numberUnaryOp); -// -// Sequence -// - +/** + * @deprecated + */ Sk.abstr.fixSeqIndex_ = function (seq, i) { i = Sk.builtin.asnum$(i); if (i < 0 && seq.sq$length) { @@ -474,207 +299,116 @@ Sk.abstr.fixSeqIndex_ = function (seq, i) { }; /** - * @param {*} seq - * @param {*} ob + * @param {pyObject} seq + * @param {pyObject} ob * @param {boolean=} canSuspend + * */ Sk.abstr.sequenceContains = function (seq, ob, canSuspend) { - var seqtypename; - var special; - var r; - if (seq.sq$contains) { - return seq.sq$contains(ob); - } - - /** - * Look for special method and call it, we have to distinguish between built-ins and - * python objects - */ - special = Sk.abstr.lookupSpecial(seq, Sk.builtin.str.$contains); - if (special != null) { - // method on builtin, provide this arg - return Sk.misceval.isTrue(Sk.misceval.callsimArray(special, [seq, ob])); - } - - if (!Sk.builtin.checkIterable(seq)) { - seqtypename = Sk.abstr.typeName(seq); - throw new Sk.builtin.TypeError("argument of type '" + seqtypename + "' is not iterable"); + return seq.sq$contains(ob, canSuspend); } - - r = Sk.misceval.iterFor(Sk.abstr.iter(seq), function(i) { - if (Sk.misceval.richCompareBool(i, ob, "Eq")) { - return new Sk.misceval.Break(true); - } else { - return false; - } - }, false); - + const r = Sk.misceval.iterFor( + Sk.abstr.iter(seq), + function (i) { + if (Sk.misceval.richCompareBool(i, ob, "Eq")) { + return new Sk.misceval.Break(true); + } else { + return false; + } + }, + false + ); return canSuspend ? r : Sk.misceval.retryOptionalSuspensionOrThrow(r); }; Sk.abstr.sequenceConcat = function (seq1, seq2) { - var seq1typename; if (seq1.sq$concat) { return seq1.sq$concat(seq2); } - seq1typename = Sk.abstr.typeName(seq1); - throw new Sk.builtin.TypeError("'" + seq1typename + "' object can't be concatenated"); + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(seq1) + "' object can't be concatenated"); }; +/** + * @param {pyObject} seq + * @param {pyObject} ob + */ Sk.abstr.sequenceGetIndexOf = function (seq, ob) { - var seqtypename; - var i, it; - var index; if (seq.index) { return Sk.misceval.callsimArray(seq.index, [seq, ob]); } - if (Sk.builtin.checkIterable(seq)) { - index = 0; - for (it = Sk.abstr.iter(seq), i = it.tp$iternext(); - i !== undefined; i = it.tp$iternext()) { - if (Sk.misceval.richCompareBool(ob, i, "Eq")) { - return new Sk.builtin.int_(index); - } - index += 1; + let index = 0; + for (let it = Sk.abstr.iter(seq), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { + if (Sk.misceval.richCompareBool(ob, i, "Eq")) { + return new Sk.builtin.int_(index); } - throw new Sk.builtin.ValueError("sequence.index(x): x not in sequence"); + index += 1; } - - seqtypename = Sk.abstr.typeName(seq); - throw new Sk.builtin.TypeError("argument of type '" + seqtypename + "' is not iterable"); + throw new Sk.builtin.ValueError("sequence.index(x): x not in sequence"); }; +/** + * @param {pyObject} seq + * @param {pyObject} ob + */ Sk.abstr.sequenceGetCountOf = function (seq, ob) { - var seqtypename; - var i, it; - var count; if (seq.count) { return Sk.misceval.callsimArray(seq.count, [seq, ob]); } - if (Sk.builtin.checkIterable(seq)) { - count = 0; - for (it = Sk.abstr.iter(seq), i = it.tp$iternext(); - i !== undefined; i = it.tp$iternext()) { - if (Sk.misceval.richCompareBool(ob, i, "Eq")) { - count += 1; - } + let count = 0; + for (let it = Sk.abstr.iter(seq), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { + if (Sk.misceval.richCompareBool(ob, i, "Eq")) { + count += 1; } - return new Sk.builtin.int_(count); } - - seqtypename = Sk.abstr.typeName(seq); - throw new Sk.builtin.TypeError("argument of type '" + seqtypename + "' is not iterable"); + return new Sk.builtin.int_(count); }; Sk.abstr.sequenceGetItem = function (seq, i, canSuspend) { - var seqtypename; - if (seq.mp$subscript) { - return seq.mp$subscript(i); + if (typeof i === "number") { + i = new Sk.builtin.int_(i); } - - seqtypename = Sk.abstr.typeName(seq); - throw new Sk.builtin.TypeError("'" + seqtypename + "' object is unsubscriptable"); + return Sk.abstr.objectGetItem(seq, i, canSuspend); }; Sk.abstr.sequenceSetItem = function (seq, i, x, canSuspend) { - var seqtypename; - if (seq.mp$ass_subscript) { - return seq.mp$ass_subscript(i, x); + if (typeof i === "number") { + i = new Sk.builtin.int_(i); } - - seqtypename = Sk.abstr.typeName(seq); - throw new Sk.builtin.TypeError("'" + seqtypename + "' object does not support item assignment"); + return Sk.abstr.objectSetItem(seq, i, x, canSuspend); }; Sk.abstr.sequenceDelItem = function (seq, i) { - var seqtypename; - if (seq.sq$del_item) { - i = Sk.abstr.fixSeqIndex_(seq, i); - seq.sq$del_item(i); - return; - } - - seqtypename = Sk.abstr.typeName(seq); - throw new Sk.builtin.TypeError("'" + seqtypename + "' object does not support item deletion"); -}; - -Sk.abstr.sequenceRepeat = function (f, seq, n) { - var ntypename; - var count; - n = Sk.builtin.asnum$(n); - count = Sk.misceval.asIndex(n); - if (count === undefined) { - ntypename = Sk.abstr.typeName(n); - throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '" + ntypename + "'"); - } - return f.call(seq, n); + return Sk.abstr.objectDelItem(seq, i); }; Sk.abstr.sequenceGetSlice = function (seq, i1, i2) { - var seqtypename; - if (seq.sq$slice) { - i1 = Sk.abstr.fixSeqIndex_(seq, i1); - i2 = Sk.abstr.fixSeqIndex_(seq, i2); - return seq.sq$slice(i1, i2); - } else if (seq.mp$subscript) { - return seq.mp$subscript(new Sk.builtin.slice(i1, i2)); - } - - seqtypename = Sk.abstr.typeName(seq); - throw new Sk.builtin.TypeError("'" + seqtypename + "' object is unsliceable"); + return Sk.abstr.objectGetItem(seq, new Sk.builtin.slice(i1, i2)); }; Sk.abstr.sequenceDelSlice = function (seq, i1, i2) { - var seqtypename; - if (seq.sq$del_slice) { - i1 = Sk.abstr.fixSeqIndex_(seq, i1); - i2 = Sk.abstr.fixSeqIndex_(seq, i2); - seq.sq$del_slice(i1, i2); - return; - } - - seqtypename = Sk.abstr.typeName(seq); - throw new Sk.builtin.TypeError("'" + seqtypename + "' doesn't support slice deletion"); + return Sk.abstr.objectDelItem(seq, new Sk.builtin.slice(i1, i2)); }; Sk.abstr.sequenceSetSlice = function (seq, i1, i2, x) { - var seqtypename; - if (seq.sq$ass_slice) { - i1 = Sk.abstr.fixSeqIndex_(seq, i1); - i2 = Sk.abstr.fixSeqIndex_(seq, i2); - seq.sq$ass_slice(i1, i2, x); - } else if (seq.mp$ass_subscript) { - seq.mp$ass_subscript(new Sk.builtin.slice(i1, i2), x); - } else { - seqtypename = Sk.abstr.typeName(seq); - throw new Sk.builtin.TypeError("'" + seqtypename + "' object doesn't support slice assignment"); - } + return Sk.abstr.objectSetItem(seq, new Sk.builtin.slice(i1, i2)); }; // seq - Python object to unpack // n - JavaScript number of items to unpack Sk.abstr.sequenceUnpack = function (seq, n) { - var res = []; - var it, i; - - if (!Sk.builtin.checkIterable(seq)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(seq) + "' object is not iterable"); - } - - for (it = Sk.abstr.iter(seq), i = it.tp$iternext(); - (i !== undefined) && (res.length < n); - i = it.tp$iternext()) { + const res = []; + const it = Sk.abstr.iter(seq); + let i; + for (i = it.tp$iternext(); i !== undefined && res.length < n; i = it.tp$iternext()) { res.push(i); } - if (res.length < n) { throw new Sk.builtin.ValueError("need more than " + res.length + " values to unpack"); } if (i !== undefined) { throw new Sk.builtin.ValueError("too many values to unpack"); } - // Return Javascript array of items return res; }; @@ -685,286 +419,329 @@ Sk.abstr.sequenceUnpack = function (seq, n) { // We should probably migrate that interface to using Python strings // at some point, but in the meantime we have this function to // unpack keyword dictionaries into our special format -Sk.abstr.mappingUnpackIntoKeywordArray = function(jsArray, pyMapping, pyCodeObject) { - return Sk.misceval.chain(pyMapping.tp$getattr(new Sk.builtin.str("items")), function(itemfn) { - if (!itemfn) { throw new Sk.builtin.TypeError("Object is not a mapping"); } - return Sk.misceval.callsimOrSuspend(itemfn); - }, function(items) { - return Sk.misceval.iterFor(Sk.abstr.iter(items), function(item) { - if (!item || !item.v) { throw new Sk.builtin.TypeError("Object is not a mapping; items() does not return tuples"); } - if (!(item.v[0] instanceof Sk.builtin.str)) { - throw new Sk.builtin.TypeError((pyCodeObject.tp$name ? pyCodeObject.tp$name +":" : "") + "keywords must be strings"); +Sk.abstr.mappingUnpackIntoKeywordArray = function (jsArray, pyMapping, pyCodeObject) { + return Sk.misceval.chain( + pyMapping.tp$getattr(new Sk.builtin.str("items")), + function (itemfn) { + if (!itemfn) { + throw new Sk.builtin.TypeError("Object is not a mapping"); } - jsArray.push(item.v[0].v, item.v[1]); - }); - }); + return Sk.misceval.callsimOrSuspend(itemfn); + }, + function (items) { + return Sk.misceval.iterFor(Sk.abstr.iter(items), function (item) { + if (!item || !item.v) { + throw new Sk.builtin.TypeError("Object is not a mapping; items() does not return tuples"); + } + if (!(item.v[0].ob$type === Sk.builtin.str)) { + throw new Sk.builtin.TypeError((pyCodeObject.tp$name ? pyCodeObject.tp$name + ":" : "") + "keywords must be strings"); + } + jsArray.push(item.v[0].v, item.v[1]); + }); + } + ); }; -// -// Object -// +/** + * + * @function + * @description + * A helper function used by native js functions whose call method is FastCall i.e. the args and kwargs are provided as Array objects. + * + * @param {string} func_name - used for error messages + * @param {Array} varnames - Argument names to map to. For position only arguments use null + * @param {Array} args - typically provided by the `tp$call` method + * @param {Array|undefined} kwargs - typically provided the `tp$call` method + * @param {Array=} defaults + * @throws {Sk.builtin.TypeError} + * + * @example + * // base is a possible keyword argument for int_ and x is a position only argument + * Sk.builtin.int_.prototype.tp$new = function(args, kwargs) { + * args = Sk.abstr.copyKeywordsToNamedArgs("int", [null, "base"], args, kwargs, [ + * new Sk.builtin.int_(0), + * Sk.builtin.none.none$ + * ]); + * } + */ +Sk.abstr.copyKeywordsToNamedArgs = function (func_name, varnames, args, kwargs, defaults) { + // args is an array, kwargs is an array or undefined + kwargs = kwargs || []; -Sk.abstr.objectFormat = function (obj, format_spec) { - var meth; // PyObject - var result; // PyObject + const nargs = args.length + kwargs.length / 2; + if (nargs > varnames.length) { + throw new Sk.builtin.TypeError(func_name + "() expected at most " + varnames.length + " arguments (" + nargs + " given)"); + } + if (!kwargs.length && defaults === undefined) { + return args; + } else if (nargs === varnames.length && !kwargs.length) { + return args; + } else if (nargs === 0 && varnames.length === (defaults ? defaults.length : defaults)) { + // a fast case + return defaults; + } + args = args.slice(0); //[...args]; // make a shallow copy of args - // Find the (unbound!) __format__ method (a borrowed reference) - meth = Sk.abstr.lookupSpecial(obj, Sk.builtin.str.$format); - if (meth == null) { - throw new Sk.builtin.TypeError("Type " + Sk.abstr.typeName(obj) + " doesn't define __format__"); + for (let i = 0; i < kwargs.length; i += 2) { + const name = kwargs[i]; // JS string + if (name === null) { + continue; + } + const value = kwargs[i + 1]; // Python value + const idx = varnames.indexOf(name); + + if (idx >= 0) { + if (args[idx] !== undefined) { + throw new Sk.builtin.TypeError(func_name + "() got multiple values for argument '" + name + "'"); + } + args[idx] = value; + } else { + throw new Sk.builtin.TypeError(func_name + "() got an unexpected keyword argument '" + name + "'"); + } + } + if (defaults) { + const nargs = varnames.length; + for (let i = nargs - 1; i >= 0; i--) { + if (args[i] === undefined) { + args[i] = defaults[defaults.length - 1 - (nargs - 1 - i)]; + } + } + const missing = varnames.filter((x, i) => args[i] === undefined); + if (missing.length) { + throw new Sk.builtin.TypeError(func_name + "() missing " + missing.length + " required positional arguments: " + missing.join(", ")); + } } - // And call it - result = Sk.misceval.callsimArray(meth, [obj, format_spec]); + return args; +}; +Sk.exportSymbol("Sk.abstr.copyKeywordsToNamedArgs", Sk.abstr.copyKeywordsToNamedArgs); + +/** + * @function + * @param {string} func_name + * @param {Array|undefined} kwargs + * @throws {Sk.builtin.TypeError} + */ +Sk.abstr.checkNoKwargs = function (func_name, kwargs) { + if (kwargs && kwargs.length) { + throw new Sk.builtin.TypeError(func_name + "() takes no keyword arguments"); + } +}; +Sk.exportSymbol("Sk.abstr.checkNoKwargs", Sk.abstr.checkNoKwargs); + +/** + * @function + * @param {string} func_name + * @param {Array} args + * @param {Array|undefined=} kwargs + * + * @throws {Sk.builtin.TypeError} + */ +Sk.abstr.checkNoArgs = function (func_name, args, kwargs) { + const nargs = args.length + (kwargs ? kwargs.length : 0); + if (nargs) { + throw new Sk.builtin.TypeError(func_name + "() takes no arguments (" + nargs + " given)"); + } +}; +Sk.exportSymbol("Sk.abstr.checkNoArgs", Sk.abstr.checkNoArgs); + +/** + * @function + * @param {string} func_name + * @param {Array} args + * @param {Array|undefined=} kwargs + * @throws {Sk.builtin.TypeError} + */ +Sk.abstr.checkOneArg = function (func_name, args, kwargs) { + Sk.abstr.checkNoKwargs(func_name, kwargs); + if (args.length !== 1) { + throw new Sk.builtin.TypeError(func_name + "() takes exactly one argument (" + args.length + " given)"); + } +}; +Sk.exportSymbol("Sk.abstr.checkOneArg", Sk.abstr.checkOneArg); + +/** + * @function + * @param {string} func_name + * @param {Array} args + * @param {number} minargs + * @param {number=} [maxargs=Infinity] + * @throws {Sk.builtin.TypeError} + * + */ +Sk.abstr.checkArgsLen = function (func_name, args, minargs, maxargs) { + const nargs = args.length; + let msg; + if (maxargs === undefined) { + maxargs = Infinity; + } + if (nargs < minargs || nargs > maxargs) { + if (minargs === maxargs) { + msg = func_name + "() takes exactly " + minargs + " arguments"; + } else if (nargs < minargs) { + msg = func_name + "() takes at least " + minargs + " arguments"; + } else { + msg = func_name + "() takes at most " + maxargs + " arguments"; + } + msg += " (" + nargs + " given)"; + throw new Sk.builtin.TypeError(msg); + } +}; +Sk.exportSymbol("Sk.abstr.checkArgsLen", Sk.abstr.checkArgsLen); + +Sk.abstr.objectFormat = function (obj, format_spec) { + const meth = Sk.abstr.lookupSpecial(obj, Sk.builtin.str.$format); // inherited from object so guaranteed to exist + const result = Sk.misceval.callsimArray(meth, [obj, format_spec]); if (!Sk.builtin.checkString(result)) { throw new Sk.builtin.TypeError("__format__ must return a str, not " + Sk.abstr.typeName(result)); } - return result; }; Sk.abstr.objectAdd = function (a, b) { - var btypename; - var atypename; if (a.nb$add) { return a.nb$add(b); } - - atypename = Sk.abstr.typeName(a); - btypename = Sk.abstr.typeName(b); + const atypename = Sk.abstr.typeName(a); + const btypename = Sk.abstr.typeName(b); throw new Sk.builtin.TypeError("unsupported operand type(s) for +: '" + atypename + "' and '" + btypename + "'"); }; // in Python 2.6, this behaviour seems to be defined for numbers and bools (converts bool to int) Sk.abstr.objectNegative = function (obj) { - var objtypename; - var obj_asnum = Sk.builtin.asnum$(obj); // this will also convert bool type to int - - if (obj instanceof Sk.builtin.bool) { - obj = new Sk.builtin.int_(obj_asnum); - } - if (obj.nb$negative) { return obj.nb$negative(); } - - objtypename = Sk.abstr.typeName(obj); - throw new Sk.builtin.TypeError("bad operand type for unary -: '" + objtypename + "'"); + throw new Sk.builtin.TypeError("bad operand type for unary +: '" + Sk.abstr.typeName(obj) + "'"); }; -// in Python 2.6, this behaviour seems to be defined for numbers and bools (converts bool to int) Sk.abstr.objectPositive = function (obj) { - var objtypename = Sk.abstr.typeName(obj); - var obj_asnum = Sk.builtin.asnum$(obj); // this will also convert bool type to int - - if (obj instanceof Sk.builtin.bool) { - obj = new Sk.builtin.int_(obj_asnum); - } - - if (obj.nb$negative) { + if (obj.nb$positive) { return obj.nb$positive(); } - - throw new Sk.builtin.TypeError("bad operand type for unary +: '" + objtypename + "'"); + throw new Sk.builtin.TypeError("bad operand type for unary +: '" + Sk.abstr.typeName(obj) + "'"); }; Sk.abstr.objectDelItem = function (o, key) { - var otypename; - var keytypename; - var keyValue; - if (o !== null) { - if (o.mp$del_subscript) { - o.mp$del_subscript(key); - return; - } - if (o.sq$ass_item) { - keyValue = Sk.misceval.asIndex(key); - if (keyValue === undefined) { - keytypename = Sk.abstr.typeName(key); - throw new Sk.builtin.TypeError("sequence index must be integer, not '" + keytypename + "'"); - } - Sk.abstr.sequenceDelItem(o, keyValue); - return; - } - // if o is a slice do something else... + if (o.mp$ass_subscript) { + return o.mp$ass_subscript(key); } - - otypename = Sk.abstr.typeName(o); - throw new Sk.builtin.TypeError("'" + otypename + "' object does not support item deletion"); + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(o) + "' object does not support item deletion"); }; Sk.exportSymbol("Sk.abstr.objectDelItem", Sk.abstr.objectDelItem); +/** + * + * @param {pyObject} o + * @param {pyObject} key + * @param {boolean=} canSuspend + */ Sk.abstr.objectGetItem = function (o, key, canSuspend) { - var otypename; - if (o !== null) { - if (o.tp$getitem) { - return o.tp$getitem(key, canSuspend); - } else if (o.mp$subscript) { - return o.mp$subscript(key, canSuspend); - } else if (Sk.misceval.isIndex(key) && o.sq$item) { - return Sk.abstr.sequenceGetItem(o, Sk.misceval.asIndex(key), canSuspend); - } + if (o.mp$subscript) { + return o.mp$subscript(key, canSuspend); } - - otypename = Sk.abstr.typeName(o); - throw new Sk.builtin.TypeError("'" + otypename + "' does not support indexing"); + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(o) + "' does not support indexing"); }; Sk.exportSymbol("Sk.abstr.objectGetItem", Sk.abstr.objectGetItem); +/** + * + * @param {pyObject} o + * @param {pyObject} key + * @param {pyObject=} v + * @param {boolean=} canSuspend + */ Sk.abstr.objectSetItem = function (o, key, v, canSuspend) { - var otypename; - if (o !== null) { - if (o.tp$setitem) { - return o.tp$setitem(key, v, canSuspend); - } else if (o.mp$ass_subscript) { - return o.mp$ass_subscript(key, v, canSuspend); - } else if (Sk.misceval.isIndex(key) && o.sq$ass_item) { - return Sk.abstr.sequenceSetItem(o, Sk.misceval.asIndex(key), v, canSuspend); - } + if (o.mp$ass_subscript) { + return o.mp$ass_subscript(key, v, canSuspend); } - - otypename = Sk.abstr.typeName(o); - throw new Sk.builtin.TypeError("'" + otypename + "' does not support item assignment"); + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(o) + "' does not support item assignment"); }; Sk.exportSymbol("Sk.abstr.objectSetItem", Sk.abstr.objectSetItem); - +/** + * + * @param {pyObject} obj + * @param {Sk.builtin.str} pyName + * @param {boolean=} canSuspend + */ Sk.abstr.gattr = function (obj, pyName, canSuspend) { - var ret, f; - var objname = Sk.abstr.typeName(obj); - var jsName = pyName.$jsstr(); - - if (obj === null) { - throw new Sk.builtin.AttributeError("'" + objname + "' object has no attribute '" + jsName + "'"); - } - - if (obj.tp$getattr !== undefined) { - ret = obj.tp$getattr(pyName, canSuspend); + // let the getattr and setattr's deal with reserved words - we don't want to pass a mangled pyName to tp$getattr!! + const ret = obj.tp$getattr(pyName, canSuspend); + if (ret === undefined) { + const error_name = obj.sk$type ? "type object '" + obj.prototype.tp$name + "'" : "'" + Sk.abstr.typeName(obj) + "' object"; + throw new Sk.builtin.AttributeError(error_name + " has no attribute '" + pyName.$jsstr() + "'"); + } else if (ret.$isSuspension) { + return Sk.misceval.chain(ret, function (r) { + if (r === undefined) { + const error_name = obj.sk$type ? "type object '" + obj.prototype.tp$name + "'" : "'" + Sk.abstr.typeName + "' object"; + throw new Sk.builtin.AttributeError(error_name + " has no attribute '" + pyName.$jsstr() + "'"); + } + return r; + }); + } else { + return ret; } - - ret = Sk.misceval.chain(ret, function(r) { - if (r === undefined) { - throw new Sk.builtin.AttributeError("'" + objname + "' object has no attribute '" + jsName + "'"); - } - return r; - }); - - return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret); }; Sk.exportSymbol("Sk.abstr.gattr", Sk.abstr.gattr); - Sk.abstr.sattr = function (obj, pyName, data, canSuspend) { - var objname = Sk.abstr.typeName(obj), r, setf; - var jsName = pyName.$jsstr(); - - if (obj === null) { - throw new Sk.builtin.AttributeError("'" + objname + "' object has no attribute '" + jsName + "'"); - } - - if (obj.tp$setattr !== undefined) { - return obj.tp$setattr(pyName, data, canSuspend); - } else { - throw new Sk.builtin.AttributeError("'" + objname + "' object has no attribute '" + jsName + "'"); - } + return obj.tp$setattr(pyName, data, canSuspend); }; Sk.exportSymbol("Sk.abstr.sattr", Sk.abstr.sattr); - Sk.abstr.iternext = function (it, canSuspend) { return it.tp$iternext(canSuspend); }; Sk.exportSymbol("Sk.abstr.iternext", Sk.abstr.iternext); - /** + * @function + * + * @description * Get the iterator for a Python object This iterator could be one of the following. * This is the preferred mechanism for consistently getting the correct iterator. You should * not just use tp$iter because that could lead to incorrect behavior of a user created class. * - * - tp$iter + * - `tp$iter` * - A user defined `__iter__` method * - A user defined `__getitem__` method * - * @param obj + * @param {pyObject} obj * - * @throws {Sk.builtin.TypeError} - * @returns {Object} + * @throws {Sk.builtin.TypeError} If the object passed is not iterable + * @returns {pyObject} */ - -Sk.abstr.iter = function(obj) { - var iter; - var getit; - var ret; - - /** - * Builds an iterator around classes that have a __getitem__ method. - * - * @constructor - */ - var seqIter = function (obj) { - this.idx = 0; - this.myobj = obj; - this.getitem = Sk.abstr.lookupSpecial(obj, Sk.builtin.str.$getitem); - this.tp$iternext = function () { - var ret; - try { - ret = Sk.misceval.callsimArray(this.getitem, [this.myobj, Sk.ffi.remapToPy(this.idx)]); - } catch (e) { - if (e instanceof Sk.builtin.IndexError || e instanceof Sk.builtin.StopIteration) { - return undefined; - } else { - throw e; - } - } - this.idx++; - return ret; - }; - }; - - if (obj.tp$getattr) { - iter = Sk.abstr.lookupSpecial(obj, Sk.builtin.str.$iter); - if (iter) { - ret = Sk.misceval.callsimArray(iter, [obj]); - if (ret.tp$iternext) { - return ret; - } - } - } +Sk.abstr.iter = function (obj) { if (obj.tp$iter) { - try { // catch and ignore not iterable error here. - ret = obj.tp$iter(); - if (ret.tp$iternext) { - return ret; - } - } catch (e) { } + const iter = obj.tp$iter(); + if (iter.tp$iternext) { + // only a valid iterator if there is a tp$iternext + return iter; + } + throw new Sk.builtin.TypeError("iter() returned non-iterator of type '" + Sk.abstr.typeName(iter) + "'"); } - getit = Sk.abstr.lookupSpecial(obj, Sk.builtin.str.$getitem); - if (getit) { - // create internal iterobject if __getitem__ - return new seqIter(obj); + if (obj.mp$subscript) { + return new Sk.builtin.seq_iter_(obj); } + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(obj) + "' object is not iterable"); }; Sk.exportSymbol("Sk.abstr.iter", Sk.abstr.iter); + /** - * Special method look up. First try getting the method via - * internal dict and getattr. If getattr is not present (builtins) - * try if method is defined on the object itself + * @description + * Special method look up. + * Checks whether the attribute is defined on object type's prototype + * + * @returns {undefined | Object} Return undefined if not found or the function * - * @returns {null|Object} Return null if not found or the function + * @param {pyObject} obj + * @param {Sk.builtin.str} pyName */ -Sk.abstr.lookupSpecial = function(op, pyName) { - var res; - var obtp; - if (op.ob$type) { - obtp = op.ob$type; - } else { - return null; - } - - return Sk.builtin.type.typeLookup(obtp, pyName); +Sk.abstr.lookupSpecial = function (obj, pyName) { + return obj.ob$type.$typeLookup(pyName); }; Sk.exportSymbol("Sk.abstr.lookupSpecial", Sk.abstr.lookupSpecial); @@ -974,31 +751,16 @@ Sk.exportSymbol("Sk.abstr.lookupSpecial", Sk.abstr.lookupSpecial); * @return {undefined} */ Sk.abstr.markUnhashable = function (thisClass) { - var proto = thisClass.prototype; + const proto = thisClass.prototype; proto.__hash__ = Sk.builtin.none.none$; proto.tp$hash = Sk.builtin.none.none$; }; /** - * Code taken from goog.inherits - * - * Newer versions of the closure library add a "base"attribute, - * which we don't want/need. So, this code is the remainder of - * the goog.inherits function. - */ -Sk.abstr.inherits = function (childCtor, parentCtor) { - /** @constructor */ - function tempCtor() {} - tempCtor.prototype = parentCtor.prototype; - childCtor.superClass_ = parentCtor.prototype; - childCtor.prototype = new tempCtor(); - /** @override */ - childCtor.prototype.constructor = childCtor; -}; - -/** + * @description * Set up inheritance between two Python classes. This allows only for single * inheritance -- multiple inheritance is not supported by Javascript. + * multiple inheritance is dealt with by tp$getattr implementations * * Javascript's inheritance is prototypal. This means that properties must * be defined on the superclass' prototype in order for subclasses to inherit @@ -1015,15 +777,474 @@ Sk.abstr.inherits = function (childCtor, parentCtor) { * builtins should inherit from Sk.builtin.object. * * @param {string} childName The Python name of the child (subclass). - * @param {*} child The subclass. - * @param {*} parent The superclass. - * @return {undefined} + * @param {!typeObject} child The subclass. + * @param {typeObject=} [parent=Sk.builtin.object] The base of child. + * @param {typeObject=} [metaclass=Sk.builtin.type] + * + * @returns {!typeObject} + * + */ +Sk.abstr.setUpInheritance = function (childName, child, parent, metaclass) { + metaclass = metaclass || Sk.builtin.type; + parent = parent || Sk.builtin.object; + Object.setPrototypeOf(child, metaclass.prototype); + + + child.prototype = Object.create(parent.prototype); + Object.defineProperties(child.prototype, { + constructor: {value: child, writable: true}, + ob$type: {value: child, writable: true}, + tp$name: {value: childName, writable: true}, + tp$base: {value: parent, writable: true}, + }); + + return child; +}; + +/** + * @summary + * Set up inheritance between type and object + * + * @description + * ```text + * type instanceof object => true + * object instanceof type => true + * type instanceof type => true + * object instanceof object => true + * + * type subclassof object => type.prototype instanceof object => true + * object subclassof type => object.prototype instanceof type => false + * ``` + * this algorithm achieves the equivalent with the following prototypical chains + * using `Object.setPrototypeOf` + * + * ``` + * type.__proto__ = type (type instanceof type) + * type.__proto__.__proto__ = object (type instanceof object) + * type.prototype.__proto__ = object (type subclassof object) + * object.__proto__ = type (object instanceof type) + * object.__proto__.__proto__ = object (object instanceof object) + * ``` + * + * while `Object.setPrototypeOf` is not considered [good practice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) + * this is a particularly unique use case and creates a lot of prototypical benefits + * all single inheritance classes (i.e. all builtins) now follow prototypical inheritance + * similarly it makes metclasses that much easier to implement + * Object.setPrototypeOf is also a feature built into the javascript language + * + * @function + * @suppress {checkTypes} + * + */ +Sk.abstr.setUpBaseInheritance = function () { + Object.setPrototypeOf(Sk.builtin.type.prototype, Sk.builtin.object.prototype); + Object.setPrototypeOf(Sk.builtin.type, Sk.builtin.type.prototype); + Object.setPrototypeOf(Sk.builtin.object, Sk.builtin.type.prototype); + + // required so that type objects can be called! + Object.defineProperties(Sk.builtin.type.prototype, /**@lends {Sk.builtin.type.prototype}*/{ + call: {value: Function.prototype.call}, + apply: {value: Function.prototype.apply}, + ob$type: {value: Sk.builtin.type, writable: true}, + tp$name: {value: "type", writable: true}, + tp$base: {value: Sk.builtin.object, writable: true}, + sk$type: {value: true}, + }); + Object.defineProperties(Sk.builtin.object.prototype, /**@lends {Sk.builtin.object.prototype}*/{ + ob$type: {value: Sk.builtin.object, writable: true}, + tp$name: {value: "object", writable: true}, + tp$base: {value: undefined, writable: true}, + sk$object: {value: true}, + }); + + Sk.builtin.object.sk$basetype = true; + Sk.builtin.type.sk$basetype = true; + + Sk.abstr.setUpBuiltinMro(Sk.builtin.type); + Sk.abstr.setUpBuiltinMro(Sk.builtin.object); +}; + +/** + * This function is called in {@link Sk.doOneTimeInitialization} + * and {@link Sk.abstr.buildNativeClass} + * + * @param {!typeObject} child + * + */ +Sk.abstr.setUpBuiltinMro = function (child) { + let parent = child.prototype.tp$base || undefined; + const bases = parent === undefined ? [] : [parent]; + if (parent === Sk.builtin.object) { + child.sk$basetype = true; + } + const mro = [child]; + for (let base = parent; base !== undefined; base = base.prototype.tp$base) { + if (!base.sk$abstract) { + mro.push(base); + } + } + // internally we keep the mro and bases as array objects + // the wrapper descripor returns the tuple of the array + Object.defineProperties(child.prototype, { + sk$prototypical: {value: true, writable: true}, + tp$mro: {value: mro, writable: true}, + tp$bases: {value: bases, writable: true}, + }); +}; +/** + * + * @param {!typeObject} klass + * @param {Object=} getsets + */ +Sk.abstr.setUpGetSets = function (klass, getsets) { + getsets = getsets || klass.prototype.tp$getsets || {}; + for (let getset_name in getsets) { + const gsd = getsets[getset_name]; + gsd.$name = getset_name; + klass.prototype[getset_name] = new Sk.builtin.getset_descriptor(klass, gsd); + } + // we check this later in onetimeInitialization + // it also means that you can create more getsets and then allocate them later + Object.defineProperty(klass.prototype, "tp$getsets", { + value: null, + writable: true, + enumerable: false, + }); +}; + +/** + * + * @param {typeObject} klass + * @param {Object=} methods + */ +Sk.abstr.setUpMethods = function (klass, methods) { + methods = methods || klass.prototype.tp$methods || {}; + for (let method_name in methods) { + const method_def = methods[method_name]; + method_def.$name = method_name; + klass.prototype[method_name] = new Sk.builtin.method_descriptor(klass, method_def); + } + Object.defineProperty(klass.prototype, "tp$methods", { + value: null, + writable: true, + enumerable: false, + }); +}; + +/** + * + * @param {typeObject} klass + * @param {Object=} methods + */ +Sk.abstr.setUpClassMethods = function (klass, methods) { + methods = methods || {}; + for (let method_name in methods) { + const method_def = methods[method_name]; + method_def.$name = method_name; + klass.prototype[method_name] = new Sk.builtin.classmethod_descriptor(klass, method_def); + } +}; + +/** + * + * @param {typeObject} klass + * @param {Object=} slots */ -Sk.abstr.setUpInheritance = function (childName, child, parent) { - Sk.abstr.inherits(child, parent); - child.prototype.tp$base = parent; - child.prototype.tp$name = childName; - child.prototype.ob$type = Sk.builtin.type.makeIntoTypeObj(childName, child); +Sk.abstr.setUpSlots = function (klass, slots) { + const proto = klass.prototype; + const op2shortcut = { + Eq: "ob$eq", + NotEq: "ob$ne", + Gt: "ob$gt", + GtE: "ob$ge", + Lt: "ob$lt", + LtE: "ob$le", + }; + if (slots === undefined) { + // make a shallow copy so that we don't accidently consider parent slots + // maybe better to use .hasOwnProperty but this allows for more code reuse + slots = {...klass.prototype}; + } + + if (slots.tp$new === Sk.generic.new) { + slots.tp$new = Sk.generic.new(klass); + } + + for (let slot_name in slots) { + Object.defineProperty(proto, slot_name, { + value: slots[slot_name], + writable: true, + enumerable: false, + }); + } + + // set up richcompare skulpt slots + if (slots.tp$richcompare !== undefined) { + for (let op in op2shortcut) { + const shortcut = op2shortcut[op]; + slots[shortcut] = + slots[shortcut] || + function (other) { + return this.tp$richcompare(other, op); + }; + Object.defineProperty(proto, shortcut, { + value: slots[shortcut], + writable: true, + enumerable: false, + }); + } + } + + if (slots.tp$new !== undefined) { + proto.__new__ = new Sk.builtin.sk_method(Sk.generic.newMethodDef, klass); + proto.tp$new.sk$static_new = true; // a flag for the slot + } + + function wrap_func(klass, dunder_name, wrapped_func) { + const slot_def = Sk.slots[dunder_name]; + // we do this here because in the generic.wrapperCall methods the wrapped_func + // the wrapped func should have a $name property and a $flags property (for minArgs) + klass.prototype[dunder_name] = new Sk.builtin.wrapper_descriptor(klass, slot_def, wrapped_func); + } + function set_up_slot(slot_name, slots, klass, slot_mapping) { + const wrapped_func = slots[slot_name]; + // some slots get multpile dunders + const dunder_name = slot_mapping[slot_name]; + if (typeof dunder_name === "string") { + wrap_func(klass, dunder_name, wrapped_func); + } else { + for (let i = 0; i < dunder_name.length; i++) { + wrap_func(klass, dunder_name[i], wrapped_func); + } + } + } + + // main slots + const main_slots = Sk.subSlots.main_slots; + for (let slot_name in main_slots) { + if (slots[slot_name] !== undefined) { + set_up_slot(slot_name, slots, klass, main_slots); + } + } + + // __hash__ + const hash = slots.tp$hash; + if (hash == Sk.builtin.none.none$) { + klass.prototype.__hash__ = hash; + } else if (hash !== undefined) { + wrap_func(klass, "__hash__", hash); + } + + // as_number_slots + const number_slots = Sk.subSlots.number_slots; + const reflected_slots = Sk.reflectedNumberSlots; + if (slots.tp$as_number !== undefined) { + for (let slot_name in reflected_slots) { + if (slots[slot_name] !== undefined) { + const reflect_name = reflected_slots[slot_name].reflected; + if (slots[reflect_name] !== undefined) { + continue; + } + const slot = reflected_slots[slot_name].slot; + if (slot == null) { + // then the reflected slot is the same as non reflected slot - like nb$add + (slots[reflect_name] = slots[slot_name]), + Object.defineProperty(proto, reflect_name, { + value: slots[slot_name], + writable: true, + enumerable: false, + }); + } else { + (slots[reflect_name] = slot), + Object.defineProperty(proto, reflect_name, { + value: slot, + writable: true, + enumerable: false, + }); + } + } + } + for (let slot_name in number_slots) { + if (slots[slot_name] !== undefined) { + set_up_slot(slot_name, slots, klass, number_slots); + } + } + } + + // as_sequence_or_mapping slots + const sequence_and_mapping_slots = Sk.subSlots.sequence_and_mapping_slots; + if (slots.tp$as_sequence_or_mapping !== undefined) { + for (let slot_name in Sk.sequenceAndMappingSlots) { + if (slots[slot_name] !== undefined) { + const equiv_slots = Sk.sequenceAndMappingSlots[slot_name]; + for (let i = 0; i < equiv_slots.length; i++) { + const equiv_slot = equiv_slots[i]; + slots[equiv_slot] = slots[slot_name]; + Object.defineProperty(proto, equiv_slot, { + value: slots[slot_name], + writable: true, + enumerable: false, + }); + } + } + } + for (let slot_name in sequence_and_mapping_slots) { + if (slots[slot_name] !== undefined) { + set_up_slot(slot_name, slots, klass, sequence_and_mapping_slots); + } + } + } + // a flag to check during doOneTimeInitialization + Object.defineProperty(proto, "sk$slots", { + value: null, + writeable: true, + }); +}; + +/** + * @function + * @param {string} typename + * @param {Object} options An object literal that provides the functionality of the typobject + * + * + * @description + * this can be called to create a native typeobj + * options include + * ``` + * - base: default to {@link Sk.builtin.object} + * - meta: default to {@link Sk.builtin.type} + * + * - slots: skulpt slot functions that will be allocated slot wrappers + * - methods: method objects `{$meth: Function, $flags: callmethod, $doc: string, $textsic: string|null}`, + * - getsets: getset objects `{$get: Function, $set: Function, $doc: string}`, + * - classmethods: classmethod objects `{$meth: Function, $flags: callmethod, $doc: string, $textsic: string|null}`, + * + * - flags: Object allocated directly onto class like `klass.sk$acceptable_as_base_class` + * - proto: Object allocated onto the prototype useful for private methods + * ``` + * See most builtin type objects for examples + * + * + */ +Sk.abstr.buildNativeClass = function (typename, options) { + + options = options || {}; + /**@type {typeObject} */ + let typeobject; + if (!options.hasOwnProperty("constructor")) { + typeobject = function klass() { + this.$d = new Sk.builtin.dict(); + }; + } else { + typeobject = options.constructor || new Function; + } + let mod; + if (typename.includes(".")) { + // you should define the module like "collections.defaultdict" for static classes + const mod_typename = typename.split("."); + typename = mod_typename.pop(); + mod = mod_typename.join("."); + } + const meta = options.meta || undefined; + + Sk.abstr.setUpInheritance(typename, typeobject, options.base, meta); + + // would need to change this for multiple inheritance. + Sk.abstr.setUpBuiltinMro(typeobject); + + if (options.slots !== undefined) { + // only setUpSlotWrappers if slots defined; + Sk.abstr.setUpSlots(typeobject, /**@lends {typeobject.prototype} */ options.slots); + } + + Sk.abstr.setUpMethods(typeobject, options.methods || {}); + Sk.abstr.setUpGetSets(typeobject, options.getsets || {}); + Sk.abstr.setUpClassMethods(typeobject, options.classmethods || {}); + + if (mod !== undefined) { + typeobject.prototype.__module__ = new Sk.builtin.str(mod); + } + const type_proto = typeobject.prototype; + const proto = options.proto || {}; + for (let p in proto) { + Object.defineProperty(type_proto, p, { + value: proto[p], + writable: true, + enumerable: false, + }); + } + const flags = options.flags || {}; + for (let f in flags) { + typeobject[f] = flags[f]; + } + + // str might not have been created yet + + if (Sk.builtin.str !== undefined && typeobject.prototype.hasOwnProperty("tp$doc") && !typeobject.prototype.hasOwnProperty("__doc__")) { + const docstr = typeobject.prototype.tp$doc || null; + if (typeof docstr === "string") { + typeobject.prototype.__doc__ = new Sk.builtin.str(docstr); + } else { + typeobject.prototype.__doc__ = Sk.builtin.none.none$; + } + } + return typeobject; +}; + +/** + * @function + * + * @param {string} typename e.g. "itertools.chain" + * @param {Object + * } iterator minimum options `{constructor: function, iternext: function}` + * + * @description + * effectively a wrapper for easily defining an iterator + * `tp$iter` slot is added and returns self + * + * define a constructor in the usual way + * + * define `tp$iternext` using iternext in the object literal + * mostly as a convenience + * you can also define `tp$iternext` in the slots which will take priority + * + * the main benefit of this helper function is to reduce some repetitive code for defining an iterator class + * + * If you want a generic iterator see {@link Sk.generic.iterator} + * + * + * @example + * Sk.builtin.tuple_iter_ = Sk.abstr.buildIteratorClass("tuple_iterator", { + constructor: function tuple_iter_(tuple) { + this.$index = 0; + this.$seq = tuple.sk$asarray(); + }, + iternext: function () { + if (this.$index >= this.$seq.length) { + return undefined; + } + return this.$seq[this.$index++]; + } +}); + * + * + */ + +Sk.abstr.buildIteratorClass = function (typename, iterator) { + Sk.asserts.assert(iterator.hasOwnProperty("constructor"), "must provide a constructor"); + iterator.slots = iterator.slots || {}; + iterator.slots.tp$iter = Sk.generic.selfIter; + iterator.slots.tp$iternext = iterator.slots.tp$iternext || iterator.iternext; + iterator.slots.tp$getattr = iterator.slots.tp$getattr || Sk.generic.getAttr; + return Sk.abstr.buildNativeClass(typename, iterator); +}; + +Sk.abstr.setUpModuleMethods = function (module_name, method_defs, module) { + for (let method_name in method_defs) { + const method_def = method_defs[method_name]; + method_def.$name = method_def.$name || method_name; + module[method_name] = new Sk.builtin.sk_method(method_def, undefined, module_name); + } }; /** @@ -1035,6 +1256,7 @@ Sk.abstr.setUpInheritance = function (childName, child, parent) { * @param {Object} self The instance of the subclas. * @param {...?} args Arguments to pass to the constructor. * @return {undefined} + * @deprecated */ Sk.abstr.superConstructor = function (thisClass, self, args) { var argumentsForConstructor = Array.prototype.slice.call(arguments, 2); diff --git a/src/assert-dev.js b/src/assert-dev.js index 990cc319cf..53f2d19c8d 100644 --- a/src/assert-dev.js +++ b/src/assert-dev.js @@ -2,7 +2,7 @@ Sk.asserts = {ENABLE_ASSERTS: true}; /** * Cause assertion failure when condition is false. - * + * * @param {*} condition condition to check * @param {string=} message error message */ @@ -20,7 +20,7 @@ Sk.exportSymbol("Sk.asserts.assert", Sk.asserts.assert); /** * Cause assertion failure. - * + * * @param {string=} message error message */ Sk.asserts.fail = function (message) { diff --git a/src/assert-prod.js b/src/assert-prod.js index 90959eac2d..1329d959d6 100644 --- a/src/assert-prod.js +++ b/src/assert-prod.js @@ -2,7 +2,7 @@ Sk.asserts = {}; /** * Cause assertion failure when condition is false. - * + * * @param {*} condition condition to check * @param {string=} message error message */ @@ -13,7 +13,7 @@ Sk.exportSymbol("Sk.asserts.assert", Sk.asserts.assert); /** * Cause assertion failure. - * + * * @param {string=} message error message */ Sk.asserts.fail = function (message) { diff --git a/src/ast.js b/src/ast.js index 81b9287d46..b0e79270a5 100644 --- a/src/ast.js +++ b/src/ast.js @@ -15,9 +15,9 @@ var COMP_GENEXP = 0; var COMP_LISTCOMP = 1; var COMP_SETCOMP = 2; var NULL = null; -var _slice_kind = { +var _slice_kind = { Slice_kind: 1, - ExtSlice_kind: 2, + ExtSlice_kind: 2, Index_kind: 3 }; @@ -29,10 +29,11 @@ var _expr_kind = { YieldFrom_kind: 15, Compare_kind: 16, Call_kind: 17, FormattedValue_kind: 18, JoinedStr_kind: 19, Constant_kind: 20, Attribute_kind: 21, Subscript_kind: 22, Starred_kind: 23, - Name_kind: 24, List_kind: 25, Tuple_kind: 26 }; + Name_kind: 24, List_kind: 25, Tuple_kind: 26 +}; /** @constructor */ -function Compiling (encoding, filename, c_flags) { +function Compiling(encoding, filename, c_flags) { this.c_encoding = encoding; this.c_filename = filename; this.c_flags = c_flags || 0; @@ -41,7 +42,7 @@ function Compiling (encoding, filename, c_flags) { /** * @return {number} */ -function NCH (n) { +function NCH(n) { Sk.asserts.assert(n !== undefined, "node must be defined"); if (n.children === null) { return 0; @@ -49,13 +50,13 @@ function NCH (n) { return n.children.length; } -function CHILD (n, i) { +function CHILD(n, i) { Sk.asserts.assert(n !== undefined, "node must be defined"); Sk.asserts.assert(i !== undefined, "index of child must be specified"); return n.children[i]; } -function REQ (n, type) { +function REQ(n, type) { Sk.asserts.assert(n.type === type, "node wasn't expected type"); } @@ -75,13 +76,13 @@ function ast_error(c, n, msg) { throw new Sk.builtin.SyntaxError(msg, c.c_filename, n.lineno); } -function strobj (s) { +function strobj(s) { Sk.asserts.assert(typeof s === "string", "expecting string, got " + (typeof s)); return new Sk.builtin.str(s); } /** @return {number} */ -function numStmts (n) { +function numStmts(n) { var ch; var i; var cnt; @@ -89,8 +90,7 @@ function numStmts (n) { case SYM.single_input: if (CHILD(n, 0).type === TOK.T_NEWLINE) { return 0; - } - else { + } else { return numStmts(CHILD(n, 0)); } case SYM.file_input: @@ -111,8 +111,7 @@ function numStmts (n) { case SYM.suite: if (NCH(n) === 1) { return numStmts(CHILD(n, 0)); - } - else { + } else { cnt = 0; for (i = 2; i < NCH(n) - 1; ++i) { cnt += numStmts(CHILD(n, i)); @@ -126,7 +125,7 @@ function numStmts (n) { return 0; } -function forbiddenCheck (c, n, x, lineno) { +function forbiddenCheck(c, n, x, lineno) { if (x instanceof Sk.builtin.str) { x = x.v; } @@ -144,7 +143,7 @@ function forbiddenCheck (c, n, x, lineno) { * Only sets context for expr kinds that can appear in assignment context as * per the asdl file. */ -function setContext (c, e, ctx, n) { +function setContext(c, e, ctx, n) { var i; var exprName; var s; @@ -255,10 +254,10 @@ Sk.setupOperators = function (py3) { delete operatorMap[TOK.T_AT]; } } -} +}; Sk.exportSymbol("Sk.setupOperators", Sk.setupOperators); -function getOperator (n) { +function getOperator(n) { if (operatorMap[n.type] === undefined) { throw new Sk.builtin.SyntaxError("invalid syntax", n.type, n.lineno); } @@ -267,13 +266,13 @@ function getOperator (n) { function new_identifier(n, c) { if (n.value) { - return Sk.builtin.str(n.value); + return new Sk.builtin.str(n.value); } - return Sk.builtin.str(n); + return new Sk.builtin.str(n); } -function astForCompOp (c, n) { +function astForCompOp(c, n) { /* comp_op: '<'|'>'|'=='|'>='|'<='|'!='|'in'|'not' 'in'|'is' |'is' 'not' */ @@ -301,8 +300,7 @@ function astForCompOp (c, n) { return Sk.astnodes.Is; } } - } - else if (NCH(n) === 2) { + } else if (NCH(n) === 2) { if (CHILD(n, 0).type === TOK.T_NAME) { if (CHILD(n, 1).value === "in") { return Sk.astnodes.NotIn; @@ -315,8 +313,7 @@ function astForCompOp (c, n) { Sk.asserts.fail("invalid comp_op"); } -function copy_location(e, n) -{ +function copy_location(e, n) { if (e) { e.lineno = LINENO(n); e.col_offset = n.col_offset; @@ -326,7 +323,7 @@ function copy_location(e, n) return e; } -function seq_for_testlist (c, n) { +function seq_for_testlist(c, n) { /* testlist: test (',' test)* [','] testlist_star_expr: test|star_expr (',' test|star_expr)* [','] */ @@ -345,7 +342,7 @@ function seq_for_testlist (c, n) { return seq; } -function astForSuite (c, n) { +function astForSuite(c, n) { /* suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT */ var j; var num; @@ -365,12 +362,10 @@ function astForSuite (c, n) { if (CHILD(n, end - 1).type === TOK.T_SEMI) { end -= 1; } - for (i = 0; i < end; i += 2) // by 2 to skip ; - { + for (i = 0; i < end; i += 2) {// by 2 to skip ; seq[pos++] = astForStmt(c, CHILD(n, i)); } - } - else { + } else { for (i = 2; i < NCH(n) - 1; ++i) { ch = CHILD(n, i); REQ(ch, SYM.stmt); @@ -378,8 +373,7 @@ function astForSuite (c, n) { if (num === 1) { // small_stmt or compound_stmt w/ only 1 child seq[pos++] = astForStmt(c, ch); - } - else { + } else { ch = CHILD(ch, 0); REQ(ch, SYM.simple_stmt); for (j = 0; j < NCH(ch); j += 2) { @@ -396,18 +390,16 @@ function astForSuite (c, n) { return seq; } -function astForExceptClause (c, exc, body) { +function astForExceptClause(c, exc, body) { /* except_clause: 'except' [test [(',' | 'as') test]] */ var e; REQ(exc, SYM.except_clause); REQ(body, SYM.suite); if (NCH(exc) === 1) { return new Sk.astnodes.ExceptHandler(null, null, astForSuite(c, body), exc.lineno, exc.col_offset, exc.end_lineno, exc.end_col_offset); - } - else if (NCH(exc) === 2) { + } else if (NCH(exc) === 2) { return new Sk.astnodes.ExceptHandler(ast_for_expr(c, CHILD(exc, 1)), null, astForSuite(c, body), exc.lineno, exc.col_offset, exc.end_lineno, exc.end_col_offset); - } - else if (NCH(exc) === 4) { + } else if (NCH(exc) === 4) { if (Sk.__future__.python3 && CHILD(exc, 2).value == ",") { ast_error(c, exc, "Old-style 'except' clauses are not supported in Python 3"); } @@ -420,7 +412,7 @@ function astForExceptClause (c, exc, body) { Sk.asserts.fail("wrong number of children for except clause"); } -function astForTryStmt (c, n) { +function astForTryStmt(c, n) { var exceptSt; var i; var handlers = []; @@ -443,15 +435,13 @@ function astForTryStmt (c, n) { finally_ = astForSuite(c, CHILD(n, nc - 1)); nexcept--; - } - else { + } else { /* we can assume it's an "else", otherwise it would have a type of except_clause */ orelse = astForSuite(c, CHILD(n, nc - 1)); nexcept--; } - } - else if (CHILD(n, nc - 3).type !== SYM.except_clause) { + } else if (CHILD(n, nc - 3).type !== SYM.except_clause) { throw new Sk.builtin.SyntaxError("malformed 'try' statement", c.c_filename, n.lineno); } @@ -466,7 +456,7 @@ function astForTryStmt (c, n) { return new Sk.astnodes.Try(body, handlers, orelse, finally_, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } -function astForDottedName (c, n) { +function astForDottedName(c, n) { var i; var e; var id; @@ -484,27 +474,24 @@ function astForDottedName (c, n) { return e; } -function astForDecorator (c, n) { +function astForDecorator(c, n) { /* decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE */ var nameExpr; REQ(n, SYM.decorator); REQ(CHILD(n, 0), TOK.T_AT); REQ(CHILD(n, NCH(n) - 1), TOK.T_NEWLINE); nameExpr = astForDottedName(c, CHILD(n, 1)); - if (NCH(n) === 3) // no args - { + if (NCH(n) === 3) {// no args return nameExpr; - } - else if (NCH(n) === 5) // call with no args - { - return new Sk.astnodes.Call(nameExpr, [], [], null, null, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); - } - else { + } else if (NCH(n) === 5) {// call with no args + return new Sk.astnodes.Call(nameExpr, [], [], + n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); + } else { return ast_for_call(c, CHILD(n, 3), nameExpr); } } -function astForDecorators (c, n) { +function astForDecorators(c, n) { var i; var decoratorSeq; REQ(n, SYM.decorators); @@ -515,7 +502,7 @@ function astForDecorators (c, n) { return decoratorSeq; } -function ast_for_decorated (c, n) { +function ast_for_decorated(c, n) { /* decorated: decorators (classdef | funcdef | async_funcdef) */ var thing = null; var decorator_seq = null; @@ -524,8 +511,8 @@ function ast_for_decorated (c, n) { decorator_seq = astForDecorators(c, CHILD(n, 0)); Sk.asserts.assert(TYPE(CHILD(n, 1)) == SYM.funcdef || - TYPE(CHILD(n, 1)) == SYM.async_funcdef || - TYPE(CHILD(n, 1)) == SYM.classdef); + TYPE(CHILD(n, 1)) == SYM.async_funcdef || + TYPE(CHILD(n, 1)) == SYM.classdef); if (TYPE(CHILD(n, 1)) == SYM.funcdef) { thing = ast_for_funcdef(c, CHILD(n, 1), decorator_seq); @@ -544,7 +531,7 @@ function ast_for_decorated (c, n) { } /* with_item: test ['as' expr] */ -function ast_for_with_item (c, n) { +function ast_for_with_item(c, n) { var context_expr, optional_vars; REQ(n, SYM.with_item); context_expr = ast_for_expr(c, CHILD(n, 0)); @@ -559,7 +546,7 @@ function ast_for_with_item (c, n) { /* with_stmt: 'with' with_item (',' with_item)* ':' suite */ function ast_for_with_stmt(c, n0, is_async) { const n = is_async ? CHILD(n0, 1) : n0; - var i + var i; var items = [], body; REQ(n, SYM.with_stmt); @@ -578,7 +565,7 @@ function ast_for_with_stmt(c, n0, is_async) { } } -function astForExecStmt (c, n) { +function astForExecStmt(c, n) { var expr1, globals = null, locals = null; var nchildren = NCH(n); Sk.asserts.assert(nchildren === 2 || nchildren === 4 || nchildren === 6); @@ -595,7 +582,7 @@ function astForExecStmt (c, n) { return new Sk.astnodes.Exec(expr1, globals, locals, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } -function astForIfStmt (c, n) { +function astForIfStmt(c, n) { /* if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] */ @@ -622,8 +609,7 @@ function astForIfStmt (c, n) { astForSuite(c, CHILD(n, 3)), astForSuite(c, CHILD(n, 6)), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); - } - else if (decider === "i") { + } else if (decider === "i") { nElif = NCH(n) - 4; hasElse = false; orelse = []; @@ -659,7 +645,7 @@ function astForIfStmt (c, n) { orelse, CHILD(n, off).lineno, CHILD(n, off).col_offset, - CHILD(n, NCH(n) - 6).end_lineno, + CHILD(n, NCH(n) - 6).end_lineno, CHILD(n, NCH(n) - 6).end_col_offset)]; } return new Sk.astnodes.If( @@ -671,7 +657,7 @@ function astForIfStmt (c, n) { Sk.asserts.fail("unexpected token in 'if' statement"); } -function ast_for_exprlist (c, n, context) { +function ast_for_exprlist(c, n, context) { var e; var i; var seq; @@ -687,13 +673,13 @@ function ast_for_exprlist (c, n, context) { return seq; } -function astForDelStmt (c, n) { +function astForDelStmt(c, n) { /* del_stmt: 'del' exprlist */ REQ(n, SYM.del_stmt); return new Sk.astnodes.Delete(ast_for_exprlist(c, CHILD(n, 1), Sk.astnodes.Del), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } -function astForGlobalStmt (c, n) { +function astForGlobalStmt(c, n) { /* global_stmt: 'global' NAME (',' NAME)* */ var i; var s = []; @@ -704,19 +690,18 @@ function astForGlobalStmt (c, n) { return new Sk.astnodes.Global(s, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } -function astForAssertStmt (c, n) { +function astForAssertStmt(c, n) { /* assert_stmt: 'assert' test [',' test] */ REQ(n, SYM.assert_stmt); if (NCH(n) === 2) { return new Sk.astnodes.Assert(ast_for_expr(c, CHILD(n, 1)), null, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); - } - else if (NCH(n) === 4) { + } else if (NCH(n) === 4) { return new Sk.astnodes.Assert(ast_for_expr(c, CHILD(n, 1)), ast_for_expr(c, CHILD(n, 3)), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } Sk.asserts.fail("improper number of parts to assert stmt"); } -function aliasForImportName (c, n) { +function aliasForImportName(c, n) { /* import_as_name: NAME ['as' NAME] dotted_as_name: dotted_name ['as' NAME] @@ -740,8 +725,7 @@ function aliasForImportName (c, n) { if (NCH(n) === 1) { n = CHILD(n, 0); continue loop; - } - else { + } else { a = aliasForImportName(c, CHILD(n, 0)); Sk.asserts.assert(!a.asname); a.asname = strobj(CHILD(n, 2).value); @@ -751,8 +735,7 @@ function aliasForImportName (c, n) { case SYM.dotted_name: if (NCH(n) === 1) { return new Sk.astnodes.alias(strobj(CHILD(n, 0).value), null); - } - else { + } else { // create a string of the form a.b.c str = ""; for (i = 0; i < NCH(n); i += 2) { @@ -770,7 +753,7 @@ function aliasForImportName (c, n) { } } -function astForImportStmt (c, n) { +function astForImportStmt(c, n) { /* import_stmt: import_name | import_from import_name: 'import' dotted_as_names @@ -792,7 +775,7 @@ function astForImportStmt (c, n) { lineno = n.lineno; col_offset = n.col_offset; end_lineno = n.end_lineno; - end_col_offset = n.end_col_offset + end_col_offset = n.end_col_offset; n = CHILD(n, 0); if (n.type === SYM.import_name) { n = CHILD(n, 1); @@ -802,8 +785,7 @@ function astForImportStmt (c, n) { aliases[i / 2] = aliasForImportName(c, CHILD(n, i)); } return new Sk.astnodes.Import(aliases, lineno, col_offset, end_lineno, end_col_offset); - } - else if (n.type === SYM.import_from) { + } else if (n.type === SYM.import_from) { mod = null; ndots = 0; @@ -812,14 +794,11 @@ function astForImportStmt (c, n) { mod = aliasForImportName(c, CHILD(n, idx)); idx++; break; - } - else if (CHILD(n, idx).type === TOK.T_DOT) { + } else if (CHILD(n, idx).type === TOK.T_DOT) { ndots++; - } - else if (CHILD(n, idx).type === TOK.T_ELLIPSIS) { + } else if (CHILD(n, idx).type === TOK.T_ELLIPSIS) { ndots += 3; - } - else { + } else { break; } } @@ -849,8 +828,7 @@ function astForImportStmt (c, n) { aliases = []; if (n.type === TOK.T_STAR) { aliases[0] = aliasForImportName(c, n); - } - else { + } else { for (i = 0; i < NCH(n); i += 2) { aliases[i / 2] = aliasForImportName(c, CHILD(n, i)); } @@ -870,18 +848,18 @@ function ast_for_testlistComp(c, n) { } return ast_for_testlist(c, n); } -function ast_for_genexp(c, n) -{ + +function ast_for_genexp(c, n) { Sk.asserts.assert(TYPE(n) == SYM.testlist_comp || TYPE(n) == SYM.argument); return ast_for_itercomp(c, n, COMP_GENEXP); } -function ast_for_listcomp(c, n) { +function ast_for_listcomp(c, n) { Sk.asserts.assert(TYPE(n) == (SYM.testlist_comp)); return ast_for_itercomp(c, n, COMP_LISTCOMP); } -function astForFactor (c, n) { +function astForFactor(c, n) { /* some random peephole thing that cpy does */ var expression; var pnum; @@ -918,7 +896,7 @@ function astForFactor (c, n) { Sk.asserts.fail("unhandled factor"); } -function astForForStmt (c, n) { +function astForForStmt(c, n) { /* for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] */ var target; var _target; @@ -932,19 +910,17 @@ function astForForStmt (c, n) { _target = ast_for_exprlist(c, nodeTarget, Sk.astnodes.Store); if (NCH(nodeTarget) === 1) { target = _target[0]; - } - else { + } else { target = new Sk.astnodes.Tuple(_target, Sk.astnodes.Store, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } return new Sk.astnodes.For(target, - ast_for_testlist(c, CHILD(n, 3)), - astForSuite(c, CHILD(n, 5)), - seq, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); + ast_for_testlist(c, CHILD(n, 3)), + astForSuite(c, CHILD(n, 5)), + seq, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } -function ast_for_call(c, n, func, allowgen) -{ +function ast_for_call(c, n, func, allowgen) { /* arglist: argument (',' argument)* [','] argument: ( test [comp_for] | '*' test | test '=' test | '**' test ) @@ -981,8 +957,8 @@ function ast_for_call(c, n, func, allowgen) } } - args = [] - keywords = [] + args = []; + keywords = []; nargs = 0; /* positional arguments + iterable argument unpackings */ nkeywords = 0; /* keyword arguments + keyword argument unpackings */ @@ -997,12 +973,12 @@ function ast_for_call(c, n, func, allowgen) if (nkeywords) { if (ndoublestars) { ast_error(c, chch, - "positional argument follows " + - "keyword argument unpacking"); + "positional argument follows " + + "keyword argument unpacking"); } else { ast_error(c, chch, - "positional argument follows " + - "keyword argument"); + "positional argument follows " + + "keyword argument"); } } e = ast_for_expr(c, chch); @@ -1015,8 +991,8 @@ function ast_for_call(c, n, func, allowgen) var starred; if (ndoublestars) { ast_error(c, chch, - "iterable argument unpacking follows " + - "keyword argument unpacking"); + "iterable argument unpacking follows " + + "keyword argument unpacking"); return NULL; } e = ast_for_expr(c, CHILD(ch, 1)); @@ -1024,7 +1000,7 @@ function ast_for_call(c, n, func, allowgen) return NULL; } starred = new Sk.astnodes.Starred(e, Sk.astnodes.Load, LINENO(chch), - chch.col_offset, chch.end_lineno, chch.end_col_offset); + chch.col_offset, chch.end_lineno, chch.end_col_offset); args[nargs++] = starred; } else if (TYPE(chch) == TOK.T_DOUBLESTAR) { /* a keyword argument unpacking */ @@ -1062,15 +1038,13 @@ function ast_for_call(c, n, func, allowgen) */ if (e.constructor === Sk.astnodes.Lambda) { ast_error(c, chch, - "lambda cannot contain assignment"); + "lambda cannot contain assignment"); return NULL; - } - else if (e.constructor !== Sk.astnodes.Name) { + } else if (e.constructor !== Sk.astnodes.Name) { ast_error(c, chch, - "keyword can't be an expression"); + "keyword can't be an expression"); return NULL; - } - else if (forbiddenCheck(c, e.id, ch, 1)) { + } else if (forbiddenCheck(c, e.id, ch, 1)) { return NULL; } key = e.id; @@ -1078,13 +1052,14 @@ function ast_for_call(c, n, func, allowgen) tmp = keywords[k].arg; if (tmp && tmp === key) { ast_error(c, chch, - "keyword argument repeated"); + "keyword argument repeated"); return NULL; } } e = ast_for_expr(c, CHILD(ch, 2)); - if (!e) + if (!e) { return NULL; + } kw = new Sk.astnodes.keyword(key, e); keywords[nkeywords++] = kw; } @@ -1101,20 +1076,20 @@ function ast_for_trailer(c, n, left_expr) { */ REQ(n, SYM.trailer); if (TYPE(CHILD(n, 0)) == TOK.T_LPAR) { - if (NCH(n) == 2) + if (NCH(n) == 2) { return new Sk.astnodes.Call(left_expr, NULL, NULL, LINENO(n), - n.col_offset, n.end_lineno, n.end_col_offset); - else + n.col_offset, n.end_lineno, n.end_col_offset); + } else { return ast_for_call(c, CHILD(n, 1), left_expr, true); - } - else if (TYPE(CHILD(n, 0)) == TOK.T_DOT) { + } + } else if (TYPE(CHILD(n, 0)) == TOK.T_DOT) { var attr_id = new_identifier(CHILD(n, 1)); - if (!attr_id) + if (!attr_id) { return NULL; + } return new Sk.astnodes.Attribute(left_expr, attr_id, Sk.astnodes.Load, - LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); - } - else { + LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); + } else { REQ(CHILD(n, 0), TOK.T_LSQB); REQ(CHILD(n, 2), TOK.T_RSQB); n = CHILD(n, 1); @@ -1124,9 +1099,8 @@ function ast_for_trailer(c, n, left_expr) { return NULL; } return new Sk.astnodes.Subscript(left_expr, slc, Sk.astnodes.Load, - LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); - } - else { + LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); + } else { /* The grammar is ambiguous here. The ambiguity is resolved by treating the sequence as a tuple literal if there are no slice features. @@ -1149,27 +1123,26 @@ function ast_for_trailer(c, n, left_expr) { } if (!simple) { return new Sk.astnodes.Subscript(left_expr, new Sk.astnodes.ExtSlice(slices), - Sk.astnodes.Load, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + Sk.astnodes.Load, LINENO(n), n.col_offset, + n.end_lineno, n.end_col_offset); } /* extract Index values and put them in a Tuple */ elts = []; for (j = 0; j < slices.length; ++j) { // @meredydd any idea how we reach this? slc = slices[j]; - Sk.asserts.assert(slc.kind == _slice_kind.Index_kind && slc.v.Index.value); + Sk.asserts.assert(slc.kind == _slice_kind.Index_kind && slc.v.Index.value); elts[j] = slc.v.Index.value; } e = new Sk.astnodes.Tuple(elts, Sk.astnodes.Load, LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); return new Sk.astnodes.Subscript(left_expr, new Sk.astnodes.Index(e), - Sk.astnodes.Load, LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); + Sk.astnodes.Load, LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); } } } -function ast_for_flow_stmt(c, n) -{ +function ast_for_flow_stmt(c, n) { /* flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt @@ -1187,50 +1160,50 @@ function ast_for_flow_stmt(c, n) switch (TYPE(ch)) { case SYM.break_stmt: return new Sk.astnodes.Break(LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); case SYM.continue_stmt: return new Sk.astnodes.Continue(LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); case SYM.yield_stmt: { /* will reduce to yield_expr */ var exp = ast_for_expr(c, CHILD(ch, 0)); if (!exp) { return null; } return new Sk.astnodes.Expr(exp, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); } case SYM.return_stmt: - if (NCH(ch) == 1) + if (NCH(ch) == 1) { return new Sk.astnodes.Return(null, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); - else { + n.end_lineno, n.end_col_offset); + } else { var expression = ast_for_testlist(c, CHILD(ch, 1)); if (!expression) { return null; } return new Sk.astnodes.Return(expression, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); } case SYM.raise_stmt: // This is tricky and Skulpt-specific, because we need to handle // both Python 3-style and Python 2-style 'raise' statements - if (NCH(ch) == 1) + if (NCH(ch) == 1) { return new Sk.astnodes.Raise(null, null, null, null, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); - else if (NCH(ch) >= 2) { + n.end_lineno, n.end_col_offset); + } else if (NCH(ch) >= 2) { var cause = null; var expression = ast_for_expr(c, CHILD(ch, 1)); var inst = null, tback = null; // raise [expression] from [cause] - if (NCH(ch) == 4 && CHILD(ch, 2).value == 'from') { + if (NCH(ch) == 4 && CHILD(ch, 2).value == "from") { if (!Sk.__future__.python3) { ast_error(c, CHILD(ch, 2), "raise ... from ... is not available in Python 2"); } cause = ast_for_expr(c, CHILD(ch, 3)); - } else if (NCH(ch) >= 4 && CHILD(ch, 2).value == ',') { + } else if (NCH(ch) >= 4 && CHILD(ch, 2).value == ",") { if (Sk.__future__.python3) { - ast_error(c, n, "Old raise syntax is not available in Python 3") + ast_error(c, n, "Old raise syntax is not available in Python 3"); } // raise [exception_type], [instantiation value] [, [traceback]] // NB traceback isn't implemented in Skulpt yet @@ -1241,17 +1214,16 @@ function ast_for_flow_stmt(c, n) } } return new Sk.astnodes.Raise(expression, cause, inst, tback, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); } - /* fall through */ + /* fall through */ default: Sk.asserts.fail("unexpected flow_stmt: ", TYPE(ch)); return null; } } -function astForArg(c, n) -{ +function astForArg(c, n) { var name; var annotation = null; var ch; @@ -1274,8 +1246,7 @@ function astForArg(c, n) ^^^ start pointing here */ -function handleKeywordonlyArgs(c, n, start, kwonlyargs, kwdefaults) -{ +function handleKeywordonlyArgs(c, n, start, kwonlyargs, kwdefaults) { var argname; var ch; var expression; @@ -1296,15 +1267,13 @@ function handleKeywordonlyArgs(c, n, start, kwonlyargs, kwdefaults) if (i + 1 < NCH(n) && CHILD(n, i + 1).type == TOK.T_EQUAL) { kwdefaults[j] = ast_for_expr(c, CHILD(n, i + 2)); i += 2; /* '=' and test */ - } - else { /* setting NULL if no default value exists */ + } else { /* setting NULL if no default value exists */ kwdefaults[j] = null; } if (NCH(ch) == 3) { /* ch is NAME ':' test */ annotation = ast_for_expr(c, CHILD(ch, 2)); - } - else { + } else { annotation = null; } ch = CHILD(ch, 0); @@ -1322,7 +1291,7 @@ function handleKeywordonlyArgs(c, n, start, kwonlyargs, kwdefaults) return i; } -function astForArguments (c, n) { +function astForArguments(c, n) { var k; var j; var i; @@ -1355,14 +1324,13 @@ function astForArguments (c, n) { */ if (n.type === SYM.parameters) { - if (NCH(n) === 2) // () as arglist - { + if (NCH(n) === 2) {// () as arglist return new Sk.astnodes.arguments_([], null, [], [], null, []); } n = CHILD(n, 1); } Sk.asserts.assert(n.type === SYM.varargslist || - n.type === SYM.typedargslist); + n.type === SYM.typedargslist); // Skulpt note: the "counting numbers of args" section @@ -1386,37 +1354,35 @@ function astForArguments (c, n) { posdefaults[j++] = ast_for_expr(c, CHILD(n, i + 2)); i += 2; foundDefault = 1; - } - else if (foundDefault) { + } else if (foundDefault) { throw new Sk.builtin.SyntaxError("non-default argument follows default argument", c.c_filename, n.lineno); } posargs[k++] = astForArg(c, ch); i += 2; /* the name and the comma */ break; case TOK.T_STAR: - if (i+1 >= NCH(n) || - (i+2 == NCH(n) && CHILD(n, i+1).type == TOK.T_COMMA)) { + if (i + 1 >= NCH(n) || + (i + 2 == NCH(n) && CHILD(n, i + 1).type == TOK.T_COMMA)) { throw new Sk.builtin.SyntaxError("named arguments must follow bare *", c.c_filename, n.lineno); } - ch = CHILD(n, i+1); /* tfpdef or COMMA */ + ch = CHILD(n, i + 1); /* tfpdef or COMMA */ if (ch.type == TOK.T_COMMA) { i += 2; /* now follows keyword only arguments */ i = handleKeywordonlyArgs(c, n, i, - kwonlyargs, kwdefaults); - } - else { + kwonlyargs, kwdefaults); + } else { vararg = astForArg(c, ch); i += 3; if (i < NCH(n) && (CHILD(n, i).type == SYM.tfpdef - || CHILD(n, i).type == SYM.vfpdef)) { + || CHILD(n, i).type == SYM.vfpdef)) { i = handleKeywordonlyArgs(c, n, i, - kwonlyargs, kwdefaults); + kwonlyargs, kwdefaults); } } break; case TOK.T_DOUBLESTAR: - ch = CHILD(n, i+1); /* tfpdef */ + ch = CHILD(n, i + 1); /* tfpdef */ Sk.asserts.assert(ch.type == SYM.tfpdef || ch.type == SYM.vfpdef); kwarg = astForArg(c, ch); i += 3; @@ -1429,8 +1395,7 @@ function astForArguments (c, n) { return new Sk.astnodes.arguments_(posargs, vararg, kwonlyargs, kwdefaults, kwarg, posdefaults); } -function ast_for_async_funcdef(c, n, decorator_seq) -{ +function ast_for_async_funcdef(c, n, decorator_seq) { /* async_funcdef: 'async' funcdef */ REQ(n, SYM.async_funcdef); REQ(CHILD(n, 0), TOK.T_NAME); @@ -1444,7 +1409,7 @@ function ast_for_async_funcdef(c, n, decorator_seq) function ast_for_funcdef(c, n, decorator_seq) { /* funcdef: 'def' NAME parameters ['->' test] ':' suite */ return ast_for_funcdef_impl(c, n, decorator_seq, - false /* is_async */); + false /* is_async */); } function ast_for_funcdef_impl(c, n0, decorator_seq, is_async) { @@ -1476,18 +1441,19 @@ function ast_for_funcdef_impl(c, n0, decorator_seq, is_async) { if (!args) { return NULL; } - if (TYPE(CHILD(n, name_i+2)) == TOK.T_RARROW) { + if (TYPE(CHILD(n, name_i + 2)) == TOK.T_RARROW) { returns = ast_for_expr(c, CHILD(n, name_i + 3)); if (!returns) { - return NULL + return NULL; } name_i += 2; } if (TYPE(CHILD(n, name_i + 3)) == TOK.T_TYPE_COMMENT) { type_comment = TOK.T_NEW_TYPE_COMMENT(CHILD(n, name_i + 3)); - if (!type_comment) + if (!type_comment) { return NULL; + } name_i += 1; } @@ -1507,30 +1473,32 @@ function ast_for_funcdef_impl(c, n0, decorator_seq, is_async) { return NULL; } type_comment = TOK.T_NEW_TYPE_COMMENT(tc); - if (!type_comment) + if (!type_comment) { return NULL; + } } } - if (is_async) + if (is_async) { return new Sk.astnodes.AsyncFunctionDef(name, args, body, decorator_seq, returns, type_comment, - LINENO(n0), n0.col_offset, n0.end_lineno, n0.end_col_offset); - else + LINENO(n0), n0.col_offset, n0.end_lineno, n0.end_col_offset); + } else { return new Sk.astnodes.FunctionDef(name, args, body, decorator_seq, returns, type_comment, - LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); + LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); + } } -function astForClassBases (c, n) { +function astForClassBases(c, n) { /* testlist: test (',' test)* [','] */ Sk.asserts.assert(NCH(n) > 0); REQ(n, SYM.testlist); if (NCH(n) === 1) { - return [ ast_for_expr(c, CHILD(n, 0)) ]; + return [ast_for_expr(c, CHILD(n, 0))]; } return seq_for_testlist(c, n); } -function astForClassdef (c, n, decoratorSeq) { +function astForClassdef(c, n, decoratorSeq) { /* classdef: 'class' NAME ['(' arglist ')'] ':' suite */ var classname; var call; @@ -1541,11 +1509,11 @@ function astForClassdef (c, n, decoratorSeq) { if (NCH(n) == 4) { /* class NAME ':' suite */ s = astForSuite(c, CHILD(n, 3)); classname = new_identifier(CHILD(n, 1).value); - forbiddenCheck(c, CHILD(n,3), classname, n.lineno); + forbiddenCheck(c, CHILD(n, 3), classname, n.lineno); return new Sk.astnodes.ClassDef(classname, [], [], s, decoratorSeq, - /*TODO docstring*/null, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + /*TODO docstring*/null, LINENO(n), n.col_offset, + n.end_lineno, n.end_col_offset); } if (TYPE(CHILD(n, 3)) === TOK.T_RPAR) { /* class NAME '(' ')' ':' suite */ @@ -1553,8 +1521,8 @@ function astForClassdef (c, n, decoratorSeq) { classname = new_identifier(CHILD(n, 1).value); forbiddenCheck(c, CHILD(n, 3), classname, CHILD(n, 3).lineno); return new Sk.astnodes.ClassDef(classname, [], [], s, decoratorSeq, - /*TODO docstring*/null, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + /*TODO docstring*/null, LINENO(n), n.col_offset, + n.end_lineno, n.end_col_offset); } /* class NAME '(' arglist ')' ':' suite */ @@ -1564,27 +1532,26 @@ function astForClassdef (c, n, decoratorSeq) { var dummy; dummy_name = new_identifier(CHILD(n, 1)); dummy = new Sk.astnodes.Name(dummy_name, Sk.astnodes.Load, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); call = ast_for_call(c, CHILD(n, 3), dummy, false); } s = astForSuite(c, CHILD(n, 6)); classname = new_identifier(CHILD(n, 1).value); - forbiddenCheck(c, CHILD(n,1), classname, CHILD(n,1).lineno); + forbiddenCheck(c, CHILD(n, 1), classname, CHILD(n, 1).lineno); return new Sk.astnodes.ClassDef(classname, call.args, call.keywords, s, - decoratorSeq, /*TODO docstring*/null, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + decoratorSeq, /*TODO docstring*/null, LINENO(n), n.col_offset, + n.end_lineno, n.end_col_offset); } -function astForLambdef (c, n) { +function astForLambdef(c, n) { /* lambdef: 'lambda' [varargslist] ':' test */ var args; var expression; if (NCH(n) === 3) { args = new Sk.astnodes.arguments_([], null, null, []); expression = ast_for_expr(c, CHILD(n, 2)); - } - else { + } else { args = astForArguments(c, CHILD(n, 1)); expression = ast_for_expr(c, CHILD(n, 3)); } @@ -1736,8 +1703,7 @@ function count_comp_fors(c, n) { } if (NCH(n) == (5 + is_async)) { n = CHILD(n, 4 + is_async); - } - else { + } else { return n_fors; } count_comp_iter: while (true) { @@ -1759,14 +1725,14 @@ function count_comp_fors(c, n) { } } -function count_comp_ifs(c, n) -{ +function count_comp_ifs(c, n) { var n_ifs = 0; while (true) { REQ(n, SYM.comp_iter); - if (TYPE(CHILD(n, 0)) == SYM.comp_for) + if (TYPE(CHILD(n, 0)) == SYM.comp_for) { return n_ifs; + } n = CHILD(n, 0); REQ(n, SYM.comp_if); n_ifs++; @@ -1794,7 +1760,7 @@ function ast_for_comprehension(c, n) { } for_ch = CHILD(n, 1 + is_async); - t = ast_for_exprlist(c, for_ch, Sk.astnodes. Store); + t = ast_for_exprlist(c, for_ch, Sk.astnodes.Store); if (!t) { return null; } @@ -1834,12 +1800,13 @@ function ast_for_comprehension(c, n) { /* Check the # of children rather than the length of t, since (x for x, in ...) has 1 element in t, but still requires a Tuple. */ first = t[0]; - if (NCH(for_ch) == 1) + if (NCH(for_ch) == 1) { comp = new Sk.astnodes.comprehension(first, expression, null, is_async); - else + } else { comp = new Sk.astnodes.comprehension(new Sk.astnodes.Tuple(t, Sk.astnodes.Store, first.lineno, first.col_offset, - for_ch.end_lineno, for_ch.end_col_offset), - expression, null, is_async); + for_ch.end_lineno, for_ch.end_col_offset), + expression, null, is_async); + } if (NCH(n) == (5 + is_async)) { var j, n_ifs; @@ -1926,13 +1893,13 @@ function ast_for_itercomp(c, n, type) { if (type == COMP_GENEXP) { return new Sk.astnodes.GeneratorExp(elt, comps, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); } else if (type == COMP_LISTCOMP) { return new Sk.astnodes.ListComp(elt, comps, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); } else if (type == COMP_SETCOMP) { return new Sk.astnodes.SetComp(elt, comps, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); } else { /* Should never happen */ return null; @@ -1943,21 +1910,21 @@ function ast_for_itercomp(c, n, type) { * of an unpacking, key is NULL. *i is advanced by the number of ast * elements. Iff successful, nonzero is returned. */ -function ast_for_dictelement(c, n, i) -{ +function ast_for_dictelement(c, n, i) { var expression; if (TYPE(CHILD(n, i)) == TOK.T_DOUBLESTAR) { Sk.asserts.assert(NCH(n) - i >= 2); expression = ast_for_expr(c, CHILD(n, i + 1)); - return { key: null, value: expression, i: i + 2 } + return {key: null, value: expression, i: i + 2}; } else { Sk.asserts.assert(NCH(n) - i >= 3); expression = ast_for_expr(c, CHILD(n, i)); - if (!expression) + if (!expression) { return 0; + } var key = expression; REQ(CHILD(n, i + 1), TOK.T_COLON); @@ -1969,7 +1936,7 @@ function ast_for_dictelement(c, n, i) var value = expression; - return { key: key, value: value, i: i + 3 }; + return {key: key, value: value, i: i + 3}; } } @@ -1984,8 +1951,7 @@ function ast_for_dictcomp(c, n) { return new Sk.astnodes.DictComp(key, value, comps, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } -function ast_for_dictdisplay(c, n) -{ +function ast_for_dictdisplay(c, n) { var i; var j; var keys = [], values = []; @@ -1993,14 +1959,14 @@ function ast_for_dictdisplay(c, n) j = 0; for (i = 0; i < NCH(n); i++) { var res = ast_for_dictelement(c, n, i); - i = res.i + i = res.i; keys[j] = res.key; values[j] = res.value; j++; } return new Sk.astnodes.Dict(keys, values, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); } function ast_for_gen_expr(c, n) { @@ -2013,19 +1979,18 @@ function ast_for_setcomp(c, n) { return astForIterComp(c, n, COMP_SETCOMP); } -function astForWhileStmt (c, n) { +function astForWhileStmt(c, n) { /* while_stmt: 'while' test ':' suite ['else' ':' suite] */ REQ(n, SYM.while_stmt); if (NCH(n) === 4) { return new Sk.astnodes.While(ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), [], n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); - } - else if (NCH(n) === 7) { + } else if (NCH(n) === 7) { return new Sk.astnodes.While(ast_for_expr(c, CHILD(n, 1)), astForSuite(c, CHILD(n, 3)), astForSuite(c, CHILD(n, 6)), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } Sk.asserts.fail("wrong number of tokens for 'while' stmt"); } -function astForAugassign (c, n) { +function astForAugassign(c, n) { REQ(n, SYM.augassign); n = CHILD(n, 0); switch (n.value.charAt(0)) { @@ -2064,7 +2029,7 @@ function astForAugassign (c, n) { } } -function astForBinop (c, n) { +function astForBinop(c, n) { /* Must account for a sequence of expressions. How should A op B op C by represented? BinOp(BinOp(A, op, B), op, C). @@ -2088,7 +2053,7 @@ function astForBinop (c, n) { return result; } -function ast_for_testlist (c, n) { +function ast_for_testlist(c, n) { /* testlist_comp: test (',' comp_for | (',' test)* [',']) */ /* testlist: test (',' test)* [','] */ Sk.asserts.assert(NCH(n) > 0); @@ -2096,20 +2061,18 @@ function ast_for_testlist (c, n) { if (NCH(n) > 1) { Sk.asserts.assert(CHILD(n, 1).type !== SYM.comp_for); } - } - else { + } else { Sk.asserts.assert(n.type === SYM.testlist || n.type === SYM.testlist_star_expr); } if (NCH(n) === 1) { return ast_for_expr(c, CHILD(n, 0)); - } - else { + } else { return new Sk.astnodes.Tuple(seq_for_testlist(c, n), Sk.astnodes.Load, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset/*, c.c_arena */); } } -function ast_for_exprStmt (c, n) { +function ast_for_exprStmt(c, n) { var expression; var value; var e; @@ -2119,9 +2082,9 @@ function ast_for_exprStmt (c, n) { var varName; var expr1; var ch; - var deep; var ann; var simple; + var deep; var expr3; REQ(n, SYM.expr_stmt); /* expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | @@ -2134,8 +2097,7 @@ function ast_for_exprStmt (c, n) { */ if (NCH(n) === 1) { return new Sk.astnodes.Expr(ast_for_testlist(c, CHILD(n, 0)), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); - } - else if (CHILD(n, 1).type === SYM.augassign) { + } else if (CHILD(n, 1).type === SYM.augassign) { ch = CHILD(n, 0); expr1 = ast_for_testlist(c, ch); setContext(c, expr1, Sk.astnodes.Store, ch); @@ -2158,14 +2120,12 @@ function ast_for_exprStmt (c, n) { ch = CHILD(n, 2); if (ch.type === SYM.testlist) { expr2 = ast_for_testlist(c, ch); - } - else { + } else { expr2 = ast_for_expr(c, ch); } return new Sk.astnodes.AugAssign(expr1, astForAugassign(c, CHILD(n, 1)), expr2, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); - } - else if (CHILD(n, 1).type === SYM.annassign) { + } else if (CHILD(n, 1).type === SYM.annassign) { if (!Sk.__future__.python3) { throw new Sk.builtin.SyntaxError("Annotated assignment is not supported in Python 2", c.c_filename, n.lineno); } @@ -2202,11 +2162,11 @@ function ast_for_exprStmt (c, n) { default: throw new Sk.builtin.SyntaxError("illegal target for annotation", c.c_filename, n.lineno); } - + if (expr1.constructor != Sk.astnodes.Name) { simple = 0; } - + ch = CHILD(ann, 1); expr2 = ast_for_expr(c, ch); if (NCH(ann) == 2) { @@ -2216,8 +2176,7 @@ function ast_for_exprStmt (c, n) { expr3 = ast_for_expr(c, ch); return new Sk.astnodes.AnnAssign(expr1, expr2, expr3, simple, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } - } - else { + } else { // normal assignment REQ(CHILD(n, 1), TOK.T_EQUAL); targets = []; @@ -2233,15 +2192,14 @@ function ast_for_exprStmt (c, n) { value = CHILD(n, NCH(n) - 1); if (value.type === SYM.testlist_star_expr) { expression = ast_for_testlist(c, value); - } - else { + } else { expression = ast_for_expr(c, value); } return new Sk.astnodes.Assign(targets, expression, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } } -function astForIfexpr (c, n) { +function astForIfexpr(c, n) { /* test: or_test 'if' or_test 'else' test */ Sk.asserts.assert(NCH(n) === 5); return new Sk.astnodes.IfExp( @@ -2253,9 +2211,9 @@ function astForIfexpr (c, n) { /** * s is a python-style string literal, including quote characters and u/r/b - * prefixes. Returns decoded string object. + * prefixes. Returns [decoded string object, is-an-fstring] */ -function parsestr (c, s) { +function parsestr(c, s) { var encodeUtf8 = function (s) { return unescape(encodeURIComponent(s)); }; @@ -2278,55 +2236,41 @@ function parsestr (c, s) { c = s.charAt(i); if (c === "n") { ret += "\n"; - } - else if (c === "\\") { + } else if (c === "\\") { ret += "\\"; - } - else if (c === "t") { + } else if (c === "t") { ret += "\t"; - } - else if (c === "r") { + } else if (c === "r") { ret += "\r"; - } - else if (c === "b") { + } else if (c === "b") { ret += "\b"; - } - else if (c === "f") { + } else if (c === "f") { ret += "\f"; - } - else if (c === "v") { + } else if (c === "v") { ret += "\v"; - } - else if (c === "0") { + } else if (c === "0") { ret += "\0"; - } - else if (c === '"') { - ret += '"'; - } - else if (c === '\'') { - ret += '\''; - } - else if (c === "\n") /* escaped newline, join lines */ { - } - else if (c === "x") { + } else if (c === "\"") { + ret += "\""; + } else if (c === "'") { + ret += "'"; + } else if (c === "\n") /* escaped newline, join lines */ { + } else if (c === "x") { d0 = s.charAt(++i); d1 = s.charAt(++i); ret += encodeUtf8(String.fromCharCode(parseInt(d0 + d1, 16))); - } - else if (c === "u" || c === "U") { + } else if (c === "u" || c === "U") { d0 = s.charAt(++i); d1 = s.charAt(++i); d2 = s.charAt(++i); d3 = s.charAt(++i); ret += encodeUtf8(String.fromCharCode(parseInt(d0 + d1, 16), parseInt(d2 + d3, 16))); - } - else { + } else { // Leave it alone ret += "\\" + c; // Sk.asserts.fail("unhandled escape: '" + c.charCodeAt(0) + "'"); } - } - else { + } else { ret += c; } } @@ -2338,26 +2282,33 @@ function parsestr (c, s) { var quote = s.charAt(0); var rawmode = false; var unicode = false; + var fmode = false; // treats every sequence as unicodes even if they are not treated with uU prefix // kinda hacking though working for most purposes - if((c.c_flags & Sk.Parser.CO_FUTURE_UNICODE_LITERALS || Sk.__future__.unicode_literals === true)) { + if ((c.c_flags & Sk.Parser.CO_FUTURE_UNICODE_LITERALS || Sk.__future__.unicode_literals === true)) { unicode = true; } - if (quote === "u" || quote === "U") { - s = s.substr(1); - quote = s.charAt(0); - unicode = true; - } - else if (quote === "r" || quote === "R") { + let seenflags = {}; + + while (true) { + if (quote === "u" || quote === "U") { + unicode = true; + } else if (quote === "r" || quote === "R") { + rawmode = true; + } else if (quote === "b" || quote === "B") { + Sk.asserts.assert(!"todo; haven't done b'' strings yet"); + } else if (quote === "f" || quote === "F") { + fmode = true; + } else { + break; + } s = s.substr(1); quote = s.charAt(0); - rawmode = true; } - Sk.asserts.assert(quote !== "b" && quote !== "B", "todo; haven't done b'' strings yet"); - Sk.asserts.assert(quote === "'" || quote === '"' && s.charAt(s.length - 1) === quote); + Sk.asserts.assert(quote === "'" || quote === "\"" && s.charAt(s.length - 1) === quote); s = s.substr(1, s.length - 2); if (unicode) { s = encodeUtf8(s); @@ -2369,27 +2320,300 @@ function parsestr (c, s) { } if (rawmode || s.indexOf("\\") === -1) { - return strobj(decodeUtf8(s)); + return [strobj(decodeUtf8(s)), fmode]; + } + return [strobj(decodeEscape(s, quote)), fmode]; +} + +function fstring_compile_expr(str, expr_start, expr_end, c, n) { + Sk.asserts.assert(expr_end >= expr_start); + Sk.asserts.assert(str.charAt(expr_start - 1) == "{"); + Sk.asserts.assert(str.charAt(expr_end) == "}" || str.charAt(expr_end) == "!" || str.charAt(expr_end) == ":"); + + let s = str.substring(expr_start, expr_end); + + /* If the substring is all whitespace, it's an error. We need to catch this + here, and not when we call PyParser_SimpleParseStringFlagsFilename, + because turning the expression '' in to '()' would go from being invalid + to valid. */ + if (/^\s*$/.test(s)) { + ast_error(c, n, "f-string: empty expression not allowed"); + } + s = "(" + s + ")"; + + let ast; + try { + let parsed = Sk.parse("", s); + ast = Sk.astFromParse(parsed.cst, "", parsed.flags); + } catch (e) { + if (e.traceback && e.traceback[0]) { + let tb = e.traceback[0]; + tb.lineno = (tb.lineno || 1) - 1 + LINENO(n); + tb.filename = c.c_filename; + } + throw e; + } + + // TODO fstring_fix_node_location + + Sk.asserts.assert(ast.body.length == 1 && ast.body[0].constructor === Sk.astnodes.Expr); + + return ast.body[0].value; +} + +function fstring_find_expr(str, start, end, raw, recurse_lvl, c, n) { + let i = start; + Sk.asserts.assert(str.charAt(i) == "{"); + i++; + let expr_start = i; + /* null if we're not in a string, else the quote char we're trying to + match (single or double quote). */ + let quote_char = null; + /* If we're inside a string, 1=normal, 3=triple-quoted. */ + let string_type = 0; + /* Keep track of nesting level for braces/parens/brackets in + expressions. */ + let nested_depth = 0; + + let format_spec, conversion; + + let unexpected_end_of_string = () => ast_error(c, n, "f-string: expecting '}'"); + + Sk.asserts.assert(i <= end); + + for (; i < end; i++) { + let ch = str.charAt(i); + + /* Nowhere inside an expression is a backslash allowed. */ + if (ch == "\\") { + /* Error: can't include a backslash character, inside + parens or strings or not. */ + ast_error(c, n, "f-string expression part cannot include a backslash"); + } + if (quote_char) { + /* We're inside a string. See if we're at the end. */ + /* This code needs to implement the same non-error logic + as tok_get from tokenizer.c, at the letter_quote + label. To actually share that code would be a + nightmare. But, it's unlikely to change and is small, + so duplicate it here. Note we don't need to catch all + of the errors, since they'll be caught when parsing the + expression. We just need to match the non-error + cases. Thus we can ignore \n in single-quoted strings, + for example. Or non-terminated strings. */ + if (ch == quote_char) { + /* Does this match the string_type (single or triple + quoted)? */ + if (string_type == 3) { + if (i + 2 < end && str.charAt(i + 1) == ch && str.charAt(i + 2) == ch) { + /* We're at the end of a triple quoted string. */ + i += 2; + string_type = 0; + quote_char = 0; + continue; + } + } else { + /* We're at the end of a normal string. */ + quote_char = 0; + string_type = 0; + continue; + } + } + } else if (ch == "'" || ch == "\"") { + /* Is this a triple quoted string? */ + if (i + 2 < end && str.charAt(i + 1) == ch && str.charAt(i + 2) == ch) { + string_type = 3; + i += 2; + } else { + /* Start of a normal string. */ + string_type = 1; + } + /* Start looking for the end of the string. */ + quote_char = ch; + } else if (ch == "[" || ch == "{" || ch == "(") { + nested_depth++; + } else if (nested_depth != 0 && + (ch == "]" || ch == "}" || ch == ")")) { + nested_depth--; + } else if (ch == "#") { + /* Error: can't include a comment character, inside parens + or not. */ + ast_error(c, n, "f-string expression part cannot include '#'"); + } else if (nested_depth == 0 && + (ch == "!" || ch == ":" || ch == "}")) { + /* First, test for the special case of "!=". Since '=' is + not an allowed conversion character, nothing is lost in + this test. */ + if (ch == "!" && i + 1 < end && str.charAt(i + 1) == "=") { + /* This isn't a conversion character, just continue. */ + continue; + } + /* Normal way out of this loop. */ + break; + } else { + /* Just consume this char and loop around. */ + } + } + + /* If we leave this loop in a string or with mismatched parens, we + don't care. We'll get a syntax error when compiling the + expression. But, we can produce a better error message, so + let's just do that.*/ + if (quote_char) { + ast_error(c, n, "f-string: unterminated string"); + } + if (nested_depth) { + ast_error(c, n, "f-string: mismatched '(', '{', or '['"); + } + + let expr_end = i; + + /* Compile the expression as soon as possible, so we show errors + related to the expression before errors related to the + conversion or format_spec. */ + let simple_expression = fstring_compile_expr(str, expr_start, expr_end, c, n); + + /* Check for a conversion char, if present. */ + if (str.charAt(i) == "!") { + i++; + if (i >= end) { + unexpected_end_of_string(); + } + + conversion = str.charAt(i); + i++; + + /* Validate the conversion. */ + if (!(conversion == "s" || conversion == "r" + || conversion == "a")) { + ast_error(c, n, "f-string: invalid conversion character: expected 's', 'r', or 'a'"); + } } - return strobj(decodeEscape(s, quote)); + + /* Check for the format spec, if present. */ + if (i >= end) { + unexpected_end_of_string(); + } + if (str.charAt(i) == ":") { + i++; + if (i >= end) { + unexpected_end_of_string(); + } + + /* Parse the format spec. */ + [format_spec, i] = fstring_parse(str, i, end, raw, recurse_lvl + 1, c, n); + } + + if (i >= end || str.charAt(i) != "}") { + unexpected_end_of_string(); + } + + /* We're at a right brace. Consume it. */ + i++; + + /* And now create the FormattedValue node that represents this + entire expression with the conversion and format spec. */ + let expr = new Sk.astnodes.FormattedValue(simple_expression, conversion, + format_spec, LINENO(n), n.col_offset, + n.end_lineno, n.end_col_offset); + + return [expr, i]; } -function parsestrplus (c, n) { - var i; - var ret; - REQ(CHILD(n, 0), TOK.T_STRING); - ret = new Sk.builtin.str(""); - for (i = 0; i < NCH(n); ++i) { +function fstring_parse(str, start, end, raw, recurse_lvl, c, n) { + let values = []; + let idx = start; + + let addLiteral = (literal) => { + if (literal.indexOf("}") !== -1) { + // We need to error out on any lone }s, and + // replace doubles with singles. + if (/(^|[^}])}(}})*($|[^}])/.test(literal)) { + throw new SyntaxError("f-string: single '}' is not allowed", LINENO(n), n.col_offset); + } + literal = literal.replace(/}}/g, "}"); + } + values.push(new Sk.astnodes.Str(new Sk.builtin.str(literal), LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset)); + }; + + + while (idx < end) { + let bidx = str.indexOf("{", idx); + if (recurse_lvl !== 0) { + // If there's a closing brace before the next open brace, + // that's our end-of-expression + let cbidx = str.indexOf("}", idx); + if (cbidx !== -1) { + if (bidx === -1) { + end = cbidx; + } else if (bidx > cbidx) { + bidx = -1; + end = cbidx; + } + } + } + if (bidx === -1) { + addLiteral(str.substring(idx, end)); + idx = end; + break; + } else if (bidx + 1 < end && str.charAt(bidx + 1) === "{") { + // Swallow the double {{ + addLiteral(str.substring(idx, bidx + 1)); + idx = bidx + 2; + continue; + } else { + addLiteral(str.substring(idx, bidx)); + idx = bidx; + + // And now parse the f-string expression itself + let [expr, endIdx] = fstring_find_expr(str, bidx, end, raw, recurse_lvl, c, n); + values.push(expr); + idx = endIdx; + } + } + return [new Sk.astnodes.JoinedStr(values, LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset), idx]; +} + +function parsestrplus(c, n) { + let strs = []; + let lastStrNode; + + for (let i = 0; i < NCH(n); ++i) { + let chstr = CHILD(n, i).value; + let str, fmode; try { - ret = ret.sq$concat(parsestr(c, CHILD(n, i).value)); + let r = parsestr(c, chstr); + str = r[0]; + fmode = r[1]; } catch (x) { throw new Sk.builtin.SyntaxError("invalid string (possibly contains a unicode character)", c.c_filename, CHILD(n, i).lineno); } + if (fmode) { + if (!Sk.__future__.python3) { + throw new Sk.builtin.SyntaxError("invalid string (f-strings are not supported in Python 2)", c.c_filename, CHILD(n, i).lineno); + } + let jss = str.$jsstr(); + let [astnode, _] = fstring_parse(jss, 0, jss.length, false, 0, c, CHILD(n, i)); + strs.push.apply(strs, astnode.values); + lastStrNode = null; + } else { + if (lastStrNode) { + lastStrNode.s = lastStrNode.s.sq$concat(str); + } else { + lastStrNode = new Sk.astnodes.Str(str, LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); + strs.push(lastStrNode); + } + } + } + + if (strs.length === 1 && strs[0].constructor === Sk.astnodes.Str) { + return strs[0]; + } else { + return new Sk.astnodes.JoinedStr(strs, LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); } - return ret; } -function parsenumber (c, s, lineno) { +function parsenumber(c, s, lineno) { var neg; var val; var tmp; @@ -2442,14 +2666,13 @@ function parsenumber (c, s, lineno) { } val = parseInt(tmp, 8); } - } - else { + } else { // Decimal val = parseInt(tmp, 10); } // Convert to long - if (val > Sk.builtin.int_.threshold$ && + if (val > Number.MAX_SAFE_INTEGER && Math.floor(val) === val && (s.indexOf("e") === -1 && s.indexOf("E") === -1)) { return Sk.longFromStr(s, 0); @@ -2463,7 +2686,7 @@ function parsenumber (c, s, lineno) { } } -function astForSlice (c, n) { +function astForSlice(c, n) { var n2; var step; var upper; @@ -2495,8 +2718,7 @@ function astForSlice (c, n) { upper = ast_for_expr(c, n2); } } - } - else if (NCH(n) > 2) { + } else if (NCH(n) > 2) { n2 = CHILD(n, 2); if (n2.type === SYM.test) { upper = ast_for_expr(c, n2); @@ -2508,8 +2730,7 @@ function astForSlice (c, n) { if (NCH(ch) === 1) { ch = CHILD(ch, 0); step = new Sk.astnodes.NameConstant(Sk.builtin.none.none$, Sk.astnodes.Load, ch.lineno, ch.col_offset, ch.end_lineno, ch.end_col_offset); - } - else { + } else { ch = CHILD(ch, 1); if (ch.type === SYM.test) { step = ast_for_expr(c, ch); @@ -2519,8 +2740,7 @@ function astForSlice (c, n) { return new Sk.astnodes.Slice(lower, upper, step); } -function ast_for_atom(c, n) -{ +function ast_for_atom(c, n) { /* atom: '(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' | '{' [dictmaker|testlist_comp] '}' | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False' @@ -2547,47 +2767,22 @@ function ast_for_atom(c, n) name = new_identifier(s, c); /* All names start in Load context, but may later be changed. */ return new Sk.astnodes.Name(name, Sk.astnodes.Load, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); - } - case TOK.T_STRING: { - var str = parsestrplus(c, n); - // if (!str) { - // const char *errtype = NULL; - // if (PyErr_ExceptionMatches(PyExc_UnicodeError)) - // errtype = "unicode error"; - // else if (PyErr_ExceptionMatches(PyExc_ValueError)) - // errtype = "value error"; - // if (errtype) { - // PyObject *type, *value, *tback, *errstr; - // PyErr_Fetch(&type, &value, &tback); - // errstr = PyObject_Str(value); - // if (errstr) { - // ast_error(c, n, "(%s) %U", errtype, errstr); - // Py_DECREF(errstr); - // } - // else { - // PyErr_Clear(); - // ast_error(c, n, "(%s) unknown error", errtype); - // } - // Py_DECREF(type); - // Py_XDECREF(value); - // Py_XDECREF(tback); - // } - // return NULL; - // } - return new Sk.astnodes.Str(str, LINENO(n), n.col_offset, n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); } + case TOK.T_STRING: + return parsestrplus(c, n); case TOK.T_NUMBER: return new Sk.astnodes.Num(parsenumber(c, ch.value, n.lineno), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); case TOK.T_ELLIPSIS: /* Ellipsis */ return new Sk.astnodes.Ellipsis(LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); case TOK.T_LPAR: /* some parenthesized expressions */ ch = CHILD(n, 1); - if (TYPE(ch) == TOK.T_RPAR) + if (TYPE(ch) == TOK.T_RPAR) { return new Sk.astnodes.Tuple([], Sk.astnodes.Load, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); + } if (TYPE(ch) == SYM.yield_expr) { return ast_for_expr(c, ch); @@ -2600,16 +2795,16 @@ function ast_for_atom(c, n) if (TYPE(CHILD(ch, 1)) == SYM.comp_for) { return copy_location(ast_for_genexp(c, ch), n); - } - else { + } else { return copy_location(ast_for_testlist(c, ch), n); } case TOK.T_LSQB: /* list (or list comprehension) */ ch = CHILD(n, 1); - if (TYPE(ch) == TOK.T_RSQB) + if (TYPE(ch) == TOK.T_RSQB) { return new Sk.astnodes.List([], Sk.astnodes.Load, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); + n.end_lineno, n.end_col_offset); + } REQ(ch, SYM.testlist_comp); if (NCH(ch) == 1 || TYPE(CHILD(ch, 1)) == TOK.T_COMMA) { @@ -2618,9 +2813,8 @@ function ast_for_atom(c, n) return null; } return new Sk.astnodes.List(elts, Sk.astnodes.Load, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); - } - else { + n.end_lineno, n.end_col_offset); + } else { return copy_location(ast_for_listcomp(c, ch), n); } case TOK.T_LBRACE: { @@ -2633,32 +2827,28 @@ function ast_for_atom(c, n) if (TYPE(ch) == TOK.T_RBRACE) { /* It's an empty dict. */ return new Sk.astnodes.Dict(null, null, LINENO(n), n.col_offset, - n.end_lineno, n.end_col_offset); - } - else { + n.end_lineno, n.end_col_offset); + } else { var is_dict = (TYPE(CHILD(ch, 0)) == TOK.T_DOUBLESTAR); if (NCH(ch) == 1 || - (NCH(ch) > 1 && + (NCH(ch) > 1 && TYPE(CHILD(ch, 1)) == TOK.T_COMMA)) { /* It's a set display. */ res = ast_for_setdisplay(c, ch); - } - else if (NCH(ch) > 1 && - TYPE(CHILD(ch, 1)) == SYM.comp_for) { + } else if (NCH(ch) > 1 && + TYPE(CHILD(ch, 1)) == SYM.comp_for) { /* It's a set comprehension. */ res = ast_for_setcomp(c, ch); - } - else if (NCH(ch) > 3 - is_dict && - TYPE(CHILD(ch, 3 - is_dict)) == SYM.comp_for) { + } else if (NCH(ch) > 3 - is_dict && + TYPE(CHILD(ch, 3 - is_dict)) == SYM.comp_for) { /* It's a dictionary comprehension. */ if (is_dict) { ast_error(c, n, - "dict unpacking cannot be used in dict comprehension"); + "dict unpacking cannot be used in dict comprehension"); return null; } res = ast_for_dictcomp(c, ch); - } - else { + } else { /* It's a dictionary display. */ res = ast_for_dictdisplay(c, ch); } @@ -2716,7 +2906,7 @@ function astForAtom(c, n) { } } - var name = new_identifier(s, c) + var name = new_identifier(s, c); /* All names start in Load context, but may later be changed. */ return new Sk.astnodes.Name(name, Sk.astnodes.Load, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); @@ -2758,8 +2948,7 @@ function astForAtom(c, n) { if (n.type === TOK.T_RBRACE) { //it's an empty dict return new Sk.astnodes.Dict([], null, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); - } - else if (NCH(ch) === 1 || (NCH(ch) !== 0 && CHILD(ch, 1).type === TOK.T_COMMA)) { + } else if (NCH(ch) === 1 || (NCH(ch) !== 0 && CHILD(ch, 1).type === TOK.T_COMMA)) { //it's a simple set elts = []; size = Math.floor((NCH(ch) + 1) / 2); @@ -2768,16 +2957,13 @@ function astForAtom(c, n) { elts[i / 2] = expression; } return new Sk.astnodes.Set(elts, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); - } - else if (NCH(ch) !== 0 && CHILD(ch, 1).type == SYM.comp_for) { + } else if (NCH(ch) !== 0 && CHILD(ch, 1).type == SYM.comp_for) { //it's a set comprehension return ast_for_setcomp(c, ch); - } - else if (NCH(ch) > 3 && CHILD(ch, 3).type === SYM.comp_for) { + } else if (NCH(ch) > 3 && CHILD(ch, 3).type === SYM.comp_for) { //it's a dict compr. I think. return ast_for_dictcomp(c, ch); - } - else { + } else { size = Math.floor((NCH(ch) + 1) / 4); // + 1 for no trailing comma case for (i = 0; i < NCH(ch); i += 4) { keys[i / 4] = ast_for_expr(c, CHILD(ch, i)); @@ -2837,13 +3023,12 @@ function astForAtomExpr(c, n) { if (start) { /* there was an AWAIT */ return new Sk.astnodes.Await(e, n.line, n.col_offset, n.end_lineno, n.end_col_offset /*, c->c_arena*/); - } - else { + } else { return e; } } -function astForPower (c, n) { +function astForPower(c, n) { /* power: atom trailer* ('**' factor)* */ var f; @@ -2867,10 +3052,10 @@ function astForStarred(c, n) { REQ(n, SYM.star_expr); /* The Load context is changed later */ - return new Sk.astnodes.Starred(ast_for_expr(c, CHILD(n ,1)), Sk.astnodes.Load, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset /*, c.c_arena */) + return new Sk.astnodes.Starred(ast_for_expr(c, CHILD(n, 1)), Sk.astnodes.Load, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset /*, c.c_arena */); } -function ast_for_expr (c, n) { +function ast_for_expr(c, n) { /* handle the full range of simple expressions test: or_test ['if' or_test 'else' test] | lambdef @@ -2902,11 +3087,10 @@ function ast_for_expr (c, n) { case SYM.test_nocond: if (CHILD(n, 0).type === SYM.lambdef || CHILD(n, 0).type === SYM.lambdef_nocond) { return astForLambdef(c, CHILD(n, 0)); - } - else if (NCH(n) > 1) { + } else if (NCH(n) > 1) { return astForIfexpr(c, n); } - // fallthrough + // fallthrough case SYM.or_test: case SYM.and_test: if (NCH(n) === 1) { @@ -2926,8 +3110,7 @@ function ast_for_expr (c, n) { if (NCH(n) === 1) { n = CHILD(n, 0); continue LOOP; - } - else { + } else { return new Sk.astnodes.UnaryOp(Sk.astnodes.Not, ast_for_expr(c, CHILD(n, 1)), n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } break; @@ -2935,8 +3118,7 @@ function ast_for_expr (c, n) { if (NCH(n) === 1) { n = CHILD(n, 0); continue LOOP; - } - else { + } else { ops = []; cmps = []; for (i = 1; i < NCH(n); i += 2) { @@ -2965,7 +3147,7 @@ function ast_for_expr (c, n) { return astForBinop(c, n); case SYM.yield_expr: var an; - var en + var en; var is_from = false; exp = null; if (NCH(n) > 1) { @@ -3011,7 +3193,7 @@ function astForAsyncStmt(c, n) { } // This is only used for Python 2 support. -function astForPrintStmt (c, n) { +function astForPrintStmt(c, n) { if (Sk.__future__.print_function) { ast_error(c, n, "Missing parentheses in call to 'print'"); @@ -3038,7 +3220,7 @@ function astForPrintStmt (c, n) { return new Sk.astnodes.Print(dest, seq, nl, n.lineno, n.col_offset, n.end_lineno, n.end_col_offset); } -function astForStmt (c, n) { +function astForStmt(c, n) { var ch; if (n.type === SYM.stmt) { Sk.asserts.assert(NCH(n) === 1); @@ -3078,8 +3260,7 @@ function astForStmt (c, n) { default: Sk.asserts.fail("unhandled small_stmt"); } - } - else { + } else { /* compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef | decorated | async_stmt */ @@ -3129,8 +3310,7 @@ Sk.astFromParse = function (n, filename, c_flags) { num = numStmts(ch); if (num === 1) { stmts[k++] = astForStmt(c, ch); - } - else { + } else { ch = CHILD(ch, 0); REQ(ch, SYM.simple_stmt); for (j = 0; j < num; ++j) { @@ -3149,8 +3329,7 @@ Sk.astFromParse = function (n, filename, c_flags) { }; Sk.astDump = function (node) { - var spaces = function (n) // todo; blurgh - { + var spaces = function (n) {// todo; blurgh var i; var ret = ""; for (i = 0; i < n; ++i) { @@ -3175,15 +3354,12 @@ Sk.astDump = function (node) { var namelen; if (node === null) { return indent + "None"; - } - else if (node.prototype && node.prototype._astname !== undefined && node.prototype._isenum) { + } else if (node.prototype && node.prototype._astname !== undefined && node.prototype._isenum) { return indent + node.prototype._astname + "()"; - } - else if (node._astname !== undefined) { + } else if (node._astname !== undefined) { namelen = spaces(node._astname.length + 1); fields = []; - for (i = 0; i < node._fields.length; i += 2) // iter_fields - { + for (i = 0; i < node._fields.length; i += 2) {// iter_fields a = node._fields[i]; // field name b = node._fields[i + 1](node); // field getter func fieldlen = spaces(a.length + 1); @@ -3196,8 +3372,7 @@ Sk.astDump = function (node) { } fieldstr = attrs.join(",\n" + indent + namelen); return indent + node._astname + "(" + fieldstr + ")"; - } - else if (Sk.isArrayLike(node)) { + } else if (Sk.isArrayLike(node)) { //Sk.debugout("arr", node.length); elems = []; for (i = 0; i < node.length; ++i) { @@ -3206,21 +3381,16 @@ Sk.astDump = function (node) { } elemsstr = elems.join(",\n"); return indent + "[" + elemsstr.replace(/^\s+/, "") + "]"; - } - else { + } else { if (node === true) { ret = "True"; - } - else if (node === false) { + } else if (node === false) { ret = "False"; - } - else if (node instanceof Sk.builtin.lng) { + } else if (node instanceof Sk.builtin.lng) { ret = node.tp$str().v; - } - else if (node instanceof Sk.builtin.str) { + } else if (node instanceof Sk.builtin.str) { ret = node["$r"]().v; - } - else { + } else { ret = "" + node; } return indent + ret; @@ -3232,11 +3402,11 @@ Sk.astDump = function (node) { Sk.INHERITANCE_MAP = { - 'mod': [Sk.astnodes.Module, + "mod": [Sk.astnodes.Module, Sk.astnodes.Interactive, Sk.astnodes.Expression, Sk.astnodes.Suite], - 'stmt': [Sk.astnodes.FunctionDef, + "stmt": [Sk.astnodes.FunctionDef, Sk.astnodes.AsyncFunctionDef, Sk.astnodes.ClassDef, Sk.astnodes.Return, @@ -3263,7 +3433,7 @@ Sk.INHERITANCE_MAP = { Sk.astnodes.Continue, Sk.astnodes.Print, Sk.astnodes.Debugger], - 'expr': [Sk.astnodes.BoolOp, + "expr": [Sk.astnodes.BoolOp, Sk.astnodes.BinOp, Sk.astnodes.UnaryOp, Sk.astnodes.Lambda, @@ -3293,17 +3463,17 @@ Sk.INHERITANCE_MAP = { Sk.astnodes.Name, Sk.astnodes.List, Sk.astnodes.Tuple], - 'expr_context': [Sk.astnodes.Load, + "expr_context": [Sk.astnodes.Load, Sk.astnodes.Store, Sk.astnodes.Del, Sk.astnodes.AugLoad, Sk.astnodes.AugStore, Sk.astnodes.Param], - 'slice': [Sk.astnodes.Slice, + "slice": [Sk.astnodes.Slice, Sk.astnodes.ExtSlice, Sk.astnodes.Index], - 'boolop': [Sk.astnodes.And, Sk.astnodes.Or], - 'operator': [Sk.astnodes.Add, + "boolop": [Sk.astnodes.And, Sk.astnodes.Or], + "operator": [Sk.astnodes.Add, Sk.astnodes.Sub, Sk.astnodes.Mult, Sk.astnodes.MatMult, @@ -3316,11 +3486,11 @@ Sk.INHERITANCE_MAP = { Sk.astnodes.BitXor, Sk.astnodes.BitAnd, Sk.astnodes.FloorDiv], - 'unaryop': [Sk.astnodes.Invert, - Sk.astnodes.Not, - Sk.astnodes.UAdd, + "unaryop": [Sk.astnodes.Invert, + Sk.astnodes.Not, + Sk.astnodes.UAdd, Sk.astnodes.USub], - 'cmpop': [Sk.astnodes.Eq, + "cmpop": [Sk.astnodes.Eq, Sk.astnodes.NotEq, Sk.astnodes.Lt, Sk.astnodes.LtE, @@ -3330,13 +3500,13 @@ Sk.INHERITANCE_MAP = { Sk.astnodes.IsNot, Sk.astnodes.In, Sk.astnodes.NotIn], - 'comprehension': [], - 'excepthandler': [Sk.astnodes.ExceptHandler], - 'arguments_': [], - 'arg': [], - 'keyword': [], - 'alias': [], - 'withitem': [] + "comprehension": [], + "excepthandler": [Sk.astnodes.ExceptHandler], + "arguments_": [], + "arg": [], + "keyword": [], + "alias": [], + "withitem": [] }; Sk.exportSymbol("Sk.astFromParse", Sk.astFromParse); diff --git a/src/biginteger.js b/src/biginteger.js index 6e8073e8c2..c677afbbec 100755 --- a/src/biginteger.js +++ b/src/biginteger.js @@ -54,1802 +54,4 @@ Sk.builtin.biginteger = function (a, b, c) { this.fromString(a, b); } } -}; - -// Bits per digit -//Sk.builtin.biginteger.dbits; - -// JavaScript engine analysis -Sk.builtin.biginteger.canary = 0xdeadbeefcafe; -Sk.builtin.biginteger.j_lm = ((Sk.builtin.biginteger.canary & 0xffffff) == 0xefcafe); - -// return new, unset Sk.builtin.biginteger -Sk.builtin.biginteger.nbi = function () { - return new Sk.builtin.biginteger(null); -}; - -// am: Compute w_j += (x*this_i), propagate carries, -// c is initial carry, returns final carry. -// c < 3*dvalue, x < 2*dvalue, this_i < dvalue -// We need to select the fastest one that works in this environment. - -// am1: use a single mult and divide to get the high bits, -// max digit bits should be 26 because -// max internal value = 2*dvalue^2-2*dvalue (< 2^53) -Sk.builtin.biginteger.prototype.am1 = function (i, x, w, j, c, n) { - var v; - while (--n >= 0) { - v = x * this[i++] + w[j] + c; - c = Math.floor(v / 0x4000000); - w[j++] = v & 0x3ffffff; - } - return c; -}; -// am2 avoids a big mult-and-extract completely. -// Max digit bits should be <= 30 because we do bitwise ops -// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) -Sk.builtin.biginteger.prototype.am2 = function (i, x, w, j, c, n) { - var m; - var h; - var l; - var xl = x & 0x7fff, xh = x >> 15; - while (--n >= 0) { - l = this[i] & 0x7fff; - h = this[i++] >> 15; - m = xh * l + h * xl; - l = xl * l + ((m & 0x7fff) << 15) + w[j] + (c & 0x3fffffff); - c = (l >>> 30) + (m >>> 15) + xh * h + (c >>> 30); - w[j++] = l & 0x3fffffff; - } - return c; -}; -// Alternately, set max digit bits to 28 since some -// browsers slow down when dealing with 32-bit numbers. -Sk.builtin.biginteger.prototype.am3 = function (i, x, w, j, c, n) { - var m; - var h; - var l; - var xl = x & 0x3fff, xh = x >> 14; - while (--n >= 0) { - l = this[i] & 0x3fff; - h = this[i++] >> 14; - m = xh * l + h * xl; - l = xl * l + ((m & 0x3fff) << 14) + w[j] + c; - c = (l >> 28) + (m >> 14) + xh * h; - w[j++] = l & 0xfffffff; - } - return c; -}; - -// We need to select the fastest one that works in this environment. -//if (Sk.builtin.biginteger.j_lm && (navigator.appName == "Microsoft Internet Explorer")) { -// Sk.builtin.biginteger.prototype.am = am2; -// Sk.builtin.biginteger.dbits = 30; -//} else if (Sk.builtin.biginteger.j_lm && (navigator.appName != "Netscape")) { -// Sk.builtin.biginteger.prototype.am = am1; -// Sk.builtin.biginteger.dbits = 26; -//} else { // Mozilla/Netscape seems to prefer am3 -// Sk.builtin.biginteger.prototype.am = am3; -// Sk.builtin.biginteger.dbits = 28; -//} - -// For node.js, we pick am3 with max Sk.builtin.biginteger.dbits to 28. -Sk.builtin.biginteger.prototype.am = Sk.builtin.biginteger.prototype.am3; -Sk.builtin.biginteger.dbits = 28; - -Sk.builtin.biginteger.prototype.DB = Sk.builtin.biginteger.dbits; -Sk.builtin.biginteger.prototype.DM = ((1 << Sk.builtin.biginteger.dbits) - 1); -Sk.builtin.biginteger.prototype.DV = (1 << Sk.builtin.biginteger.dbits); - -Sk.builtin.biginteger.BI_FP = 52; -Sk.builtin.biginteger.prototype.FV = Math.pow(2, Sk.builtin.biginteger.BI_FP); -Sk.builtin.biginteger.prototype.F1 = Sk.builtin.biginteger.BI_FP - Sk.builtin.biginteger.dbits; -Sk.builtin.biginteger.prototype.F2 = 2 * Sk.builtin.biginteger.dbits - Sk.builtin.biginteger.BI_FP; - -// Digit conversions -Sk.builtin.biginteger.BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"; -Sk.builtin.biginteger.BI_RC = []; -var rr, vv; -rr = "0".charCodeAt(0); -for (vv = 0; vv <= 9; ++vv) { - Sk.builtin.biginteger.BI_RC[rr++] = vv; -} -rr = "a".charCodeAt(0); -for (vv = 10; vv < 36; ++vv) { - Sk.builtin.biginteger.BI_RC[rr++] = vv; -} -rr = "A".charCodeAt(0); -for (vv = 10; vv < 36; ++vv) { - Sk.builtin.biginteger.BI_RC[rr++] = vv; -} - -Sk.builtin.biginteger.int2char = function (n) { - return Sk.builtin.biginteger.BI_RM.charAt(n); -}; -Sk.builtin.biginteger.intAt = function (s, i) { - var c = Sk.builtin.biginteger.BI_RC[s.charCodeAt(i)]; - return (c == null) ? -1 : c; -}; - -// (protected) copy this to r -Sk.builtin.biginteger.prototype.bnpCopyTo = function (r) { - var i; - for (i = this.t - 1; i >= 0; --i) { - r[i] = this[i]; - } - r.t = this.t; - r.s = this.s; -}; - -// (protected) set from integer value x, -DV <= x < DV -Sk.builtin.biginteger.prototype.bnpFromInt = function (x) { - this.t = 1; - this.s = (x < 0) ? -1 : 0; - if (x > 0) { - this[0] = x; - } else if (x < -1) { - this[0] = x + this.DV; - } else { - this.t = 0; - } -}; - -// return bigint initialized to value -Sk.builtin.biginteger.nbv = function (i) { - var r = new Sk.builtin.biginteger(null); - r.bnpFromInt(i); - return r; -}; - -// (protected) set from string and radix -Sk.builtin.biginteger.prototype.bnpFromString = function (s, b) { - var x; - var i, mi, sh; - var k; - if (b == 16) { - k = 4; - } else if (b == 8) { - k = 3; - } else if (b == 256) { - k = 8; - } else if (b == 2) { - // byte array - k = 1; - } else if (b == 32) { - k = 5; - } else if (b == 4) { - k = 2; - } else { - this.fromRadix(s, b); - return; - } - this.t = 0; - this.s = 0; - i = s.length; - mi = false; - sh = 0; - while (--i >= 0) { - x = (k == 8) ? s[i] & 0xff : Sk.builtin.biginteger.intAt(s, i); - if (x < 0) { - if (s.charAt(i) == "-") { - mi = true; - } - continue; - } - mi = false; - if (sh === 0) { - this[this.t++] = x; - } else if (sh + k > this.DB) { - this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh; - this[this.t++] = (x >> (this.DB - sh)); - } else { - this[this.t - 1] |= x << sh; - } - sh += k; - if (sh >= this.DB) { - sh -= this.DB; - } - } - if (k == 8 && (s[0] & 0x80) !== 0) { - this.s = -1; - if (sh > 0) { - this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh; - } - } - this.clamp(); - if (mi) { - Sk.builtin.biginteger.ZERO.subTo(this, this); - } -}; - -// (protected) clamp off excess high words -Sk.builtin.biginteger.prototype.bnpClamp = function () { - var c = this.s & this.DM; - while (this.t > 0 && this[this.t - 1] == c) { - --this.t; - } -}; - -// (public) return string representation in given radix -Sk.builtin.biginteger.prototype.bnToString = function (b) { - var p; - var km, d, m, r, i; - var k; - if (this.s < 0) { - return "-" + this.negate().toString(b); - } - if (b == 16) { - k = 4; - } else if (b == 8) { - k = 3; - } else if (b == 2) { - k = 1; - } else if (b == 32) { - k = 5; - } else if (b == 4) { - k = 2; - } else { - return this.toRadix(b); - } - km = (1 << k) - 1, m = false, r = "", i = this.t; - p = this.DB - (i * this.DB) % k; - if (i-- > 0) { - if (p < this.DB && (d = this[i] >> p) > 0) { - m = true; - r = Sk.builtin.biginteger.int2char(d); - } - while (i >= 0) { - if (p < k) { - d = (this[i] & ((1 << p) - 1)) << (k - p); - d |= this[--i] >> (p += this.DB - k); - } else { - d = (this[i] >> (p -= k)) & km; - if (p <= 0) { - p += this.DB; - --i; - } - } - if (d > 0) { - m = true; - } - if (m) { - r += Sk.builtin.biginteger.int2char(d); - } - } - } - return m ? r : "0"; -}; - -// (public) -this -Sk.builtin.biginteger.prototype.bnNegate = function () { - var r = Sk.builtin.biginteger.nbi(); - Sk.builtin.biginteger.ZERO.subTo(this, r); - return r; -}; - -// (public) |this| -Sk.builtin.biginteger.prototype.bnAbs = function () { - return (this.s < 0) ? this.negate() : this; -}; - -// (public) return + if this > a, - if this < a, 0 if equal -Sk.builtin.biginteger.prototype.bnCompareTo = function (a) { - var i; - var r = this.s - a.s; - if (r !== 0) { - return r; - } - i = this.t; - r = i - a.t; - if (r !== 0) { - return (this.s < 0) ? -r : r; - } - while (--i >= 0) { - if ((r = this[i] - a[i]) !== 0) { - return r; - } - } - return 0; -}; - -// returns bit length of the integer x -Sk.builtin.biginteger.nbits = function (x) { - var r = 1, t; - if ((t = x >>> 16) !== 0) { - x = t; - r += 16; - } - if ((t = x >> 8) !== 0) { - x = t; - r += 8; - } - if ((t = x >> 4) !== 0) { - x = t; - r += 4; - } - if ((t = x >> 2) !== 0) { - x = t; - r += 2; - } - if ((t = x >> 1) !== 0) { - x = t; - r += 1; - } - return r; -}; - -// (public) return the number of bits in "this" -Sk.builtin.biginteger.prototype.bnBitLength = function () { - if (this.t <= 0) { - return 0; - } - return this.DB * (this.t - 1) + Sk.builtin.biginteger.nbits(this[this.t - 1] ^ (this.s & this.DM)); -}; - -// (protected) r = this << n*DB -Sk.builtin.biginteger.prototype.bnpDLShiftTo = function (n, r) { - var i; - for (i = this.t - 1; i >= 0; --i) { - r[i + n] = this[i]; - } - for (i = n - 1; i >= 0; --i) { - r[i] = 0; - } - r.t = this.t + n; - r.s = this.s; -}; - -// (protected) r = this >> n*DB -Sk.builtin.biginteger.prototype.bnpDRShiftTo = function (n, r) { - var i; - for (i = n; i < this.t; ++i) { - r[i - n] = this[i]; - } - r.t = Math.max(this.t - n, 0); - r.s = this.s; -}; - -// (protected) r = this << n -Sk.builtin.biginteger.prototype.bnpLShiftTo = function (n, r) { - var bs = n % this.DB; - var cbs = this.DB - bs; - var bm = (1 << cbs) - 1; - var ds = Math.floor(n / this.DB), c = (this.s << bs) & this.DM, i; - for (i = this.t - 1; i >= 0; --i) { - r[i + ds + 1] = (this[i] >> cbs) | c; - c = (this[i] & bm) << bs; - } - for (i = ds - 1; i >= 0; --i) { - r[i] = 0; - } - r[ds] = c; - r.t = this.t + ds + 1; - r.s = this.s; - r.clamp(); -}; - -// (protected) r = this >> n -Sk.builtin.biginteger.prototype.bnpRShiftTo = function (n, r) { - var i; - var bm; - var cbs; - var bs; - var ds; - r.s = this.s; - ds = Math.floor(n / this.DB); - if (ds >= this.t) { - r.t = 0; - return; - } - bs = n % this.DB; - cbs = this.DB - bs; - bm = (1 << bs) - 1; - r[0] = this[ds] >> bs; - for (i = ds + 1; i < this.t; ++i) { - r[i - ds - 1] |= (this[i] & bm) << cbs; - r[i - ds] = this[i] >> bs; - } - if (bs > 0) { - r[this.t - ds - 1] |= (this.s & bm) << cbs; - } - r.t = this.t - ds; - r.clamp(); -}; - -// (protected) r = this - a -Sk.builtin.biginteger.prototype.bnpSubTo = function (a, r) { - var i = 0, c = 0, m = Math.min(a.t, this.t); - while (i < m) { - c += this[i] - a[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - if (a.t < this.t) { - c -= a.s; - while (i < this.t) { - c += this[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - c += this.s; - } else { - c += this.s; - while (i < a.t) { - c -= a[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - c -= a.s; - } - r.s = (c < 0) ? -1 : 0; - if (c < -1) { - r[i++] = this.DV + c; - } else if (c > 0) { - r[i++] = c; - } - r.t = i; - r.clamp(); -}; - -// (protected) r = this * a, r != this,a (HAC 14.12) -// "this" should be the larger one if appropriate. -Sk.builtin.biginteger.prototype.bnpMultiplyTo = function (a, r) { - var x = this.abs(), y = a.abs(); - var i = x.t; - r.t = i + y.t; - while (--i >= 0) { - r[i] = 0; - } - for (i = 0; i < y.t; ++i) { - r[i + x.t] = x.am(0, y[i], r, i, 0, x.t); - } - r.s = 0; - r.clamp(); - if (this.s != a.s) { - Sk.builtin.biginteger.ZERO.subTo(r, r); - } -}; - -// (protected) r = this^2, r != this (HAC 14.16) -Sk.builtin.biginteger.prototype.bnpSquareTo = function (r) { - var c; - var x = this.abs(); - var i = r.t = 2 * x.t; - while (--i >= 0) { - r[i] = 0; - } - for (i = 0; i < x.t - 1; ++i) { - c = x.am(i, x[i], r, 2 * i, 0, 1); - if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) { - r[i + x.t] -= x.DV; - r[i + x.t + 1] = 1; - } - } - if (r.t > 0) { - r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1); - } - r.s = 0; - r.clamp(); -}; - -// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) -// r != q, this != m. q or r may be null. -Sk.builtin.biginteger.prototype.bnpDivRemTo = function (m, q, r) { - var qd; - var i, j, t; - var d1, d2, e; - var yt; - var y0; - var ys; - var nsh; - var y, ts, ms; - var pt; - var pm = m.abs(); - if (pm.t <= 0) { - return; - } - pt = this.abs(); - if (pt.t < pm.t) { - if (q != null) { - q.fromInt(0); - } - if (r != null) { - this.copyTo(r); - } - return; - } - if (r == null) { - r = Sk.builtin.biginteger.nbi(); - } - y = Sk.builtin.biginteger.nbi(); - ts = this.s; - ms = m.s; - nsh = this.DB - Sk.builtin.biginteger.nbits(pm[pm.t - 1]); // normalize modulus - if (nsh > 0) { - pm.lShiftTo(nsh, y); - pt.lShiftTo(nsh, r); - } else { - pm.copyTo(y); - pt.copyTo(r); - } - ys = y.t; - y0 = y[ys - 1]; - if (y0 === 0) { - return; - } - yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0); - d1 = this.FV / yt, d2 = (1 << this.F1) / yt; - e = 1 << this.F2; - i = r.t, j = i - ys; - t = (q == null) ? Sk.builtin.biginteger.nbi() : q; - y.dlShiftTo(j, t); - if (r.compareTo(t) >= 0) { - r[r.t++] = 1; - r.subTo(t, r); - } - Sk.builtin.biginteger.ONE.dlShiftTo(ys, t); - t.subTo(y, y); // "negative" y so we can replace sub with am later - while (y.t < ys) { - y[y.t++] = 0; - } - while (--j >= 0) { - // Estimate quotient digit - qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2); - if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out - y.dlShiftTo(j, t); - r.subTo(t, r); - while (r[i] < --qd) { - r.subTo(t, r); - } - } - } - if (q != null) { - r.drShiftTo(ys, q); - if (ts != ms) { - Sk.builtin.biginteger.ZERO.subTo(q, q); - } - } - r.t = ys; - r.clamp(); - if (nsh > 0) { - r.rShiftTo(nsh, r); - } // Denormalize remainder - if (ts < 0) { - Sk.builtin.biginteger.ZERO.subTo(r, r); - } -}; - -// (public) this mod a -Sk.builtin.biginteger.prototype.bnMod = function (a) { - var r = Sk.builtin.biginteger.nbi(); - this.abs().divRemTo(a, null, r); - if (this.s < 0 && r.compareTo(Sk.builtin.biginteger.ZERO) > 0) { - a.subTo(r, r); - } - return r; -}; - -// Modular reduction using "classic" algorithm -/** - * @constructor - * @extends Sk.builtin.biginteger - */ -Sk.builtin.biginteger.Classic = function (m) { - this.m = m; -}; -Sk.builtin.biginteger.prototype.cConvert = function (x) { - if (x.s < 0 || x.compareTo(this.m) >= 0) { - return x.mod(this.m); - } else { - return x; - } -}; -Sk.builtin.biginteger.prototype.cRevert = function (x) { - return x; -}; -Sk.builtin.biginteger.prototype.cReduce = function (x) { - x.divRemTo(this.m, null, x); -}; -Sk.builtin.biginteger.prototype.cMulTo = function (x, y, r) { - x.multiplyTo(y, r); - this.reduce(r); -}; -Sk.builtin.biginteger.prototype.cSqrTo = function (x, r) { - x.squareTo(r); - this.reduce(r); -}; - -Sk.builtin.biginteger.Classic.prototype.convert = Sk.builtin.biginteger.prototype.cConvert; -Sk.builtin.biginteger.Classic.prototype.revert = Sk.builtin.biginteger.prototype.cRevert; -Sk.builtin.biginteger.Classic.prototype.reduce = Sk.builtin.biginteger.prototype.cReduce; -Sk.builtin.biginteger.Classic.prototype.mulTo = Sk.builtin.biginteger.prototype.cMulTo; -Sk.builtin.biginteger.Classic.prototype.sqrTo = Sk.builtin.biginteger.prototype.cSqrTo; - -// (protected) return "-1/this % 2^DB"; useful for Mont. reduction -// justification: -// xy == 1 (mod m) -// xy = 1+km -// xy(2-xy) = (1+km)(1-km) -// x[y(2-xy)] = 1-k^2m^2 -// x[y(2-xy)] == 1 (mod m^2) -// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 -// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. -// JS multiply "overflows" differently from C/C++, so care is needed here. -Sk.builtin.biginteger.prototype.bnpInvDigit = function () { - var y; - var x; - if (this.t < 1) { - return 0; - } - x = this[0]; - if ((x & 1) === 0) { - return 0; - } - y = x & 3; // y == 1/x mod 2^2 - y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4 - y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8 - y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16 - // last step - calculate inverse mod DV directly; - // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints - y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^Sk.builtin.biginteger.dbits - // we really want the negative inverse, and -DV < y < DV - return (y > 0) ? this.DV - y : -y; -}; - -// Sk.builtin.Montgomery reduction -/** - * @constructor - * @extends Sk.builtin.biginteger - */ -Sk.builtin.biginteger.Montgomery = function (m) { - this.m = m; - this.mp = m.invDigit(); - this.mpl = this.mp & 0x7fff; - this.mph = this.mp >> 15; - this.um = (1 << (m.DB - 15)) - 1; - this.mt2 = 2 * m.t; -}; - -// xR mod m -Sk.builtin.biginteger.prototype.montConvert = function (x) { - var r = Sk.builtin.biginteger.nbi(); - x.abs().dlShiftTo(this.m.t, r); - r.divRemTo(this.m, null, r); - if (x.s < 0 && r.compareTo(Sk.builtin.biginteger.ZERO) > 0) { - this.m.subTo(r, r); - } - return r; -}; - -// x/R mod m -Sk.builtin.biginteger.prototype.montRevert = function (x) { - var r = Sk.builtin.biginteger.nbi(); - x.copyTo(r); - this.reduce(r); - return r; -}; - -// x = x/R mod m (HAC 14.32) -Sk.builtin.biginteger.prototype.montReduce = function (x) { - var u0; - var j; - var i; - while (x.t <= this.mt2) { - // pad x so am has enough room later - x[x.t++] = 0; - } - for (i = 0; i < this.m.t; ++i) { - // faster way of calculating u0 = x[i]*mp mod DV - j = x[i] & 0x7fff; - u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM; - // use am to combine the multiply-shift-add into one call - j = i + this.m.t; - x[j] += this.m.am(0, u0, x, i, 0, this.m.t); - // propagate carry - while (x[j] >= x.DV) { - x[j] -= x.DV; - x[++j]++; - } - } - x.clamp(); - x.drShiftTo(this.m.t, x); - if (x.compareTo(this.m) >= 0) { - x.subTo(this.m, x); - } -}; - -// r = "x^2/R mod m"; x != r -Sk.builtin.biginteger.prototype.montSqrTo = function (x, r) { - x.squareTo(r); - this.reduce(r); -}; - -// r = "xy/R mod m"; x,y != r -Sk.builtin.biginteger.prototype.montMulTo = function (x, y, r) { - x.multiplyTo(y, r); - this.reduce(r); -}; - -Sk.builtin.biginteger.Montgomery.prototype.convert = Sk.builtin.biginteger.prototype.montConvert; -Sk.builtin.biginteger.Montgomery.prototype.revert = Sk.builtin.biginteger.prototype.montRevert; -Sk.builtin.biginteger.Montgomery.prototype.reduce = Sk.builtin.biginteger.prototype.montReduce; -Sk.builtin.biginteger.Montgomery.prototype.mulTo = Sk.builtin.biginteger.prototype.montMulTo; -Sk.builtin.biginteger.Montgomery.prototype.sqrTo = Sk.builtin.biginteger.prototype.montSqrTo; - -// (protected) true iff this is even -Sk.builtin.biginteger.prototype.bnpIsEven = function () { - return ((this.t > 0) ? (this[0] & 1) : this.s) === 0; -}; - -// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) -Sk.builtin.biginteger.prototype.bnpExp = function (e, z) { - var t; - var r, r2, g, i; - if (e > 0xffffffff || e < 1) { - return Sk.builtin.biginteger.ONE; - } - r = Sk.builtin.biginteger.nbi(); - r2 = Sk.builtin.biginteger.nbi(); - g = z.convert(this); - i = Sk.builtin.biginteger.nbits(e) - 1; - g.copyTo(r); - while (--i >= 0) { - z.sqrTo(r, r2); - if ((e & (1 << i)) > 0) { - z.mulTo(r2, g, r); - } else { - t = r; - r = r2; - r2 = t; - } - } - return z.revert(r); -}; - -// (public) this^e % m, 0 <= e < 2^32 -Sk.builtin.biginteger.prototype.bnModPowInt = function (e, m) { - var z; - if (e < 256 || m.isEven()) { - z = new Sk.builtin.biginteger.Classic(m); - } else { - z = new Sk.builtin.biginteger.Montgomery(m); - } - return this.exp(e, z); -}; - -// protected -Sk.builtin.biginteger.prototype.copyTo = Sk.builtin.biginteger.prototype.bnpCopyTo; -Sk.builtin.biginteger.prototype.fromInt = Sk.builtin.biginteger.prototype.bnpFromInt; -Sk.builtin.biginteger.prototype.fromString = Sk.builtin.biginteger.prototype.bnpFromString; -Sk.builtin.biginteger.prototype.clamp = Sk.builtin.biginteger.prototype.bnpClamp; -Sk.builtin.biginteger.prototype.dlShiftTo = Sk.builtin.biginteger.prototype.bnpDLShiftTo; -Sk.builtin.biginteger.prototype.drShiftTo = Sk.builtin.biginteger.prototype.bnpDRShiftTo; -Sk.builtin.biginteger.prototype.lShiftTo = Sk.builtin.biginteger.prototype.bnpLShiftTo; -Sk.builtin.biginteger.prototype.rShiftTo = Sk.builtin.biginteger.prototype.bnpRShiftTo; -Sk.builtin.biginteger.prototype.subTo = Sk.builtin.biginteger.prototype.bnpSubTo; -Sk.builtin.biginteger.prototype.multiplyTo = Sk.builtin.biginteger.prototype.bnpMultiplyTo; -Sk.builtin.biginteger.prototype.squareTo = Sk.builtin.biginteger.prototype.bnpSquareTo; -Sk.builtin.biginteger.prototype.divRemTo = Sk.builtin.biginteger.prototype.bnpDivRemTo; -Sk.builtin.biginteger.prototype.invDigit = Sk.builtin.biginteger.prototype.bnpInvDigit; -Sk.builtin.biginteger.prototype.isEven = Sk.builtin.biginteger.prototype.bnpIsEven; -Sk.builtin.biginteger.prototype.exp = Sk.builtin.biginteger.prototype.bnpExp; - -// public -Sk.builtin.biginteger.prototype.toString = Sk.builtin.biginteger.prototype.bnToString; -Sk.builtin.biginteger.prototype.negate = Sk.builtin.biginteger.prototype.bnNegate; -Sk.builtin.biginteger.prototype.abs = Sk.builtin.biginteger.prototype.bnAbs; -Sk.builtin.biginteger.prototype.compareTo = Sk.builtin.biginteger.prototype.bnCompareTo; -Sk.builtin.biginteger.prototype.bitLength = Sk.builtin.biginteger.prototype.bnBitLength; -Sk.builtin.biginteger.prototype.mod = Sk.builtin.biginteger.prototype.bnMod; -Sk.builtin.biginteger.prototype.modPowInt = Sk.builtin.biginteger.prototype.bnModPowInt; - -// "constants" -Sk.builtin.biginteger.ZERO = Sk.builtin.biginteger.nbv(0); -Sk.builtin.biginteger.ONE = Sk.builtin.biginteger.nbv(1); - -//Copyright (c) 2005-2009 Tom Wu -//All Rights Reserved. -//See "LICENSE" for details. - -//Extended JavaScript BN functions, required for RSA private ops. - -//Version 1.1: new Sk.builtin.biginteger("0", 10) returns "proper" zero - -//(public) -Sk.builtin.biginteger.prototype.bnClone = function () { - var r = Sk.builtin.biginteger.nbi(); - this.copyTo(r); - return r; -}; - -//(public) return value as integer -Sk.builtin.biginteger.prototype.bnIntValue = function () { - if (this.s < 0) { - if (this.t == 1) { - return this[0] - this.DV; - } else if (this.t === 0) { - return -1; - } - } else if (this.t == 1) { - return this[0]; - } else if (this.t === 0) { - return 0; - } - return ((this[1] & ((1 << (32 - this.DB)) - 1)) << this.DB) | this[0]; -}; - -//(public) return value as byte -Sk.builtin.biginteger.prototype.bnByteValue = function () { - return (this.t === 0) ? this.s : (this[0] << 24) >> 24; -}; - -//(public) return value as short (assumes DB>=16) -Sk.builtin.biginteger.prototype.bnShortValue = function () { - return (this.t === 0) ? this.s : (this[0] << 16) >> 16; -}; - -//(protected) return x s.t. r^x < DV -Sk.builtin.biginteger.prototype.bnpChunkSize = function (r) { - return Math.floor(Math.LN2 * this.DB / Math.log(r)); -}; - -//(public) 0 if this == 0, 1 if this > 0 -Sk.builtin.biginteger.prototype.bnSigNum = function () { - if (this.s < 0) { - return -1; - } else if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) { - return 0; - } else { - return 1; - } -}; - -//(protected) convert to radix string -Sk.builtin.biginteger.prototype.bnpToRadix = function (b) { - var d, y, z, r; - var a; - var cs; - if (b == null) { - b = 10; - } - if (this.signum() === 0 || b < 2 || b > 36) { - return "0"; - } - cs = this.chunkSize(b); - a = Math.pow(b, cs); - d = Sk.builtin.biginteger.nbv(a); - y = Sk.builtin.biginteger.nbi(); z = Sk.builtin.biginteger.nbi(); - r = ""; - this.divRemTo(d, y, z); - while (y.signum() > 0) { - r = (a + z.intValue()).toString(b).substr(1) + r; - y.divRemTo(d, y, z); - } - return z.intValue().toString(b) + r; -}; - -//(protected) convert from radix string -Sk.builtin.biginteger.prototype.bnpFromRadix = function (s, b) { - var x; - var i; - var d, mi, j, w; - var cs; - this.fromInt(0); - if (b == null) { - b = 10; - } - cs = this.chunkSize(b); - d = Math.pow(b, cs); - mi = false; - j = 0; - w = 0; - for (i = 0; i < s.length; ++i) { - x = Sk.builtin.biginteger.intAt(s, i); - if (x < 0) { - if (s.charAt(i) == "-" && this.signum() === 0) { - mi = true; - } - if (s.charAt(i) == ".") { - break; - } - continue; - } - w = b * w + x; - if (++j >= cs) { - this.dMultiply(d); - this.dAddOffset(w, 0); - j = 0; - w = 0; - } - } - if (j > 0) { - this.dMultiply(Math.pow(b, j)); - this.dAddOffset(w, 0); - } - if (mi) { - Sk.builtin.biginteger.ZERO.subTo(this, this); - } -}; - -//(protected) alternate constructor -Sk.builtin.biginteger.prototype.bnpFromNumber = function (a, b, c) { - if ("number" == typeof b) { - // new Sk.builtin.biginteger(int,int,RNG) - if (a < 2) { - this.fromInt(1); - } else { - this.fromNumber(a, c); - if (!this.testBit(a - 1)) { - // force MSB set - this.bitwiseTo(Sk.builtin.biginteger.ONE.shiftLeft(a - 1), Sk.builtin.biginteger.op_or, this); - } - if (this.isEven()) { - this.dAddOffset(1, 0); - } // force odd - while (!this.isProbablePrime(b)) { - this.dAddOffset(2, 0); - if (this.bitLength() > a) { - this.subTo(Sk.builtin.biginteger.ONE.shiftLeft(a - 1), this); - } - } - } - } - // Constructor to support Java BigInteger random generation. Forget it. - this.fromString(a + ""); -}; - -//(public) convert to bigendian byte array -Sk.builtin.biginteger.prototype.bnToByteArray = function () { - var p, d, k; - var i = this.t, r = []; - r[0] = this.s; - p = this.DB - (i * this.DB) % 8; - k = 0; - if (i-- > 0) { - if (p < this.DB && (d = this[i] >> p) != (this.s & this.DM) >> p) { - r[k++] = d | (this.s << (this.DB - p)); - } - while (i >= 0) { - if (p < 8) { - d = (this[i] & ((1 << p) - 1)) << (8 - p); - d |= this[--i] >> (p += this.DB - 8); - } else { - d = (this[i] >> (p -= 8)) & 0xff; - if (p <= 0) { - p += this.DB; - --i; - } - } - if ((d & 0x80) !== 0) { - d |= -256; - } - if (k === 0 && (this.s & 0x80) != (d & 0x80)) { - ++k; - } - if (k > 0 || d != this.s) { - r[k++] = d; - } - } - } - return r; -}; - -Sk.builtin.biginteger.prototype.bnEquals = function (a) { - return(this.compareTo(a) === 0); -}; -Sk.builtin.biginteger.prototype.bnMin = function (a) { - return(this.compareTo(a) < 0) ? this : a; -}; -Sk.builtin.biginteger.prototype.bnMax = function (a) { - return(this.compareTo(a) > 0) ? this : a; -}; - -//(protected) r = this op a (bitwise) -Sk.builtin.biginteger.prototype.bnpBitwiseTo = function (a, op, r) { - var i, f, m = Math.min(a.t, this.t); - for (i = 0; i < m; ++i) { - r[i] = op(this[i], a[i]); - } - if (a.t < this.t) { - f = a.s & this.DM; - for (i = m; i < this.t; ++i) { - r[i] = op(this[i], f); - } - r.t = this.t; - } else { - f = this.s & this.DM; - for (i = m; i < a.t; ++i) { - r[i] = op(f, a[i]); - } - r.t = a.t; - } - r.s = op(this.s, a.s); - r.clamp(); -}; - -//(public) this & a -Sk.builtin.biginteger.op_and = function (x, y) { - return x & y; -}; -Sk.builtin.biginteger.prototype.bnAnd = function (a) { - var r = Sk.builtin.biginteger.nbi(); - this.bitwiseTo(a, Sk.builtin.biginteger.op_and, r); - return r; -}; - -//(public) this | a -Sk.builtin.biginteger.op_or = function (x, y) { - return x | y; -}; -Sk.builtin.biginteger.prototype.bnOr = function (a) { - var r = Sk.builtin.biginteger.nbi(); - this.bitwiseTo(a, Sk.builtin.biginteger.op_or, r); - return r; -}; - -//(public) this ^ a -Sk.builtin.biginteger.op_xor = function (x, y) { - return x ^ y; -}; -Sk.builtin.biginteger.prototype.bnXor = function (a) { - var r = Sk.builtin.biginteger.nbi(); - this.bitwiseTo(a, Sk.builtin.biginteger.op_xor, r); - return r; -}; - -//(public) this & ~a -Sk.builtin.biginteger.op_andnot = function (x, y) { - return x & ~y; -}; -Sk.builtin.biginteger.prototype.bnAndNot = function (a) { - var r = Sk.builtin.biginteger.nbi(); - this.bitwiseTo(a, Sk.builtin.biginteger.op_andnot, r); - return r; -}; - -//(public) ~this -Sk.builtin.biginteger.prototype.bnNot = function () { - var i; - var r = Sk.builtin.biginteger.nbi(); - for (i = 0; i < this.t; ++i) { - r[i] = this.DM & ~this[i]; - } - r.t = this.t; - r.s = ~this.s; - return r; -}; - -//(public) this << n -Sk.builtin.biginteger.prototype.bnShiftLeft = function (n) { - var r = Sk.builtin.biginteger.nbi(); - if (n < 0) { - this.rShiftTo(-n, r); - } else { - this.lShiftTo(n, r); - } - return r; -}; - -//(public) this >> n -Sk.builtin.biginteger.prototype.bnShiftRight = function (n) { - var r = Sk.builtin.biginteger.nbi(); - if (n < 0) { - this.lShiftTo(-n, r); - } else { - this.rShiftTo(n, r); - } - return r; -}; - -//return index of lowest 1-bit in x, x < 2^31 -Sk.builtin.biginteger.lbit = function (x) { - var r; - if (x === 0) { - return -1; - } - r = 0; - if ((x & 0xffff) === 0) { - x >>= 16; - r += 16; - } - if ((x & 0xff) === 0) { - x >>= 8; - r += 8; - } - if ((x & 0xf) === 0) { - x >>= 4; - r += 4; - } - if ((x & 3) === 0) { - x >>= 2; - r += 2; - } - if ((x & 1) === 0) { - ++r; - } - return r; -}; - -//(public) returns index of lowest 1-bit (or -1 if none) -Sk.builtin.biginteger.prototype.bnGetLowestSetBit = function () { - var i; - for (i = 0; i < this.t; ++i) { - if (this[i] !== 0) { - return i * this.DB + Sk.builtin.biginteger.lbit(this[i]); - } - } - if (this.s < 0) { - return this.t * this.DB; - } - return -1; -}; - -//return number of 1 bits in x -Sk.builtin.biginteger.cbit = function (x) { - var r = 0; - while (x !== 0) { - x &= x - 1; - ++r; - } - return r; -}; - -//(public) return number of set bits -Sk.builtin.biginteger.prototype.bnBitCount = function () { - var i; - var r = 0, x = this.s & this.DM; - for (i = 0; i < this.t; ++i) { - r += Sk.builtin.biginteger.cbit(this[i] ^ x); - } - return r; -}; - -//(public) true iff nth bit is set -Sk.builtin.biginteger.prototype.bnTestBit = function (n) { - var j = Math.floor(n / this.DB); - if (j >= this.t) { - return(this.s !== 0); - } - return((this[j] & (1 << (n % this.DB))) !== 0); -}; - -//(protected) this op (1<>= this.DB; - } - if (a.t < this.t) { - c += a.s; - while (i < this.t) { - c += this[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - c += this.s; - } else { - c += this.s; - while (i < a.t) { - c += a[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - c += a.s; - } - r.s = (c < 0) ? -1 : 0; - if (c > 0) { - r[i++] = c; - } else if (c < -1) { - r[i++] = this.DV + c; - } - r.t = i; - r.clamp(); -}; - -//(public) this + a -Sk.builtin.biginteger.prototype.bnAdd = function (a) { - var r = Sk.builtin.biginteger.nbi(); - this.addTo(a, r); - return r; -}; - -//(public) this - a -Sk.builtin.biginteger.prototype.bnSubtract = function (a) { - var r = Sk.builtin.biginteger.nbi(); - this.subTo(a, r); - return r; -}; - -//(public) this * a -Sk.builtin.biginteger.prototype.bnMultiply = function (a) { - var r = Sk.builtin.biginteger.nbi(); - this.multiplyTo(a, r); - return r; -}; - -//(public) this / a -Sk.builtin.biginteger.prototype.bnDivide = function (a) { - var r = Sk.builtin.biginteger.nbi(); - this.divRemTo(a, r, null); - return r; -}; - -//(public) this % a -Sk.builtin.biginteger.prototype.bnRemainder = function (a) { - var r = Sk.builtin.biginteger.nbi(); - this.divRemTo(a, null, r); - return r; -}; - -//(public) [this/a,this%a] -Sk.builtin.biginteger.prototype.bnDivideAndRemainder = function (a) { - var q = Sk.builtin.biginteger.nbi(), r = Sk.builtin.biginteger.nbi(); - this.divRemTo(a, q, r); - return new Array(q, r); -}; - -//(protected) this *= n, this >= 0, 1 < n < DV -Sk.builtin.biginteger.prototype.bnpDMultiply = function (n) { - this[this.t] = this.am(0, n - 1, this, 0, 0, this.t); - ++this.t; - this.clamp(); -}; - -//(protected) this += n << w words, this >= 0 -Sk.builtin.biginteger.prototype.bnpDAddOffset = function (n, w) { - if (n === 0) { - return; - } - while (this.t <= w) { - this[this.t++] = 0; - } - this[w] += n; - while (this[w] >= this.DV) { - this[w] -= this.DV; - if (++w >= this.t) { - this[this.t++] = 0; - } - ++this[w]; - } -}; - -//A "null" reducer -/** - * @constructor - * @extends Sk.builtin.biginteger - */ -Sk.builtin.biginteger.NullExp = function () { -}; -Sk.builtin.biginteger.prototype.nNop = function (x) { - return x; -}; -Sk.builtin.biginteger.prototype.nMulTo = function (x, y, r) { - x.multiplyTo(y, r); -}; -Sk.builtin.biginteger.prototype.nSqrTo = function (x, r) { - x.squareTo(r); -}; - -Sk.builtin.biginteger.NullExp.prototype.convert = Sk.builtin.biginteger.prototype.nNop; -Sk.builtin.biginteger.NullExp.prototype.revert = Sk.builtin.biginteger.prototype.nNop; -Sk.builtin.biginteger.NullExp.prototype.mulTo = Sk.builtin.biginteger.prototype.nMulTo; -Sk.builtin.biginteger.NullExp.prototype.sqrTo = Sk.builtin.biginteger.prototype.nSqrTo; - -//(public) this^e -Sk.builtin.biginteger.prototype.bnPow = function (e) { - return this.exp(e, new Sk.builtin.biginteger.NullExp()); -}; - -//(protected) r = lower n words of "this * a", a.t <= n -//"this" should be the larger one if appropriate. -Sk.builtin.biginteger.prototype.bnpMultiplyLowerTo = function (a, n, r) { - var j; - var i = Math.min(this.t + a.t, n); - r.s = 0; // assumes a,this >= 0 - r.t = i; - while (i > 0) { - r[--i] = 0; - } - for (j = r.t - this.t; i < j; ++i) { - r[i + this.t] = this.am(0, a[i], r, i, 0, this.t); - } - for (j = Math.min(a.t, n); i < j; ++i) { - this.am(0, a[i], r, i, 0, n - i); - } - r.clamp(); -}; - -//(protected) r = "this * a" without lower n words, n > 0 -//"this" should be the larger one if appropriate. -Sk.builtin.biginteger.prototype.bnpMultiplyUpperTo = function (a, n, r) { - var i; - --n; - i = r.t = this.t + a.t - n; - r.s = 0; // assumes a,this >= 0 - while (--i >= 0) { - r[i] = 0; - } - for (i = Math.max(n - this.t, 0); i < a.t; ++i) { - r[this.t + i - n] = this.am(n - i, a[i], r, 0, 0, this.t + i - n); - } - r.clamp(); - r.drShiftTo(1, r); -}; - -//Barrett modular reduction -/** - * @constructor - * @extends Sk.builtin.biginteger - */ -Sk.builtin.biginteger.Barrett = function (m) { - this.r2 = Sk.builtin.biginteger.nbi(); - this.q3 = Sk.builtin.biginteger.nbi(); - Sk.builtin.biginteger.ONE.dlShiftTo(2 * m.t, this.r2); - this.mu = this.r2.divide(m); - this.m = m; -}; - -Sk.builtin.biginteger.prototype.barrettConvert = function (x) { - var r; - if (x.s < 0 || x.t > 2 * this.m.t) { - return x.mod(this.m); - } else if (x.compareTo(this.m) < 0) { - return x; - } else { - r = Sk.builtin.biginteger.nbi(); - x.copyTo(r); - this.reduce(r); - return r; - } -}; - -Sk.builtin.biginteger.prototype.barrettRevert = function (x) { - return x; -}; - -//x = x mod m (HAC 14.42) -Sk.builtin.biginteger.prototype.barrettReduce = function (x) { - x.drShiftTo(this.m.t - 1, this.r2); - if (x.t > this.m.t + 1) { - x.t = this.m.t + 1; - x.clamp(); - } - this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3); - this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2); - while (x.compareTo(this.r2) < 0) { - x.dAddOffset(1, this.m.t + 1); - } - x.subTo(this.r2, x); - while (x.compareTo(this.m) >= 0) { - x.subTo(this.m, x); - } -}; - -//r = x^2 mod m; x != r -Sk.builtin.biginteger.prototype.barrettSqrTo = function (x, r) { - x.squareTo(r); - this.reduce(r); -}; - -//r = x*y mod m; x,y != r -Sk.builtin.biginteger.prototype.barrettMulTo = function (x, y, r) { - x.multiplyTo(y, r); - this.reduce(r); -}; - -Sk.builtin.biginteger.Barrett.prototype.convert = Sk.builtin.biginteger.prototype.barrettConvert; -Sk.builtin.biginteger.Barrett.prototype.revert = Sk.builtin.biginteger.prototype.barrettRevert; -Sk.builtin.biginteger.Barrett.prototype.reduce = Sk.builtin.biginteger.prototype.barrettReduce; -Sk.builtin.biginteger.Barrett.prototype.mulTo = Sk.builtin.biginteger.prototype.barrettMulTo; -Sk.builtin.biginteger.Barrett.prototype.sqrTo = Sk.builtin.biginteger.prototype.barrettSqrTo; - -//(public) this^e % m (HAC 14.85) -Sk.builtin.biginteger.prototype.bnModPow = function (e, m) { - var j, w, is1, r2, t; - var g2; - var g, n, k1, km; - var i = e.bitLength(), k, r = Sk.builtin.biginteger.nbv(1), z; - if (i <= 0) { - return r; - } else if (i < 18) { - k = 1; - } else if (i < 48) { - k = 3; - } else if (i < 144) { - k = 4; - } else if (i < 768) { - k = 5; - } else { - k = 6; - } - if (i < 8) { - z = new Sk.builtin.biginteger.Classic(m); - } else if (m.isEven()) { - z = new Sk.builtin.biginteger.Barrett(m); - } else { - z = new Sk.builtin.biginteger.Montgomery(m); - } - - g = []; - n = 3; - k1 = k - 1; - km = (1 << k) - 1; - g[1] = z.convert(this); - if (k > 1) { - g2 = Sk.builtin.biginteger.nbi(); - z.sqrTo(g[1], g2); - while (n <= km) { - g[n] = Sk.builtin.biginteger.nbi(); - z.mulTo(g2, g[n - 2], g[n]); - n += 2; - } - } - - j = e.t - 1; - is1 = true; - r2 = Sk.builtin.biginteger.nbi(); - i = Sk.builtin.biginteger.nbits(e[j]) - 1; - while (j >= 0) { - if (i >= k1) { - w = (e[j] >> (i - k1)) & km; - } else { - w = (e[j] & ((1 << (i + 1)) - 1)) << (k1 - i); - if (j > 0) { - w |= e[j - 1] >> (this.DB + i - k1); - } - } - - n = k; - while ((w & 1) === 0) { - w >>= 1; - --n; - } - if ((i -= n) < 0) { - i += this.DB; - --j; - } - if (is1) { // ret == 1, don't bother squaring or multiplying it - g[w].copyTo(r); - is1 = false; - } else { - while (n > 1) { - z.sqrTo(r, r2); - z.sqrTo(r2, r); - n -= 2; - } - if (n > 0) { - z.sqrTo(r, r2); - } else { - t = r; - r = r2; - r2 = t; - } - z.mulTo(r2, g[w], r); - } - - while (j >= 0 && (e[j] & (1 << i)) === 0) { - z.sqrTo(r, r2); - t = r; - r = r2; - r2 = t; - if (--i < 0) { - i = this.DB - 1; - --j; - } - } - } - return z.revert(r); -}; - -//(public) gcd(this,a) (HAC 14.54) -Sk.builtin.biginteger.prototype.bnGCD = function (a) { - var i, g; - var t; - var x = (this.s < 0) ? this.negate() : this.clone(); - var y = (a.s < 0) ? a.negate() : a.clone(); - if (x.compareTo(y) < 0) { - t = x; - x = y; - y = t; - } - i = x.getLowestSetBit(); - g = y.getLowestSetBit(); - if (g < 0) { - return x; - } - if (i < g) { - g = i; - } - if (g > 0) { - x.rShiftTo(g, x); - y.rShiftTo(g, y); - } - while (x.signum() > 0) { - if ((i = x.getLowestSetBit()) > 0) { - x.rShiftTo(i, x); - } - if ((i = y.getLowestSetBit()) > 0) { - y.rShiftTo(i, y); - } - if (x.compareTo(y) >= 0) { - x.subTo(y, x); - x.rShiftTo(1, x); - } else { - y.subTo(x, y); - y.rShiftTo(1, y); - } - } - if (g > 0) { - y.lShiftTo(g, y); - } - return y; -}; - -//(protected) this % n, n < 2^26 -Sk.builtin.biginteger.prototype.bnpModInt = function (n) { - var i; - var d, r; - if (n <= 0) { - return 0; - } - d = this.DV % n; - r = (this.s < 0) ? n - 1 : 0; - if (this.t > 0) { - if (d === 0) { - r = this[0] % n; - } else { - for (i = this.t - 1; i >= 0; --i) { - r = (d * r + this[i]) % n; - } - } - } - return r; -}; - -//(public) 1/this % m (HAC 14.61) -Sk.builtin.biginteger.prototype.bnModInverse = function (m) { - var a, b, c, d; - var u, v; - var ac = m.isEven(); - if ((this.isEven() && ac) || m.signum() === 0) { - return Sk.builtin.biginteger.ZERO; - } - u = m.clone(); - v = this.clone(); - a = Sk.builtin.biginteger.nbv(1); - b = Sk.builtin.biginteger.nbv(0); - c = Sk.builtin.biginteger.nbv(0); - d = Sk.builtin.biginteger.nbv(1); - while (u.signum() !== 0) { - while (u.isEven()) { - u.rShiftTo(1, u); - if (ac) { - if (!a.isEven() || !b.isEven()) { - a.addTo(this, a); - b.subTo(m, b); - } - a.rShiftTo(1, a); - } else if (!b.isEven()) { - b.subTo(m, b); - } - b.rShiftTo(1, b); - } - while (v.isEven()) { - v.rShiftTo(1, v); - if (ac) { - if (!c.isEven() || !d.isEven()) { - c.addTo(this, c); - d.subTo(m, d); - } - c.rShiftTo(1, c); - } else if (!d.isEven()) { - d.subTo(m, d); - } - d.rShiftTo(1, d); - } - if (u.compareTo(v) >= 0) { - u.subTo(v, u); - if (ac) { - a.subTo(c, a); - } - b.subTo(d, b); - } else { - v.subTo(u, v); - if (ac) { - c.subTo(a, c); - } - d.subTo(b, d); - } - } - if (v.compareTo(Sk.builtin.biginteger.ONE) !== 0) { - return Sk.builtin.biginteger.ZERO; - } - if (d.compareTo(m) >= 0) { - return d.subtract(m); - } - if (d.signum() < 0) { - d.addTo(m, d); - } else { - return d; - } - if (d.signum() < 0) { - return d.add(m); - } else { - return d; - } -}; - -Sk.builtin.biginteger.lowprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509]; -Sk.builtin.biginteger.lplim = (1 << 26) / Sk.builtin.biginteger.lowprimes[Sk.builtin.biginteger.lowprimes.length - 1]; - -//(public) test primality with certainty >= 1-.5^t -Sk.builtin.biginteger.prototype.bnIsProbablePrime = function (t) { - var m, j; - var i, x = this.abs(); - if (x.t == 1 && x[0] <= Sk.builtin.biginteger.lowprimes[Sk.builtin.biginteger.lowprimes.length - 1]) { - for (i = 0; i < Sk.builtin.biginteger.lowprimes.length; ++i) { - if (x[0] == Sk.builtin.biginteger.lowprimes[i]) { - return true; - } - } - return false; - } - if (x.isEven()) { - return false; - } - i = 1; - while (i < Sk.builtin.biginteger.lowprimes.length) { - m = Sk.builtin.biginteger.lowprimes[i]; - j = i + 1; - while (j < Sk.builtin.biginteger.lowprimes.length && m < Sk.builtin.biginteger.lplim) { - m *= Sk.builtin.biginteger.lowprimes[j++]; - } - m = x.modInt(m); - while (i < j) { - if (m % Sk.builtin.biginteger.lowprimes[i++] === 0) { - return false; - } - } - } - return x.millerRabin(t); -}; - -//(protected) true if probably prime (HAC 4.24, Miller-Rabin) -Sk.builtin.biginteger.prototype.bnpMillerRabin = function (t) { - var j; - var y; - var i; - var a; - var r; - var n1 = this.subtract(Sk.builtin.biginteger.ONE); - var k = n1.getLowestSetBit(); - if (k <= 0) { - return false; - } - r = n1.shiftRight(k); - t = (t + 1) >> 1; - if (t > Sk.builtin.biginteger.lowprimes.length) { - t = Sk.builtin.biginteger.lowprimes.length; - } - a = Sk.builtin.biginteger.nbi(); - for (i = 0; i < t; ++i) { - a.fromInt(Sk.builtin.biginteger.lowprimes[i]); - y = a.modPow(r, this); - if (y.compareTo(Sk.builtin.biginteger.ONE) !== 0 && y.compareTo(n1) !== 0) { - j = 1; - while (j++ < k && y.compareTo(n1) !== 0) { - y = y.modPowInt(2, this); - if (y.compareTo(Sk.builtin.biginteger.ONE) === 0) { - return false; - } - } - if (y.compareTo(n1) !== 0) { - return false; - } - } - } - return true; -}; - -Sk.builtin.biginteger.prototype.isnegative = function () { - return this.s < 0; -}; -Sk.builtin.biginteger.prototype.ispositive = function () { - return this.s >= 0; -}; -Sk.builtin.biginteger.prototype.trueCompare = function (a) { - if (this.s >= 0 && a.s < 0) { - return 1; - } - if (this.s < 0 && a.s >= 0) { - return -1; - } - return this.compare(a); -}; - -//protected -Sk.builtin.biginteger.prototype.chunkSize = Sk.builtin.biginteger.prototype.bnpChunkSize; -Sk.builtin.biginteger.prototype.toRadix = Sk.builtin.biginteger.prototype.bnpToRadix; -Sk.builtin.biginteger.prototype.fromRadix = Sk.builtin.biginteger.prototype.bnpFromRadix; -Sk.builtin.biginteger.prototype.fromNumber = Sk.builtin.biginteger.prototype.bnpFromNumber; -Sk.builtin.biginteger.prototype.bitwiseTo = Sk.builtin.biginteger.prototype.bnpBitwiseTo; -Sk.builtin.biginteger.prototype.changeBit = Sk.builtin.biginteger.prototype.bnpChangeBit; -Sk.builtin.biginteger.prototype.addTo = Sk.builtin.biginteger.prototype.bnpAddTo; -Sk.builtin.biginteger.prototype.dMultiply = Sk.builtin.biginteger.prototype.bnpDMultiply; -Sk.builtin.biginteger.prototype.dAddOffset = Sk.builtin.biginteger.prototype.bnpDAddOffset; -Sk.builtin.biginteger.prototype.multiplyLowerTo = Sk.builtin.biginteger.prototype.bnpMultiplyLowerTo; -Sk.builtin.biginteger.prototype.multiplyUpperTo = Sk.builtin.biginteger.prototype.bnpMultiplyUpperTo; -Sk.builtin.biginteger.prototype.modInt = Sk.builtin.biginteger.prototype.bnpModInt; -Sk.builtin.biginteger.prototype.millerRabin = Sk.builtin.biginteger.prototype.bnpMillerRabin; - -//public -Sk.builtin.biginteger.prototype.clone = Sk.builtin.biginteger.prototype.bnClone; -Sk.builtin.biginteger.prototype.intValue = Sk.builtin.biginteger.prototype.bnIntValue; -Sk.builtin.biginteger.prototype.byteValue = Sk.builtin.biginteger.prototype.bnByteValue; -Sk.builtin.biginteger.prototype.shortValue = Sk.builtin.biginteger.prototype.bnShortValue; -Sk.builtin.biginteger.prototype.signum = Sk.builtin.biginteger.prototype.bnSigNum; -Sk.builtin.biginteger.prototype.toByteArray = Sk.builtin.biginteger.prototype.bnToByteArray; -Sk.builtin.biginteger.prototype.equals = Sk.builtin.biginteger.prototype.bnEquals; -Sk.builtin.biginteger.prototype.compare = Sk.builtin.biginteger.prototype.compareTo; -Sk.builtin.biginteger.prototype.min = Sk.builtin.biginteger.prototype.bnMin; -Sk.builtin.biginteger.prototype.max = Sk.builtin.biginteger.prototype.bnMax; -Sk.builtin.biginteger.prototype.and = Sk.builtin.biginteger.prototype.bnAnd; -Sk.builtin.biginteger.prototype.or = Sk.builtin.biginteger.prototype.bnOr; -Sk.builtin.biginteger.prototype.xor = Sk.builtin.biginteger.prototype.bnXor; -Sk.builtin.biginteger.prototype.andNot = Sk.builtin.biginteger.prototype.bnAndNot; -Sk.builtin.biginteger.prototype.not = Sk.builtin.biginteger.prototype.bnNot; -Sk.builtin.biginteger.prototype.shiftLeft = Sk.builtin.biginteger.prototype.bnShiftLeft; -Sk.builtin.biginteger.prototype.shiftRight = Sk.builtin.biginteger.prototype.bnShiftRight; -Sk.builtin.biginteger.prototype.getLowestSetBit = Sk.builtin.biginteger.prototype.bnGetLowestSetBit; -Sk.builtin.biginteger.prototype.bitCount = Sk.builtin.biginteger.prototype.bnBitCount; -Sk.builtin.biginteger.prototype.testBit = Sk.builtin.biginteger.prototype.bnTestBit; -Sk.builtin.biginteger.prototype.setBit = Sk.builtin.biginteger.prototype.bnSetBit; -Sk.builtin.biginteger.prototype.clearBit = Sk.builtin.biginteger.prototype.bnClearBit; -Sk.builtin.biginteger.prototype.flipBit = Sk.builtin.biginteger.prototype.bnFlipBit; -Sk.builtin.biginteger.prototype.add = Sk.builtin.biginteger.prototype.bnAdd; -Sk.builtin.biginteger.prototype.subtract = Sk.builtin.biginteger.prototype.bnSubtract; -Sk.builtin.biginteger.prototype.multiply = Sk.builtin.biginteger.prototype.bnMultiply; -Sk.builtin.biginteger.prototype.divide = Sk.builtin.biginteger.prototype.bnDivide; -Sk.builtin.biginteger.prototype.remainder = Sk.builtin.biginteger.prototype.bnRemainder; -Sk.builtin.biginteger.prototype.divideAndRemainder = Sk.builtin.biginteger.prototype.bnDivideAndRemainder; -Sk.builtin.biginteger.prototype.modPow = Sk.builtin.biginteger.prototype.bnModPow; -Sk.builtin.biginteger.prototype.modInverse = Sk.builtin.biginteger.prototype.bnModInverse; -Sk.builtin.biginteger.prototype.pow = Sk.builtin.biginteger.prototype.bnPow; -Sk.builtin.biginteger.prototype.gcd = Sk.builtin.biginteger.prototype.bnGCD; -Sk.builtin.biginteger.prototype.isProbablePrime = Sk.builtin.biginteger.prototype.bnIsProbablePrime; -//Sk.builtin.biginteger.int2char = int2char; - -//Sk.builtin.biginteger interfaces not implemented in jsbn: - -//Sk.builtin.biginteger(int signum, byte[] magnitude) -//double doubleValue() -//float floatValue() -//int hashCode() -//long longValue() -//static Sk.builtin.biginteger valueOf(long val) - -//module.exports = Sk.builtin.biginteger; +}; \ No newline at end of file diff --git a/src/bool.js b/src/bool.js index d1f02bb47e..71f11da05a 100644 --- a/src/bool.js +++ b/src/bool.js @@ -5,44 +5,85 @@ * @description * Constructor for Python bool. Also used for builtin bool() function. * - * Where possible, do not create a new instance but use the constants + * Where possible, do not create a new instance but use the constants * Sk.builtin.bool.true$ or Sk.builtin.bool.false$. These are defined in src/constant.js * - * @extends {Sk.builtin.object} - * + * @extends {Sk.builtin.int_} + * * @param {(Object|number|boolean)} x Value to evaluate as true or false * @return {Sk.builtin.bool} Sk.builtin.bool.true$ if x is true, Sk.builtin.bool.false$ otherwise */ -Sk.builtin.bool = function (x) { - Sk.builtin.pyCheckArgsLen("bool", arguments.length, 1); - if (Sk.misceval.isTrue(x)) { - return Sk.builtin.bool.true$; - } else { - return Sk.builtin.bool.false$; - } -}; +Sk.builtin.bool = Sk.abstr.buildNativeClass("bool", { + constructor: function bool(x) { + if (Sk.misceval.isTrue(x)) { + return Sk.builtin.bool.true$; + } else { + return Sk.builtin.bool.false$; + } + }, + base: Sk.builtin.int_, + slots: { + tp$doc: + "bool(x) -> bool\n\nReturns True when the argument x is true, False otherwise.\nThe builtins True and False are the only two instances of the class bool.\nThe class bool is a subclass of the class int, and cannot be subclassed.", + tp$new: function (args, kwargs) { + Sk.abstr.checkNoKwargs("bool", kwargs); + Sk.abstr.checkArgsLen("bool", args, 0, 1); + return new Sk.builtin.bool(args[0]); //technically we don't need new but easier to keep consistent + }, + $r: function () { + return this.v ? new Sk.builtin.str("True") : new Sk.builtin.str("False"); + }, -Sk.abstr.setUpInheritance("bool", Sk.builtin.bool, Sk.builtin.int_); - -Sk.builtin.bool.prototype["$r"] = function () { - if (this.v) { - return new Sk.builtin.str("True"); + tp$as_number: true, + nb$and: function (other) { + if (other.ob$type === Sk.builtin.bool) { + return new Sk.builtin.bool(this.v & other.v); + } + return Sk.builtin.int_.prototype.nb$and.call(this, other); + }, + nb$or: function (other) { + if (other.ob$type === Sk.builtin.bool) { + return new Sk.builtin.bool(this.v | other.v); + } + return Sk.builtin.int_.prototype.nb$or.call(this, other); + }, + nb$xor: function (other) { + if (other.ob$type === Sk.builtin.bool) { + return new Sk.builtin.bool(this.v ^ other.v); + } + return Sk.builtin.int_.prototype.nb$xor.call(this, other); + }, + }, + flags: { + sk$acceptable_as_base_class: false, + }, + methods: { + __format__: { + $meth: function () { + return this.$r(); + }, + $flags: {OneArg: true}, + } } - return new Sk.builtin.str("False"); -}; - -Sk.builtin.bool.prototype.tp$hash = function () { - return new Sk.builtin.int_(this.v); -}; - -Sk.builtin.bool.prototype.__int__ = new Sk.builtin.func(function(self) { - var v = Sk.builtin.asnum$(self); - - return new Sk.builtin.int_(v); }); +Sk.exportSymbol("Sk.builtin.bool", Sk.builtin.bool); -Sk.builtin.bool.prototype.__float__ = new Sk.builtin.func(function(self) { - return new Sk.builtin.float_(Sk.ffi.remapToJs(self)); +/** + * Python bool True constant. + * @type {Sk.builtin.bool} + * @member {Sk.builtin.bool} + * @suppress {checkTypes} + */ +Sk.builtin.bool.true$ = Object.create(Sk.builtin.bool.prototype, { + v: {value: 1, enumerable: true}, }); -Sk.exportSymbol("Sk.builtin.bool", Sk.builtin.bool); +/** + * Python bool False constant. + * @type {Sk.builtin.bool} + * @member {Sk.builtin.bool} + * @suppress {checkTypes} + */ +Sk.builtin.bool.false$ = Object.create(Sk.builtin.bool.prototype, { + v: {value: 0, enumerable: true}, +}); diff --git a/src/builtin.js b/src/builtin.js index 2506449401..c608fc3746 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -1,3 +1,5 @@ +/** @typedef {Sk.builtin.object} */ var pyObject; + /** * builtins are supposed to come from the __builtin__ module, but we don't do * that yet. @@ -5,47 +7,7 @@ * work, etc. */ -Sk.builtin.range = function range (start, stop, step) { - var ret = []; - var i; - - Sk.builtin.pyCheckArgsLen("range", arguments.length, 1, 3); - Sk.builtin.pyCheckType("start", "integer", Sk.builtin.checkInt(start)); - if (stop !== undefined) { - Sk.builtin.pyCheckType("stop", "integer", Sk.builtin.checkInt(stop)); - } - if (step !== undefined) { - Sk.builtin.pyCheckType("step", "integer", Sk.builtin.checkInt(step)); - } - - start = Sk.builtin.asnum$(start); - stop = Sk.builtin.asnum$(stop); - step = Sk.builtin.asnum$(step); - - if ((stop === undefined) && (step === undefined)) { - stop = start; - start = 0; - step = 1; - } else if (step === undefined) { - step = 1; - } - - if (step === 0) { - throw new Sk.builtin.ValueError("range() step argument must not be zero"); - } - - if (step > 0) { - for (i = start; i < stop; i += step) { - ret.push(new Sk.builtin.int_(i)); - } - } else { - for (i = start; i > stop; i += step) { - ret.push(new Sk.builtin.int_(i)); - } - } - - return new Sk.builtin.list(ret); -}; +const JSBI = require("jsbi"); Sk.builtin.asnum$ = function (a) { if (a === undefined) { @@ -54,41 +16,24 @@ Sk.builtin.asnum$ = function (a) { if (a === null) { return a; } - if (a instanceof Sk.builtin.none) { - return null; - } - if (a instanceof Sk.builtin.bool) { - if (a.v) { - return 1; - } - return 0; - } if (typeof a === "number") { return a; } - if (typeof a === "string") { - return a; - } if (a instanceof Sk.builtin.int_) { - return a.v; + if (typeof a.v === "number") { + return a.v; + } + return a.v.toString(); // then we have a BigInt } if (a instanceof Sk.builtin.float_) { return a.v; } - if (a instanceof Sk.builtin.lng) { - if (a.cantBeInt()) { - return a.str$(10, true); - } - return a.toInt$(); + if (a === Sk.builtin.none.none$) { + return null; } - if (a.constructor === Sk.builtin.biginteger) { - if ((a.trueCompare(new Sk.builtin.biginteger(Sk.builtin.int_.threshold$)) > 0) || - (a.trueCompare(new Sk.builtin.biginteger(-Sk.builtin.int_.threshold$)) < 0)) { - return a.toString(); - } - return a.intValue(); + if (typeof a === "string") { + return a; } - return a; }; @@ -117,33 +62,18 @@ Sk.builtin.asnum$nofloat = function (a) { var expon; if (a === undefined) { return a; - } - if (a === null) { + } else if (a === null) { return a; - } - if (a.constructor === Sk.builtin.none) { - return null; - } - if (a.constructor === Sk.builtin.bool) { - if (a.v) { - return 1; - } - return 0; - } - if (typeof a === "number") { + } else if (typeof a === "number") { a = a.toString(); - } - if (a.constructor === Sk.builtin.int_) { + } else if (a instanceof Sk.builtin.int_) { a = a.v.toString(); - } - if (a.constructor === Sk.builtin.float_) { + } else if (a instanceof Sk.builtin.float_) { a = a.v.toString(); - } - if (a.constructor === Sk.builtin.lng) { - a = a.str$(10, true); - } - if (a.constructor === Sk.builtin.biginteger) { - a = a.toString(); + } else if (a === Sk.builtin.none.none$) { + return null; + } else { + return undefined; } // Sk.debugout("INITIAL: " + a); @@ -210,33 +140,30 @@ Sk.builtin.asnum$nofloat = function (a) { }; Sk.exportSymbol("Sk.builtin.asnum$nofloat", Sk.builtin.asnum$nofloat); -Sk.builtin.round = function round (number, ndigits) { - var special; - Sk.builtin.pyCheckArgsLen("round", arguments.length, 1, 2); - - if (!Sk.builtin.checkNumber(number)) { - if (!Sk.builtin.checkFunction(number)) { +Sk.builtin.round = function round(number, ndigits) { + if (number === undefined) { + throw new Sk.builtin.TypeError("a float is required"); + } + if (!Sk.__future__.dunder_round) { + if (!Sk.builtin.checkNumber(number)) { throw new Sk.builtin.TypeError("a float is required"); + } + if (number.round$) { + return number.round$(ndigits); } else { - if (!Sk.__future__.exceptions) { - throw new Sk.builtin.AttributeError(Sk.abstr.typeName(number) + " instance has no attribute '__float__'"); - } + throw new Sk.builtin.AttributeError(Sk.abstr.typeName(number) + " instance has no attribute '__float__'"); } } - if ((ndigits !== undefined) && !Sk.misceval.isIndex(ndigits)) { + if (ndigits !== undefined && !Sk.builtin.checkNone(ndigits) && !Sk.misceval.isIndex(ndigits)) { throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(ndigits) + "' object cannot be interpreted as an index"); } - if (!Sk.__future__.dunder_round && number.round$) { - return number.round$(number, ndigits); - } - // try calling internal magic method - special = Sk.abstr.lookupSpecial(number, Sk.builtin.str.$round); - if (special != null) { + const special = Sk.abstr.lookupSpecial(number, Sk.builtin.str.$round); + if (special !== undefined) { // method on builtin, provide this arg - if (!Sk.builtin.checkFunction(number)) { + if (ndigits !== undefined) { return Sk.misceval.callsimArray(special, [number, ndigits]); } else { return Sk.misceval.callsimArray(special, [number]); @@ -246,181 +173,177 @@ Sk.builtin.round = function round (number, ndigits) { } }; -Sk.builtin.len = function len (item) { - var intcheck; - var special; - Sk.builtin.pyCheckArgsLen("len", arguments.length, 1, 1); - - var int_ = function(i) { return new Sk.builtin.int_(i); }; - intcheck = function(j) { - if (Sk.builtin.checkInt(j)) { - return int_(j); - } else { - if (Sk.__future__.exceptions) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(j) + "' object cannot be interpreted as an integer"); - } else { - throw new Sk.builtin.TypeError("__len__() should return an int"); - } - } - }; - +Sk.builtin.len = function len(item) { + // checking will happen in slot wrapper + let res; if (item.sq$length) { - return Sk.misceval.chain(item.sq$length(true), intcheck); + res = item.sq$length(true); + } + if (res === undefined) { + throw new Sk.builtin.TypeError("object of type '" + Sk.abstr.typeName(item) + "' has no len()"); } + return Sk.misceval.chain(res, (r) => { + return new Sk.builtin.int_(r); + }); +}; - if (item.mp$length) { - return Sk.misceval.chain(item.mp$length(), int_); +Sk.builtin.min = function min(args, kwargs) { + let iter; + const nargs = args.length; + if (!nargs) { + throw new Sk.builtin.TypeError("min expected 1 argument, got 0"); } + const default_key = Sk.abstr.copyKeywordsToNamedArgs("min", ["default", "key"], [], kwargs, [null, Sk.builtin.none.none$]); + const $default = default_key[0]; + const key = default_key[1]; - if (item.tp$length) { - if (Sk.builtin.checkFunction(item)) { - special = Sk.abstr.lookupSpecial(item.__class__, Sk.builtin.str.$len); - if (special != null) { - return Sk.misceval.callsimArray(special, [this, item]); - } else { - if (Sk.__future__.exceptions) { - throw new Sk.builtin.TypeError("object of type '" + Sk.abstr.typeName(item) + "' has no len()"); - } else { - throw new Sk.builtin.AttributeError(Sk.abstr.typeName(item) + " instance has no attribute '__len__'"); - } - } - } else { - return Sk.misceval.chain(item.tp$length(true), intcheck); - } + // if args is not a single iterable then default should not be included as a kwarg + if (nargs > 1 && $default !== null) { + throw new Sk.builtin.TypeError("Cannot specify a default for min() with multiple positional arguments"); } - throw new Sk.builtin.TypeError("object of type '" + Sk.abstr.typeName(item) + "' has no len()"); -}; + if (nargs == 1) { + iter = Sk.abstr.iter(args[0]); + } else { + iter = Sk.abstr.iter(new Sk.builtin.tuple(args)); + } -Sk.builtin.min = function min () { - var i; - var lowest; - var args; - Sk.builtin.pyCheckArgsLen("min", arguments.length, 1); + if (!Sk.builtin.checkNone(key) && !Sk.builtin.checkCallable(key)) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(key) + "' object is not callable"); + } - args = Sk.misceval.arrayFromArguments(arguments); - lowest = args[0]; + let lowest = iter.tp$iternext(); if (lowest === undefined) { - throw new Sk.builtin.ValueError("min() arg is an empty sequence"); + if ($default === null) { + throw new Sk.builtin.ValueError("min() arg is an empty sequence"); + } else { + return $default; + } } - - for (i = 1; i < args.length; ++i) { - if (Sk.misceval.richCompareBool(args[i], lowest, "Lt")) { - lowest = args[i]; + if (Sk.builtin.checkNone(key)) { + for (let i = iter.tp$iternext(); i !== undefined; i = iter.tp$iternext()) { + if (Sk.misceval.richCompareBool(i, lowest, "Lt")) { + lowest = i; + } + } + } else { + let lowest_compare = Sk.misceval.callsimArray(key, [lowest]); + for (let i = iter.tp$iternext(); i !== undefined; i = iter.tp$iternext()) { + let i_compare = Sk.misceval.callsimArray(key, [i]); + if (Sk.misceval.richCompareBool(i_compare, lowest_compare, "Lt")) { + lowest = i; + lowest_compare = i_compare; + } } } return lowest; }; -Sk.builtin.max = function max () { - var i; - var highest; - var args; - Sk.builtin.pyCheckArgsLen("max", arguments.length, 1); - - args = Sk.misceval.arrayFromArguments(arguments); - highest = args[0]; +Sk.builtin.max = function max(args, kwargs) { + let iter; + const nargs = args.length; - if (highest === undefined) { - throw new Sk.builtin.ValueError("max() arg is an empty sequence"); + if (!nargs) { + throw new Sk.builtin.TypeError("max expected 1 argument, got 0"); } + const default_key = Sk.abstr.copyKeywordsToNamedArgs("min", ["default", "key"], [], kwargs, [null, Sk.builtin.none.none$]); + const $default = default_key[0]; + const key = default_key[1]; - for (i = 1; i < args.length; ++i) { - if (Sk.misceval.richCompareBool(args[i], highest, "Gt")) { - highest = args[i]; - } + // if args is not a single iterable then default should not be included as a kwarg + if (nargs > 1 && $default !== null) { + throw new Sk.builtin.TypeError("Cannot specify a default for max() with multiple positional arguments"); } - return highest; -}; -Sk.builtin.any = function any (iter) { - var it, i; + if (nargs === 1) { + iter = Sk.abstr.iter(args[0]); + } else { + iter = Sk.abstr.iter(new Sk.builtin.tuple(args)); + } - Sk.builtin.pyCheckArgsLen("any", arguments.length, 1, 1); - if (!Sk.builtin.checkIterable(iter)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(iter) + - "' object is not iterable"); + if (!Sk.builtin.checkNone(key) && !Sk.builtin.checkCallable(key)) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(key) + "' object is not callable"); } - it = Sk.abstr.iter(iter); - for (i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - if (Sk.misceval.isTrue(i)) { - return Sk.builtin.bool.true$; + let highest = iter.tp$iternext(); + + if (highest === undefined) { + if ($default === null) { + throw new Sk.builtin.ValueError("max() arg is an empty sequence"); + } else { + return $default; } } - - return Sk.builtin.bool.false$; + if (Sk.builtin.checkNone(key)) { + for (let i = iter.tp$iternext(); i !== undefined; i = iter.tp$iternext()) { + if (Sk.misceval.richCompareBool(i, highest, "Gt")) { + highest = i; + } + } + } else { + let highest_compare = Sk.misceval.callsimArray(key, [highest]); + for (let i = iter.tp$iternext(); i !== undefined; i = iter.tp$iternext()) { + let i_compare = Sk.misceval.callsimArray(key, [i]); + if (Sk.misceval.richCompareBool(i_compare, highest_compare, "Gt")) { + highest = i; + highest_compare = i_compare; + } + } + } + return highest; }; -Sk.builtin.all = function all (iter) { - var it, i; - - Sk.builtin.pyCheckArgsLen("all", arguments.length, 1, 1); - if (!Sk.builtin.checkIterable(iter)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(iter) + - "' object is not iterable"); - } +Sk.builtin.any = function any(iter) { + const ret = Sk.misceval.iterFor(Sk.abstr.iter(iter), function (i) { + if (Sk.misceval.isTrue(i)) { + return new Sk.misceval.Break(true); + } + }); + return ret === undefined ? Sk.builtin.bool.false$ : Sk.builtin.bool.true$; +}; - it = Sk.abstr.iter(iter); - for (i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { +Sk.builtin.all = function all(iter) { + const ret = Sk.misceval.iterFor(Sk.abstr.iter(iter), function (i) { if (!Sk.misceval.isTrue(i)) { - return Sk.builtin.bool.false$; + return new Sk.misceval.Break(false); } - } - - return Sk.builtin.bool.true$; + }); + return ret === undefined ? Sk.builtin.bool.true$ : Sk.builtin.bool.false$; }; -Sk.builtin.sum = function sum (iter, start) { +Sk.builtin.sum = function sum(iter, start) { var tot; - var intermed; - var it, i; var has_float; - - Sk.builtin.pyCheckArgsLen("sum", arguments.length, 1, 2); + // this follows the order python checks errors Sk.builtin.pyCheckType("iter", "iterable", Sk.builtin.checkIterable(iter)); - if (start !== undefined && Sk.builtin.checkString(start)) { + if (Sk.builtin.checkString(start)) { throw new Sk.builtin.TypeError("sum() can't sum strings [use ''.join(seq) instead]"); } - if (start === undefined) { - tot = new Sk.builtin.int_(0); - } else { - tot = start; - } - - it = Sk.abstr.iter(iter); - for (i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - if (i instanceof Sk.builtin.float_) { + tot = start; + Sk.misceval.iterFor(Sk.abstr.iter(iter), function (i) { + if (!has_float && i instanceof Sk.builtin.float_) { has_float = true; - if (!(tot instanceof Sk.builtin.float_)) { - tot = new Sk.builtin.float_(Sk.builtin.asnum$(tot)); - } - } else if (i instanceof Sk.builtin.lng) { - if (!has_float) { - if (!(tot instanceof Sk.builtin.lng)) { - tot = new Sk.builtin.lng(tot); - } - } + tot = new Sk.builtin.float_(Sk.builtin.asnum$(tot)); } - + // else if (i instanceof Sk.builtin.lng) { + // if (!has_float && !(tot instanceof Sk.builtin.lng)) { + // tot = new Sk.builtin.lng(tot); + // } + // } if (tot.nb$add !== undefined) { - intermed = tot.nb$add(i); - if ((intermed !== undefined) && (intermed !== Sk.builtin.NotImplemented.NotImplemented$)) { - tot = tot.nb$add(i); - continue; + const itermed = tot.nb$add(i); + if (itermed !== undefined && !(itermed instanceof Sk.builtin.NotImplemented)) { + tot = itermed; + return; } } - - throw new Sk.builtin.TypeError("unsupported operand type(s) for +: '" + - Sk.abstr.typeName(tot) + "' and '" + - Sk.abstr.typeName(i) + "'"); - } - + throw new Sk.builtin.TypeError("unsupported operand type(s) for +: '" + Sk.abstr.typeName(tot) + "' and '" + Sk.abstr.typeName(i) + "'"); + }); return tot; }; -Sk.builtin.zip = function zip () { +Sk.builtin.zip = function zip() { var el; var tup; var done; @@ -458,27 +381,10 @@ Sk.builtin.zip = function zip () { return new Sk.builtin.list(res); }; -Sk.builtin.abs = function abs (x) { - Sk.builtin.pyCheckArgsLen("abs", arguments.length, 1, 1); - - if (x instanceof Sk.builtin.int_) { - return new Sk.builtin.int_(Math.abs(x.v)); - } - if (x instanceof Sk.builtin.float_) { - return new Sk.builtin.float_(Math.abs(x.v)); - } - if (Sk.builtin.checkNumber(x)) { - return Sk.builtin.assk$(Math.abs(Sk.builtin.asnum$(x))); - } else if (Sk.builtin.checkComplex(x)) { - return Sk.misceval.callsimArray(x.__abs__, [x]); - } - - // call custom __abs__ methods - if (x.__class__.tp$getattr) { - var f = x.__class__.tp$getattr(Sk.builtin.str.$abs); - return Sk.misceval.callsimArray(f, [x]); +Sk.builtin.abs = function abs(x) { + if (x.nb$abs) { + return x.nb$abs(); } - throw new TypeError("bad operand type for abs(): '" + Sk.abstr.typeName(x) + "'"); }; @@ -488,34 +394,29 @@ Sk.builtin.fabs = function fabs(x) { return Sk.builtin.abs(x); }; -Sk.builtin.ord = function ord (x) { - Sk.builtin.pyCheckArgsLen("ord", arguments.length, 1, 1); - +Sk.builtin.ord = function ord(x) { if (!Sk.builtin.checkString(x)) { throw new Sk.builtin.TypeError("ord() expected a string of length 1, but " + Sk.abstr.typeName(x) + " found"); } else if (x.v.length !== 1) { throw new Sk.builtin.TypeError("ord() expected a character, but string of length " + x.v.length + " found"); } - return new Sk.builtin.int_((x.v).charCodeAt(0)); + return new Sk.builtin.int_(x.v.charCodeAt(0)); }; -Sk.builtin.chr = function chr (x) { - Sk.builtin.pyCheckArgsLen("chr", arguments.length, 1, 1); +Sk.builtin.chr = function chr(x) { if (!Sk.builtin.checkInt(x)) { throw new Sk.builtin.TypeError("an integer is required"); } x = Sk.builtin.asnum$(x); - - if ((x < 0) || (x > 255)) { + if (x < 0 || x > 255) { throw new Sk.builtin.ValueError("chr() arg not in range(256)"); } return new Sk.builtin.str(String.fromCharCode(x)); }; -Sk.builtin.unichr = function unichr (x) { - Sk.builtin.pyCheckArgsLen("chr", arguments.length, 1, 1); +Sk.builtin.unichr = function unichr(x) { if (!Sk.builtin.checkInt(x)) { throw new Sk.builtin.TypeError("an integer is required"); } @@ -531,39 +432,37 @@ Sk.builtin.unichr = function unichr (x) { } }; -Sk.builtin.int2str_ = function helper_ (x, radix, prefix) { - var suffix; - var str = ""; - if (x instanceof Sk.builtin.lng) { - suffix = ""; - if (radix !== 2 && (!(Sk.__future__.python3))) { - suffix = "L"; - } - str = x.str$(radix, false); - if (x.nb$isnegative()) { - return new Sk.builtin.str("-" + prefix + str + suffix); - } - return new Sk.builtin.str(prefix + str + suffix); +/** + * This is a helper function and we already know that x is an int or has an nb$index slot + */ +Sk.builtin.int2str_ = function helper_(x, radix, prefix) { + let v; + if (x.constructor === Sk.builtin.int_ || x instanceof Sk.builtin.int_) { + v = x.v; // we don't use asnum$ because it returns a str rather than a bigint. } else { - x = Sk.misceval.asIndex(x); - str = x.toString(radix); - if (x < 0) { - return new Sk.builtin.str("-" + prefix + str.slice(1)); - } - return new Sk.builtin.str(prefix + str); + x = x.nb$index(); + v = x.v; } + let str = v.toString(radix); + if (x.nb$isnegative()) { + str = "-" + prefix + str.slice(1); + } else { + str = prefix + str; + } + if (radix !== 2 && !Sk.__future__.python3 && (x instanceof Sk.builtin.lng || v instanceof JSBI)) { + str += "L"; + } + return new Sk.builtin.str(str); }; -Sk.builtin.hex = function hex (x) { - Sk.builtin.pyCheckArgsLen("hex", arguments.length, 1, 1); +Sk.builtin.hex = function hex(x) { if (!Sk.misceval.isIndex(x)) { throw new Sk.builtin.TypeError("hex() argument can't be converted to hex"); } return Sk.builtin.int2str_(x, 16, "0x"); }; -Sk.builtin.oct = function oct (x) { - Sk.builtin.pyCheckArgsLen("oct", arguments.length, 1, 1); +Sk.builtin.oct = function oct(x) { if (!Sk.misceval.isIndex(x)) { throw new Sk.builtin.TypeError("oct() argument can't be converted to hex"); } @@ -574,143 +473,31 @@ Sk.builtin.oct = function oct (x) { } }; -Sk.builtin.bin = function bin (x) { - Sk.builtin.pyCheckArgsLen("bin", arguments.length, 1, 1); +Sk.builtin.bin = function bin(x) { if (!Sk.misceval.isIndex(x)) { throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(x) + "' object can't be interpreted as an index"); } return Sk.builtin.int2str_(x, 2, "0b"); }; -Sk.builtin.dir = function dir (x) { - var last; - var it; - var prop; - var base; - var mro; - var i; - var s; - var k; - var names; - var getName; - Sk.builtin.pyCheckArgsLen("dir", arguments.length, 1, 1); - - getName = function (k) { - var s = null; - var internal = [ - "__bases__", "__mro__", "__class__", "__name__", "GenericGetAttr", - "GenericSetAttr", "GenericPythonGetAttr", "GenericPythonSetAttr", - "pythonFunctions", "HashNotImplemented", "constructor", "__dict__" - ]; - if (internal.indexOf(k) !== -1) { - return null; - } - if (k.indexOf("$") !== -1) { - s = Sk.builtin.dir.slotNameToRichName(k); - } else if (k.charAt(k.length - 1) !== "_") { - s = k; - } else if (k.charAt(0) === "_") { - s = k; - } - return s; - }; - - names = []; - var _seq; - - // try calling magic method - var special = Sk.abstr.lookupSpecial(x, Sk.builtin.str.$dir); - if(special != null) { - // method on builtin, provide this arg - _seq = Sk.misceval.callsimArray(special, [x]); - - if (!Sk.builtin.checkSequence(_seq)) { - throw new Sk.builtin.TypeError("__dir__ must return sequence."); - } - - // proper unwrapping - _seq = Sk.ffi.remapToJs(_seq); - - for (i = 0; i < _seq.length; ++i) { - names.push(new Sk.builtin.str(_seq[i])); - } - } else { - // Add all object properties - for (k in x.constructor.prototype) { - s = getName(k); - if (s) { - names.push(new Sk.builtin.str(s)); - } - } - - // Add all attributes - if (x["$d"]) { - if (x["$d"].tp$iter) { - // Dictionary - it = x["$d"].tp$iter(); - for (i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - s = new Sk.builtin.str(i); - s = getName(s.v); - if (s) { - names.push(new Sk.builtin.str(s)); - } - } - } else { - // Object - for (s in x["$d"]) { - names.push(new Sk.builtin.str(s)); - } - } - } - - // Add all class attributes - mro = x.tp$mro; - if(!mro && x.ob$type) { - mro = x.ob$type.tp$mro; - } - if (mro) { - for (i = 0; i < mro.v.length; ++i) { - base = mro.v[i]; - for (prop in base) { - if (base.hasOwnProperty(prop)) { - s = getName(prop); - if (s) { - names.push(new Sk.builtin.str(s)); - } - } - } - } - } +Sk.builtin.dir = function dir(obj) { + if (obj !== undefined) { + const obj_dir_func = Sk.abstr.lookupSpecial(obj, Sk.builtin.str.$dir); + const dir = Sk.misceval.callsimArray(obj_dir_func, [obj]); + return Sk.builtin.sorted(dir); + // now iter through the keys and check they are all stings } - - // Sort results - names.sort(function (a, b) { - return (a.v > b.v) - (a.v < b.v); - }); - - // Get rid of duplicates before returning, as duplicates should - // only occur when they are shadowed - last = function (value, index, self) { - // Returns true iff the value is not the same as the next value - return value !== self[index + 1]; - }; - return new Sk.builtin.list(names.filter(last)); + // then we want all the objects in the global scope + //todo + throw new Sk.builtin.NotImplementedError("skulpt does not yet support dir with no args"); }; -Sk.builtin.dir.slotNameToRichName = function (k) { - // todo; map tp$xyz to __xyz__ properly - return undefined; +Sk.builtin.repr = function repr(x) { + return x.$r(); }; -Sk.builtin.repr = function repr (x) { - Sk.builtin.pyCheckArgsLen("repr", arguments.length, 1, 1); - - return Sk.misceval.objectRepr(x); -}; - -Sk.builtin.open = function open (filename, mode, bufsize) { - Sk.builtin.pyCheckArgsLen("open", arguments.length, 1, 3); +Sk.builtin.open = function open(filename, mode, bufsize) { if (mode === undefined) { mode = new Sk.builtin.str("r"); } @@ -724,87 +511,48 @@ Sk.builtin.open = function open (filename, mode, bufsize) { return new Sk.builtin.file(filename, mode, bufsize); }; -Sk.builtin.isinstance = function isinstance (obj, type) { - var issubclass; - var i; - Sk.builtin.pyCheckArgsLen("isinstance", arguments.length, 2, 2); - if (!Sk.builtin.checkClass(type) && !(type instanceof Sk.builtin.tuple)) { - throw new Sk.builtin.TypeError("isinstance() arg 2 must be a class, type, or tuple of classes and types"); - } - - if (type === Sk.builtin.none.prototype.ob$type) { - if (obj instanceof Sk.builtin.none) { - return Sk.builtin.bool.true$; - } else { - return Sk.builtin.bool.false$; +const issubclass_multiple_inheritance = function (klass, base) { + const mro = klass.prototype.tp$mro; + for (let i = 0; i < mro.length; i++) { + if (base === mro[i]) { + return true; } } + return false; +}; - var objType; - - // Overridden __class__ - var __class__ = obj.tp$getattr(Sk.builtin.str.$class); - if (__class__ !== undefined) { - objType = __class__; - } else { - objType = obj.ob$type; +Sk.builtin.isinstance = function isinstance(obj, type) { + if (!Sk.builtin.checkClass(type) && !(type instanceof Sk.builtin.tuple)) { + throw new Sk.builtin.TypeError("isinstance() arg 2 must be a class, type, or tuple of classes and types"); } // Normal case - if (objType === type) { - return Sk.builtin.bool.true$; + if (!(type instanceof Sk.builtin.tuple)) { + return obj.ob$type.$isSubType(type) ? Sk.builtin.bool.true$ : Sk.builtin.bool.false$; } - // Handle tuple type argument - if (type instanceof Sk.builtin.tuple) { - for (i = 0; i < type.v.length; ++i) { - if (Sk.misceval.isTrue(Sk.builtin.isinstance(obj, type.v[i]))) { - return Sk.builtin.bool.true$; - } - } - return Sk.builtin.bool.false$; - } - - // Check for Javascript inheritance - if (obj instanceof type) { - return Sk.builtin.bool.true$; - } - - - issubclass = function (klass, base) { - var i; - var bases; - if (klass === base) { + for (let i = 0; i < type.v.length; ++i) { + if (Sk.misceval.isTrue(Sk.builtin.isinstance(obj, type.v[i]))) { return Sk.builtin.bool.true$; } - if (klass["$d"] === undefined) { - return Sk.builtin.bool.false$; - } - bases = klass["$d"].mp$subscript(Sk.builtin.type.basesStr_); - for (i = 0; i < bases.v.length; ++i) { - if (Sk.misceval.isTrue(issubclass(bases.v[i], base))) { - return Sk.builtin.bool.true$; - } - } - return Sk.builtin.bool.false$; - }; - - return issubclass(objType, type); + } + return Sk.builtin.bool.false$; }; -Sk.builtin.hash = function hash (value) { +Sk.builtin.hash = function hash(value) { var junk; - Sk.builtin.pyCheckArgsLen("hash", arguments.length, 1, 1); // Useless object to get compiler to allow check for __hash__ property - junk = {__hash__: function () { - return 0; - }}; + junk = { + __hash__: function () { + return 0; + }, + }; if (value instanceof Object) { if (Sk.builtin.checkNone(value.tp$hash)) { // python sets the hash function to None , so we have to catch this case here - throw new Sk.builtin.TypeError(new Sk.builtin.str("unhashable type: '" + Sk.abstr.typeName(value) + "'")); + throw new Sk.builtin.TypeError("unhashable type: '" + Sk.abstr.typeName(value) + "'"); } else if (value.tp$hash !== undefined) { if (value.$savedHash_) { return value.$savedHash_; @@ -818,76 +566,73 @@ Sk.builtin.hash = function hash (value) { } return new Sk.builtin.int_(value.__hash); } - } else if (typeof value === "number" || value === null || - value === true || value === false) { + } else if (typeof value === "number" || value === null || value === true || value === false) { throw new Sk.builtin.TypeError("unsupported Javascript type"); } - return new Sk.builtin.str((typeof value) + " " + String(value)); + return new Sk.builtin.str(typeof value + " " + String(value)); // todo; throw properly for unhashable types }; -Sk.builtin.getattr = function getattr (obj, pyName, default_) { - var ret, mangledName, jsName; - Sk.builtin.pyCheckArgsLen("getattr", arguments.length, 2, 3); +Sk.builtin.getattr = function getattr(obj, pyName, default_) { if (!Sk.builtin.checkString(pyName)) { throw new Sk.builtin.TypeError("attribute name must be string"); } - - jsName = pyName.$jsstr(); - mangledName = new Sk.builtin.str(Sk.fixReservedWords(jsName)); - ret = obj.tp$getattr(mangledName); - if (ret === undefined) { - if (default_ !== undefined) { - return default_; - } else { - throw new Sk.builtin.AttributeError("'" + Sk.abstr.typeName(obj) + "' object has no attribute '" + jsName + "'"); + const res = Sk.misceval.tryCatch( + () => obj.tp$getattr(pyName, true), + (e) => { + if (e instanceof Sk.builtin.AttributeError) { + return undefined; + } else { + throw e; + } } - } - return ret; + ); + return Sk.misceval.chain(res, (r) => { + if (r === undefined) { + if (default_ !== undefined) { + return default_; + } + throw new Sk.builtin.AttributeError("'" + Sk.abstr.typeName(obj) + "' object has no attribute " + pyName.$jsstr()); + } + return r; + }); }; -Sk.builtin.setattr = function setattr (obj, pyName, value) { - var jsName; - Sk.builtin.pyCheckArgsLen("setattr", arguments.length, 3, 3); +Sk.builtin.setattr = function setattr(obj, pyName, value) { // cannot set or del attr from builtin type - if (obj === undefined || obj["$r"] === undefined || obj["$r"]().v.slice(1,5) !== "type") { - if (!Sk.builtin.checkString(pyName)) { - throw new Sk.builtin.TypeError("attribute name must be string"); - } - jsName = pyName.$jsstr(); - - if (obj.tp$setattr) { - obj.tp$setattr(new Sk.builtin.str(Sk.fixReservedWords(jsName)), value); - } else { - throw new Sk.builtin.AttributeError("object has no attribute " + jsName); - } - return Sk.builtin.none.none$; + if (!Sk.builtin.checkString(pyName)) { + throw new Sk.builtin.TypeError("attribute name must be string"); } - - throw new Sk.builtin.TypeError("can't set attributes of built-in/extension type '" + obj.tp$name + "'"); + const res = obj.tp$setattr(pyName, value, true); + return Sk.misceval.chain(res, () => { + return Sk.builtin.none.none$; + }); }; Sk.builtin.raw_input = function (prompt) { var lprompt = prompt ? prompt : ""; return Sk.misceval.chain(Sk.importModule("sys", false, true), function (sys) { - if (Sk.inputfunTakesPrompt) { - return Sk.misceval.callsimOrSuspendArray(Sk.builtin.file.$readline, [sys["$d"]["stdin"], null, lprompt]); + return Sk.builtin.file.$readline(sys["$d"]["stdin"], null, lprompt); } else { - return Sk.misceval.chain(undefined, function() { - return Sk.misceval.callsimOrSuspendArray(sys["$d"]["stdout"]["write"], [sys["$d"]["stdout"], new Sk.builtin.str(lprompt)]); - }, function () { - return Sk.misceval.callsimOrSuspendArray(sys["$d"]["stdin"]["readline"], [sys["$d"]["stdin"]]); - }); + return Sk.misceval.chain( + undefined, + function () { + return Sk.misceval.callsimOrSuspendArray(sys["$d"]["stdout"]["write"], [sys["$d"]["stdout"], new Sk.builtin.str(lprompt)]); + }, + function () { + return Sk.misceval.callsimOrSuspendArray(sys["$d"]["stdin"]["readline"], [sys["$d"]["stdin"]]); + } + ); } }); }; Sk.builtin.input = Sk.builtin.raw_input; -Sk.builtin.jseval = function jseval (evalcode) { +Sk.builtin.jseval = function jseval(evalcode) { var result = Sk.global["eval"](Sk.ffi.remapToJs(evalcode)); try { return Sk.ffi.remapToPy(result); @@ -900,16 +645,16 @@ Sk.builtin.jseval = function jseval (evalcode) { } }; -Sk.builtin.jsmillis = function jsmillis () { +Sk.builtin.jsmillis = function jsmillis() { var now = new Date(); return now.valueOf(); }; -Sk.builtin.eval_ = function eval_ () { +Sk.builtin.eval_ = function eval_() { throw new Sk.builtin.NotImplementedError("eval is not yet implemented"); }; -Sk.builtin.map = function map (fun, seq) { +Sk.builtin.map = function map(fun, seq) { var retval = []; var next; var nones; @@ -959,36 +704,37 @@ Sk.builtin.map = function map (fun, seq) { throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(seq) + "' object is not iterable"); } - return Sk.misceval.chain(Sk.misceval.iterFor(Sk.abstr.iter(seq), function (item) { + return Sk.misceval.chain( + Sk.misceval.iterFor(Sk.abstr.iter(seq), function (item) { + if (fun === Sk.builtin.none.none$) { + if (item instanceof Array) { + // With None function and multiple sequences, + // map should return a list of tuples + item = new Sk.builtin.tuple(item); + } + retval.push(item); + } else { + if (!(item instanceof Array)) { + // If there was only one iterable, convert to Javascript + // Array for call to apply. + item = [item]; + } - if (fun === Sk.builtin.none.none$) { - if (item instanceof Array) { - // With None function and multiple sequences, - // map should return a list of tuples - item = new Sk.builtin.tuple(item); + return Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(fun, item), function (result) { + retval.push(result); + }); } - retval.push(item); - } else { - if (!(item instanceof Array)) { - // If there was only one iterable, convert to Javascript - // Array for call to apply. - item = [item]; - } - - return Sk.misceval.chain(Sk.misceval.applyOrSuspend(fun, undefined, undefined, undefined, item), function (result) { - retval.push(result); - }); + }), + function () { + return new Sk.builtin.list(retval); } - }), function () { - return new Sk.builtin.list(retval); - }); + ); }; -Sk.builtin.reduce = function reduce (fun, seq, initializer) { +Sk.builtin.reduce = function reduce(fun, seq, initializer) { var item; var accum_value; var iter; - Sk.builtin.pyCheckArgsLen("reduce", arguments.length, 2, 3); if (!Sk.builtin.checkIterable(seq)) { throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(seq) + "' object is not iterable"); } @@ -1001,16 +747,30 @@ Sk.builtin.reduce = function reduce (fun, seq, initializer) { } } accum_value = initializer; - for (item = iter.tp$iternext(); - item !== undefined; - item = iter.tp$iternext()) { + for (item = iter.tp$iternext(); item !== undefined; item = iter.tp$iternext()) { accum_value = Sk.misceval.callsimArray(fun, [accum_value, item]); } return accum_value; }; -Sk.builtin.filter = function filter (fun, iterable) { +/** + * + * @param {pyObject} iterable + * @param {*=} cmp + * @param {*=} key + * @param {*=} reverse + */ +Sk.builtin.sorted = function sorted(iterable, cmp, key, reverse) { + const lst = Sk.misceval.arrayFromIterable(iterable, true); + return Sk.misceval.chain(lst, (L) => { + L = new Sk.builtin.list(L); + L.$list_sort(cmp, key, reverse); + return L; + }); +}; + +Sk.builtin.filter = function filter(fun, iterable) { var result; var iter, item; var retval; @@ -1018,11 +778,9 @@ Sk.builtin.filter = function filter (fun, iterable) { var add; var ctor; Sk.builtin.pyCheckArgsLen("filter", arguments.length, 2, 2); - if (!Sk.builtin.checkIterable(iterable)) { throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(iterable) + "' object is not iterable"); } - ctor = function () { return []; }; @@ -1034,7 +792,7 @@ Sk.builtin.filter = function filter (fun, iterable) { return new Sk.builtin.list(iter); }; - if (iterable.__class__ === Sk.builtin.str) { + if (iterable.ob$type === Sk.builtin.str) { ctor = function () { return new Sk.builtin.str(""); }; @@ -1044,7 +802,7 @@ Sk.builtin.filter = function filter (fun, iterable) { ret = function (iter) { return iter; }; - } else if (iterable.__class__ === Sk.builtin.tuple) { + } else if (iterable.ob$type === Sk.builtin.tuple) { ret = function (iter) { return new Sk.builtin.tuple(iter); }; @@ -1052,11 +810,9 @@ Sk.builtin.filter = function filter (fun, iterable) { retval = ctor(); - for (iter = Sk.abstr.iter(iterable), item = iter.tp$iternext(); - item !== undefined; - item = iter.tp$iternext()) { + for (iter = Sk.abstr.iter(iterable), item = iter.tp$iternext(); item !== undefined; item = iter.tp$iternext()) { if (fun === Sk.builtin.none.none$) { - result = new Sk.builtin.bool( item); + result = new Sk.builtin.bool(item); } else { result = Sk.misceval.callsimArray(fun, [item]); } @@ -1069,26 +825,24 @@ Sk.builtin.filter = function filter (fun, iterable) { return ret(retval); }; -Sk.builtin.hasattr = function hasattr (obj, attr) { - Sk.builtin.pyCheckArgsLen("hasattr", arguments.length, 2, 2); - var special, ret; - if (!Sk.builtin.checkString(attr)) { +Sk.builtin.hasattr = function hasattr(obj, pyName) { + if (!Sk.builtin.checkString(pyName)) { throw new Sk.builtin.TypeError("hasattr(): attribute name must be string"); } - - if (obj.tp$getattr) { - if (obj.tp$getattr(attr)) { - return Sk.builtin.bool.true$; - } else { - return Sk.builtin.bool.false$; + const res = Sk.misceval.tryCatch( + () => obj.tp$getattr(pyName, true), + (e) => { + if (e instanceof Sk.builtin.AttributeError) { + return undefined; + } else { + throw e; + } } - } else { - throw new Sk.builtin.AttributeError("Object has no tp$getattr method"); - } + ); + return Sk.misceval.chain(res, (val) => (val === undefined ? Sk.builtin.bool.false$ : Sk.builtin.bool.true$)); }; - -Sk.builtin.pow = function pow (a, b, c) { +Sk.builtin.pow = function pow(a, b, c) { var ret; var res; var right; @@ -1096,7 +850,6 @@ Sk.builtin.pow = function pow (a, b, c) { var c_num; var b_num; var a_num; - Sk.builtin.pyCheckArgsLen("pow", arguments.length, 2, 3); if (c instanceof Sk.builtin.none) { c = undefined; @@ -1113,27 +866,25 @@ Sk.builtin.pow = function pow (a, b, c) { if (!Sk.builtin.checkNumber(a) || !Sk.builtin.checkNumber(b)) { if (c === undefined) { - throw new Sk.builtin.TypeError("unsupported operand type(s) for pow(): '" + Sk.abstr.typeName(a) + "' and '" + Sk.abstr.typeName(b) + "'"); + throw new Sk.builtin.TypeError( + "unsupported operand type(s) for pow(): '" + Sk.abstr.typeName(a) + "' and '" + Sk.abstr.typeName(b) + "'" + ); } - throw new Sk.builtin.TypeError("unsupported operand type(s) for pow(): '" + Sk.abstr.typeName(a) + "', '" + Sk.abstr.typeName(b) + "', '" + Sk.abstr.typeName(c) + "'"); + throw new Sk.builtin.TypeError( + "unsupported operand type(s) for pow(): '" + Sk.abstr.typeName(a) + "', '" + Sk.abstr.typeName(b) + "', '" + Sk.abstr.typeName(c) + "'" + ); } if (a_num < 0 && b instanceof Sk.builtin.float_) { throw new Sk.builtin.ValueError("negative number cannot be raised to a fractional power"); } if (c === undefined) { - if ((a instanceof Sk.builtin.float_ || b instanceof Sk.builtin.float_) || (b_num < 0)) { + if (a instanceof Sk.builtin.float_ || b instanceof Sk.builtin.float_ || b_num < 0) { return new Sk.builtin.float_(Math.pow(a_num, b_num)); } - left = new Sk.builtin.int_(a_num); right = new Sk.builtin.int_(b_num); res = left.nb$power(right); - - if (a instanceof Sk.builtin.lng || b instanceof Sk.builtin.lng) { - return new Sk.builtin.lng(res); - } - return res; } else { if (!Sk.builtin.checkInt(a) || !Sk.builtin.checkInt(b) || !Sk.builtin.checkInt(c)) { @@ -1149,10 +900,7 @@ Sk.builtin.pow = function pow (a, b, c) { if (c_num === 0) { throw new Sk.builtin.ValueError("pow() 3rd argument cannot be 0"); } - if ((a instanceof Sk.builtin.lng || b instanceof Sk.builtin.lng || c instanceof Sk.builtin.lng) || - (Math.pow(a_num, b_num) === Infinity)) { - // convert a to a long so that we can use biginteger's modPowInt method - a = new Sk.builtin.lng(a); + if (a instanceof Sk.builtin.lng || b instanceof Sk.builtin.lng || c instanceof Sk.builtin.lng || Math.pow(a_num, b_num) === Infinity) { return a.nb$power(b, c); } else { ret = new Sk.builtin.int_(Math.pow(a_num, b_num)); @@ -1161,72 +909,32 @@ Sk.builtin.pow = function pow (a, b, c) { } }; -Sk.builtin.quit = function quit (msg) { +Sk.builtin.quit = function quit(msg) { var s = new Sk.builtin.str(msg).v; throw new Sk.builtin.SystemExit(s); }; - -Sk.builtin.issubclass = function issubclass (c1, c2) { - var i; - var issubclass_internal; - Sk.builtin.pyCheckArgsLen("issubclass", arguments.length, 2, 2); +Sk.builtin.issubclass = function issubclass(c1, c2) { if (!Sk.builtin.checkClass(c1)) { throw new Sk.builtin.TypeError("issubclass() arg 1 must be a class"); } - - if (!Sk.builtin.checkClass(c2) && !(c2 instanceof Sk.builtin.tuple)) { + let c2_isClass = Sk.builtin.checkClass(c2); + if (!c2_isClass && !(c2 instanceof Sk.builtin.tuple)) { throw new Sk.builtin.TypeError("issubclass() arg 2 must be a class or tuple of classes"); } - - issubclass_internal = function (klass, base) { - var i; - var bases; - if (klass === base) { - return true; - } - if (klass["$d"] === undefined) { - return false; - } - if (klass["$d"].mp$subscript) { - // old style classes don't have bases - if (klass["$d"].sq$contains(Sk.builtin.type.basesStr_)) { - bases = klass["$d"].mp$subscript(Sk.builtin.type.basesStr_); - } else { - return false; - } - } else { - return false; - } - for (i = 0; i < bases.v.length; ++i) { - if (issubclass_internal(bases.v[i], base)) { - return true; - } - } - return false; - }; - - if (Sk.builtin.checkClass(c2)) { - /* Quick test for an exact match */ - if (c1 === c2) { - return true; - } - - return issubclass_internal(c1, c2); + if (c2_isClass) { + return c1.$isSubType(c2) ? Sk.builtin.bool.true$ : Sk.builtin.bool.false$; } - // Handle tuple type argument - if (c2 instanceof Sk.builtin.tuple) { - for (i = 0; i < c2.v.length; ++i) { - if (Sk.builtin.issubclass(c1, c2.v[i])) { - return true; - } + for (let i = 0; i < c2.v.length; ++i) { + if (Sk.misceval.isTrue(Sk.builtin.issubclass(c1, c2.v[i]))) { + return Sk.builtin.bool.true$; } - return false; } + return Sk.misceval.bool.false$; }; -Sk.builtin.globals = function globals () { +Sk.builtin.globals = function globals() { var i; var ret = new Sk.builtin.dict([]); for (i in Sk["globals"]) { @@ -1234,10 +942,9 @@ Sk.builtin.globals = function globals () { } return ret; - }; -Sk.builtin.divmod = function divmod (a, b) { +Sk.builtin.divmod = function divmod(a, b) { return Sk.abstr.numberBinOp(a, b, "DivMod"); }; @@ -1246,9 +953,7 @@ Sk.builtin.divmod = function divmod (a, b) { * will depend on the type of the value argument, however there is a standard formatting syntax that is used by most * built-in types: Format Specification Mini-Language. */ -Sk.builtin.format = function format (value, format_spec) { - Sk.builtin.pyCheckArgsLen("format", arguments.length, 1, 2); - +Sk.builtin.format = function format(value, format_spec) { if (format_spec === undefined) { format_spec = Sk.builtin.str.$emptystr; } @@ -1256,7 +961,7 @@ Sk.builtin.format = function format (value, format_spec) { return Sk.abstr.objectFormat(value, format_spec); }; -Sk.builtin.reversed = function reversed (seq) { +Sk.builtin.reversed = function reversed(seq) { Sk.builtin.pyCheckArgsLen("reversed", arguments.length, 1, 1); var special = Sk.abstr.lookupSpecial(seq, Sk.builtin.str.$reversed); @@ -1275,12 +980,13 @@ Sk.builtin.reversed = function reversed (seq) { var reverseIter = function (obj) { this.idx = obj.sq$length() - 1; this.myobj = obj; - this.ob$type = Sk.builtin.type.makeTypeObj("reversed", function _reversed(){}); + this.ob$type = Sk.builtin.type.makeTypeObj("reversed", function _reversed() { + }); this.getitem = Sk.abstr.lookupSpecial(obj, Sk.builtin.str.$getitem); - this["$r"] = function() { + this["$r"] = function () { return new Sk.builtin.str(""); }, - this.tp$iter = function() { + this.tp$iter = function () { return this; }, this.tp$iternext = function () { @@ -1309,8 +1015,6 @@ Sk.builtin.reversed = function reversed (seq) { }; Sk.builtin.id = function (obj) { - Sk.builtin.pyCheckArgsLen("id", arguments.length, 1, 1); - if (obj.__id === undefined) { Sk.builtin.idCount += 1; obj.__id = Sk.builtin.idCount; @@ -1319,13 +1023,12 @@ Sk.builtin.id = function (obj) { return new Sk.builtin.int_(obj.__id); }; -Sk.builtin.bytearray = function bytearray () { +Sk.builtin.bytearray = function bytearray() { throw new Sk.builtin.NotImplementedError("bytearray is not yet implemented"); }; -Sk.builtin.callable = function callable (obj) { +Sk.builtin.callable = function callable(obj) { // check num of args - Sk.builtin.pyCheckArgsLen("callable", arguments.length, 1, 1); if (Sk.builtin.checkCallable(obj)) { return Sk.builtin.bool.true$; @@ -1333,38 +1036,11 @@ Sk.builtin.callable = function callable (obj) { return Sk.builtin.bool.false$; }; -Sk.builtin.delattr = function delattr (obj, attr) { - Sk.builtin.pyCheckArgsLen("delattr", arguments.length, 2, 2); - if (obj["$d"][attr.v] !== undefined) { - var ret = Sk.misceval.tryCatch(function() { - var try1 = Sk.builtin.setattr(obj, attr, undefined); - return try1; - }, function(e) { - Sk.misceval.tryCatch(function() { - var try2 = Sk.builtin.setattr(obj["$d"], attr, undefined); - - return try2; - }, function(e) { - if (e instanceof Sk.builtin.AttributeError) { - throw new Sk.builtin.AttributeError(Sk.abstr.typeName(obj) + " instance has no attribute '"+ attr.v+ "'"); - } else { - throw e; - } - }); - }); - return ret; - } // cannot set or del attr from builtin type - if (obj["$r"]().v.slice(1,5) !== "type") { - if (obj.ob$type === Sk.builtin.type && obj[attr.v] !== undefined) { - obj[attr.v] = undefined; - return Sk.builtin.none.none$; - } - throw new Sk.builtin.AttributeError(Sk.abstr.typeName(obj) + " instance has no attribute '"+ attr.v+ "'"); - } - throw new Sk.builtin.TypeError("can't set attributes of built-in/extension type '" + obj.tp$name + "'"); +Sk.builtin.delattr = function delattr(obj, attr) { + return Sk.builtin.setattr(obj, attr, undefined); }; -Sk.builtin.execfile = function execfile () { +Sk.builtin.execfile = function execfile() { throw new Sk.builtin.NotImplementedError("execfile is not yet implemented"); }; @@ -1376,7 +1052,7 @@ Sk.builtin.execfile = function execfile () { * @param source * @returns {Sk.builtin|*} */ -Sk.builtin.code = function(filename, source) { +Sk.builtin.code = function (filename, source) { if (!(this instanceof Sk.builtin.code)) { return new Sk.builtin.code(filename, source); } @@ -1387,24 +1063,19 @@ Sk.builtin.code = function(filename, source) { }; Sk.abstr.setUpInheritance("code", Sk.builtin.code, Sk.builtin.object); Sk.builtin.code.prototype["$r"] = function () { - return new Sk.builtin.str(""); + return new Sk.builtin.str(""); }; Sk.exportSymbol("Sk.builtin.code", Sk.builtin.code); -Sk.builtin.compile = function(source, filename, mode, flags, dont_inherit, optimize) { +Sk.builtin.compile = function (source, filename, mode, flags, dont_inherit, optimize) { Sk.builtin.pyCheckArgsLen("compile", arguments.length, 3, 6); source = Sk.ffi.remapToJs(source); filename = Sk.ffi.remapToJs(filename); - if (!Sk.compiledSources) { - Sk.compiledSources = {}; - } - Sk.compiledSources[filename] = source; return new Sk.builtin.code(filename, source); }; - -var extractDict = function(obj) { +var extractDict = function (obj) { var ret = {}; var k, v, kAsJs, iter; if (obj === undefined) { @@ -1425,7 +1096,7 @@ var extractDict = function(obj) { Sk.builtin.exec = function execf(pythonCode, new_globals) { Sk.builtin.pyCheckArgs("exec", arguments, 1, 2); - var prom = new Promise(function(resolve, reject) { + var prom = new Promise(function (resolve, reject) { var backupRG = Sk.retainGlobals; Sk.retainGlobals = true; var filename = ""; @@ -1446,10 +1117,11 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { new_globals_copy.__package__ = Sk.builtin.none.none$; } var backupGlobals = Sk.globals; + console.log(Sk.globals); Sk.globals = new_globals_copy; // Possibly copy over some "default" ones? var name = filename.endsWith(".py") ? filename.slice(0, -3) : filename; - var pyName = Sk.builtin.str(name); - var sysModules = Sk.getCurrentSysModules(); + var pyName = new Sk.builtin.str(name); + var sysModules = Sk.sysmodules.mp$subscript(new Sk.builtin.str("sys"));//Sk.getCurrentSysModules(); var modname = name; var caughtError = null; try { @@ -1458,11 +1130,11 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { console.log("SYSTEMATIC ERROR"); caughtError = e; } - console.log("FINISHED EVAL"); Sk.globals = backupGlobals; // Only try to delete if we succeeded in creating it! - if (sysModules.mp$lookup(pyName)) { - Sk.getCurrentSysModules().mp$del_subscript(pyName); + if (Sk.sysmodules.mp$lookup(pyName)) { + Sk.sysmodules.del$item(pyName); + //Sk.sysmodules.mp$del_subscript(pyName); } for (var key in new_globals_copy) { if (new_globals_copy.hasOwnProperty(key)) { @@ -1480,45 +1152,33 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { return Sk.misceval.promiseToSuspension(prom); }; -Sk.builtin.frozenset = function frozenset () { +Sk.builtin.frozenset = function frozenset() { throw new Sk.builtin.NotImplementedError("frozenset is not yet implemented"); }; -Sk.builtin.help = function help () { +Sk.builtin.help = function help() { throw new Sk.builtin.NotImplementedError("help is not yet implemented"); }; -Sk.builtin.iter = function iter (obj, sentinel) { - Sk.builtin.pyCheckArgsLen("iter", arguments.length, 1, 2); +Sk.builtin.iter = function iter(obj, sentinel) { if (arguments.length === 1) { - if (!Sk.builtin.checkIterable(obj)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(obj) + - "' object is not iterable"); - } else { - return new Sk.builtin.iterator(obj); - } + return Sk.abstr.iter(obj); } else { - if (Sk.builtin.checkCallable(obj)) { - return new Sk.builtin.iterator(obj, sentinel); - } else { - throw new TypeError("iter(v, w): v must be callable"); - } + return Sk.abstr.iter(new Sk.builtin.callable_iter_(obj, sentinel)); } }; -Sk.builtin.locals = function locals () { +Sk.builtin.locals = function locals() { throw new Sk.builtin.NotImplementedError("locals is not yet implemented"); }; -Sk.builtin.memoryview = function memoryview () { +Sk.builtin.memoryview = function memoryview() { throw new Sk.builtin.NotImplementedError("memoryview is not yet implemented"); }; -Sk.builtin.next_ = function next_ (iter, default_) { +Sk.builtin.next_ = function next_(iter, default_) { var nxt; - Sk.builtin.pyCheckArgsLen("next", arguments.length, 1, 2); if (!iter.tp$iternext) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(iter) + - "' object is not an iterator"); + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(iter) + "' object is not an iterator"); } nxt = iter.tp$iternext(); if (nxt === undefined) { @@ -1530,23 +1190,23 @@ Sk.builtin.next_ = function next_ (iter, default_) { return nxt; }; -Sk.builtin.reload = function reload () { +Sk.builtin.reload = function reload() { throw new Sk.builtin.NotImplementedError("reload is not yet implemented"); }; -Sk.builtin.vars = function vars () { +Sk.builtin.vars = function vars() { throw new Sk.builtin.NotImplementedError("vars is not yet implemented"); }; -Sk.builtin.xrange = Sk.builtin.range; -Sk.builtin.apply_ = function apply_ () { + +Sk.builtin.apply_ = function apply_() { throw new Sk.builtin.NotImplementedError("apply is not yet implemented"); }; -Sk.builtin.buffer = function buffer_ () { +Sk.builtin.buffer = function buffer_() { throw new Sk.builtin.NotImplementedError("buffer is not yet implemented"); }; -Sk.builtin.coerce = function coerce () { +Sk.builtin.coerce = function coerce() { throw new Sk.builtin.NotImplementedError("coerce is not yet implemented"); }; -Sk.builtin.intern = function intern () { +Sk.builtin.intern = function intern() { throw new Sk.builtin.NotImplementedError("intern is not yet implemented"); }; diff --git a/src/builtin/sys.js b/src/builtin/sys.js index 9fb4b4a773..52e29a6909 100644 --- a/src/builtin/sys.js +++ b/src/builtin/sys.js @@ -9,22 +9,30 @@ var $builtinmodule = function (name) { } sys.argv = new Sk.builtins["list"](args); - sys.copyright = Sk.builtin["str"]("Copyright 2009-2010 Scott Graham.\nAll Rights Reserved.\n"); + sys.copyright = new Sk.builtin["str"]("Copyright 2009-2010 Scott Graham.\nAll Rights Reserved.\n"); - sys.maxint = new Sk.builtin.int_(Math.pow(2,53)-1); + if (Sk.__future__.python3) { + sys.version = "3.7(ish) [Skulpt]"; + sys.version_info = new Sk.builtin.tuple([new Sk.builtin.int_(3), new Sk.builtin.int_(7)]); + } else { + sys.version = "2.7(ish) [Skulpt]"; + sys.version_info = new Sk.builtin.tuple([new Sk.builtin.int_(2), new Sk.builtin.int_(7)]); + } + + sys.maxint = new Sk.builtin.int_(Math.pow(2, 53) - 1); /* The largest positive integer supported by the platform’s Py_ssize_t type, * and thus the maximum size lists, strings, dicts, and many other containers can have. * * In skulpt this is the same as maxint, due to the underlying implementation in javascript */ - sys.maxsize = new Sk.builtin.int_(Math.pow(2,53)-1); + sys.maxsize = new Sk.builtin.int_(Math.pow(2, 53) - 1); sys.modules = Sk.sysmodules; sys.path = Sk.realsyspath; - - sys.platform = Sk.builtin.str("skulpt"); + + sys.platform = new Sk.builtin.str("skulpt"); sys.getExecutionLimit = new Sk.builtin.func(function () { if (Sk.execLimit === null) { @@ -72,7 +80,7 @@ var $builtinmodule = function (name) { sys.stdout = sys.__stdout__; sys.stdin = sys.__stdin__; - + sys.exc_info = new Sk.builtin.func(function () { if (Sk.err) { var type = Sk.err.ob$type; diff --git a/src/builtin/this.py b/src/builtin/this.py index 37754b785a..c088e2a2b1 100644 --- a/src/builtin/this.py +++ b/src/builtin/this.py @@ -23,6 +23,6 @@ d = {} for c in (65, 97): for i in range(26): - d[chr(i+c)] = chr((i+13) % 26 + c) + d[chr(i + c)] = chr((i + 13) % 26 + c) -print "".join([d.get(c, c) for c in s]) +print("".join([d.get(c, c) for c in s])) diff --git a/src/builtindict.js b/src/builtindict.js index 4b8bf82bd3..c2c532cf9f 100644 --- a/src/builtindict.js +++ b/src/builtindict.js @@ -1,130 +1,539 @@ // Note: the hacky names on int, long, float have to correspond with the // uniquization that the compiler does for words that are reserved in // Javascript. This is a bit hokey. + Sk.builtins = { - "range" : new Sk.builtin.func(Sk.builtin.range), - "round" : new Sk.builtin.func(Sk.builtin.round), - "len" : new Sk.builtin.func(Sk.builtin.len), - "min" : new Sk.builtin.func(Sk.builtin.min), - "max" : new Sk.builtin.func(Sk.builtin.max), - "sum" : new Sk.builtin.func(Sk.builtin.sum), - "abs" : new Sk.builtin.func(Sk.builtin.abs), - "fabs" : new Sk.builtin.func(Sk.builtin.fabs), - "ord" : new Sk.builtin.func(Sk.builtin.ord), - "chr" : new Sk.builtin.func(Sk.builtin.chr), - "hex" : new Sk.builtin.func(Sk.builtin.hex), - "oct" : new Sk.builtin.func(Sk.builtin.oct), - "bin" : new Sk.builtin.func(Sk.builtin.bin), - "dir" : new Sk.builtin.func(Sk.builtin.dir), - "repr" : new Sk.builtin.func(Sk.builtin.repr), - "open" : new Sk.builtin.func(Sk.builtin.open), - "isinstance": new Sk.builtin.func(Sk.builtin.isinstance), - "hash" : new Sk.builtin.func(Sk.builtin.hash), - "getattr" : new Sk.builtin.func(Sk.builtin.getattr), - "hasattr" : new Sk.builtin.func(Sk.builtin.hasattr), - "id" : new Sk.builtin.func(Sk.builtin.id), - - "reduce" : new Sk.builtin.func(Sk.builtin.reduce), - "sorted" : new Sk.builtin.func(Sk.builtin.sorted), - "any" : new Sk.builtin.func(Sk.builtin.any), - "all" : new Sk.builtin.func(Sk.builtin.all), - - "BaseException" : Sk.builtin.BaseException, - "AttributeError" : Sk.builtin.AttributeError, - "ValueError" : Sk.builtin.ValueError, - "Exception" : Sk.builtin.Exception, - "ZeroDivisionError" : Sk.builtin.ZeroDivisionError, - "SyntaxError" : Sk.builtin.SyntaxError, - "AssertionError" : Sk.builtin.AssertionError, - "ImportError" : Sk.builtin.ImportError, - "IndentationError" : Sk.builtin.IndentationError, - "IndexError" : Sk.builtin.IndexError, - "KeyError" : Sk.builtin.KeyError, - "TypeError" : Sk.builtin.TypeError, - "NameError" : Sk.builtin.NameError, - "OSError" : Sk.builtin.OSError, - "IOError" : Sk.builtin.IOError, + "round": null, + "len": null, + "min": null, + "max": null, + "sum": null, + "abs": null, + "fabs": null, + "ord": null, + "chr": null, + "hex": null, + "oct": null, + "bin": null, + "dir": null, + "repr": null, + "open": null, + "isinstance": null, + "hash": null, + "getattr": null, + "hasattr": null, + "id": null, + + "reduce": new Sk.builtin.func(Sk.builtin.reduce), + "sorted": null, + "any": null, + "all": null, + + // iterator objects if py2 mode we replace these with sk_methods + "enumerate": Sk.builtin.enumerate, + "filter": Sk.builtin.filter_, + "map": Sk.builtin.map_, + "range": Sk.builtin.range_, + "reversed": Sk.builtin.reversed, + "zip": Sk.builtin.zip_, + + "BaseException": Sk.builtin.BaseException, + "AttributeError": Sk.builtin.AttributeError, + "ValueError": Sk.builtin.ValueError, + "Exception": Sk.builtin.Exception, + "ZeroDivisionError": Sk.builtin.ZeroDivisionError, + "AssertionError": Sk.builtin.AssertionError, + "ImportError": Sk.builtin.ImportError, + "IndentationError": Sk.builtin.IndentationError, + "IndexError": Sk.builtin.IndexError, + "KeyError": Sk.builtin.KeyError, + "TypeError": Sk.builtin.TypeError, + "NameError": Sk.builtin.NameError, + "OSError": Sk.builtin.OSError, + "TimeoutError": Sk.builtin.TimeoutError, + "IOError": Sk.builtin.IOError, "NotImplementedError": Sk.builtin.NotImplementedError, - "StandardError" : Sk.builtin.StandardError, - "SystemExit" : Sk.builtin.SystemExit, - "OverflowError" : Sk.builtin.OverflowError, - "OperationError" : Sk.builtin.OperationError, - "NegativePowerError" : Sk.builtin.NegativePowerError, - "RuntimeError" : Sk.builtin.RuntimeError, - "StopIteration" : Sk.builtin.StopIteration, + "SystemExit": Sk.builtin.SystemExit, + "OverflowError": Sk.builtin.OverflowError, + "OperationError": Sk.builtin.OperationError, + "NegativePowerError": Sk.builtin.NegativePowerError, + "RuntimeError": Sk.builtin.RuntimeError, + "StopIteration": Sk.builtin.StopIteration, + "SyntaxError": Sk.builtin.SyntaxError, + "EOFError": Sk.builtin.EOFError, + "MemoryError": Sk.builtin.MemoryError, + "ReferenceError": Sk.builtin.ReferenceError, "float_$rw$": Sk.builtin.float_, - "int_$rw$" : Sk.builtin.int_, - "bool" : Sk.builtin.bool, - "complex" : Sk.builtin.complex, - "enumerate" : Sk.builtin.enumerate, - "dict" : Sk.builtin.dict, - "file" : Sk.builtin.file, - "function" : Sk.builtin.func, - "generator" : Sk.builtin.generator, - "list" : Sk.builtin.list, - "long_$rw$" : Sk.builtin.lng, - "method" : Sk.builtin.method, - "object" : Sk.builtin.object, - "slice" : Sk.builtin.slice, - "str" : Sk.builtin.str, - "set" : Sk.builtin.set, - "tuple" : Sk.builtin.tuple, - "type" : Sk.builtin.type, - - "input" : new Sk.builtin.func(Sk.builtin.input), - "raw_input" : new Sk.builtin.func(Sk.builtin.raw_input), - "setattr" : new Sk.builtin.func(Sk.builtin.setattr), + "int_$rw$": Sk.builtin.int_, + "bool": Sk.builtin.bool, + "complex": Sk.builtin.complex, + "dict": Sk.builtin.dict, + "file": Sk.builtin.file, + "frozenset": Sk.builtin.frozenset, + "function": Sk.builtin.func, + "generator": Sk.builtin.generator, + "list": Sk.builtin.list, + "long_$rw$": Sk.builtin.lng, + "method": Sk.builtin.method, + "object": Sk.builtin.object, + "slice": Sk.builtin.slice, + "str": Sk.builtin.str, + "set": Sk.builtin.set, + "tuple": Sk.builtin.tuple, + "type": Sk.builtin.type, + + "input": null, + "raw_input": new Sk.builtin.func(Sk.builtin.raw_input), + "setattr": null, /*'read': Sk.builtin.read,*/ - "jseval" : Sk.builtin.jseval, - "jsmillis" : Sk.builtin.jsmillis, - "quit" : new Sk.builtin.func(Sk.builtin.quit), - "exit" : new Sk.builtin.func(Sk.builtin.quit), - "print" : Sk.builtin.print, - "divmod" : new Sk.builtin.func(Sk.builtin.divmod), - "format" : new Sk.builtin.func(Sk.builtin.format), - "globals" : new Sk.builtin.func(Sk.builtin.globals), - "issubclass": new Sk.builtin.func(Sk.builtin.issubclass), - "iter" : Sk.builtin.iter, + "jseval": Sk.builtin.jseval, + "jsmillis": Sk.builtin.jsmillis, + "quit": new Sk.builtin.func(Sk.builtin.quit), + "exit": new Sk.builtin.func(Sk.builtin.quit), + "print": null, + "divmod": null, + "format": null, + "globals": null, + "issubclass": null, + "iter": null, // Functions below are not implemented - "bytearray" : Sk.builtin.bytearray, - "callable" : Sk.builtin.callable, - "delattr" : Sk.builtin.delattr, - "__import__": Sk.builtin.__import__, - "eval_$rn$" : Sk.builtin.eval_, - "compile" : Sk.builtin.compile, - "exec" : Sk.builtin.exec, - "execfile" : Sk.builtin.execfile, - "frozenset" : Sk.builtin.frozenset, - "help" : Sk.builtin.help, - "locals" : Sk.builtin.locals, + // "bytearray" : Sk.builtin.bytearray, + // "callable" : Sk.builtin.callable, + // "delattr" : Sk.builtin.delattr, + // "eval_$rw$" : Sk.builtin.eval_, + "execfile": Sk.builtin.execfile, + "exec": Sk.builtin.exec, + "compile": Sk.builtin.compile, + + "help": Sk.builtin.help, + // "locals" : Sk.builtin.locals, "memoryview": Sk.builtin.memoryview, - "next" : Sk.builtin.next_, - "pow" : Sk.builtin.pow, - "reload" : Sk.builtin.reload, - "reversed" : Sk.builtin.reversed, - "super" : Sk.builtin.super_, - "unichr" : Sk.builtin.unichr, - "vars" : Sk.builtin.vars, - "xrange" : Sk.builtin.xrange, - "apply_$rn$": Sk.builtin.apply_, - "buffer" : Sk.builtin.buffer, - "coerce" : Sk.builtin.coerce, - "intern" : Sk.builtin.intern, - //"classmethod": Sk.builtin.classmethod, + // "next" : Sk.builtin.next_, + // "pow" : Sk.builtin.pow, + "reload": Sk.builtin.reload, + "super_$rw$": Sk.builtin.super_, + "unichr": Sk.builtin.unichr, + "vars": Sk.builtin.vars, + "apply_$rw$": Sk.builtin.apply_, + "buffer": Sk.builtin.buffer, + "coerce": Sk.builtin.coerce, + "intern": Sk.builtin.intern, + + + "property": Sk.builtin.property, + "classmethod": Sk.builtin.classmethod, + "staticmethod": Sk.builtin.staticmethod, }; +Sk.builtins.$method_defs = { + // __build_class__: { + // $meth: Sk.builtin.__build_class__, + // $flags: {}, + // $textsig: null, + // $doc: "__build_class__(func, name, *bases, metaclass=None, **kwds) -> class\n\nInternal helper function used by the class statement." + // }, + + __import__: { + $meth: Sk.builtin.__import__, + $flags: {NamedArgs: ["name", "globals", "locals", "fromlist", "level"]}, + $textsig: null, + $doc: + "__import__(name, globals=None, locals=None, fromlist=(), level=0) -> module\n\nImport a module. Because this function is meant for use by the Python\ninterpreter and not for general use, it is better to use\nimportlib.import_module() to programmatically import a module.\n\nThe globals argument is only used to determine the context;\nthey are not modified. The locals argument is unused. The fromlist\nshould be a list of names to emulate ``from name import ...'', or an\nempty list to emulate ``import name''.\nWhen importing a module from a package, note that __import__('A.B', ...)\nreturns package A when fromlist is empty, but its submodule B when\nfromlist is not empty. The level argument is used to determine whether to\nperform absolute or relative imports: 0 is absolute, while a positive number\nis the number of parent directories to search relative to the current module.", + }, + + abs: { + $meth: Sk.builtin.abs, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the absolute value of the argument.", + }, + + all: { + $meth: Sk.builtin.all, + $flags: {OneArg: true}, + $textsig: "($module, iterable, /)", + $doc: "Return True if bool(x) is True for all values x in the iterable.\n\nIf the iterable is empty, return True.", + }, + + any: { + $meth: Sk.builtin.any, + $flags: {OneArg: true}, + $textsig: "($module, iterable, /)", + $doc: "Return True if bool(x) is True for any x in the iterable.\n\nIf the iterable is empty, return False.", + }, + + // ascii: { + // $meth: Sk.builtin.ascii, + // $flags: {OneArg: true}, + // $textsig: "($module, obj, /)", + // $doc: "Return an ASCII-only representation of an object.\n\nAs repr(), return a string containing a printable representation of an\nobject, but escape the non-ASCII characters in the string returned by\nrepr() using \\\\x, \\\\u or \\\\U escapes. This generates a string similar\nto that returned by repr() in Python 2." + // }, + + bin: { + $meth: Sk.builtin.bin, + $flags: {OneArg: true}, + $textsig: "($module, number, /)", + $doc: "Return the binary representation of an integer.\n\n >>> bin(2796202)\n '0b1010101010101010101010'", + }, + + // breakpoint: { + // $meth: Sk.builtin.breakpoint, + // $flags: {}, + // $textsig: null, + // $doc: "breakpoint(*args, **kws)\n\nCall sys.breakpointhook(*args, **kws). sys.breakpointhook() must accept\nwhatever arguments are passed.\n\nBy default, this drops you into the pdb debugger." + // }, + + callable: { + $meth: Sk.builtin.callable, + $flags: {OneArg: true}, + $textsig: "($module, obj, /)", + $doc: + "Return whether the object is callable (i.e., some kind of function).\n\nNote that classes are callable, as are instances of classes with a\n__call__() method.", + }, + + chr: { + $meth: Sk.builtin.chr, + $flags: {OneArg: true}, + $textsig: "($module, i, /)", + $doc: "Return a Unicode string of one character with ordinal i; 0 <= i <= 0x10ffff.", + }, + + compile: { + $meth: Sk.builtin.compile, + $flags: {}, + $textsig: "($module, /, source, filename, mode, flags=0,\n dont_inherit=False, optimize=-1)", + $doc: "Compile source into a code object that can be executed by exec() or eval().\n\nThe source code may represent a Python module, statement or expression.\nThe filename will be used for run-time error messages.\nThe mode must be 'exec' to compile a module, 'single' to compile a\nsingle (interactive) statement, or 'eval' to compile an expression.\nThe flags argument, if present, controls which future statements influence\nthe compilation of the code.\nThe dont_inherit argument, if true, stops the compilation inheriting\nthe effects of any future statements in effect in the code calling\ncompile; if absent or false these statements do influence the compilation,\nin addition to any features explicitly specified." + }, + + delattr: { + $meth: Sk.builtin.delattr, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, obj, name, /)", + $doc: "Deletes the named attribute from the given object.\n\ndelattr(x, 'y') is equivalent to ``del x.y''", + }, + + dir: { + $meth: Sk.builtin.dir, + $flags: {MinArgs: 0, MaxArgs: 1}, + $textsig: null, + $doc: + "dir([object]) -> list of strings\n\nIf called without an argument, return the names in the current scope.\nElse, return an alphabetized list of names comprising (some of) the attributes\nof the given object, and of attributes reachable from it.\nIf the object supplies a method named __dir__, it will be used; otherwise\nthe default dir() logic is used and returns:\n for a module object: the module's attributes.\n for a class object: its attributes, and recursively the attributes\n of its bases.\n for any other object: its attributes, its class's attributes, and\n recursively the attributes of its class's base classes.", + }, + + divmod: { + $meth: Sk.builtin.divmod, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, x, y, /)", + $doc: "Return the tuple (x//y, x%y). Invariant: div*y + mod == x.", + }, + + eval_$rw$: { + $name: "eval", + $meth: Sk.builtin.eval_, + $flags: {MinArgs: 1, MaxArgs: 3}, + $textsig: "($module, source, globals=None, locals=None, /)", + $doc: + "Evaluate the given source in the context of globals and locals.\n\nThe source may be a string representing a Python expression\nor a code object as returned by compile().\nThe globals must be a dictionary and locals can be any mapping,\ndefaulting to the current globals and locals.\nIf only globals is given, locals defaults to it.", + }, + + exec: { + $meth: Sk.builtin.exec, + $flags: {MinArgs:2, MaxArgs: 3}, + $textsig: "($module, source, globals=None, locals=None, /)", + $doc: "Execute the given source in the context of globals and locals.\n\nThe source may be a string representing one or more Python statements\nor a code object as returned by compile().\nThe globals must be a dictionary and locals can be any mapping,\ndefaulting to the current globals and locals.\nIf only globals is given, locals defaults to it." + }, + + format: { + $meth: Sk.builtin.format, + $flags: {MinArgs: 1, MaxArgs: 2}, + $textsig: "($module, value, format_spec='', /)", + $doc: + "Return value.__format__(format_spec)\n\nformat_spec defaults to the empty string.\nSee the Format Specification Mini-Language section of help('FORMATTING') for\ndetails.", + }, + + getattr: { + $meth: Sk.builtin.getattr, + $flags: {MinArgs: 2, MaxArgs: 3}, + $textsig: null, + $doc: + "getattr(object, name[, default]) -> value\n\nGet a named attribute from an object; getattr(x, 'y') is equivalent to x.y.\nWhen a default argument is given, it is returned when the attribute doesn't\nexist; without it, an exception is raised in that case.", + }, + + globals: { + $meth: Sk.builtin.globals, + $flags: {NoArgs: true}, + $textsig: "($module, /)", + $doc: + "Return the dictionary containing the current scope's global variables.\n\nNOTE: Updates to this dictionary *will* affect name lookups in the current\nglobal scope and vice-versa.", + }, + + hasattr: { + $meth: Sk.builtin.hasattr, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, obj, name, /)", + $doc: + "Return whether the object has an attribute with the given name.\n\nThis is done by calling getattr(obj, name) and catching AttributeError.", + }, + + hash: { + $meth: Sk.builtin.hash, + $flags: {OneArg: true}, + $textsig: "($module, obj, /)", + $doc: + "Return the hash value for the given object.\n\nTwo objects that compare equal must also have the same hash value, but the\nreverse is not necessarily true.", + }, + + hex: { + $meth: Sk.builtin.hex, + $flags: {OneArg: true}, + $textsig: "($module, number, /)", + $doc: "Return the hexadecimal representation of an integer.\n\n >>> hex(12648430)\n '0xc0ffee'", + }, + + id: { + $meth: Sk.builtin.id, + $flags: {OneArg: true}, + $textsig: "($module, obj, /)", + $doc: + "Return the identity of an object.\n\nThis is guaranteed to be unique among simultaneously existing objects.\n(CPython uses the object's memory address.)", + }, + + input: { + $meth: Sk.builtin.input, + $flags: {MinArgs: 0, MaxArgs: 1}, + $textsig: "($module, prompt=None, /)", + $doc: + "Read a string from standard input. The trailing newline is stripped.\n\nThe prompt string, if given, is printed to standard output without a\ntrailing newline before reading input.\n\nIf the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.\nOn *nix systems, readline is used if available.", + }, + + isinstance: { + $meth: Sk.builtin.isinstance, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, obj, class_or_tuple, /)", + $doc: + "Return whether an object is an instance of a class or of a subclass thereof.\n\nA tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to\ncheck against. This is equivalent to ``isinstance(x, A) or isinstance(x, B)\nor ...`` etc.", + }, + + issubclass: { + $meth: Sk.builtin.issubclass, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, cls, class_or_tuple, /)", + $doc: + "Return whether 'cls' is a derived from another class or is the same class.\n\nA tuple, as in ``issubclass(x, (A, B, ...))``, may be given as the target to\ncheck against. This is equivalent to ``issubclass(x, A) or issubclass(x, B)\nor ...`` etc.", + }, + + iter: { + $meth: Sk.builtin.iter, + $flags: {MinArgs: 1, MaxArgs: 2}, + $textsig: "($module, iterable /)", + $doc: + "iter(iterable) -> iterator\niter(callable, sentinel) -> iterator\n\nGet an iterator from an object. In the first form, the argument must\nsupply its own iterator, or be a sequence.\nIn the second form, the callable is called until it returns the sentinel.", + }, + + len: { + $meth: Sk.builtin.len, + $flags: {OneArg: true}, + $textsig: "($module, obj, /)", + $doc: "Return the number of items in a container.", + }, + + locals: { + $meth: Sk.builtin.locals, + $flags: {NoArgs: true}, + $textsig: "($module, /)", + $doc: + "Return a dictionary containing the current scope's local variables.\n\nNOTE: Whether or not updates to this dictionary will affect name lookups in\nthe local scope and vice-versa is *implementation dependent* and not\ncovered by any backwards compatibility guarantees.", + }, + + max: { + $meth: Sk.builtin.max, + $flags: {FastCall: true}, + $textsig: null, + $doc: + "max(iterable, *[, default=obj, key=func]) -> value\nmax(arg1, arg2, *args, *[, key=func]) -> value\n\nWith a single iterable argument, return its biggest item. The\ndefault keyword-only argument specifies an object to return if\nthe provided iterable is empty.\nWith two or more arguments, return the largest argument.", + }, + + min: { + $meth: Sk.builtin.min, + $flags: {FastCall: true}, + $textsig: null, + $doc: + "min(iterable, *[, default=obj, key=func]) -> value\nmin(arg1, arg2, *args, *[, key=func]) -> value\n\nWith a single iterable argument, return its smallest item. The\ndefault keyword-only argument specifies an object to return if\nthe provided iterable is empty.\nWith two or more arguments, return the smallest argument.", + }, + + next: { + $name: "next", + $meth: Sk.builtin.next_, + $flags: {MinArgs: 1, MaxArgs: 2}, + $textsig: null, + $doc: + "next(iterator[, default])\n\nReturn the next item from the iterator. If default is given and the iterator\nis exhausted, it is returned instead of raising StopIteration.", + }, + + oct: { + $meth: Sk.builtin.oct, + $flags: {OneArg: true}, + $textsig: "($module, number, /)", + $doc: "Return the octal representation of an integer.\n\n >>> oct(342391)\n '0o1234567'", + }, + + open: { + $meth: Sk.builtin.open, + $flags: { + MinArgs: 1, + MaxArgs: 3, + //NamedArgs: ["file, mode, buffering, encoding, errors, newline, closefd, opener"], + //Defaults: [new Sk.builtin.str("r"), new Sk.builtin.int_(-1), Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.bool.true$, Sk.builtin.none.none$] + }, + $textsig: null, + // $textsig: "($module, /, file, mode='r', buffering=-1, encoding=None,\n errors=None, newline=None, closefd=True, opener=None)", + // this is the python 2 documentation since we don't support the py3 version + $doc: + "open(name[, mode[, buffering]]) -> file object\n\nOpen a file using the file() type, returns a file object. This is the\npreferred way to open a file. See file.__doc__ for further information.", + }, + + ord: { + $meth: Sk.builtin.ord, + $flags: {OneArg: true}, + $textsig: "($module, c, /)", + $doc: "Return the Unicode code point for a one-character string.", + }, + + pow: { + $meth: Sk.builtin.pow, + $flags: {MinArgs: 2, MaxArgs: 3}, + $textsig: "($module, x, y, z=None, /)", + $doc: + "Equivalent to x**y (with two arguments) or x**y % z (with three arguments)\n\nSome types, such as ints, are able to use a more efficient algorithm when\ninvoked using the three argument form.", + }, + + print: { + $meth: Sk.builtin.print, + $flags: {FastCall: true}, + $textsig: null, + $doc: + "print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n\nPrints the values to a stream, or to sys.stdout by default.\nOptional keyword arguments:\nfile: a file-like object (stream); defaults to the current sys.stdout.\nsep: string inserted between values, default a space.\nend: string appended after the last value, default a newline.\nflush: whether to forcibly flush the stream.", + }, + + repr: { + $meth: Sk.builtin.repr, + $flags: {OneArg: true}, + $textsig: "($module, obj, /)", + $doc: "Return the canonical string representation of the object.\n\nFor many object types, including most builtins, eval(repr(obj)) == obj.", + }, + + round: { + $meth: Sk.builtin.round, + $flags: { + NamedArgs: ["number", "ndigits"], + }, + $textsig: "($module, /, number, ndigits=None)", + $doc: + "Round a number to a given precision in decimal digits.\n\nThe return value is an integer if ndigits is omitted or None. Otherwise\nthe return value has the same type as the number. ndigits may be negative.", + }, + + setattr: { + $meth: Sk.builtin.setattr, + $flags: {MinArgs: 3, MaxArgs: 3}, + $textsig: "($module, obj, name, value, /)", + $doc: "Sets the named attribute on the given object to the specified value.\n\nsetattr(x, 'y', v) is equivalent to ``x.y = v''", + }, + + sorted: { + $meth: Sk.builtin.sorted, + $flags: { + NamedArgs: [null, "cmp", "key", "reverse"], + Defaults: [Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.bool.false$], + }, // should be fast call leave for now + $textsig: "($module, iterable, /, *, key=None, reverse=False)", + $doc: + "Return a new list containing all items from the iterable in ascending order.\n\nA custom key function can be supplied to customize the sort order, and the\nreverse flag can be set to request the result in descending order.", + }, + + sum: { + $meth: Sk.builtin.sum, + $flags: { + NamedArgs: [null, "start"], + Defaults: [new Sk.builtin.int_(0)], + }, + $textsig: "($module, iterable, /, start=0)", //changed in python 3.8 start + $doc: + "Return the sum of a 'start' value (default: 0) plus an iterable of numbers\n\nWhen the iterable is empty, return the start value.\nThis function is intended specifically for use with numeric values and may\nreject non-numeric types.", + }, + + vars: { + $meth: Sk.builtin.vars, + $flags: {MinArgs: 0, MaxArgs: 1}, + $textsig: null, + $doc: "vars([object]) -> dictionary\n\nWithout arguments, equivalent to locals().\nWith an argument, equivalent to object.__dict__.", + }, +}; + +for (let def_name in Sk.builtins.$method_defs) { + const method_def = Sk.builtins.$method_defs[def_name]; + method_def.$name = def_name; + Sk.builtins[def_name] = new Sk.builtin.sk_method(method_def, undefined, "builtins"); +} + Sk.setupObjects = function (py3) { if (py3) { Sk.builtins["filter"] = Sk.builtin.filter_; Sk.builtins["map"] = Sk.builtin.map_; Sk.builtins["zip"] = Sk.builtin.zip_; + Sk.builtins["range"] = Sk.builtin.range_; + delete Sk.builtins["xrange"]; + delete Sk.builtins["StandardError"]; + delete Sk.builtins["unicode"]; + delete Sk.builtins["long_$rw$"]; + Sk.builtin.int_.prototype.$r = function () { + return new Sk.builtin.str(this.v.toString()); + }; + delete Sk.builtin.int_.prototype.tp$str; + delete Sk.builtin.bool.prototype.tp$str; } else { + Sk.builtins["range"] = new Sk.builtin.sk_method( + { + $meth: Sk.builtin.range, + $name: "range", + $flags: {MinArgs: 1, MaxArgs: 3}, + }, + undefined, + "builtins" + ); + Sk.builtins["xrange"] = new Sk.builtin.sk_method( + { + $meth: Sk.builtin.xrange, + $name: "xrange", + $flags: {MinArgs: 1, MaxArgs: 3}, + }, + undefined, + "builtins" + ); Sk.builtins["filter"] = new Sk.builtin.func(Sk.builtin.filter); Sk.builtins["map"] = new Sk.builtin.func(Sk.builtin.map); Sk.builtins["zip"] = new Sk.builtin.func(Sk.builtin.zip); + + Sk.builtins["StandardError"] = Sk.builtin.Exception; + Sk.builtins["unicode"] = Sk.builtin.str; + Sk.builtins["long_$rw$"] = Sk.builtin.lng; + Sk.builtin.int_.prototype.$r = function () { + const v = this.v; + if (typeof v === "number") { + return new Sk.builtin.str(v.toString()); + } else { + return new Sk.builtin.str(v.toString() + "L"); + } + }; + Sk.builtin.int_.prototype.tp$str = function () { + return new Sk.builtin.str(this.v.toString()); + }; + Sk.builtin.bool.prototype.tp$str = function () { + return this.$r(); + }; } }; + Sk.exportSymbol("Sk.setupObjects", Sk.setupObjects); -Sk.exportSymbol("Sk.builtins", Sk.builtins); \ No newline at end of file +Sk.exportSymbol("Sk.builtins", Sk.builtins); diff --git a/src/check.js b/src/check.js new file mode 100644 index 0000000000..7d57839b68 --- /dev/null +++ b/src/check.js @@ -0,0 +1,252 @@ +/** + * Check arguments to Python functions to ensure the correct number of + * arguments are passed. + * + * @param {string} name the name of the function + * @param {Object} args the args passed to the function + * @param {number} minargs the minimum number of allowable arguments + * @param {number=} maxargs optional maximum number of allowable + * arguments (default: Infinity) + * @param {boolean=} kwargs optional true if kwargs, false otherwise + * (default: false) + * @param {boolean=} free optional true if free vars, false otherwise + * (default: false) + */ +Sk.builtin.pyCheckArgs = function (name, args, minargs, maxargs, kwargs, free) { + var nargs = args.length; + var msg = ""; + + if (maxargs === undefined) { + maxargs = Infinity; + } + if (kwargs) { + nargs -= 1; + } + if (free) { + nargs -= 1; + } + if (nargs < minargs || nargs > maxargs) { + if (minargs === maxargs) { + msg = name + "() takes exactly " + minargs + " arguments"; + } else if (nargs < minargs) { + msg = name + "() takes at least " + minargs + " arguments"; + } else if (minargs > 0) { + msg = name + "() takes at most " + maxargs + " arguments"; + } else { + msg = name + "() takes no arguments"; + } + msg += " (" + nargs + " given)"; + throw new Sk.builtin.TypeError(msg); + } +}; +Sk.exportSymbol("Sk.builtin.pyCheckArgs", Sk.builtin.pyCheckArgs); + +/** + * Check arguments to Python functions to ensure the correct number of + * arguments are passed. + * + * @param {string} name the name of the function + * @param {number} nargs the args passed to the function + * @param {number} minargs the minimum number of allowable arguments + * @param {number=} maxargs optional maximum number of allowable + * arguments (default: Infinity) + * @param {boolean=} kwargs optional true if kwargs, false otherwise + * (default: false) + * @param {boolean=} free optional true if free vars, false otherwise + * (default: false) + */ +Sk.builtin.pyCheckArgsLen = function (name, nargs, minargs, maxargs, kwargs, free) { + var msg = ""; + + if (maxargs === undefined) { + maxargs = Infinity; + } + if (kwargs) { + nargs -= 1; + } + if (free) { + nargs -= 1; + } + if (nargs < minargs || nargs > maxargs) { + if (minargs === maxargs) { + msg = name + "() takes exactly " + minargs + " arguments"; + } else if (nargs < minargs) { + msg = name + "() takes at least " + minargs + " arguments"; + } else { + msg = name + "() takes at most " + maxargs + " arguments"; + } + msg += " (" + nargs + " given)"; + throw new Sk.builtin.TypeError(msg); + } +}; + +/** + * Check type of argument to Python functions. + * + * @param {string} name the name of the argument + * @param {string} exptype string of the expected type name + * @param {boolean} check truthy if type check passes, falsy otherwise + */ +Sk.builtin.pyCheckType = function (name, exptype, check) { + if (!check) { + throw new Sk.builtin.TypeError(name + " must be a " + exptype); + } +}; +Sk.exportSymbol("Sk.builtin.pyCheckType", Sk.builtin.pyCheckType); + +/** + * @function + * @param {*} arg + * + * @description + * Does the arg have a valid `__getitem__` method? + */ +Sk.builtin.checkSequence = function (arg) { + return arg != null && arg.mp$subscript !== undefined; +}; +Sk.exportSymbol("Sk.builtin.checkSequence", Sk.builtin.checkSequence); + +/** + * @description + * Use this to test whether or not a Python object is iterable. You should **not** rely + * on the presence of tp$iter on the object as a good test, as it could be a user defined + * class with `__iter__` defined or ``__getitem__`` This tests for all of those cases + * + * Note in most cases it will be more pragmatic to simply call {@link Sk.abstr.iter} which will + * throw the appropriate error if the pyObject is not iterable. + * + * @param arg {Object} A Python object + * @returns {boolean} true if the object is iterable + */ +Sk.builtin.checkIterable = function (arg) { + let ret = false; + if (arg !== undefined) { + try { + ret = Sk.abstr.iter(arg); + if (ret) { + return true; + } else { + return false; + } + } catch (e) { + if (e instanceof Sk.builtin.TypeError) { + return false; + } else { + throw e; + } + } + } + return ret; +}; +Sk.exportSymbol("Sk.builtin.checkIterable", Sk.builtin.checkIterable); + +/** + * @function + * @param {*} obj + */ +Sk.builtin.checkCallable = function (obj) { + // takes care of builtin functions and methods, builtins + return obj.tp$call !== undefined; +}; + +/** + * @function + * @description + * Is the object an instance of {@link Sk.builtin.int_} or {@link Sk.builtin.float_} + * + * @param {*} arg + */ +Sk.builtin.checkNumber = function (arg) { + return ( + arg != null && + (typeof arg === "number" || arg instanceof Sk.builtin.int_ || arg instanceof Sk.builtin.float_ || arg instanceof Sk.builtin.lng) + ); +}; +Sk.exportSymbol("Sk.builtin.checkNumber", Sk.builtin.checkNumber); + +/** + * @description + * Is the arg an instance of {@link Sk.builtin.complex} + */ +Sk.builtin.checkComplex = function (arg) { + return arg instanceof Sk.builtin.complex; +}; +Sk.exportSymbol("Sk.builtin.checkComplex", Sk.builtin.checkComplex); + +/** + * @description + * Supports both JS Number and pyObject + * @param {*} arg + */ +Sk.builtin.checkInt = function (arg) { + return arg != null && ((typeof arg === "number" && arg === (arg | 0)) || arg instanceof Sk.builtin.int_ || arg instanceof Sk.builtin.lng); +}; +Sk.exportSymbol("Sk.builtin.checkInt", Sk.builtin.checkInt); + +/** + * @description + * Is the arg an instance of {@link Sk.builtin.float_} + * @param {*} arg + */ +Sk.builtin.checkFloat = function (arg) { + return arg != null && arg instanceof Sk.builtin.float_; +}; +Sk.exportSymbol("Sk.builtin.checkFloat", Sk.builtin.checkFloat); + +/** + * @description + * Is the arg a strict instance of {@link Sk.builtin.str} + * @param {*} arg + */ +Sk.builtin.checkString = function (arg) { + return arg != null && arg.ob$type == Sk.builtin.str; +}; +Sk.exportSymbol("Sk.builtin.checkString", Sk.builtin.checkString); + +/** + * Is the arg an instance of {@link Sk.builtin.type} + * @param {*} arg + */ +Sk.builtin.checkClass = function (arg) { + return arg != null && arg.sk$type; +}; +Sk.exportSymbol("Sk.builtin.checkClass", Sk.builtin.checkClass); + +/** + * @description + * Is the arg an instance of {@link Sk.builtin.bool} + * @param {*} arg + */ +Sk.builtin.checkBool = function (arg) { + return arg instanceof Sk.builtin.bool; +}; +Sk.exportSymbol("Sk.builtin.checkBool", Sk.builtin.checkBool); + +Sk.builtin.checkNone = function (arg) { + return arg === Sk.builtin.none.none$; +}; +Sk.exportSymbol("Sk.builtin.checkNone", Sk.builtin.checkNone); + +/** + * @description + * Is the arg callable? + * @param {*} arg + */ +Sk.builtin.checkFunction = function (arg) { + return arg != null && arg.tp$call !== undefined; +}; +Sk.exportSymbol("Sk.builtin.checkFunction", Sk.builtin.checkFunction); + +Sk.builtin.checkDataDescr = function (arg) { + return arg && arg.tp$descr_set !== undefined; +}; +Sk.exportSymbol("Sk.builtin.checkDataDescr", Sk.builtin.checkDataDescr); + +/** + * @description + * Is the arg ain instance of {@link Sk.builtin.set} or {@link Sk.builtin.frozenset} + * @param {*} arg + */ +Sk.builtin.checkAnySet = function (arg) { + return arg != null && (arg instanceof Sk.builtin.set || arg instanceof Sk.builtin.frozenset); +}; diff --git a/src/classmethod.js.disabled b/src/classmethod.js.disabled index 72463013bf..22ec61d3bf 100644 --- a/src/classmethod.js.disabled +++ b/src/classmethod.js.disabled @@ -4,8 +4,8 @@ Sk.builtin.classmethod = function classmethod(f) { return new Sk.builtin.classmethod(f); } this["$d"] = new Sk.builtin.dict([]); - this["$d"].mp$ass_subscript(Sk.builtin.str("f"), f); - this["$d"].mp$ass_subscript(Sk.builtin.str("__dict__"), this.$d); + this["$d"].mp$ass_subscript(new Sk.builtin.str("f"), f); + this["$d"].mp$ass_subscript(new Sk.builtin.str("__dict__"), this.$d); var __get__ = new Sk.builtin.func(function __get__(self, obj, klass) { Sk.builtin.pyCheckArgsLen("__get__", arguments.length, 1, 2, false, true); if (obj === Sk.builtin.none.none$ && klass === Sk.builtin.none.none$) { @@ -17,7 +17,7 @@ Sk.builtin.classmethod = function classmethod(f) { } var newFunction = new Sk.builtins.function(function(args) { - var f = Sk.abstr.gattr(self, Sk.builtin.str("f"), true); + var f = Sk.abstr.gattr(self, new Sk.builtin.str("f"), true); var fArgs = [klass]; Sk.misceval.iterFor(Sk.abstr.iter(args), function (e) { fArgs.push(e); @@ -33,7 +33,7 @@ Sk.builtin.classmethod = function classmethod(f) { __get__.co_name = new Sk.builtin.str("__get__"); __get__.$defaults = [Sk.builtin.none.none$]; __get__.co_varnames = ["self", "obj", "klass"]; - this["$d"].mp$ass_subscript(Sk.builtin.str("__get__"), __get__); + this["$d"].mp$ass_subscript(new Sk.builtin.str("__get__"), __get__); this.__class__ = Sk.builtin.classmethod; //return Sk.misceval.buildClass({}, this, "classmethod", [Sk.builtin.object]); return this; @@ -43,7 +43,7 @@ Sk.builtin.classmethod.co_varnames = ["f"]; Sk.abstr.setUpInheritance("classmethod", Sk.builtin.classmethod, Sk.builtin.object); /*var fields = new Sk.builtin.dict([]); -fields.mp$ass_subscript(Sk.builtin.str("__module__"), new Sk.builtin.str("classmethod")); +fields.mp$ass_subscript(new Sk.builtin.str("__module__"), new Sk.builtin.str("classmethod")); Sk.builtin.classmethod = Sk.builtin.type(new Sk.builtin.str("classmethod"), new Sk.builtin.tuple([Sk.builtin.object]), fields);*/ //Sk.builtin.classmethod = Sk.builtin.type.makeIntoTypeObj("classmethod", Sk.builtin.classmethod); diff --git a/src/classmethod.py b/src/classmethod.py index b27b0ce6c3..c6bbf90a1a 100644 --- a/src/classmethod.py +++ b/src/classmethod.py @@ -7,6 +7,8 @@ def __init__(self, f): def __get__(self, obj, klass=None): if klass is None: klass = type(obj) + def newfunc(*args): return self.f(klass, *args) + return newfunc diff --git a/src/compile.js b/src/compile.js index a2fb670bd0..e1263fe5c3 100644 --- a/src/compile.js +++ b/src/compile.js @@ -11,7 +11,7 @@ Sk.gensymcount = 0; * @param {boolean=} canSuspend whether compiled code can suspend * @param {string=} sourceCodeForAnnotation used to add original source to listing if desired */ -function Compiler (filename, st, flags, canSuspend, sourceCodeForAnnotation) { +function Compiler(filename, st, flags, canSuspend, sourceCodeForAnnotation) { this.filename = filename; this.st = st; this.flags = flags; @@ -30,6 +30,7 @@ function Compiler (filename, st, flags, canSuspend, sourceCodeForAnnotation) { this.allUnits = []; this.source = sourceCodeForAnnotation ? sourceCodeForAnnotation.split("\n") : false; + this.retainComments = false; } /** @@ -43,7 +44,7 @@ function Compiler (filename, st, flags, canSuspend, sourceCodeForAnnotation) { * Effectively a frame. */ -function CompilerUnit () { +function CompilerUnit() { this.ste = null; this.name = null; this.canSuspend = false; @@ -116,12 +117,12 @@ Compiler.prototype.annotateSource = function (ast, shouldStep) { out("^\n//\n");*/ Sk.asserts.assert(ast.lineno !== undefined && ast.col_offset !== undefined); - out("\n$currLineNo=Sk.currLineNo=",lineno, ";$currColNo=Sk.currColNo=",col_offset,";"); - out("Sk.currFilename='",this.filename,"';$currSource=",JSON.stringify(sourceLine),";"); + out("\n$currLineNo=Sk.currLineNo=", lineno, ";$currColNo=Sk.currColNo=", col_offset, ";"); + out("Sk.currFilename='", this.filename, "';$currSource=", JSON.stringify(sourceLine), ";"); // Do not trace the standard library - if (shouldStep && (!this.filename || - !this.filename.startsWith("src/lib/"))) { - out("Sk.afterSingleExecution && Sk.afterSingleExecution($gbl,$loc,"+lineno+","+col_offset+","+JSON.stringify(this.filename)+");\n"); + if (shouldStep && (!this.filename || + !this.filename.startsWith("src/lib/"))) { + out("Sk.afterSingleExecution && Sk.afterSingleExecution($gbl,$loc," + lineno + "," + col_offset + "," + JSON.stringify(this.filename) + ");\n"); } } }; @@ -137,118 +138,21 @@ Compiler.prototype.niceName = function (roughName) { return this.gensym(roughName.replace("<", "").replace(">", "").replace(" ", "_")); }; -var reservedWords_ = { - "abstract": true, - "as": true, - "boolean": true, - "break": true, - "byte": true, - "case": true, - "catch": true, - "char": true, - "class": true, - "continue": true, - "const": true, - "debugger": true, - "default": true, - "delete": true, - "do": true, - "double": true, - "else": true, - "enum": true, - "export": true, - "extends": true, - "false": true, - "final": true, - "finally": true, - "float": true, - "for": true, - "function": true, - "goto": true, - "if": true, - "implements": true, - "import": true, - "in": true, - "instanceof": true, - "int": true, - "interface": true, - "is": true, - "long": true, - "namespace": true, - "native": true, - "new": true, - "null": true, - "package": true, - "private": true, - "protected": true, - "public": true, - "return": true, - "short": true, - "static": true, - "super": false, - "switch": true, - "synchronized": true, - "this": true, - "throw": true, - "throws": true, - "transient": true, - "true": true, - "try": true, - "typeof": true, - "use": true, - "var": true, - "void": true, - "volatile": true, - "while": true, - "with": true -}; +var reservedWords_ = Sk.builtin.str.reservedWords_; // defined in str.js -/** - * Fix reserved words - * - * @param {string} name - */ -function fixReservedWords(name) { - if (reservedWords_[name] !== true) { + +function fixReserved(name) { + if (reservedWords_[name] === undefined) { return name; } return name + "_$rw$"; } -var reservedNames_ = { - "__defineGetter__": true, - "__defineSetter__": true, - "apply": true, - "call": true, - "eval": true, - "hasOwnProperty": true, - "isPrototypeOf": true, - "__lookupGetter__": true, - "__lookupSetter__": true, - "__noSuchMethod__": true, - "propertyIsEnumerable": true, - "toSource": true, - "toLocaleString": true, - "toString": true, - "unwatch": true, - "valueOf": true, - "watch": true, - "length": true, - "name": true, -}; - -function fixReservedNames (name) { - if (reservedNames_[name]) { - return name + "_$rn$"; - } - return name; -} - function unfixReserved(name) { - return name.replace(/_\$r[wn]\$$/, ""); + return name.replace(/_\$rw\$$/, ""); } -function mangleName (priv, ident) { +function mangleName(priv, ident) { var name = ident.v; var strpriv = null; @@ -329,12 +233,12 @@ Compiler.prototype.outputInterruptTest = function () { // Added by RNL if (Sk.execLimit !== null || Sk.yieldLimit !== null && this.u.canSuspend) { output += "var $dateNow = Date.now();"; if (Sk.execLimit !== null) { - output += ("if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit){"+ - "throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg())}"); + output += ("if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit){" + + "throw new Sk.builtin.TimeoutError(Sk.timeoutMsg())}"); } if (Sk.yieldLimit !== null && this.u.canSuspend) { output += "if ($dateNow - Sk.lastYield > Sk.yieldLimit) {"; - output += "var $susp = $saveSuspension({data: {type: 'Sk.yield'}, resume: function() {}}, '"+this.filename+"',$currLineNo,$currColNo, $currSource);"; + output += "var $susp = $saveSuspension({data: {type: 'Sk.yield'}, resume: function() {}}, '" + this.filename + "',$currLineNo,$currColNo, $currSource);"; output += "$susp.$blk = $blk;"; output += "$susp.optional = true;"; output += "return $susp;"; @@ -373,7 +277,7 @@ Compiler.prototype._jump = function (block) { /** * @param {Object=} e Object with keys 'lineno' and 'col_offset' */ -Compiler.prototype._checkSuspension = function(e) { +Compiler.prototype._checkSuspension = function (e) { var retblk; if (this.u.canSuspend) { @@ -383,16 +287,16 @@ Compiler.prototype._checkSuspension = function(e) { e = e || {lineno: "$currLineNo", col_offset: "$currColNo", source: "$currSource"}; - out ("if ($ret && $ret.$isSuspension) { return $saveSuspension($ret,$fname,"+e.lineno+","+e.col_offset+","+e.source+"); }"); + out("if ($ret && $ret.$isSuspension) { return $saveSuspension($ret,$fname," + e.lineno + "," + e.col_offset + "," + e.source + "); }"); this.u.doesSuspend = true; this.u.tempsToSave = this.u.tempsToSave.concat(this.u.localtemps); } else { - out ("if ($ret && $ret.$isSuspension) { $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); }"); + out("if ($ret && $ret.$isSuspension) { $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); }"); } }; -Compiler.prototype.cunpackstarstoarray = function(elts, permitEndOnly) { +Compiler.prototype.cunpackstarstoarray = function (elts, permitEndOnly) { if (!elts || elts.length == 0) { return "[]"; } @@ -413,9 +317,9 @@ Compiler.prototype.cunpackstarstoarray = function(elts, permitEndOnly) { let arr = this._gr("unpack", "[]"); for (let elt of elts) { if (elt.constructor !== Sk.astnodes.Starred) { - out(arr,".push(",this.vexpr(elt),");"); + out(arr, ".push(", this.vexpr(elt), ");"); } else { - out("$ret = Sk.misceval.iterFor(Sk.abstr.iter(",this.vexpr(elt.value),"), function(e) { ",arr,".push(e); });"); + out("$ret = Sk.misceval.iterFor(Sk.abstr.iter(", this.vexpr(elt.value), "), function(e) { ", arr, ".push(e); });"); this._checkSuspension(); } } @@ -426,7 +330,7 @@ Compiler.prototype.cunpackstarstoarray = function(elts, permitEndOnly) { } }; -Compiler.prototype.ctuplelistorset = function(e, data, tuporlist) { +Compiler.prototype.ctuplelistorset = function (e, data, tuporlist) { var i; var items; var item; @@ -435,7 +339,10 @@ Compiler.prototype.ctuplelistorset = function(e, data, tuporlist) { let hasStars = false; for (let elt of e.elts) { - if (elt.constructor === Sk.astnodes.Starred) { hasStars = true; break; } + if (elt.constructor === Sk.astnodes.Starred) { + hasStars = true; + break; + } } if (e.ctx === Sk.astnodes.Store) { @@ -505,19 +412,19 @@ Compiler.prototype.cdict = function (e) { return this._gr("loaddict", "new Sk.builtins['dict']([", items, "])"); }; -Compiler.prototype.clistcomp = function(e) { +Compiler.prototype.clistcomp = function (e) { Sk.asserts.assert(e instanceof Sk.astnodes.ListComp); var tmp = this._gr("_compr", "new Sk.builtins['list']([])"); // note: _ is impt. for hack in name mangling (same as cpy) return this.ccompgen("list", tmp, e.generators, 0, e.elt, null, e); }; -Compiler.prototype.cdictcomp = function(e) { +Compiler.prototype.cdictcomp = function (e) { Sk.asserts.assert(e instanceof Sk.astnodes.DictComp); var tmp = this._gr("_dcompr", "new Sk.builtins.dict([])"); return this.ccompgen("dict", tmp, e.generators, 0, e.value, e.key, e); }; -Compiler.prototype.csetcomp = function(e) { +Compiler.prototype.csetcomp = function (e) { Sk.asserts.assert(e instanceof Sk.astnodes.SetComp); var tmp = this._gr("_setcompr", "new Sk.builtins.set([])"); return this.ccompgen("set", tmp, e.generators, 0, e.elt, null, e); @@ -582,9 +489,9 @@ Compiler.prototype.ccompgen = function (type, tmpname, generators, genIndex, val return tmpname; }; -Compiler.prototype.cyield = function(e) { +Compiler.prototype.cyield = function (e) { if (this.u.ste.blockType !== Sk.SYMTAB_CONSTS.FunctionBlock) { - throw new SyntaxError("'yield' outside function"); + throw new Sk.builtin.SyntaxError("'yield' outside function", this.filename, e.lineno); } var val = "null", nextBlock; @@ -633,8 +540,6 @@ Compiler.prototype.ccall = function (e) { // and we need to unpack those too. Then we make a call. // The existing Sk.misceval.call() and .apply() signatures do not // help us here; we do it by hand. - // This is less than optimal (yep, that's the @rixner bat-sign), - // but should be correct. let positionalArgs = this.cunpackstarstoarray(e.args, !Sk.__future__.python3); let keywordArgs = "undefined"; @@ -658,7 +563,7 @@ Compiler.prototype.ccall = function (e) { keywordArgs = this._gr("keywordArgs", keywordArgs); for (let kw of e.keywords) { if (!kw.arg) { - out("$ret = Sk.abstr.mappingUnpackIntoKeywordArray(",keywordArgs,",",this.vexpr(kw.value),",",func,");"); + out("$ret = Sk.abstr.mappingUnpackIntoKeywordArray(", keywordArgs, ",", this.vexpr(kw.value), ",", func, ");"); this._checkSuspension(); } } @@ -673,13 +578,7 @@ Compiler.prototype.ccall = function (e) { out("if (typeof self === \"undefined\" || self.toString().indexOf(\"Window\") > 0) { throw new Sk.builtin.RuntimeError(\"super(): no arguments\") };"); positionalArgs = "[$gbl.__class__,self]"; } - if (keywordArgs !== "undefined") { - out("$ret = Sk.misceval.applyOrSuspend(",func,",undefined,undefined,",keywordArgs,",",positionalArgs,");"); - } else if (positionalArgs != "[]") { - out ("$ret = Sk.misceval.callsimOrSuspendArray(", func, ", ", positionalArgs, ");"); - } else { - out ("$ret = Sk.misceval.callsimOrSuspendArray(", func, ");"); - } + out("$ret = (", func, ".tp$call)?", func, ".tp$call(", positionalArgs, ",", keywordArgs, ") : Sk.misceval.applyOrSuspend(", func, ",undefined,undefined,", keywordArgs, ",", positionalArgs, ");"); this._checkSuspension(e); @@ -790,6 +689,45 @@ Compiler.prototype.cboolop = function (e) { }; +Compiler.prototype.cjoinedstr = function (e) { + let ret; + Sk.asserts.assert(e instanceof Sk.astnodes.JoinedStr); + + for (let s of e.values) { + let v = this.vexpr(s); + if (!ret) { + ret = this._gr("joinedstr", v); + } else { + out(ret, "=", ret, ".sq$concat(", v, ");"); + } + } + + if (!ret) { + ret = "Sk.builtin.str.$emptystr"; + } + + return ret; +}; + +Compiler.prototype.cformattedvalue = function (e) { + let value = this.vexpr(e.value); + switch (e.conversion) { + case "s": + value = this._gr("value", "new Sk.builtin.str(", value, ")"); + break; + case "a": + // TODO when repr() becomes more unicode-aware, + // we'll want to handle repr() and ascii() differently. + // For now, they're the same + case "r": + value = this._gr("value", "Sk.builtin.repr(", value, ")"); + break; + } + let formatSpec = (e.format_spec ? this.vexpr(e.format_spec) : "Sk.builtin.str.$emptystr"); + return this._gr("formatted", "Sk.abstr.objectFormat(" + value + "," + formatSpec + ")"); +}; + + /** * * compiles an expression. to 'return' something, it'll gensym a var and store @@ -841,25 +779,27 @@ Compiler.prototype.vexpr = function (e, data, augvar, augsubs) { case Sk.astnodes.Call: result = this.ccall(e); // After the function call, we've returned to this line - this.annotateSource(e); + this.annotateSource(e, true); return result; case Sk.astnodes.Num: if (typeof e.n === "number") { return e.n; + } else if (e.n instanceof Sk.builtin.lng) { + return this.makeConstant("new Sk.builtin.lng('" + e.n.v.toString() + "')"); } else if (e.n instanceof Sk.builtin.int_) { - return this.makeConstant("new Sk.builtin.int_(" + e.n.v + ")"); + if (typeof e.n.v === "number") { + return this.makeConstant("new Sk.builtin.int_(" + e.n.v + ")"); + } + return this.makeConstant("new Sk.builtin.int_('" + e.n.v.toString() + "')"); } else if (e.n instanceof Sk.builtin.float_) { // Preserve sign of zero for floats - nStr = e.n.v === 0 && 1/e.n.v === -Infinity ? "-0" : e.n.v; + nStr = e.n.v === 0 && 1 / e.n.v === -Infinity ? "-0" : e.n.v; return this.makeConstant("new Sk.builtin.float_(" + nStr + ")"); - } else if (e.n instanceof Sk.builtin.lng) { - // long uses the tp$str() method which delegates to nmber.str$ which preserves the sign - return this.makeConstant("Sk.longFromStr('" + e.n.tp$str().v + "')"); } else if (e.n instanceof Sk.builtin.complex) { // preserve sign of zero here too - var real_val = e.n.real.v === 0 && 1/e.n.real.v === -Infinity ? "-0" : e.n.real.v; - var imag_val = e.n.imag.v === 0 && 1/e.n.imag.v === -Infinity ? "-0" : e.n.imag.v; - return this.makeConstant("new Sk.builtin.complex(new Sk.builtin.float_(" + real_val + "), new Sk.builtin.float_(" + imag_val + "))"); + var real_val = e.n.real === 0 && 1 / e.n.real === -Infinity ? "-0" : e.n.real; + var imag_val = e.n.imag === 0 && 1 / e.n.imag === -Infinity ? "-0" : e.n.imag; + return this.makeConstant("new Sk.builtin.complex(" + real_val + ", " + imag_val + ")"); } Sk.asserts.fail("unhandled Num type"); case Sk.astnodes.Str: @@ -871,16 +811,22 @@ Compiler.prototype.vexpr = function (e, data, augvar, augsubs) { mangled = e.attr["$r"]().v; mangled = mangled.substring(1, mangled.length - 1); mangled = mangleName(this.u.private_, new Sk.builtin.str(mangled)).v; - mangled = fixReservedWords(mangled); - mangled = fixReservedNames(mangled); mname = this.makeConstant("new Sk.builtin.str('" + mangled + "')"); switch (e.ctx) { case Sk.astnodes.AugLoad: - out("$ret = Sk.abstr.gattr(", augvar, ",", mname, ", true);"); + out("$ret = ", augvar, ".tp$getattr(", mname, ", true);"); + out("\nif ($ret === undefined) {"); + out("\nconst error_name =", augvar, ".sk$type ? \"type object '\" +", augvar, ".prototype.tp$name + \"'\" : \"'\" + Sk.abstr.typeName(", augvar, ") + \"' object\";"); + out("\nthrow new Sk.builtin.AttributeError(error_name + \" has no attribute '\" + ", mname, ".$jsstr() + \"'\");"); + out("\n};"); this._checkSuspension(e); return this._gr("lattr", "$ret"); case Sk.astnodes.Load: - out("$ret = Sk.abstr.gattr(", val, ",", mname, ", true);"); + out("$ret = ", val, ".tp$getattr(", mname, ", true);"); + out("\nif ($ret === undefined) {"); + out("\nconst error_name =", val, ".sk$type ? \"type object '\" +", val, ".prototype.tp$name + \"'\" : \"'\" + Sk.abstr.typeName(", val, ") + \"' object\";"); + out("\nthrow new Sk.builtin.AttributeError(error_name + \" has no attribute '\" + ", mname, ".$jsstr() + \"'\");"); + out("\n};"); this._checkSuspension(e); return this._gr("lattr", "$ret"); case Sk.astnodes.AugStore: @@ -889,12 +835,12 @@ Compiler.prototype.vexpr = function (e, data, augvar, augsubs) { // so this will never *not* execute. But it could, if Sk.abstr.numberInplaceBinOp were fixed. out("$ret = undefined;"); out("if(", data, "!==undefined){"); - out("$ret = Sk.abstr.sattr(", augvar, ",", mname, ",", data, ", true);"); + out("$ret = ", augvar, ".tp$setattr(", mname, ",", data, ", true);"); out("}"); this._checkSuspension(e); break; case Sk.astnodes.Store: - out("$ret = Sk.abstr.sattr(", val, ",", mname, ",", data, ", true);"); + out("$ret = ", val, ".tp$setattr(", mname, ",", data, ", true);"); this._checkSuspension(e); break; case Sk.astnodes.Del: @@ -908,7 +854,7 @@ Compiler.prototype.vexpr = function (e, data, augvar, augsubs) { case Sk.astnodes.Subscript: switch (e.ctx) { case Sk.astnodes.AugLoad: - out("$ret = Sk.abstr.objectGetItem(",augvar,",",augsubs,", true);"); + out("$ret = Sk.abstr.objectGetItem(", augvar, ",", augsubs, ", true);"); this._checkSuspension(e); return this._gr("gitem", "$ret"); case Sk.astnodes.Load: @@ -922,7 +868,7 @@ Compiler.prototype.vexpr = function (e, data, augvar, augsubs) { out("$ret=undefined;"); out("if(", data, "!==undefined){"); - out("$ret=Sk.abstr.objectSetItem(",augvar,",",augsubs,",",data,", true)"); + out("$ret=Sk.abstr.objectSetItem(", augvar, ",", augsubs, ",", data, ", true)"); out("}"); this._checkSuspension(e); break; @@ -957,6 +903,10 @@ Compiler.prototype.vexpr = function (e, data, augvar, augsubs) { return this.ctuplelistorset(e, data, "set"); case Sk.astnodes.Starred: break; + case Sk.astnodes.JoinedStr: + return this.cjoinedstr(e); + case Sk.astnodes.FormattedValue: + return this.cformattedvalue(e); default: Sk.asserts.fail("unhandled case " + e.constructor.name + " vexpr"); } @@ -1081,8 +1031,8 @@ Compiler.prototype.pushFinallyBlock = function (n) { Compiler.prototype.popFinallyBlock = function () { this.u.finallyBlocks.pop(); }; -Compiler.prototype.peekFinallyBlock = function() { - return (this.u.finallyBlocks.length > 0) ? this.u.finallyBlocks[this.u.finallyBlocks.length-1] : undefined; +Compiler.prototype.peekFinallyBlock = function () { + return (this.u.finallyBlocks.length > 0) ? this.u.finallyBlocks[this.u.finallyBlocks.length - 1] : undefined; }; Compiler.prototype.setupExcept = function (eb) { @@ -1127,7 +1077,7 @@ Compiler.prototype.outputSuspensionHelpers = function (unit) { var localsToSaveWithoutDuplicates = []; for (i = 0; i < localsToSave.length; i++) { t = localsToSave[i]; - if (seenTemps[t]===undefined) { + if (seenTemps[t] === undefined) { localsToSaveWithoutDuplicates.push(t); seenTemps[t] = true; } @@ -1137,37 +1087,37 @@ Compiler.prototype.outputSuspensionHelpers = function (unit) { var hasCell = unit.ste.blockType === Sk.SYMTAB_CONSTS.FunctionBlock && unit.ste.childHasFree; var output = (localsToSave.length > 0 ? ("var " + localsToSave.join(",") + ";") : ""); output += "var $wakeFromSuspension = function() {" + - "var susp = "+unit.scopename+".$wakingSuspension; "+unit.scopename+".$wakingSuspension = undefined;" + - "$blk=susp.$blk; $loc=susp.$loc; $gbl=susp.$gbl; $exc=susp.$exc; $err=susp.$err; $postfinally=susp.$postfinally;" + - "$currLineNo=susp.$lineno;$currColNo=susp.$colno;$currSource=susp.$source;Sk.lastYield=Date.now();" + - (hasCell?"$cell=susp.$cell;":""); + "var susp = " + unit.scopename + ".$wakingSuspension; " + unit.scopename + ".$wakingSuspension = undefined;" + + "$blk=susp.$blk; $loc=susp.$loc; $gbl=susp.$gbl; $exc=susp.$exc; $err=susp.$err; $postfinally=susp.$postfinally;" + + "$currLineNo=susp.$lineno;$currColNo=susp.$colno;$currSource=susp.$source;Sk.lastYield=Date.now();" + + (hasCell ? "$cell=susp.$cell;" : ""); for (i = 0; i < localsToSave.length; i++) { t = localsToSave[i]; output += t + "=susp.$tmps." + t + ";"; } - output += ("try {"+ - "$ret=susp.child.resume();"+ - this.handleTraceback(false, unit.scopename)+ - // Close out function - ";"); + output += ("try {" + + "$ret=susp.child.resume();" + + this.handleTraceback(false, unit.scopename) + + // Close out function + ";"); output += "var $saveSuspension = function($child, $filename, $lineno, $colno, $source) {" + - "var susp = new Sk.misceval.Suspension(); susp.child=$child;" + - "susp.resume=function(){"+unit.scopename+".$wakingSuspension=susp; return "+unit.scopename+"("+(unit.ste.generator?"$gen":"")+"); };" + - "susp.data=susp.child.data;susp.$blk=$blk;susp.$loc=$loc;susp.$gbl=$gbl;susp.$exc=$exc;susp.$err=$err;susp.$postfinally=$postfinally;" + - "susp.$filename=$filename;susp.$lineno=$lineno;susp.$colno=$colno;susp.source=$source;" + - "susp.optional=susp.child.optional;" + - (hasCell ? "susp.$cell=$cell;" : ""); + "var susp = new Sk.misceval.Suspension(); susp.child=$child;" + + "susp.resume=function(){" + unit.scopename + ".$wakingSuspension=susp; return " + unit.scopename + "(" + (unit.ste.generator ? "$gen" : "") + "); };" + + "susp.data=susp.child.data;susp.$blk=$blk;susp.$loc=$loc;susp.$gbl=$gbl;susp.$exc=$exc;susp.$err=$err;susp.$postfinally=$postfinally;" + + "susp.$filename=$filename;susp.$lineno=$lineno;susp.$colno=$colno;susp.source=$source;" + + "susp.optional=susp.child.optional;" + + (hasCell ? "susp.$cell=$cell;" : ""); for (i = 0; i < localsToSave.length; i++) { t = localsToSave[i]; localSaveCode.push("\"" + t + "\":" + t); } - output += "susp.$tmps={" + localSaveCode.join(",") + "};" + - "return susp;" + - "};"; + output += "susp.$tmps={" + localSaveCode.join(",") + "};" + + "return susp;" + + "};"; return output; }; @@ -1193,7 +1143,9 @@ Compiler.prototype.outputAllUnits = function () { generatedBlocks = Object.create(null); for (i = 0; i < blocks.length; ++i) { block = i; - if (block in generatedBlocks) {continue;} + if (block in generatedBlocks) { + continue; + } while (true) { generatedBlocks[block] = true; @@ -1287,10 +1239,10 @@ Compiler.prototype.cwhile = function (s) { if ((Sk.debugging || Sk.killableWhile) && this.u.canSuspend) { var suspType = "Sk.delay"; - var debugBlock = this.newBlock("debug breakpoint for line "+s.lineno); - out("if (Sk.breakpoints('"+this.filename+"',"+s.lineno+","+s.col_offset+")) {", - "var $susp = $saveSuspension({data: {type: '"+suspType+"'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+","+s.source+");", - "$susp.$blk = "+debugBlock+";", + var debugBlock = this.newBlock("debug breakpoint for line " + s.lineno); + out("if (Sk.breakpoints('" + this.filename + "'," + s.lineno + "," + s.col_offset + ")) {", + "var $susp = $saveSuspension({data: {type: '" + suspType + "'}, resume: function() {}}, '" + this.filename + "'," + s.lineno + "," + s.col_offset + "," + s.source + ");", + "$susp.$blk = " + debugBlock + ";", "$susp.optional = true;", "return $susp;", "}"); @@ -1345,7 +1297,7 @@ Compiler.prototype.cfor = function (s) { this.setBlock(start); // load targets - out ("$ret = Sk.abstr.iternext(", iter,(this.u.canSuspend?", true":", false"),");"); + out("$ret = Sk.abstr.iternext(", iter, (this.u.canSuspend ? ", true" : ", false"), ");"); this._checkSuspension(s); @@ -1355,10 +1307,10 @@ Compiler.prototype.cfor = function (s) { if ((Sk.debugging || Sk.killableFor) && this.u.canSuspend) { var suspType = "Sk.delay"; - var debugBlock = this.newBlock("debug breakpoint for line "+s.lineno); - out("if (Sk.breakpoints('"+this.filename+"',"+s.lineno+","+s.col_offset+")) {", - "var $susp = $saveSuspension({data: {type: '"+suspType+"'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+","+s.source+");", - "$susp.$blk = "+debugBlock+";", + var debugBlock = this.newBlock("debug breakpoint for line " + s.lineno); + out("if (Sk.breakpoints('" + this.filename + "'," + s.lineno + "," + s.col_offset + ")) {", + "var $susp = $saveSuspension({data: {type: '" + suspType + "'}, resume: function() {}}, '" + this.filename + "'," + s.lineno + "," + s.col_offset + "," + s.source + ");", + "$susp.$blk = " + debugBlock + ";", "$susp.optional = true;", "return $susp;", "}"); @@ -1400,15 +1352,15 @@ Compiler.prototype.craise = function (s) { // Instantiate exc with inst if (s.inst) { var inst = this._gr("inst", this.vexpr(s.inst)); - out("if(!(",inst," instanceof Sk.builtin.tuple)) {", - inst,"= new Sk.builtin.tuple([",inst,"]);", + out("if(!(", inst, " instanceof Sk.builtin.tuple)) {", + inst, "= new Sk.builtin.tuple([", inst, "]);", "}"); - out("$ret = Sk.misceval.callsimOrSuspendArray(",exc,",",inst,".v);"); + out("$ret = Sk.misceval.callsimOrSuspendArray(", exc, ",", inst, ".v);"); } else { - out("$ret = Sk.misceval.callsimOrSuspend(",exc,");"); + out("$ret = Sk.misceval.callsimOrSuspend(", exc, ");"); } this._checkSuspension(s); - out(exc,"=$ret;"); + out(exc, "=$ret;"); this._jump(instantiatedException); @@ -1417,7 +1369,7 @@ Compiler.prototype.craise = function (s) { // TODO TODO TODO set cause appropriately // (and perhaps traceback for py2 if we care before it gets fully deprecated) - out("throw ",exc,";"); + out("throw ", exc, ";"); } else { // re-raise out("throw $err;"); @@ -1462,7 +1414,7 @@ Compiler.prototype.outputFinallyCascade = function (thisFinally) { "if ($postfinally.returning", (nextFinally.breakDepth == thisFinally.breakDepth) ? "|| $postfinally.isBreak" : "", ") {", - "$blk=",nextFinally.blk,";continue;", + "$blk=", nextFinally.blk, ";continue;", "} else {", "$blk=$postfinally.gotoBlock;$postfinally=undefined;continue;", "}", @@ -1518,7 +1470,7 @@ Compiler.prototype.ctry = function (s) { this.setBlock(handlers[i]); handler = s.handlers[i]; if (!handler.type && i < n - 1) { - throw new SyntaxError("default 'except:' must be last"); + throw new Sk.builtin.SyntaxError("default 'except:' must be last", this.filename, handler.lineno); } if (handler.type) { @@ -1560,7 +1512,7 @@ Compiler.prototype.ctry = function (s) { this.setBlock(finalExceptionHandler); // Exception handling also goes to the finally body, // stashing the original exception to re-raise - out(finalExceptionToReRaise,"=$err;"); + out(finalExceptionToReRaise, "=$err;"); this._jump(finalBody); this.setBlock(finalBody); @@ -1568,7 +1520,7 @@ Compiler.prototype.ctry = function (s) { this.vseqstmt(s.finalbody); // If finalbody executes normally, AND we have an exception // to re-raise, we raise it. - out("if(",finalExceptionToReRaise,"!==undefined) { throw ",finalExceptionToReRaise,";}"); + out("if(", finalExceptionToReRaise, "!==undefined) { throw ", finalExceptionToReRaise, ";}"); this.outputFinallyCascade(thisFinally); // Else, we continue from here. @@ -1588,13 +1540,13 @@ Compiler.prototype.cwith = function (s, itemIdx) { mgr = this._gr("mgr", this.vexpr(s.items[itemIdx].context_expr)); // exit = mgr.__exit__ - out("$ret = Sk.abstr.gattr(",mgr,",Sk.builtin.str.$exit, true);"); + out("$ret = Sk.abstr.gattr(", mgr, ",Sk.builtin.str.$exit, true);"); this._checkSuspension(s); exit = this._gr("exit", "$ret"); this.u.tempsToSave.push(exit); // value = mgr.__enter__() - out("$ret = Sk.abstr.gattr(",mgr,",Sk.builtin.str.$enter, true);"); + out("$ret = Sk.abstr.gattr(", mgr, ",Sk.builtin.str.$enter, true);"); this._checkSuspension(s); out("$ret = Sk.misceval.callsimOrSuspendArray($ret);"); this._checkSuspension(s); @@ -1602,7 +1554,7 @@ Compiler.prototype.cwith = function (s, itemIdx) { // try: this.pushFinallyBlock(tidyUp); - thisFinallyBlock = this.u.finallyBlocks[this.u.finallyBlocks.length-1]; + thisFinallyBlock = this.u.finallyBlocks[this.u.finallyBlocks.length - 1]; this.setupExcept(exceptionHandler); // VAR = value @@ -1612,7 +1564,7 @@ Compiler.prototype.cwith = function (s, itemIdx) { // (try body) - if (itemIdx +1 < s.items.length) { + if (itemIdx + 1 < s.items.length) { // "with" statements with multiple items (context managers) are // treated as nested "with" statements this.cwith(s, itemIdx + 1); @@ -1628,7 +1580,7 @@ Compiler.prototype.cwith = function (s, itemIdx) { // if not exit(*sys.exc_info()): // raise - out("$ret = Sk.misceval.applyOrSuspend(",exit,",undefined,Sk.builtin.getExcInfo($err),undefined,[]);"); + out("$ret = Sk.misceval.applyOrSuspend(", exit, ",undefined,Sk.builtin.getExcInfo($err),undefined,[]);"); this._checkSuspension(s); this._jumptrue("$ret", carryOn); out("throw $err;"); @@ -1639,7 +1591,7 @@ Compiler.prototype.cwith = function (s, itemIdx) { this.popFinallyBlock(); // exit(None, None, None) - out("$ret = Sk.misceval.callsimOrSuspendArray(",exit,",[Sk.builtin.none.none$,Sk.builtin.none.none$,Sk.builtin.none.none$]);"); + out("$ret = Sk.misceval.callsimOrSuspendArray(", exit, ",[Sk.builtin.none.none$,Sk.builtin.none.none$,Sk.builtin.none.none$]);"); this._checkSuspension(s); // Ignore $ret. @@ -1698,7 +1650,7 @@ Compiler.prototype.cimport = function (s) { var n = s.names.length; for (i = 0; i < n; ++i) { alias = s.names[i]; - out("$ret = Sk.builtin.__import__(", alias.name["$r"]().v, ",$gbl,$loc,[],",(Sk.__future__.absolute_import?0:-1),");"); + out("$ret = Sk.builtin.__import__(", alias.name["$r"]().v, ",$gbl,$loc,[],", (Sk.__future__.absolute_import ? 0 : -1), ");"); this._checkSuspension(s); @@ -1731,9 +1683,9 @@ Compiler.prototype.cfromimport = function (s) { level = -1; } for (i = 0; i < n; ++i) { - names[i] = "'" + fixReservedWords(s.names[i].name.v) + "'"; + names[i] = "'" + fixReserved(s.names[i].name.v) + "'"; } - out("$ret = Sk.builtin.__import__(", s.module["$r"]().v, ",$gbl,$loc,[", names, "],",level,");"); + out("$ret = Sk.builtin.__import__(", s.module["$r"]().v, ",$gbl,$loc,[", names, "],", level, ");"); this._checkSuspension(s); @@ -1742,7 +1694,7 @@ Compiler.prototype.cfromimport = function (s) { mod = this._gr("module", "$ret"); for (i = 0; i < n; ++i) { alias = s.names[i]; - aliasOut = "'" + fixReservedWords(alias.name.v) + "'"; + aliasOut = "'" + alias.name.v + "'"; if (i === 0 && alias.name.v === "*") { Sk.asserts.assert(n === 1); out("Sk.importStar(", mod, ",$loc, $gbl);"); @@ -1750,7 +1702,7 @@ Compiler.prototype.cfromimport = function (s) { } //out("print(\"getting Sk.abstr.gattr(", mod, ",", alias.name["$r"]().v, ")\");"); - got = this._gr("item", "Sk.abstr.gattr(", mod, ", new Sk.builtin.str(", aliasOut, "))"); + got = this._gr("item", "Sk.abstr.gattr(", mod, ", new Sk.builtin.str(", aliasOut, "), undefined)"); //out("print('got');"); storeName = alias.name; if (alias.asname) { @@ -1834,7 +1786,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal // // enter the new scope, and create the first block // - scopename = this.enterScope(coname, n, n.lineno, this.canSuspend); + scopename = this.enterScope(coname, n, n.lineno, this.canSuspend, coname.v); isGenerator = this.u.ste.generator; hasFree = this.u.ste.hasFree; @@ -1851,10 +1803,12 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal if (isGenerator) { // TODO make generators deal with arguments properly if (kwarg) { - throw new SyntaxError(coname.v + "(): keyword arguments in generators not supported"); + throw new Sk.builtin.SyntaxError(coname.v + "(): keyword arguments in generators not supported", + this.filename, n.lineno); } if (vararg) { - throw new SyntaxError(coname.v + "(): variable number of arguments in generators not supported"); + throw new Sk.builtin.SyntaxError(coname.v + "(): variable number of arguments in generators not supported", + this.filename, n.lineno); } funcArgs.push("$gen"); } else { @@ -1872,12 +1826,24 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal funcArgs.push(this.nameop(args.vararg.arg, Sk.astnodes.Param)); } } + // Are we using the new fast-call mechanism, where the + // function we define implements the tp$call interface? + // (Right now we haven't migrated generators because they're + // a mess, but if this works we can move everything over) + let fastCall = !isGenerator; + if (hasFree) { - funcArgs.push("$free"); + if (!fastCall) { + funcArgs.push("$free"); + } this.u.tempsToSave.push("$free"); } - this.u.prefixCode += funcArgs.join(","); + if (fastCall) { + this.u.prefixCode += "$posargs,$kwargs"; + } else { + this.u.prefixCode += funcArgs.join(","); + } this.u.prefixCode += "){"; @@ -1891,6 +1857,10 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal this.u.prefixCode += "\n// has cell\n"; } + if (fastCall) { + this.u.prefixCode += "\n// fast call\n"; + } + // // set up standard dicts/variables // @@ -1908,7 +1878,8 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal // note special usage of 'this' to avoid having to slice globals into // all function invocations in call - this.u.varDeclsCode += "var $blk=" + entryBlock + ",$exc=[],$loc=" + locals + cells + ",$gbl=this,$err=undefined,$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;$currSource=undefined;"; + // (fastcall doesn't need to do this, as 'this' is the func object) + this.u.varDeclsCode += "var $blk=" + entryBlock + ",$exc=[],$loc=" + locals + cells + ",$gbl=" +(fastCall?"this.func_globals":"this") + ((fastCall&&hasFree)?",$free=this.func_closure":"") + ",$err=undefined,$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined,$currSource=undefined;"; if (Sk.execLimit !== null) { this.u.varDeclsCode += "Sk.misceval.startTimer();"; } @@ -1920,7 +1891,23 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal // If there is a suspension, resume from it. Otherwise, initialise // parameters appropriately. // - this.u.varDeclsCode += "if ("+scopename+".$wakingSuspension!==undefined) { $wakeFromSuspension(); } else {"; + this.u.varDeclsCode += "if (" + scopename + ".$wakingSuspension!==undefined) { $wakeFromSuspension(); } else {"; + + if (fastCall) { + // Resolve our arguments from $posargs+$kwargs. + // If we're posargs-only, we can handle the fast path + // without even calling out + if (!kwarg && !vararg && (!args || !args.kwonlyargs || args.kwonlyargs.length === 0)) { + this.u.varDeclsCode += "var $args = ((!$kwargs || $kwargs.length===0) && $posargs.length===" + funcArgs.length + ") ? $posargs : this.$resolveArgs($posargs,$kwargs)"; + } else { + this.u.varDeclsCode += "\nvar $args = this.$resolveArgs($posargs,$kwargs)\n"; + } + for (let i = 0; i < funcArgs.length; i++) { + this.u.varDeclsCode += "," + funcArgs[i] + "=$args[" + i + "]"; + } + this.u.varDeclsCode += ";\n"; + } + // TODO update generators to do their arg checks in outside generated code, // like functions do @@ -1947,19 +1934,19 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal for (i = 0; args && i < args.args.length; ++i) { id = args.args[i].arg; if (this.isCell(id)) { - let mangled = fixReservedNames(mangleName(this.u.private_, id).v); + let mangled = fixReserved(mangleName(this.u.private_, id).v); this.u.varDeclsCode += "$cell." + mangled + "=" + mangled + ";"; } } for (i = 0; args && args.kwonlyargs && i < args.kwonlyargs.length; ++i) { id = args.kwonlyargs[i].arg; if (this.isCell(id)) { - let mangled = fixReservedNames(mangleName(this.u.private_, id).v); + let mangled = fixReserved(mangleName(this.u.private_, id).v); this.u.varDeclsCode += "$cell." + mangled + "=" + mangled + ";"; } } if (vararg && this.isCell(vararg.arg)) { - let mangled = fixReservedNames(mangleName(this.u.private_, vararg.arg).v); + let mangled = fixReserved(mangleName(this.u.private_, vararg.arg).v); this.u.varDeclsCode += "$cell." + mangled + "=" + mangled + ";"; } @@ -1970,7 +1957,8 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal this.u.localnames.push(kwarg.arg.v); this.u.varDeclsCode += kwarg.arg.v + "=new Sk.builtins['dict']($kwa);"; if (this.isCell(kwarg.arg)) { - this.u.varDeclsCode += "$cell." + kwarg.arg.v + "=" + kwarg.arg.v + ";"; + let mangled = fixReserved(mangleName(this.u.private_, kwarg.arg).v); + this.u.varDeclsCode += "$cell." + mangled + "=" + mangled + ";"; } } @@ -1981,7 +1969,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal // inject __class__ cell when running python3 if (Sk.python3 && class_for_super) { - this.u.varDeclsCode += "$gbl.__class__=this." + class_for_super.v + ";"; + this.u.varDeclsCode += "$gbl.__class__=$gbl." + class_for_super.v + ";"; } // finally, set up the block switch that the jump code expects @@ -1992,12 +1980,12 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal // New switch code to catch exceptions this.u.switchCode = "while(true){try{"; - this.u.switchCode += this.outputInterruptTest(); - this.u.switchCode += "switch($blk){"; + this.u.switchCode += this.outputInterruptTest(); + this.u.switchCode += "switch($blk){"; this.u.suffixCode = "}" + this.handleTraceback(true, coname.v); this.u.suffixCode += "});"; /*this.u.suffixCode = ("} }catch(err){ "+ - "if (err instanceof Sk.builtin.TimeLimitError) {"+ + "if (err instanceof Sk.builtin.TimeoutError) {"+ "Sk.execStart = Date.now();Sk.execPaused=0"+ "} if (!(err instanceof Sk.builtin.BaseException)) {"+ "err = new Sk.builtin.ExternalError(err);" + @@ -2069,6 +2057,9 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal if (vararg) { out(scopename, ".co_varargs=1;"); } + if (!isGenerator) { + out(scopename, ".co_fastcall=1;"); + } // // build either a 'function' or 'generator'. the function is just a simple @@ -2095,8 +2086,8 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal } } if (isGenerator) { - // Keyword and variable arguments are not currently supported in generators. - // The call to pyCheckArgs assumes they can't be true. + // Keyword and variable arguments are not currently supported in generators. + // The call to pyCheckArgs assumes they can't be true. if (args && args.args.length > 0) { return this._gr("gener", "new Sk.builtins['function']((function(){var $origargs=Array.prototype.slice.call(arguments);Sk.builtin.pyCheckArgsLen(\"", coname.v, "\",arguments.length,", args.args.length - defaults.length, ",", args.args.length, @@ -2108,8 +2099,11 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal } else { var res; if (decos.length > 0) { - out("$ret = Sk.misceval.callsimOrSuspendArray(", scopename, ".$decorators[0], [new Sk.builtins['function'](", scopename, ",$gbl", frees, ")]);"); - this._checkSuspension(); + out("$ret = new Sk.builtins['function'](", scopename, ",$gbl", frees, ");"); + for (let decorator of decos) { + out("$ret = Sk.misceval.callsimOrSuspendArray(", decorator, ",[$ret]);"); + this._checkSuspension(); + } return this._gr("funcobj", "$ret"); } @@ -2185,10 +2179,10 @@ Compiler.prototype.cgenexpgen = function (generators, genIndex, elt) { this._jump(start); this.setBlock(start); - this.annotateSource(elt); + this.annotateSource(elt, true); // load targets - out ("$ret = Sk.abstr.iternext(", iter,(this.u.canSuspend?", true":", false"),");"); + out("$ret = Sk.abstr.iternext(", iter, (this.u.canSuspend ? ", true" : ", false"), ");"); this._checkSuspension(elt); @@ -2198,7 +2192,7 @@ Compiler.prototype.cgenexpgen = function (generators, genIndex, elt) { n = ge.ifs ? ge.ifs.length : 0; for (i = 0; i < n; ++i) { - this.annotateSource(ge.ifs[i]); + this.annotateSource(ge.ifs[i], true); ifres = this.vexpr(ge.ifs[i]); this._jumpfalse(ifres, start); @@ -2209,7 +2203,7 @@ Compiler.prototype.cgenexpgen = function (generators, genIndex, elt) { } if (genIndex >= generators.length) { - this.annotateSource(elt); + this.annotateSource(elt, true); velt = this.vexpr(elt); out("return [", skip, "/*resume*/,", velt, "/*ret*/];"); @@ -2249,14 +2243,12 @@ Compiler.prototype.cclass = function (s) { var bases; var decos; Sk.asserts.assert(s instanceof Sk.astnodes.ClassDef); - decos = s.decorator_list; - // decorators and bases need to be eval'd out here - //this.vseqexpr(decos); + decos = this.vseqexpr(s.decorator_list); bases = this.vseqexpr(s.bases); - scopename = this.enterScope(s.name, s, s.lineno); + scopename = this.enterScope(s.name, s, s.lineno, s.name.v); entryBlock = this.newBlock("class entry"); this.u.prefixCode = "var " + scopename + "=(function $" + s.name.v + "$class_outer($globals,$locals,$cell){var $gbl=$globals,$loc=$locals;$free=$globals;"; @@ -2274,7 +2266,7 @@ Compiler.prototype.cclass = function (s) { this.u.switchCode += this.outputInterruptTest(); this.u.switchCode += "switch($blk){"; this.u.suffixCode = "}" + this.handleTraceback(true, s.name.v); - /*this.u.suffixCode = ("}}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) {"+ + /*this.u.suffixCode = ("}}catch(err){ if (err instanceof Sk.builtin.TimeoutError) {"+ "Sk.execStart = Date.now();Sk.execPaused=0"+ "} if (!(err instanceof Sk.builtin.BaseException)) {"+ " err = new Sk.builtin.ExternalError(err);"+ @@ -2294,22 +2286,29 @@ Compiler.prototype.cclass = function (s) { this.exitScope(); // todo; metaclass - wrapped = this._gr("built", "Sk.misceval.buildClass($gbl,", scopename, ",", s.name["$r"]().v, ",[", bases, "], $cell)"); + out("$ret = Sk.misceval.buildClass($gbl,", scopename, ",", s.name["$r"]().v, ",[", bases, "], $cell);"); + + // apply decorators + + for (let decorator of decos) { + out("$ret = Sk.misceval.callsimOrSuspendArray(", decorator, ", [$ret]);"); + this._checkSuspension(); + } // store our new class under the right name - this.nameop(s.name, Sk.astnodes.Store, wrapped); + this.nameop(s.name, Sk.astnodes.Store, "$ret"); }; Compiler.prototype.ccontinue = function (s) { var nextFinally = this.peekFinallyBlock(), gotoBlock; if (this.u.continueBlocks.length == 0) { - throw new SyntaxError("'continue' outside loop"); + throw new Sk.builtin.SyntaxError("'continue' outside loop", this.filename, s.lineno); } // todo; continue out of exception blocks gotoBlock = this.u.continueBlocks[this.u.continueBlocks.length - 1]; Sk.asserts.assert(this.u.breakBlocks.length === this.u.continueBlocks.length); if (nextFinally && nextFinally.breakDepth == this.u.continueBlocks.length) { - out("$postfinally={isBreak:true,gotoBlock:",gotoBlock,"};"); + out("$postfinally={isBreak:true,gotoBlock:", gotoBlock, "};"); } else { this._jump(gotoBlock); } @@ -2319,11 +2318,11 @@ Compiler.prototype.cbreak = function (s) { var nextFinally = this.peekFinallyBlock(), gotoBlock; if (this.u.breakBlocks.length === 0) { - throw new SyntaxError("'break' outside loop"); + throw new Sk.builtin.SyntaxError("'break' outside loop", this.filename, s.lineno); } gotoBlock = this.u.breakBlocks[this.u.breakBlocks.length - 1]; if (nextFinally && nextFinally.breakDepth == this.u.breakBlocks.length) { - out("$postfinally={isBreak:true,gotoBlock:",gotoBlock,"};"); + out("$postfinally={isBreak:true,gotoBlock:", gotoBlock, "};"); } else { this._jump(gotoBlock); } @@ -2344,9 +2343,9 @@ Compiler.prototype.vstmt = function (s, class_for_super) { this.u.localtemps = []; if (Sk.debugging && this.u.canSuspend) { - debugBlock = this.newBlock("debug breakpoint for line "+s.lineno); - out("if (Sk.breakpoints('"+this.filename+"',"+s.lineno+","+s.col_offset+")) {", - "var $susp = $saveSuspension({data: {type: 'Sk.debug'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+","+s.source+");", + debugBlock = this.newBlock("debug breakpoint for line " + s.lineno); + out("if (Sk.breakpoints('" + this.filename + "'," + s.lineno + "," + s.col_offset + ")) {", + "var $susp = $saveSuspension({data: {type: 'Sk.debug'}, resume: function() {}}, '" + this.filename + "'," + s.lineno + "," + s.col_offset + "," + s.source + ");", "$susp.$blk = " + debugBlock + ";", "$susp.optional = true;", "return $susp;", @@ -2367,13 +2366,13 @@ Compiler.prototype.vstmt = function (s, class_for_super) { break; case Sk.astnodes.Return: if (this.u.ste.blockType !== Sk.SYMTAB_CONSTS.FunctionBlock) { - throw new SyntaxError("'return' outside function"); + throw new Sk.builtin.SyntaxError("'return' outside function", this.filename, s.lineno); } val = s.value ? this.vexpr(s.value) : "Sk.builtin.none.none$"; if (this.u.finallyBlocks.length == 0) { out("return ", val, ";"); } else { - out("$postfinally={returning:",val,"};"); + out("$postfinally={returning:", val, "};"); this._jump(this.peekFinallyBlock().blk); } break; @@ -2420,7 +2419,10 @@ Compiler.prototype.vstmt = function (s, class_for_super) { case Sk.astnodes.Global: break; case Sk.astnodes.Expr: - this.vexpr(s.value); + // TODO: Check if str, then decide if we retain string literal comments + if (this.retainComments || s.value.constructor !== Sk.astnodes.Str) { + this.vexpr(s.value); + } break; case Sk.astnodes.Pass: break; @@ -2454,8 +2456,7 @@ var D_FREEVARS = 1; var D_CELLVARS = 2; Compiler.prototype.isCell = function (name) { - var mangled = mangleName(this.u.private_, name).v; - mangled = fixReservedNames(mangled); + var mangled = fixReserved(mangleName(this.u.private_, name).v); var scope = this.u.ste.getScope(mangled); var dict = null; return scope === Sk.SYMTAB_CONSTS.CELL; @@ -2476,7 +2477,7 @@ Compiler.prototype.nameop = function (name, ctx, dataToStore) { var op; var mangled; if ((ctx === Sk.astnodes.Store || ctx === Sk.astnodes.AugStore || ctx === Sk.astnodes.Del) && name.v === "__debug__") { - throw new Sk.builtin.SyntaxError("can not assign to __debug__"); + throw new Sk.builtin.SyntaxError("can not assign to __debug__", this.filename, this.u.lineno); } Sk.asserts.assert(name.v !== "None"); @@ -2486,7 +2487,7 @@ Compiler.prototype.nameop = function (name, ctx, dataToStore) { mangled = mangleName(this.u.private_, name).v; // Have to do this before looking it up in the scope - mangled = fixReservedNames(mangled); + mangled = fixReserved(mangled); op = 0; optype = OP_NAME; scope = this.u.ste.getScope(mangled); @@ -2517,10 +2518,8 @@ Compiler.prototype.nameop = function (name, ctx, dataToStore) { break; } - // have to do this after looking it up in the scope - mangled = fixReservedWords(mangled); - //console.log("mangled", mangled); + //print("mangled", mangled); // TODO TODO TODO todo; import * at global scope failing here Sk.asserts.assert(scope || name.v.charAt(1) === "_"); @@ -2539,8 +2538,7 @@ Compiler.prototype.nameop = function (name, ctx, dataToStore) { case Sk.astnodes.Load: case Sk.astnodes.Param: // Need to check that it is bound! - //out("if (", mangled, " === undefined) { throw new Sk.builtin.UnboundLocalError('local variable \\\'", mangled, "\\\' referenced before assignment'); }\n"); - out("if (", mangled, " === undefined) { throw Sk.misceval.errorUL('",mangled,"'); }\n"); + out("if (", mangled, " === undefined) { throw new Sk.builtin.UnboundLocalError('local variable \\\'", mangled, "\\\' referenced before assignment'); }\n"); return mangled; case Sk.astnodes.Store: out(mangled, "=", dataToStore, ";"); @@ -2606,8 +2604,9 @@ Compiler.prototype.nameop = function (name, ctx, dataToStore) { * @param {Object} key * @param {number} lineno * @param {boolean=} canSuspend + * @param {String=} hint */ -Compiler.prototype.enterScope = function (name, key, lineno, canSuspend) { +Compiler.prototype.enterScope = function (name, key, lineno, canSuspend, hint) { var scopeName; var u = new CompilerUnit(); u.ste = this.st.getStsForAst(key); @@ -2621,7 +2620,9 @@ Compiler.prototype.enterScope = function (name, key, lineno, canSuspend) { this.stack.push(this.u); this.allUnits.push(u); - scopeName = this.gensym("scope"); + hint = hint || ""; + hint = hint.replace(/[\<\>\/\.]/g, "_"); + scopeName = this.gensym("scope"+hint); u.scopename = scopeName; this.u = u; @@ -2648,9 +2649,17 @@ Compiler.prototype.exitScope = function () { if (prev.name.v !== "") {// todo; hacky mangled = prev.name["$r"]().v; mangled = mangled.substring(1, mangled.length - 1); - mangled = fixReservedWords(mangled); - mangled = fixReservedNames(mangled); + // mangled = fixReserved(mangled); out(prev.scopename, ".co_name=new Sk.builtins['str']('", mangled, "');"); + if (this.stack.length && this.u.ste.blockType == "class") { + const classname = this.u.name.v; + out(prev.scopename, ".co_qualname=new Sk.builtins['str']('" + classname + "." + mangled + "');"); + } + } + for (var constant in prev.consts) { + if (prev.consts.hasOwnProperty(constant)) { + prev.suffixCode += constant + " = " + prev.consts[constant] + ";"; + } } }; @@ -2685,12 +2694,13 @@ Compiler.prototype.cprint = function (s) { out("$ret = Sk.misceval.print_(", /*dest, ',*/ "\"\\n\");"); this._checkSuspension(s); } + }; Compiler.prototype.cmod = function (mod) { //print("-----"); //print(Sk.astDump(mod)); - var modf = this.enterScope(new Sk.builtin.str(""), mod, 0, this.canSuspend); + var modf = this.enterScope(new Sk.builtin.str(""), mod, 0, this.canSuspend, this.filename); var entryBlock = this.newBlock("module entry"); this.u.prefixCode = "var " + modf + "=(function($forcegbl){"; @@ -2708,7 +2718,7 @@ Compiler.prototype.cmod = function (mod) { this.u.varDeclsCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}"; } - this.u.varDeclsCode += "if ("+modf+".$wakingSuspension!==undefined) { $wakeFromSuspension(); }" + + this.u.varDeclsCode += "if (" + modf + ".$wakingSuspension!==undefined) { $wakeFromSuspension(); }" + "if (Sk.retainGlobals) {" + //" if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; }" + " if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; $loc.__file__=new Sk.builtins.str('" + this.filename + "');}" + @@ -2729,7 +2739,7 @@ Compiler.prototype.cmod = function (mod) { this.u.switchCode += "switch($blk){"; this.u.suffixCode = "}" + this.handleTraceback(true, ""); this.u.suffixCode += "});"; - //this.u.suffixCode += "}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now();Sk.execPaused=0} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} } });"; + //this.u.suffixCode += "}catch(err){ if (err instanceof Sk.builtin.TimeoutError) { Sk.execStart = Date.now();Sk.execPaused=0} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} } });"; // Note - this change may need to be adjusted for all the other instances of // switchCode and suffixCode in this file. Not knowing how to test those @@ -2758,11 +2768,11 @@ Compiler.prototype.cmod = function (mod) { return modf; }; -Compiler.prototype.handleTraceback = function(doContinue, scopeName) { +Compiler.prototype.handleTraceback = function (doContinue, scopeName) { doContinue = doContinue ? "continue" : ""; - return "}catch(err){"+ - "err=Sk.misceval.handleTraceback(err,$currLineNo,$currColNo,$currSource,$fname,'" + scopeName + "');"+ - "if($exc.length>0){$err=err;$blk=$exc.pop();"+doContinue+"}else{throw err;}}}"; + return "}catch(err){" + + "err=Sk.misceval.handleTraceback(err,$currLineNo,$currColNo,$currSource,$fname,'" + scopeName + "');" + + "if($exc.length>0){$err=err;$blk=$exc.pop();" + doContinue + "}else{throw err;}}}"; }; /** @@ -2796,7 +2806,7 @@ Sk.compile = function (source, filename, mode, canSuspend, annotate) { // Restore the global __future__ flags Sk.__future__ = savedFlags; - var shortCutConstants = "const $fname='"+filename+"',$moduleConstants={};"; + var shortCutConstants = "const $fname='" + filename + "',$moduleConstants={};"; var constantDefinitions = []; for (var constant in c.consts) { if (c.consts.hasOwnProperty(constant)) { @@ -2811,7 +2821,7 @@ Sk.compile = function (source, filename, mode, canSuspend, annotate) { "\nreturn " + funcname + ";}();"); return { funcname: "$compiledmod", - code : ret + code: ret }; }; @@ -2823,14 +2833,14 @@ Sk.resetCompiler = function () { Sk.exportSymbol("Sk.resetCompiler", Sk.resetCompiler); -Sk.fixReservedWords = fixReservedWords; -Sk.exportSymbol("Sk.fixReservedWords", Sk.fixReservedWords); - -Sk.fixReservedNames = fixReservedNames; -Sk.exportSymbol("Sk.fixReservedNames", Sk.fixReservedNames); +Sk.fixReserved = fixReserved; +Sk.exportSymbol("Sk.fixReserved", Sk.fixReserved); Sk.unfixReserved = unfixReserved; Sk.exportSymbol("Sk.unfixReserved", Sk.unfixReserved); Sk.mangleName = mangleName; Sk.exportSymbol("Sk.mangleName", Sk.mangleName); + +Sk.reservedWords_ = reservedWords_; +Sk.exportSymbol("Sk.reservedWords_", Sk.reservedWords_); diff --git a/src/complex.js b/src/complex.js index c5ad2ee65d..69863d3b1a 100644 --- a/src/complex.js +++ b/src/complex.js @@ -1,103 +1,328 @@ +const JSBI = require("jsbi"); + /** - * hypot is a ESCMA6 function and maybe not available across all browsers + * @description + * see [Cpython complex_new](https://hg.python.org/cpython/file/f0e2caad4200/Objects/complexobject.c#l911) + * @constructor + * @param {number} real part of the complex number + * @param {number} imag part of the complex number + * + * Prefering here == instead of ===, otherwise also undefined has to be matched explicitly + * @extends {Sk.builtin.object} + * */ -Math.hypot = Math.hypot || function() { - var y = 0; - var length = arguments.length; +Sk.builtin.complex = Sk.abstr.buildNativeClass("complex", { + constructor: function complex(real, imag) { + Sk.asserts.assert(this instanceof Sk.builtin.complex, "bad call to complex constructor, use 'new'"); + this.real = real; + this.imag = imag; + }, + slots: /**@lends {Sk.builtin.complex.prototype}*/{ + tp$as_number: true, + tp$doc: + "Create a complex number from a real part and an optional imaginary part.\n\nThis is equivalent to (real + imag*1j) where imag defaults to 0.", + tp$hash: function () { + // _PyHASH_IMAG refers to _PyHASH_MULTIPLIER which refers to 1000003 + const v = this.imag * 1000003 + this.real; + if (Sk.builtin.int_.withinThreshold(v)) { + return new Sk.builtin.int_(parseInt(v, 10)); + } + return new Sk.builtin.int_(JSBI.BigInt(v)); + }, + tp$getattr: Sk.generic.getAttr, + tp$new: function (args, kwargs) { + args = Sk.abstr.copyKeywordsToNamedArgs("complex", ["real", "imag"], args, kwargs, [null, null]); + return complex_from_py.call(this, args[0], args[1]); + }, + tp$richcompare: function (w, op) { + if (op !== "Eq" && op !== "NotEq") { + if (Sk.builtin.checkNumber(w) || _complex_check(w)) { + throw new Sk.builtin.TypeError("no ordering relation is defined for complex numbers"); + } + return Sk.builtin.NotImplemented.NotImplemented$; + } + return complexNumberSlot(function (a_real, a_imag, b_real, b_imag) { + const equal = a_real == b_real && a_imag == b_imag; + return op === "Eq" ? equal : !equal; + }, true).call(this, w); + }, + $r: function () { + return complex_format(this, null, "g"); + }, + + // number slots + nb$int_: function () { + throw new Sk.builtin.TypeError("can't convert complex to int"); + }, + nb$lng: function () { + throw new Sk.builtin.TypeError("can't convert complex to long"); + }, + nb$float_: function () { + throw new Sk.builtin.TypeError("can't convert complex to float"); + }, + nb$positive: function () { + return new Sk.builtin.complex(this.real, this.imag); + }, + nb$negative: function () { + return new Sk.builtin.complex(-this.real, -this.imag); + }, + nb$bool: function () { + return this.real || this.imag; + }, + nb$add: complexNumberSlot((a_real, a_imag, b_real, b_imag) => { + return new Sk.builtin.complex(a_real + b_real, a_imag + b_imag); + }), + nb$subtract: complexNumberSlot((a_real, a_imag, b_real, b_imag) => { + return new Sk.builtin.complex(a_real - b_real, a_imag - b_imag); + }), + nb$reflected_subtract: complexNumberSlot((a_real, a_imag, b_real, b_imag) => { + return new Sk.builtin.complex(b_real - a_real, b_imag - a_imag); + }), + nb$multiply: complexNumberSlot((a_real, a_imag, b_real, b_imag) => { + return new Sk.builtin.complex(b_real * a_real - b_imag * a_imag, a_real * b_imag + a_imag * b_real); + }), + nb$divide: complexNumberSlot(divide), + nb$reflected_divide: complexNumberSlot((a_real, a_imag, b_real, b_imag) => { + return divide(b_real, b_imag, a_real, a_imag); + }), + nb$floor_divide: function (other) { + throw new Sk.builtin.TypeError("can't take floor of complex number."); + }, + nb$reflected_floor_divide: function (other) { + throw new Sk.builtin.TypeError("can't take floor of complex number."); + }, + nb$remainder: function (other) { + throw new Sk.builtin.TypeError("can't mod complex numbers."); + }, + nb$reflected_remainder: function (other) { + throw new Sk.builtin.TypeError("can't mod complex numbers."); + }, + nb$divmod: function (other) { + throw new Sk.builtin.TypeError("can't take floor or mod of complex number."); + }, + nb$power: function (other, z) { + if (z != null && !Sk.builtin.checkNone(z)) { + throw new Sk.builtin.ValueError("complex modulo"); + } + return power.call(this, other); + }, + + nb$abs: function () { + const _real = this.real; + const _imag = this.imag; + if (!_is_finite(_real) || !_is_finite(_imag)) { + /* C99 rules: if either the real or the imaginary part is an + infinity, return infinity, even if the other part is a + NaN. + */ + if (_is_infinity(_real)) { + return new Sk.builtin.float_(Math.abs(_real)); + } else if (_is_infinity(_imag)) { + return new Sk.builtin.float_(Math.abs(_imag)); + } + /* either the real or imaginary part is a NaN, + and neither is infinite. Result should be NaN. */ + return new Sk.builtin.float_(NaN); + } + const result = Math.hypot(_real, _imag); + if (!_is_finite(result)) { + throw new Sk.builtin.OverflowError("absolute value too large"); + } + return new Sk.builtin.float_(result); + }, + }, + getsets: { + real: { + $get: function () { + return new Sk.builtin.float_(this.real); + }, + }, + imag: { + $get: function () { + return new Sk.builtin.float_(this.imag); + }, + }, + }, + methods: /**@lends {Sk.builtin.complex.prototype}*/{ + conjugate: { + $meth: function () { + return new Sk.builtin.complex(this.real, -this.imag); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "complex.conjugate() -> complex\n\nReturn the complex conjugate of its argument. (3-4j).conjugate() == 3+4j.", + }, + __getnewargs__: { + $meth: function () { + return new Sk.builtin.tuple([new Sk.builtin.float_(this.real), new Sk.builtin.float_(this.imag)]); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: Sk.builtin.none.none$, + }, + __format__: { + $meth: function (format_spec) { + if (Sk.builtin.checkString(format_spec)) { + // currently just returns not implemented. + return _PyComplex_FormatAdvanced(this, format_spec); + } + throw new Sk.builtin.TypeError("__format__ requires str"); + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "complex.__format__() -> str\n\nConvert to a string according to format_spec.", + }, + }, +}); + +Sk.exportSymbol("Sk.builtin.complex", Sk.builtin.complex); - for (var i = 0; i < length; i++) { - if (arguments[i] === Infinity || arguments[i] === -Infinity) { - return Infinity; +/** + * hypot is a ESCMA6 function and maybe not available across all browsers + * @ignore + */ +Math.hypot = + Math.hypot || + function () { + var y = 0; + var length = arguments.length; + + for (var i = 0; i < length; i++) { + if (arguments[i] === Infinity || arguments[i] === -Infinity) { + return Infinity; + } + y += arguments[i] * arguments[i]; } - y += arguments[i] * arguments[i]; + return Math.sqrt(y); + }; + +/** + * @function + * + * @description + * returns a Number if the object passed as a __float__ method + * Otherwise throws an error + * + * @param {Sk.builtin.object} op + * + * @ignore + */ +function PyFloat_AsDouble(op) { + let v = op.v; + if (typeof v === "number") { + return v; + } else if (op.nb$float_) { + v = op.nb$float_(); } - return Math.sqrt(y); -}; + if (v === undefined) { + throw new Sk.builtin.TypeError("a float is required"); + } + return v.v; +} /** - * complex_new see https://hg.python.org/cpython/file/f0e2caad4200/Objects/complexobject.c#l911 - * @constructor - * @param {Object} real part of the complex number - * @param {?Object=} imag part of the complex number - * @this {Sk.builtin.object} + * @function + * + * @description + * checks and tries the __complex__ method + * throws an error if this returns a non complex object + * returns null if that function does not exist * - * Prefering here == instead of ===, otherwise also undefined has to be matched explicitly * - * FIXME: it seems that we somehow need to call __float__/__int__ if arguments provide those methods + * @param {Sk.builtin.object} op * + * @ignore */ -Sk.builtin.complex = function (real, imag) { - Sk.builtin.pyCheckArgsLen("complex", arguments.length, 0, 2); - - var r, i, tmp; // PyObject - var nbr, nbi; // real, imag as numbers - var own_r = false; - var cr = {}; // PyComplexObject - var ci = {}; // PyComplexObject - var cr_is_complex = false; - var ci_is_complex = false; - - // not sure why this is required - if (!(this instanceof Sk.builtin.complex)) { - return new Sk.builtin.complex(real, imag); +function try_complex_special_method(op) { + // the lookup special method does already all the magic + if (op == null) { + return null; + } + const f = Sk.abstr.lookupSpecial(op, Sk.builtin.str.$complex); + if (f !== undefined) { + // method on builtin, provide this arg + return Sk.misceval.callsimArray(f, [op]); } + return null; +} + +/** + * @function + * + * @description + * copied here for easy access + * checks whether the argument is an instance of Sk.builtin.complex + * + * @return {boolean} + * + * @param {Sk.builtin.object} op + * @ignore + */ +const _complex_check = Sk.builtin.checkComplex; +/** + * @function + * + * @description + * this is the logic for tp$new + * + * @param {Sk.builtin.object} real + * @param {Sk.builtin.object} imag + * + * @ignore + */ +function complex_from_py(real, imag) { + let tmp; // pyObject + // var nbr, nbi; // real, imag as numbers + const cr = {}; // PyComplexObject + const ci = {}; // PyComplexObject + let cr_is_complex = false; + let ci_is_complex = false; - // check if kwargs - // ToDo: this is only a temporary replacement - r = real == null ? Sk.builtin.bool.false$ : real; // r = Py_False; - i = imag; + let r = real; + let i = imag; // handle case if passed in arguments are of type complex - if (r instanceof Sk.builtin.complex && i == null) { - return real; + if (r != null && r.constructor === Sk.builtin.complex && i == null) { + // subtypes are handled later; + return r; } - if (r != null && Sk.builtin.checkString(r)) { - if(i != null) { + if (Sk.builtin.checkString(r)) { + if (i != null) { throw new Sk.builtin.TypeError("complex() can't take second arg if first is a string"); } - - return Sk.builtin.complex.complex_subtype_from_string(r); + return Sk.builtin.complex.complex_subtype_from_string(r, this); } if (i != null && Sk.builtin.checkString(i)) { throw new Sk.builtin.TypeError("complex() second arg can't be a string"); } - // try_complex_special_method - tmp = Sk.builtin.complex.try_complex_special_method(r); + tmp = try_complex_special_method(r); if (tmp != null && tmp !== Sk.builtin.NotImplemented.NotImplemented$) { - if (!Sk.builtin.checkComplex(tmp)) { + if (!_complex_check(tmp)) { throw new Sk.builtin.TypeError("__complex__ should return a complex object"); } - r = tmp; } - // this check either returns a javascript number or the passed object - // but it actually, should check for r->ob_type->tp_as_number - // this check is useless - nbr = Sk.builtin.asnum$(r); - if (i != null) { - nbi = Sk.builtin.asnum$(i); + // just a temporary function to match cpython + function check_number(nb) { + return nb.nb$float_ !== undefined; } - // this function mimics the tp_as_number->nb_float check in cpython - var nb_float = function(op) { - if(Sk.builtin.checkNumber(op)) { - return true; + if (r != null) { + if (!check_number(r)) { + throw new Sk.builtin.TypeError("complex() first argument must be a string or a number, not '" + Sk.abstr.typeName(r) + "'"); } + } - if(Sk.builtin.type.typeLookup(op.ob$type, Sk.builtin.str.$float_) !== undefined) { - return true; + if (i != null) { + if (!check_number(i)) { + throw new Sk.builtin.TypeError("complex() second argument must be a number, not '" + Sk.abstr.typeName(r) + "'"); } - }; - - // check for valid arguments - if (nbr == null || (!nb_float(r) && !Sk.builtin.checkComplex(r)) || ((i != null) && (nbi == null || (!nb_float(i) && !Sk.builtin.checkComplex(i))))) { - throw new Sk.builtin.TypeError("complex() argument must be a string or number"); } /* If we get this far, then the "real" and "imag" parts should @@ -107,45 +332,36 @@ Sk.builtin.complex = function (real, imag) { Note that we do NOT assume the input to already be in canonical form; the "real" and "imag" parts might themselves be complex numbers, which slightly complicates the code below. */ - - if (Sk.builtin.complex._complex_check(r)) { + if (r == null) { + cr.real = 0.0; + cr.imag = 0.0; + } else if (_complex_check(r)) { /* Note that if r is of a complex subtype, we're only retaining its real & imag parts here, and the return value is (properly) of the builtin complex type. */ - cr.real = r.real.v; - cr.imag = r.imag.v; + cr.real = r.real; + cr.imag = r.imag; cr_is_complex = true; } else { /* The "real" part really is entirely real, and contributes nothing in the imaginary direction. Just treat it as a double. */ - tmp = Sk.builtin.float_.PyFloat_AsDouble(r); // tmp = PyNumber_Float(r); - - if (tmp == null) { - return null; - } - - cr.real = tmp; + cr.real = PyFloat_AsDouble(r); cr.imag = 0.0; } if (i == null) { ci.real = 0.0; - } else if (Sk.builtin.complex._complex_check(i)) { - ci.real = i.real.v; - ci.imag = i.imag.v; + ci.imag = 0.0; + } else if (_complex_check(i)) { + ci.real = i.real; + ci.imag = i.imag; ci_is_complex = true; } else { /* The "imag" part really is entirely imaginary, and contributes nothing in the real direction. Just treat it as a double. */ - tmp = Sk.builtin.float_.PyFloat_AsDouble(i); - - if (tmp == null) { - return null; - } - - ci.real = tmp; + ci.real = PyFloat_AsDouble(i); ci.imag = 0.0; } @@ -160,97 +376,45 @@ Sk.builtin.complex = function (real, imag) { if (cr_is_complex === true) { ci.real += cr.imag; } - - // adjust for negated imaginary literal - if (cr.real === 0 && (ci.real < 0 || Sk.builtin.complex._isNegativeZero(ci.real))) { - cr.real = -0; - } - - // save them as properties - this.real = new Sk.builtin.float_(cr.real); - this.imag = new Sk.builtin.float_(ci.real); - - return this; -}; - -Sk.abstr.setUpInheritance("complex", Sk.builtin.complex, Sk.builtin.numtype); -//Sk.builtin.complex.co_kwargs = true; - -Sk.builtin.complex.prototype.__class__ = Sk.builtin.complex; - -Sk.builtin.complex.prototype.nb$int_ = function () { - throw new Sk.builtin.TypeError("can't convert complex to int"); -}; - -Sk.builtin.complex.prototype.nb$float_ = function() { - throw new Sk.builtin.TypeError("can't convert complex to float"); -}; - -Sk.builtin.complex.prototype.nb$lng = function () { - throw new Sk.builtin.TypeError("can't convert complex to long"); -}; - -Sk.builtin.complex.prototype.__doc__ = new Sk.builtin.str("complex(real[, imag]) -> complex number\n\nCreate a complex number from a real part and an optional imaginary part.\nThis is equivalent to (real + imag*1j) where imag defaults to 0."); - -Sk.builtin.complex._isNegativeZero = function (val) { - if (val !== 0) { - return false; - } - - return 1/val === -Infinity; -}; - -/** - * Internal method to check if op has __complex__ - */ -Sk.builtin.complex.try_complex_special_method = function (op) { - var f; // PyObject - var res; - - // return early - if (op == null) { - return null; - } - - // the lookup special method does already all the magic - f = Sk.abstr.lookupSpecial(op, Sk.builtin.str.$complex); - - if (f != null) { - // method on builtin, provide this arg - res = Sk.misceval.callsimArray(f, [op]); - - return res; - } - - return null; -}; + return complex_subtype_from_doubles(cr.real, ci.real, this); +} /** - Check if given argument is number or complex and always - returns complex type. + * @function + * + * @return {Sk.builtin.complex} an instance of complex - could be a subtype's instance + * + * @param {number} real + * @param {number} imag + * @param {Object} type_prototype Sk.builtin.complex.prototype + * @ignore */ -Sk.builtin.complex.check_number_or_complex = function (other) { - /* exit early */ - if (!Sk.builtin.checkNumber(other) && other.tp$name !== "complex") { - throw new Sk.builtin.TypeError("unsupported operand type(s) for +: 'complex' and '" + Sk.abstr.typeName(other) + "'"); - } - - /* converting to complex allows us to use always only one formula */ - if (Sk.builtin.checkNumber(other)) { - other = new Sk.builtin.complex(other); // create complex +function complex_subtype_from_doubles(real, imag, type_prototype) { + if (type_prototype === Sk.builtin.complex.prototype) { + return new Sk.builtin.complex(real, imag); + } else { + const instance = new type_prototype.constructor(); + Sk.builtin.complex.call(instance, real, imag); + return instance; } - - return other; -}; +} /** - Parses a string repr of a complex number + * + * @function + * @description Parses a string repr of a complex number + * @param {*} val + * @param {Object=} type_prototype + * We leave this as Sk.builtin.complex since it is called by the compiler + * @ignore */ -Sk.builtin.complex.complex_subtype_from_string = function (val) { +Sk.builtin.complex.complex_subtype_from_string = function (val, type_prototype) { + type_prototype = type_prototype || Sk.builtin.complex.prototype; var index; var start; var val_wws; // val with removed beginning ws and ( - var x = 0.0, y = 0.0; // real, imag parts + var x = 0.0, + y = 0.0; // real, imag parts var got_bracket = false; // flag for braces var len; // total length of val var match; // regex result @@ -339,7 +503,7 @@ Sk.builtin.complex.complex_subtype_from_string = function (val) { if (val[index] === "j" || val[index] === "J") { y = parseFloat(match[0]); index++; - } else if(val[index] === "+" || val[index] === "-") { + } else if (val[index] === "+" || val[index] === "-") { /* j | j */ x = parseFloat(match[0]); @@ -401,456 +565,204 @@ Sk.builtin.complex.complex_subtype_from_string = function (val) { } // return here complex number parts - return new Sk.builtin.complex(new Sk.builtin.float_(x), new Sk.builtin.float_(y)); + return complex_subtype_from_doubles(x, y, type_prototype); }; /** - _PyHASH_IMAG refers to _PyHASH_MULTIPLIER which refers to 1000003 + * + * @function + * @description + * + * A helper function for converting a big int to a number or throwing OverFlow + * @ignore */ -Sk.builtin.complex.prototype.tp$hash = function () { - return new Sk.builtin.int_(this.tp$getattr(Sk.builtin.str.$imag).v * 1000003 + this.tp$getattr(Sk.builtin.str.$real).v); -}; - -Sk.builtin.complex.prototype.nb$add = function (other) { - var real; - var imag; - - other = Sk.builtin.complex.check_number_or_complex(other); - - real = this.tp$getattr(Sk.builtin.str.$real).v + other.tp$getattr(Sk.builtin.str.$real).v; - imag = this.tp$getattr(Sk.builtin.str.$imag).v + other.tp$getattr(Sk.builtin.str.$imag).v; - - return new Sk.builtin.complex(new Sk.builtin.float_(real), new Sk.builtin.float_(imag)); -}; - -/* internal subtract/diff function that calls internal float diff */ -Sk.builtin.complex._c_diff = function (a, b) { - var r, i; // Py_Float - r = a.real.nb$subtract.call(a.real, b.real); - i = a.imag.nb$subtract.call(a.imag, b.imag); - - return new Sk.builtin.complex(r, i); -}; - -Sk.builtin.complex.prototype.nb$subtract = function (other) { - var result; // Py_complex - var a, b; // Py_complex - - a = Sk.builtin.complex.check_number_or_complex(this); - b = Sk.builtin.complex.check_number_or_complex(other); - - result = Sk.builtin.complex._c_diff(a, b); - - return result; -}; - -Sk.builtin.complex.prototype.nb$multiply = function (other) { - var real; - var imag; - var a, b; // Py_complex - - a = this; - b = Sk.builtin.complex.check_number_or_complex(other); - - real = a.real.v * b.real.v - a.imag.v * b.imag.v; - imag = a.real.v * b.imag.v + a.imag.v * b.real.v; - - return new Sk.builtin.complex(new Sk.builtin.float_(real), new Sk.builtin.float_(imag)); -}; +function fromBigIntToNumberOrOverflow(big) { + const x = parseFloat(JSBI.toNumber(big)); + if (x == Infinity || x == -Infinity) { + //trying to convert a large js string to a float + throw new Sk.builtin.OverflowError("int too large to convert to float"); + } + return x; +} /** - * Otherwise google closure complains about ZeroDivisionError not being - * defined - * @suppress {missingProperties} * - * implementation based on complexobject.c:c_quot + * @function + * @description + * A wrapper to do the checks before passing the this.real, this.imag, other.real, other.imag + * to the number function + * @ignore + * @param {function(number, number, number, number)} f + * @param {boolean=} suppressOverflow */ -Sk.builtin.complex.prototype.nb$divide = function (other) { - var real; - var imag; - - other = Sk.builtin.complex.check_number_or_complex(other); - - var ratio; - var denom; - - // other == b - var breal = other.real.v; - var bimag = other.imag.v; - // this == a - var areal = this.real.v; - var aimag = this.imag.v; - - var abs_breal = Math.abs(breal); - var abs_bimag = Math.abs(bimag); +function complexNumberSlot(f, suppressOverflow) { + return function (other) { + const a_real = this.real; + const a_imag = this.imag; + let b_real = other.real; + let b_imag; + const other_v = other.v; + if (typeof b_real === "number") { + b_imag = other.imag; + } else if (typeof other_v === "number") { + b_real = other_v; + b_imag = 0.0; + } else if (other_v instanceof JSBI) { + if (suppressOverflow === undefined) { + b_real = fromBigIntToNumberOrOverflow(other_v); + } else { + b_real = other_v.toString(); // weird case for tp_richcompare + } + b_imag = 0.0; + } else { + return Sk.builtin.NotImplemented.NotImplemented$; + } - if (abs_breal >= abs_bimag) { - // divide tops and bottom by breal - if (abs_breal === 0.0) { + return f(a_real, a_imag, b_real, b_imag); + }; +} + +function divide(a_real, a_imag, b_real, b_imag) { + let ratio, denom, real, imag; + const abs_b_real = Math.abs(b_real); + const abs_b_imag = Math.abs(b_imag); + if (abs_b_real >= abs_b_imag) { + // divide tops and bottom by b_real + if (abs_b_real === 0.0) { throw new Sk.builtin.ZeroDivisionError("complex division by zero"); } else { - ratio = bimag / breal; - denom = breal + bimag * ratio; - real = (areal + aimag * ratio) / denom; - imag = (aimag - areal * ratio) / denom; + ratio = b_imag / b_real; + denom = b_real + b_imag * ratio; + real = (a_real + a_imag * ratio) / denom; + imag = (a_imag - a_real * ratio) / denom; } - } else if (abs_bimag >= abs_breal) { + } else if (abs_b_imag >= abs_b_real) { // divide tops and bottom by b.imag - ratio = breal / bimag; - denom = breal * ratio + bimag; - Sk.asserts.assert(bimag !== 0.0); - real = (areal * ratio + aimag) / denom; - imag = (aimag * ratio - areal) / denom; + ratio = b_real / b_imag; + denom = b_real * ratio + b_imag; + Sk.asserts.assert(b_imag !== 0.0); + real = (a_real * ratio + a_imag) / denom; + imag = (a_imag * ratio - a_real) / denom; } else { // At least one of b.real or b.imag is a NaN real = NaN; imag = NaN; } - return new Sk.builtin.complex(new Sk.builtin.float_(real), new Sk.builtin.float_(imag)); -}; - -Sk.builtin.complex.prototype.nb$floor_divide = function (other) { - throw new Sk.builtin.TypeError("can't take floor of complex number."); -}; - -Sk.builtin.complex.prototype.nb$remainder = function (other) { - throw new Sk.builtin.TypeError("can't mod complex numbers."); -}; - -/** - * @param {?Object=} z, modulo operation - */ -Sk.builtin.complex.prototype.nb$power = function (other, z) { - var p; - var exponent; - var int_exponent; - var a, b; - - // none is allowed - if (z != null && !Sk.builtin.checkNone(z)) { - throw new Sk.builtin.ValueError("complex modulo"); - } - - a = this; - b = Sk.builtin.complex.check_number_or_complex(other); + return new Sk.builtin.complex(real, imag); +} - exponent = b; - int_exponent = b.real.v | 0; // js convert to int - if (exponent.imag.v === 0.0 && exponent.real.v === int_exponent) { - p = Sk.builtin.complex.c_powi(a, int_exponent); +const power = complexNumberSlot((a_real, a_imag, b_real, b_imag) => { + const int_exponent = b_real | 0; // js convert to int + if (b_imag === 0.0 && b_real === int_exponent) { + return c_powi(a_real, a_imag, int_exponent); } else { - p = Sk.builtin.complex.c_pow(a, exponent); + return c_pow(a_real, a_imag, b_real, b_imag); } - - return p; -}; +}); // power of complex a and complex exponent b -Sk.builtin.complex.c_pow = function (a, b) { - var real, imag; // Py_complex - - var vabs; - var len; - var at; - var phase; - - // other == b - var breal = b.real.v; - var bimag = b.imag.v; - // this == a - var areal = a.real.v; - var aimag = a.imag.v; - - if (breal === 0.0 && bimag === 0.0) { +function c_pow(a_real, a_imag, b_real, b_imag) { + let len, phase, real, imag; + + if (b_real === 0.0 && b_imag === 0.0) { real = 1.0; imag = 0.0; - } else if (areal === 0.0 && aimag === 0.0) { - if(bimag !== 0.0 || breal < 0.0) { + } else if (a_real === 0.0 && a_imag === 0.0) { + if (b_imag !== 0.0 || b_real < 0.0) { throw new Sk.builtin.ZeroDivisionError("complex division by zero"); } real = 0.0; imag = 0.0; } else { - vabs = Math.hypot(areal, aimag); - len = Math.pow(vabs, breal); - at = Math.atan2(aimag, areal); - phase = at * breal; - - if(bimag !== 0.0) { - len /= Math.exp(at * bimag); - phase += bimag * Math.log(vabs); + const vabs = Math.hypot(a_real, a_imag); + len = Math.pow(vabs, b_real); + const at = Math.atan2(a_imag, a_real); + phase = at * b_real; + + if (b_imag !== 0.0) { + len /= Math.exp(at * b_imag); + phase += b_imag * Math.log(vabs); } real = len * Math.cos(phase); imag = len * Math.sin(phase); } - - return new Sk.builtin.complex(new Sk.builtin.float_(real), new Sk.builtin.float_(imag)); -}; + return new Sk.builtin.complex(real, imag); +} // power of complex x and integer exponent n -Sk.builtin.complex.c_powi = function (x, n) { - var cn; // Py_complex - var c1; - +function c_powi(a_real, a_imag, n) { if (n > 100 || n < -100) { - cn = new Sk.builtin.complex(new Sk.builtin.float_(n), new Sk.builtin.float_(0.0)); - return Sk.builtin.complex.c_pow(x, cn); + return c_pow(a_real, a_imag, n, 0.0); } else if (n > 0) { - return Sk.builtin.complex.c_powu(x, n); + return c_powu(a_real, a_imag, n); } else { // return c_quot(c_1,c_powu(x,-n)); - c1 = new Sk.builtin.complex(new Sk.builtin.float_(1.0), new Sk.builtin.float_(0.0)); - return c1.nb$divide(Sk.builtin.complex.c_powu(x,-n)); + const r = c_powu(a_real, a_imag, -n); + return divide(1.0, 0.0, r.real, r.imag); } -}; +} -Sk.builtin.complex.c_powu = function (x, n) { +function c_powu(a_real, a_imag, n) { var r, p; // Py_complex - var mask = 1; - r = new Sk.builtin.complex(new Sk.builtin.float_(1.0), new Sk.builtin.float_(0.0)); - p = x; + let mask = 1; + r = new Sk.builtin.complex(1.0, 0.0); + p = new Sk.builtin.complex(a_real, a_imag); while (mask > 0 && n >= mask) { if (n & mask) { - r = r.nb$multiply(p); + r = new Sk.builtin.complex(r.real * p.real - r.imag * p.imag, r.real * p.imag + p.real * r.imag); } mask <<= 1; - p = p.nb$multiply(p); + p = new Sk.builtin.complex(p.real * p.real - p.imag * p.imag, 2 * p.real * p.imag); } return r; -}; - - -Sk.builtin.complex.prototype.nb$inplace_add = Sk.builtin.complex.prototype.nb$add; - -Sk.builtin.complex.prototype.nb$inplace_subtract = Sk.builtin.complex.prototype.nb$subtract; - -Sk.builtin.complex.prototype.nb$inplace_multiply = Sk.builtin.complex.prototype.nb$multiply; - -Sk.builtin.complex.prototype.nb$inplace_divide = Sk.builtin.complex.prototype.nb$divide; - -Sk.builtin.complex.prototype.nb$inplace_remainder = Sk.builtin.complex.prototype.nb$remainder; - -Sk.builtin.complex.prototype.nb$inplace_floor_divide = Sk.builtin.complex.prototype.nb$floor_divide; - -Sk.builtin.complex.prototype.nb$inplace_power = Sk.builtin.complex.prototype.nb$power; - -Sk.builtin.complex.prototype.nb$negative = function () { - var real; - var imag; - // this == a - var areal = this.real.v; - var aimag = this.imag.v; - - real = -areal; - imag = -aimag; - - return new Sk.builtin.complex(new Sk.builtin.float_(real), new Sk.builtin.float_(imag)); -}; - -Sk.builtin.complex.prototype.nb$positive = function () { - return Sk.builtin.complex.check_number_or_complex(this); -}; - -/** - * check if op is instance of complex or a sub-type - */ -Sk.builtin.complex._complex_check = function (op) { - if (op === undefined) { - return false; - } - - if (op instanceof Sk.builtin.complex || (op.tp$name && op.tp$name === "complex")) { - return true; - } - - // check if type of ob is a subclass - if (Sk.builtin.issubclass(new Sk.builtin.type(op), Sk.builtin.complex)) { - return true; - } - - return false; -}; - -Sk.builtin.complex.prototype.tp$richcompare = function (w, op) { - var result; - var equal; - var i; - - if (op !== "Eq" && op !== "NotEq") { - if(Sk.builtin.checkNumber(w) || Sk.builtin.complex._complex_check(w)) { - throw new Sk.builtin.TypeError("no ordering relation is defined for complex numbers"); - } - - return Sk.builtin.NotImplemented.NotImplemented$; - } - - // assert(PyComplex_Check(v))); - i = Sk.builtin.complex.check_number_or_complex(this); - var _real = i.tp$getattr(Sk.builtin.str.$real).v; - var _imag = i.tp$getattr(Sk.builtin.str.$imag).v; - - if (Sk.builtin.checkInt(w)) { - /* Check for 0.0 imaginary part first to avoid the rich - * comparison when possible. - */ - - // if true, the complex number has just a real part - if (_imag === 0.0) { - equal = Sk.misceval.richCompareBool(new Sk.builtin.float_(_real), w, op); - result = new Sk.builtin.bool(equal); - return result; - } else { - equal = false; - } - } else if (Sk.builtin.checkFloat(w)) { - equal = (_real === Sk.builtin.float_.PyFloat_AsDouble(w) && _imag === 0.0); - } else if (Sk.builtin.complex._complex_check(w)) { - // ToDo: figure if we need to call to_complex - var w_real = w.tp$getattr(Sk.builtin.str.$real).v; - var w_imag = w.tp$getattr(Sk.builtin.str.$imag).v; - equal = _real === w_real && _imag === w_imag; - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } - - // invert result if op == NotEq - if(op === "NotEq") { - equal = !equal; - } - - // wrap as bool - result = new Sk.builtin.bool(equal); - - return result; -}; - -// Despite what jshint may want us to do, these two functions need to remain -// as == and != Unless you modify the logic of numberCompare do not change -// these. -Sk.builtin.complex.prototype.__eq__ = function (me, other) { - return Sk.builtin.complex.prototype.tp$richcompare.call(me, other, "Eq"); -}; - -Sk.builtin.complex.prototype.__ne__ = function (me, other) { - return Sk.builtin.complex.prototype.tp$richcompare.call(me, other, "NotEq"); -}; - -/** - * Do we really need to implement those? Otherwise I can't find in Sk.abstr a place where this particular - * expcetion is thrown.git co - */ -Sk.builtin.complex.prototype.__lt__ = function (me, other) { - throw new Sk.builtin.TypeError("unorderable types: " + Sk.abstr.typeName(me) + " < " + Sk.abstr.typeName(other)); -}; - -Sk.builtin.complex.prototype.__le__ = function (me, other) { - throw new Sk.builtin.TypeError("unorderable types: " + Sk.abstr.typeName(me) + " <= " + Sk.abstr.typeName(other)); -}; - -Sk.builtin.complex.prototype.__gt__ = function (me, other) { - throw new Sk.builtin.TypeError("unorderable types: " + Sk.abstr.typeName(me) + " > " + Sk.abstr.typeName(other)); -}; - -Sk.builtin.complex.prototype.__ge__ = function (me, other) { - throw new Sk.builtin.TypeError("unorderable types: " + Sk.abstr.typeName(me) + " >= " + Sk.abstr.typeName(other)); -}; - -Sk.builtin.complex.prototype.__float__ = function (self) { - throw new Sk.builtin.TypeError("can't convert complex to float"); -}; - -Sk.builtin.complex.prototype.__int__ = function (self) { - throw new Sk.builtin.TypeError("can't convert complex to int"); -}; - - -Sk.builtin.complex.prototype._internalGenericGetAttr = Sk.builtin.object.prototype.GenericGetAttr; - -/** - * Custom getattr impl. to get the c.real and c.imag to work. Though we should - * consider to implement tp$members that always are attributs on the class and - * will be used in the genericgetattr method. - * Would be super easy to implement the readonly stuff too. - * - */ -Sk.builtin.complex.prototype.tp$getattr = function (name) { - if (name != null && (Sk.builtin.checkString(name) || typeof name === "string")) { - var _name = name; - - // get javascript string - if (Sk.builtin.checkString(name)) { - _name = Sk.ffi.remapToJs(name); - } - - if (_name === "real" || _name === "imag") { - return this[_name]; - } - } - - // if we have not returned yet, try the genericgetattr - return this._internalGenericGetAttr(name); -}; - - -Sk.builtin.complex.prototype.tp$setattr = function (name, value) { - if (name != null && (Sk.builtin.checkString(name) || typeof name === "string")) { - var _name = name; - - // get javascript string - if (Sk.builtin.checkString(name)) { - _name = Sk.ffi.remapToJs(name); - } - - if (_name === "real" || _name === "imag") { - throw new Sk.builtin.AttributeError("readonly attribute"); - } - } - - // builtin: --> all is readonly (I am not happy with this) - throw new Sk.builtin.AttributeError("'complex' object attribute '" + name + "' is readonly"); -}; +} /** * Internal format function for repr and str * It is not intended for __format__ calls * * This functions assumes, that v is always instance of Sk.builtin.complex + * @ignore */ -Sk.builtin.complex.complex_format = function (v, precision, format_code){ - function copysign (a, b) { - return b < 0 ? -Math.abs(a) : Math.abs(a); - } - - if (v == null || !Sk.builtin.complex._complex_check(v)) { - throw new Error("Invalid internal method call: Sk.complex.complex_format() called with invalid value type."); +function complex_format(v, precision, format_code) { + function copysign(a, b) { + let sign; + if (b) { + sign = b < 0 ? -1 : 1; + } else { + sign = 1 / b < 0 ? -1 : 1; + } + return sign * Math.abs(a); } - var result; // PyObject + let result; // pyObject - var pre = ""; - var im = ""; - var re = null; - var lead = ""; - var tail = ""; + let pre = ""; + let im = ""; + let re = null; + let lead = ""; + let tail = ""; + const real = v.real; + const imag = v.imag; - if (v.real.v === 0.0 && copysign(1.0, v.real.v) == 1.0) { + if (real === 0.0 && copysign(1.0, real) == 1.0) { re = ""; - im = Sk.builtin.complex.PyOS_double_to_string(v.imag.v, format_code, precision, 0, null); - // im = v.imag.v; + im = PyOS_double_to_string(imag, format_code, precision, 0, null); + // im = imag; } else { /* Format imaginary part with sign, real part without */ - pre = Sk.builtin.complex.PyOS_double_to_string(v.real.v, format_code, precision, 0, null); + pre = PyOS_double_to_string(real, format_code, precision, 0, null); re = pre; - im = Sk.builtin.complex.PyOS_double_to_string(v.imag.v, format_code, precision, Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_SIGN, null); + im = PyOS_double_to_string(imag, format_code, precision, PyOS_double_to_string.Py_DTSF_SIGN, null); - if (v.imag.v === 0 && 1/v.imag.v === -Infinity && im && im[0] !== "-"){ + if (imag === 0 && 1 / imag === -Infinity && im && im[0] !== "-") { im = "-" + im; // force negative zero sign } @@ -859,259 +771,30 @@ Sk.builtin.complex.complex_format = function (v, precision, format_code){ } result = "" + lead + re + im + "j" + tail; // concat all parts - return new Sk.builtin.str(result); -}; - -Sk.builtin.complex.prototype["$r"] = function () { - return Sk.builtin.complex.complex_format(this, 0, "r"); -}; - -Sk.builtin.complex.prototype.tp$str = function () { - return Sk.builtin.complex.complex_format(this, null, "g"); // g, 12 == Py_Float_STR_PRECISION -}; +} /** * https://hg.python.org/cpython/file/3cf2990d19ab/Objects/complexobject.c#l907 * also see _PyComplex_FormatAdvanced - * - * We currently use the signature (self, format_spec) instead of (self, args). So we do - * not need to unwrap the args. + * @ignore */ -Sk.builtin.complex.prototype.int$format = function __format__(self, format_spec){ - var result; // PyObject - - if (format_spec == null) { - return null; - } - - if (Sk.builtin.checkString(format_spec)) { - result = Sk.builtin.complex._PyComplex_FormatAdvanced(self, format_spec); - - return result; - } - - - throw new Sk.builtin.TypeError("__format__ requires str or unicode"); -}; -Sk.builtin.complex.prototype.int$format.co_name = new Sk.builtin.str("__format__"); -Sk.builtin.complex.prototype.__format__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$format); - -Sk.builtin.complex._PyComplex_FormatAdvanced = function(self, format_spec) { +function _PyComplex_FormatAdvanced(self, format_spec) { throw new Sk.builtin.NotImplementedError("__format__ is not implemented for complex type."); -}; +} /** - Return true if float or double are is neither infinite nor NAN, else false - Value is already a Javascript object + Return true if float or double are is neither infinite nor NAN, else false + Value is already a Javascript object + @ignore */ -Sk.builtin.complex._is_finite = function (val) { - return !isNaN(val) && val !== Infinity && val !== -Infinity; -}; +function _is_finite(val) { + return Number.isFinite(val); +} -Sk.builtin.complex._is_infinity = function (val) { +function _is_infinity(val) { return val === Infinity || val === -Infinity; -}; - -/** - * @suppress {missingProperties} - */ -Sk.builtin.complex.prototype.int$abs = function __abs__(self) { - var result; - var _real = self.real.v; - var _imag = self.imag.v; - - if (!Sk.builtin.complex._is_finite(_real) || !Sk.builtin.complex._is_finite(_imag)) { - /* C99 rules: if either the real or the imaginary part is an - infinity, return infinity, even if the other part is a - NaN. - */ - - if (Sk.builtin.complex._is_infinity(_real)) { - result = Math.abs(_real); - return new Sk.builtin.float_(result); - } - - if (Sk.builtin.complex._is_infinity(_imag)) { - result = Math.abs(_imag); - return new Sk.builtin.float_(result); - } - - /* either the real or imaginary part is a NaN, - and neither is infinite. Result should be NaN. */ - - return new Sk.builtin.float_(NaN); - } - - result = Math.hypot(_real, _imag); - - if (!Sk.builtin.complex._is_finite(result)) { - throw new Sk.builtin.OverflowError("absolute value too large"); - } - - return new Sk.builtin.float_(result); -}; -Sk.builtin.complex.prototype.int$abs.co_name = new Sk.builtin.str("__abs__"); -Sk.builtin.complex.prototype.__abs__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$abs); - -Sk.builtin.complex.prototype.int$bool = function __bool__(self) { - return new Sk.builtin.bool(self.tp$getattr(Sk.builtin.str.$real).v || self.tp$getattr(Sk.builtin.str.$real).v); -}; -Sk.builtin.complex.prototype.int$bool.co_name = new Sk.builtin.str("__bool__"); -Sk.builtin.complex.prototype.__bool__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$bool); - -Sk.builtin.complex.prototype.int$truediv = function __truediv__(self, other){ - Sk.builtin.pyCheckArgsLen("__truediv__", arguments.length, 1, 1, true); - return self.nb$divide.call(self, other); -}; -Sk.builtin.complex.prototype.int$truediv.co_name = new Sk.builtin.str("__truediv__"); -Sk.builtin.complex.prototype.__truediv__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$truediv); - -Sk.builtin.complex.prototype.int$hash = function __hash__(self){ - Sk.builtin.pyCheckArgsLen("__hash__", arguments.length, 0, 0, true); - - return self.tp$hash.call(self); -}; -Sk.builtin.complex.prototype.int$hash.co_name = new Sk.builtin.str("__hash__"); -Sk.builtin.complex.prototype.__hash__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$hash); - -Sk.builtin.complex.prototype.int$add = function __add__(self, other){ - Sk.builtin.pyCheckArgsLen("__add__", arguments.length, 1, 1, true); - return self.nb$add.call(self, other); -}; -Sk.builtin.complex.prototype.int$add.co_name = new Sk.builtin.str("__add__"); -Sk.builtin.complex.prototype.__add__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$add); - - -Sk.builtin.complex.prototype.int$repr = function __repr__(self){ - Sk.builtin.pyCheckArgsLen("__repr__", arguments.length, 0, 0, true); - - return self["r$"].call(self); -}; -Sk.builtin.complex.prototype.int$repr.co_name = new Sk.builtin.str("__repr__"); -Sk.builtin.complex.prototype.__repr__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$repr); - -Sk.builtin.complex.prototype.int$str = function __str__(self){ - Sk.builtin.pyCheckArgsLen("__str__", arguments.length, 0, 0, true); - - return self.tp$str.call(self); -}; -Sk.builtin.complex.prototype.int$str.co_name = new Sk.builtin.str("__str__"); -Sk.builtin.complex.prototype.__str__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$str); - -Sk.builtin.complex.prototype.int$sub = function __sub__(self, other){ - Sk.builtin.pyCheckArgsLen("__sub__", arguments.length, 1, 1, true); - return self.nb$subtract.call(self, other); -}; -Sk.builtin.complex.prototype.int$sub.co_name = new Sk.builtin.str("__sub__"); -Sk.builtin.complex.prototype.__sub__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$sub); - -Sk.builtin.complex.prototype.int$mul = function __mul__(self, other){ - Sk.builtin.pyCheckArgsLen("__mul__", arguments.length, 1, 1, true); - return self.nb$multiply.call(self, other); -}; -Sk.builtin.complex.prototype.int$mul.co_name = new Sk.builtin.str("__mul__"); -Sk.builtin.complex.prototype.__mul__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$mul); - -Sk.builtin.complex.prototype.int$div = function __div__(self, other){ - Sk.builtin.pyCheckArgsLen("__div__", arguments.length, 1, 1, true); - return self.nb$divide.call(self, other); -}; -Sk.builtin.complex.prototype.int$div.co_name = new Sk.builtin.str("__div__"); -Sk.builtin.complex.prototype.__div__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$div); - -Sk.builtin.complex.prototype.int$floordiv = function __floordiv__(self, other){ - Sk.builtin.pyCheckArgsLen("__floordiv__", arguments.length, 1, 1, true); - return self.nb$floor_divide.call(self, other); -}; -Sk.builtin.complex.prototype.int$floordiv.co_name = new Sk.builtin.str("__floordiv__"); -Sk.builtin.complex.prototype.__floordiv__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$floordiv); - -Sk.builtin.complex.prototype.int$mod = function __mod__(self, other){ - Sk.builtin.pyCheckArgsLen("__mod__", arguments.length, 1, 1, true); - return self.nb$remainder.call(self, other); -}; -Sk.builtin.complex.prototype.int$mod.co_name = new Sk.builtin.str("__mod__"); -Sk.builtin.complex.prototype.__mod__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$mod); - -Sk.builtin.complex.prototype.int$pow = function __pow__(self, other, z){ - Sk.builtin.pyCheckArgsLen("__pow__", arguments.length, 1, 2, true); - return self.nb$power.call(self, other, z); -}; -Sk.builtin.complex.prototype.int$pow.co_name = new Sk.builtin.str("__pow__"); -Sk.builtin.complex.prototype.__pow__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$pow); - -Sk.builtin.complex.prototype.int$neg = function __neg__(self){ - Sk.builtin.pyCheckArgsLen("__neg__", arguments.length, 0, 0, true); - return self.nb$negative.call(self); -}; -Sk.builtin.complex.prototype.__neg__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$neg); - -Sk.builtin.complex.prototype.int$pos = function __pos__(self){ - Sk.builtin.pyCheckArgsLen("__pos__", arguments.length, 0, 0, true); - return self.nb$positive.call(self); -}; -Sk.builtin.complex.prototype.int$pos.co_name = new Sk.builtin.str("__pos__"); -Sk.builtin.complex.prototype.__pos__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$pos); - -Sk.builtin.complex.prototype.int$conjugate = function conjugate(self){ - Sk.builtin.pyCheckArgsLen("conjugate", arguments.length, 0, 0, true); - var _imag = self.imag.v; - _imag = -_imag; - - return new Sk.builtin.complex(self.real, new Sk.builtin.float_(_imag)); -}; -Sk.builtin.complex.prototype.int$conjugate.co_name = new Sk.builtin.str("conjugate"); -Sk.builtin.complex.prototype.conjugate = new Sk.builtin.func(Sk.builtin.complex.prototype.int$conjugate); - -// deprecated -Sk.builtin.complex.prototype.int$divmod = function __divmod__(self, other){ - Sk.builtin.pyCheckArgsLen("__divmod__", arguments.length, 1, 1, true); - - var div, mod; // Py_complex - var d, m, z; // PyObject - var a, b; // Py_complex - a = Sk.builtin.complex.check_number_or_complex(self); - b = Sk.builtin.complex.check_number_or_complex(other); - - div = a.nb$divide.call(a, b); // the raw divisor value - - div.real = new Sk.builtin.float_(Math.floor(div.real.v)); - div.imag = new Sk.builtin.float_(0.0); - - mod = a.nb$subtract.call(a, b.nb$multiply.call(b, div)); - - z = new Sk.builtin.tuple([div, mod]); - - return z; -}; -Sk.builtin.complex.prototype.int$divmod.co_name = new Sk.builtin.str("__divmod__"); -Sk.builtin.complex.prototype.__divmod__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$divmod); - -Sk.builtin.complex.prototype.int$getnewargs = function __getnewargs__(self){ - Sk.builtin.pyCheckArgsLen("__getnewargs__", arguments.length, 0, 0, true); - - return new Sk.builtin.tuple([self.real, self.imag]); -}; -Sk.builtin.complex.prototype.int$getnewargs.co_name = new Sk.builtin.str("__getnewargs__"); -Sk.builtin.complex.prototype.__getnewargs__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$getnewargs); - -Sk.builtin.complex.prototype.int$nonzero = function __nonzero__(self){ - Sk.builtin.pyCheckArgsLen("__nonzero__", arguments.length, 0, 0, true); - - if(self.real.v !== 0.0 || self.imag.v !== 0.0) { - return Sk.builtin.bool.true$; - } else { - return Sk.builtin.bool.false$; - } -}; -Sk.builtin.complex.prototype.int$nonzero.co_name = new Sk.builtin.str("__nonzero__"); -Sk.builtin.complex.prototype.__nonzero__ = new Sk.builtin.func(Sk.builtin.complex.prototype.int$nonzero); - - -// ToDo: think about inplace methods too -Sk.exportSymbol("Sk.builtin.complex", Sk.builtin.complex); - +} /** * Convert a double val to a string using supplied format_code, precision, and flags. @@ -1125,19 +808,17 @@ Sk.exportSymbol("Sk.builtin.complex", Sk.builtin.complex); * Py_DTSF_ALT means to apply “alternate” formatting rules. See the documentation for the PyOS_snprintf() '#' specifier for details. * If ptype is non-NULL, then the value it points to will be set to one of Py_DTST_FINITE, Py_DTST_INFINITE, or Py_DTST_NAN, signifying that val is a finite number, an * infinite number, or not a number, respectively. + * @ignore */ -Sk.builtin.complex.PyOS_double_to_string = function(val, format_code, precision, flags, type) { - var format; - var buf; - var t; - var exp; - var upper = false; - +function PyOS_double_to_string(val, format_code, precision, flags, type) { + let buf, + t, + upper = false; // Validate format code, and map upper and lower case - switch(format_code) { + switch (format_code) { case "e": /* exponent */ case "f": /* fixed */ - case "g": /* general */ + case "g" /* general */: break; case "E": upper = true; @@ -1147,12 +828,11 @@ Sk.builtin.complex.PyOS_double_to_string = function(val, format_code, precision, upper = true; format_code = "f"; break; - case "r": /* repr format */ + case "r" /* repr format */: // Supplied precision is unused, must be 0. - if(precision !== 0) { + if (precision !== 0) { throw new Error("Bad internall call"); // only happens when somebody messes up calling this in js } - // repr() precision is 17 significant decimal digits precision = 17; format_code = "g"; @@ -1160,69 +840,62 @@ Sk.builtin.complex.PyOS_double_to_string = function(val, format_code, precision, default: throw new Error("Bad internall call"); } - // no need for buffer size calculation like in cpython - // Handle nan and inf - if(isNaN(val)) { + if (isNaN(val)) { buf = "nan"; - t = Sk.builtin.complex.PyOS_double_to_string.Py_DTST_NAN; + t = PyOS_double_to_string.Py_DTST_NAN; } else if (val === Infinity) { buf = "inf"; - t = Sk.builtin.complex.PyOS_double_to_string.Py_DTST_INFINITE; + t = PyOS_double_to_string.Py_DTST_INFINITE; } else if (val === -Infinity) { buf = "-inf"; - t = Sk.builtin.complex.PyOS_double_to_string.Py_DTST_INFINITE; + t = PyOS_double_to_string.Py_DTST_INFINITE; } else { - t = Sk.builtin.complex.PyOS_double_to_string.Py_DTST_FINITE; - if(flags & Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_ADD_DOT_0) { + t = PyOS_double_to_string.Py_DTST_FINITE; + if (flags & PyOS_double_to_string.Py_DTSF_ADD_DOT_0) { format_code = "g"; // "Z"; _PyOS_ascii_formatd converts "Z" to "g" } - // ToDo: call snprintf here // ToDo: call ascii_formatd var format_str = "%"; - format_str += flags & Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_ALT ? "#" : ""; + format_str += flags & PyOS_double_to_string.Py_DTSF_ALT ? "#" : ""; - if(precision != null) { + if (precision != null) { format_str += "."; format_str += precision; } format_str += format_code; format_str = new Sk.builtin.str(format_str); - /** - * We cann call nb$remainder with val, because it gets unwrapped and it doesn't matter if it is + * We can call nb$remainder with val, because it gets unwrapped and it doesn't matter if it is * already a javascript number. If we do not pass a float, we can't distinguish between ints and floats * and therefore we can't adjust the sign of the zero accordingly */ buf = format_str.nb$remainder(new Sk.builtin.float_(val)); buf = buf.v; // get javascript string } - /** * Add sign when requested. It's convenient (esp. when formatting complex numbers) to * include sign even for inf and nan. */ - if(flags & Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_SIGN && buf[0] !== "-") { + if (flags & PyOS_double_to_string.Py_DTSF_SIGN && buf[0] !== "-") { buf = "+" + buf; } - - if(upper) { + if (upper) { // Convert to upper case buf = buf.toUpperCase(); } - return buf; -}; +} /* PyOS_double_to_string's "flags" parameter can be set to 0 or more of: */ -Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_SIGN = 0x01; // always add the sign -Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_ADD_DOT_0 = 0x02; // if the result is an integer add ".0" -Sk.builtin.complex.PyOS_double_to_string.Py_DTSF_ALT = 0x04; // "alternate" formatting. it's format_code specific +PyOS_double_to_string.Py_DTSF_SIGN = 0x01; // always add the sign +PyOS_double_to_string.Py_DTSF_ADD_DOT_0 = 0x02; // if the result is an integer add ".0" +PyOS_double_to_string.Py_DTSF_ALT = 0x04; // "alternate" formatting. it's format_code specific /* PyOS_double_to_string's "type", if non-NULL, will be set to one of: */ -Sk.builtin.complex.PyOS_double_to_string.Py_DTST_FINITE = 0; -Sk.builtin.complex.PyOS_double_to_string.Py_DTST_INFINITE = 1; -Sk.builtin.complex.PyOS_double_to_string.Py_DTST_NAN = 2; +PyOS_double_to_string.Py_DTST_FINITE = 0; +PyOS_double_to_string.Py_DTST_INFINITE = 1; +PyOS_double_to_string.Py_DTST_NAN = 2; diff --git a/src/constants.js b/src/constants.js index 8cb5faf65f..cf14bfb36b 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,39 +1,10 @@ -Sk.builtin.str.$emptystr = new Sk.builtin.str(""); -/** - * Python bool True constant. - * @type {Sk.builtin.bool} - * @memberOf Sk.builtin.bool - */ -Sk.builtin.bool.true$ = /** @type {Sk.builtin.bool} */ (Object.create(Sk.builtin.bool.prototype, {v: {value: 1, enumerable: true}})); - -/** - * Python bool False constant. - * @type {Sk.builtin.bool} - * @memberOf Sk.builtin.bool - */ -Sk.builtin.bool.false$ = /** @type {Sk.builtin.bool} */ (Object.create(Sk.builtin.bool.prototype, {v: {value: 0, enumerable: true}})); /* Constants used for kwargs */ -// Sk.builtin.int_ -Sk.builtin.int_.co_varnames = [ "number", "base" ]; -Sk.builtin.int_.$defaults = [ 0, Sk.builtin.none.none$ ]; - -// Sk.builtin.lng -Sk.builtin.lng.co_varnames = [ "number", "base" ]; -Sk.builtin.lng.$defaults = [ 0, Sk.builtin.none.none$ ]; - -// Sk.builtin.sorted -Sk.builtin.sorted.co_varnames = ["list", "cmp", "key", "reverse"]; -Sk.builtin.sorted.$defaults = [Sk.builtin.none.none$, Sk.builtin.none.none$, Sk.builtin.bool.false$]; - -// Sk.builtin.dict.fromkeys -Sk.builtin.dict.$fromkeys.co_name = new Sk.builtin.str("fromkeys"); -Sk.builtin.dict.prototype["fromkeys"] = new Sk.builtin.func(Sk.builtin.dict.$fromkeys); - // String constants Sk.builtin.str.$empty = new Sk.builtin.str(""); +Sk.builtin.str.$emptystr = Sk.builtin.str.$empty; Sk.builtin.str.$default_factory = new Sk.builtin.str("default_factory"); Sk.builtin.str.$imag = new Sk.builtin.str("imag"); @@ -62,15 +33,16 @@ Sk.builtin.str.$ge = new Sk.builtin.str("__ge__"); Sk.builtin.str.$getattr = new Sk.builtin.str("__getattr__"); Sk.builtin.str.$getattribute = new Sk.builtin.str("__getattribute__"); Sk.builtin.str.$getitem = new Sk.builtin.str("__getitem__"); +Sk.builtin.str.$class_getitem = new Sk.builtin.str("__class_getitem__"); Sk.builtin.str.$gt = new Sk.builtin.str("__gt__"); Sk.builtin.str.$le = new Sk.builtin.str("__le__"); Sk.builtin.str.$len = new Sk.builtin.str("__len__"); Sk.builtin.str.$lt = new Sk.builtin.str("__lt__"); +Sk.builtin.str.$module = new Sk.builtin.str("__module__"); Sk.builtin.str.$name = new Sk.builtin.str("__name__"); Sk.builtin.str.$ne = new Sk.builtin.str("__ne__"); Sk.builtin.str.$new = new Sk.builtin.str("__new__"); -Sk.builtin.str.$next2 = new Sk.builtin.str("next"); -Sk.builtin.str.$next3 = new Sk.builtin.str("__next__"); +Sk.builtin.str.$next = new Sk.builtin.str("__next__"); Sk.builtin.str.$path = new Sk.builtin.str("__path__"); Sk.builtin.str.$package = new Sk.builtin.str("__package__"); Sk.builtin.str.$repr = new Sk.builtin.str("__repr__"); @@ -83,119 +55,11 @@ Sk.builtin.str.$trunc = new Sk.builtin.str("__trunc__"); Sk.builtin.str.$write = new Sk.builtin.str("write"); Sk.misceval.op2method_ = { - "Eq" : Sk.builtin.str.$eq, + "Eq": Sk.builtin.str.$eq, "NotEq": Sk.builtin.str.$ne, - "Gt" : Sk.builtin.str.$gt, - "GtE" : Sk.builtin.str.$ge, - "Lt" : Sk.builtin.str.$lt, - "LtE" : Sk.builtin.str.$le + "Gt": Sk.builtin.str.$gt, + "GtE": Sk.builtin.str.$ge, + "Lt": Sk.builtin.str.$lt, + "LtE": Sk.builtin.str.$le }; -var builtinNames = [ - "int_", - "float_", - "bool", - "dict", - "list", - "str", - "lng", - "sorted", - "range", - "round", - "len", - "min", - "max", - "sum", - "zip", - "abs", - "fabs", - "ord", - "chr", - "hex", - "oct", - "bin", - "dir", - "repr", - "open", - "isinstance", - "hash", - "getattr", - "hasattr", - "id", - "map", - "filter", - "reduce", - "sorted", - "any", - "all", - "input", - "raw_input", - "setattr", - "quit", - "quit", - "divmod", - "format", - "globals", - "issubclass", - - "BaseException", - "Exception", - "StandardError", - "AssertionError", - "AttributeError", - "ImportError", - "IndentationError", - "IndexError", - "KeyError", - "NameError", - "UnboundLocalError", - "OverflowError", - "SyntaxError", - "RuntimeError", - "OSError", - "SuspensionError", - "SystemExit", - "TypeError", - "ValueError", - "ZeroDivisionError", - "TimeLimitError", - "IOError", - "NotImplementedError", - "NegativePowerError", - "ExternalError", - "OperationError", - "SystemError", - "StopIteration", -]; - -for (var i = 0; i < builtinNames.length; i++) { - var renamed = builtinNames[i]; - if (renamed.endsWith("_")) { - renamed = renamed.slice(0, -1); - } - Sk.builtin[builtinNames[i]].co_name = new Sk.builtin.str(renamed); - Sk.builtin[builtinNames[i]].__name__ = new Sk.builtin.str(renamed); -} - -Sk.builtin.str.prototype["split"].co_varnames = ["sep", "maxsplit"]; -Sk.builtin.str.prototype["split"].$defaults = [Sk.builtin.none.none$, Sk.builtin.int_(-1)]; -Sk.builtin.str.prototype["split"].co_kwargs = true; -Sk.builtin.str.prototype["rsplit"].co_varnames = ["sep", "maxsplit"]; -Sk.builtin.str.prototype["rsplit"].$defaults = [Sk.builtin.none.none$, Sk.builtin.int_(-1)]; -Sk.builtin.str.prototype["rsplit"].co_kwargs = true; - -var builtinStringMethods = [ - "capitalize", "center", "count", "encode", "endswith", "expandtabs", - "find", "format", "index", "isalnum", "isalpha", - "isdigit", "islower", "isnumeric", "isspace", - "istitle", "isupper", "join", "ljust", "lower", "lstrip", - "partition", "replace", "rfind", "rindex", "rjust", "rpartition", "rsplit", - "rstrip", "split", "splitlines", "startswith", "strip", "swapcase", "title", - "upper", "zfill" -]; -for (i = 0; i < builtinStringMethods.length; i++) { - renamed = builtinStringMethods[i]; - Sk.builtin.str.prototype[renamed].co_name = new Sk.builtin.str(renamed); - Sk.builtin.str.prototype[renamed].__name__ = new Sk.builtin.str(renamed); - Sk.builtin.str.prototype[renamed].tp$name = renamed; -} \ No newline at end of file diff --git a/src/descr.js b/src/descr.js new file mode 100644 index 0000000000..0afa1c9d12 --- /dev/null +++ b/src/descr.js @@ -0,0 +1,378 @@ +/** @typedef {Sk.builtin.type|Function|Object} */ var typeObject; +/** @constructor @extends {Sk.builtin.object} */ var descr_object = new Function(); // keep closure compiler happy + +/** + * @function + * @param {string} type_name + * @param {string|undefined} repr_name + * @param {Function} descr_constructor + */ +function buildDescriptor(type_name, repr_name, descr_constructor) { + const descr = Sk.abstr.buildNativeClass(type_name, { + constructor: descr_constructor, + flags: {sk$acceptable_as_base_class: false}, + // we can't use slots/methods/getsets yet since they're not defined! + proto: /**@lends {descr_object.prototype}*/ { + d$repr_name: repr_name || type_name, + d$check: descriptorCheck, + d$set_check: descriptorSetCheck, + $r: descriptorRepr, + tp$getsets: descriptorGetsets, + tp$getattr: Sk.generic.getAttr, + }, + }); + return descr; +} + +function descriptorCheck(obj) { + if (obj == null) { + return this; + } else if (!obj.ob$type.$isSubType(this.d$type)) { + throw new Sk.builtin.TypeError( + "descriptor '" + + this.d$name + + "' requires a '" + + this.d$type.prototype.tp$name + + "' object but received a '" + + Sk.abstr.typeName(obj) + + "' object" + ); + } + return; +} + +function descriptorSetCheck(obj) { + if (!obj.ob$type.$isSubType(this.d$type)) { + throw new Sk.builtin.TypeError( + "descriptor '" + + this.d$name + + "' requires a '" + + this.d$type.prototype.tp$name + + "' object but received a '" + + Sk.abstr.typeName(obj) + + "' object" + ); + } +} + +function descriptorRepr() { + return new Sk.builtin.str("<" + this.d$repr_name + " '" + this.d$name + "' of '" + this.d$type.prototype.tp$name + "' objects>"); +} + +const descriptorGetsets = { + __doc__: { + $get: function () { + return this.d$def.$doc ? new Sk.builtin.str(this.d$def.$doc) : Sk.builtin.none.none$; + }, + }, + __obj_class__: { + $get: function () { + return this.d$type; + }, + }, + __name__: { + $get: function () { + return new Sk.builtin.str(this.d$name); + }, + }, +}; + +/** + * @constructor + * @param {typeObject} type_obj + * @param {Object} gsd + * @extends {descr_object} + */ +Sk.builtin.getset_descriptor = buildDescriptor("getset_descriptor", undefined, function getset_descr(typeobj, d_base) { + this.d$def = d_base; + this.$get = d_base.$get; + this.$set = d_base.$set; + this.d$type = typeobj; + this.d$name = d_base.$name; +}); + +Sk.builtin.getset_descriptor.prototype.tp$descr_get = function (obj, type) { + let ret; + if ((ret = this.d$check(obj))) { + return ret; + } + if (this.$get !== undefined) { + return this.$get.call(obj); + } + + throw new Sk.builtin.AttributeError("getset_descriptor '" + this.d$name + "' of '" + this.d$type.prototype.tp$name + "' objects is not readable"); +}; + +Sk.builtin.getset_descriptor.prototype.tp$descr_set = function (obj, value) { + this.d$set_check(obj); + + if (this.$set !== undefined) { + return this.$set.call(obj, value); + } + throw new Sk.builtin.AttributeError( + "getset_descriptor '" + this.d$name + "' of '" + this.d$type.prototype.tp$name + "' objects is not writeable" + ); +}; + +/** + * @constructor + * @param {typeObject} type_obj + * @param {Object} method + * @extends {descr_object} + */ + +Sk.builtin.method_descriptor = buildDescriptor("method_descriptor", "method", function (typeobj, method_def) { + this.d$def = method_def; + this.$meth = method_def.$meth; //useful for internal fast calls + this.d$type = typeobj; + this.d$name = method_def.$name || ""; + const flags = method_def.$flags || {}; + this.$flags = flags; + if (flags.FastCall && flags.NoKwargs) { + this.tp$call = this.$methodFastCallNoKwargs; + } else if (flags.FastCall) { + this.tp$call = this.$methodFastCall; + } else if (flags.NoArgs) { + this.tp$call = this.$methodCallNoArgs; + } else if (flags.OneArg) { + this.tp$call = this.$methodCallOneArg; + } else if (flags.NamedArgs) { + this.tp$call = this.$methodCallNamedArgs; + } else if (flags.MinArgs !== undefined) { + this.tp$call = this.$methodCallMinArgs; + } else { + // for legacy methods that haven't defined flags yet + this.func_code = method_def.$meth; + this.tp$call = this.$defaultCall; + this.$memoiseFlags = Sk.builtin.func.prototype.$memoiseFlags; + this.$resolveArgs = Sk.builtin.func.prototype.$resolveArgs; + } +}); + +Sk.builtin.method_descriptor.prototype.tp$call = function (args, kwargs) { + return this.tp$call(args, kwargs); +}; +Sk.builtin.method_descriptor.prototype.$methodFastCall = function (args, kwargs) { + const self = args.shift(); + this.m$checkself(self); + return this.$meth.call(self, args, kwargs); +}; +Sk.builtin.method_descriptor.prototype.$methodFastCallNoKwargs = function (args, kwargs) { + const self = args.shift(); + this.m$checkself(self); + Sk.abstr.checkNoKwargs(this.d$name, kwargs); + return this.$meth.call(self, args); +}; +Sk.builtin.method_descriptor.prototype.$methodCallNoArgs = function (args, kwargs) { + const self = args.shift(); + this.m$checkself(self); + Sk.abstr.checkNoArgs(this.d$name, args, kwargs); + return this.$meth.call(self); +}; +Sk.builtin.method_descriptor.prototype.$methodCallOneArg = function (args, kwargs) { + const self = args.shift(); + this.m$checkself(self); + Sk.abstr.checkOneArg(this.d$name, args, kwargs); + return this.$meth.call(self, args[0]); +}; +Sk.builtin.method_descriptor.prototype.$methodCallNamedArgs = function (args, kwargs) { + const self = args.shift(); + this.m$checkself(self); + args = Sk.abstr.copyKeywordsToNamedArgs(this.d$name, this.$flags.NamedArgs, args, kwargs, this.$flags.Defaults); + return this.$meth.call(self, ...args); +}; +Sk.builtin.method_descriptor.prototype.$methodCallMinArgs = function (args, kwargs) { + const self = args.shift(); + this.m$checkself(self); + Sk.abstr.checkNoKwargs(this.d$name, kwargs); + Sk.abstr.checkArgsLen(this.d$name, args, this.$flags.MinArgs, this.$flags.MaxArgs); + return this.$meth.call(self, ...args); +}; +Sk.builtin.method_descriptor.prototype.$defaultCall = function (args, kwargs) { + this.m$checkself(args[0]); + return Sk.builtin.func.prototype.tp$call.call(this, args, kwargs); +}; + +Sk.builtin.method_descriptor.prototype.m$checkself = function (self) { + if (self === undefined) { + throw new Sk.builtin.TypeError("descriptor '" + this.d$name + "' of '" + this.d$type.prototype.tp$name + "' object needs an argument"); + } + this.d$check(self); +}; + +Sk.builtin.method_descriptor.prototype.tp$descr_get = function (obj, type) { + let ret; + if ((ret = this.d$check(obj))) { + return ret; + } + return new Sk.builtin.sk_method(this.d$def, obj); +}; + +Sk.builtin.method_descriptor.prototype.tp$getsets.__text_signature__ = { + $get: function () { + return this.d$def.$textsig ? new Sk.builtin.str(this.d$def.$textsig) : Sk.builtin.none.none$; + }, +}; + +/** + * @constructor + * @extends {descr_object} + * + * @param {typeObject} type_obj + * @param {Object} wrapper_base + * @param {Function} wrapped + */ +Sk.builtin.wrapper_descriptor = buildDescriptor("wrapper_descriptor", "slot wrapper", function wrapper_descriptor(typeobj, slot_def, wrapped) { + this.d$def = slot_def; + this.d$type = typeobj; + this.d$name = wrapped.$name = slot_def.$name; + this.d$wrapped = wrapped; +}); + +Sk.builtin.wrapper_descriptor.prototype.tp$call = function (args, kwargs) { + // make sure the first argument is acceptable as self + if (args.length < 1) { + throw new Sk.builtin.TypeError("descriptor '" + this.d$name + "' of '" + this.d$type.prototype.tp$name + "' object needs an argument"); + } + const self = args.shift(); + if (!self.ob$type.$isSubType(this.d$type)) { + throw new Sk.builtin.TypeError( + "descriptor '" + + this.d$name + + "' requires a '" + + this.d$type.prototype.tp$name + + "' object but received a '" + + Sk.abstr.typeName(self) + + "'" + ); + } + return this.raw$call(self, args, kwargs); +}; + +Sk.builtin.wrapper_descriptor.prototype.raw$call = function (self, args, kwargs) { + // the base might have some flags I guess... + return this.d$def.$wrapper.call(this.d$wrapped, self, args, kwargs); +}; + +Sk.builtin.wrapper_descriptor.prototype.tp$descr_get = function (obj, type) { + let ret; + if ((ret = this.d$check(obj))) { + return ret; + } + return new Sk.builtin.method_wrapper(this, obj); +}; + +/** + * @constructor + * @extends {descr_object} + * @param {Sk.builtin.wrapper_descriptor} type_obj + * @param wrapper_base + */ + +Sk.builtin.method_wrapper = buildDescriptor("method_wrapper", undefined, function method_wrapper(wrapper_descr, self) { + this.m$descr = wrapper_descr; + this.m$self = self; + this.d$def = wrapper_descr.d$def; + this.d$name = wrapper_descr.d$name; + this.d$type = wrapper_descr.d$type; +}); +Sk.builtin.method_wrapper.prototype.tp$call = function (args, kwargs) { + return this.m$descr.raw$call(this.m$self, args, kwargs); +}; + +Sk.builtin.method_wrapper.prototype.$r = function () { + return new Sk.builtin.str(""); +}; + +Sk.builtin.method_wrapper.prototype.tp$getsets.__self__ = { + $get: function () { + return this.m$self; + }, +}; + +/** + * + * @constructor + * @extends {descr_object} + * @param {typeObject} typeobj + * @param {Object} method_def + * + * @description + * This is for classmethods in Native Js Classes, not for "f = classmethod(f)" in Python + * See dict.fromkeys for a native example + * + */ +Sk.builtin.classmethod_descriptor = buildDescriptor("classmethod_descriptor", "method", function classmethod_descriptor(typeobj, method_def) { + this.d$def = method_def; + this.$meth = method_def.$meth; //useful for internal fast calls + this.d$type = typeobj; + this.d$name = method_def.$name || ""; +}); + +Sk.builtin.classmethod_descriptor.prototype.tp$getsets.__text_signature__ = Sk.builtin.method_descriptor.prototype.tp$getsets.__text_signature__; + +Sk.builtin.classmethod_descriptor.prototype.tp$call = function (args, kwargs) { + if (args.length < 1) { + throw new Sk.builtin.TypeError("descriptor '" + this.d$name + "' of '" + this.d$type.prototype.tp$name + "' object needs an argument"); + } + const self = args.shift(); + const bound = this.tp$descr_get(null, self); + return bound.tp$call(args, kwargs); +}; + +/** + * @param {*} obj + * @param {*} type + * @param {boolean=} canSuspend + */ +Sk.builtin.classmethod_descriptor.prototype.tp$descr_get = function (obj, type, canSuspend) { + if (type === undefined) { + if (obj !== null) { + type = type || obj.ob$type; + } else { + throw new Sk.builtin.TypeError( + "descriptor '" + this.d$name + "' for type '" + this.d$type.prototype.tp$name + "' needs an object or a type" + ); + } + } + if (type.ob$type !== Sk.builtin.type) { + throw new Sk.builtin.TypeError( + "descriptor '" + + this.d$name + + "' for type '" + + this.d$type.prototype.tp$name + + "' needs a type not a '" + + Sk.abstr.typeName(type) + + "' as arg 2" + ); + } + + if (!type.$isSubType(this.d$type)) { + throw new Sk.builtin.TypeError( + "descriptor '" + + this.d$name + + "' requires a '" + + this.d$type.prototype.tp$name + + "' object but received a '" + + Sk.abstr.typeName(type) + + "' object" + ); + } + return new Sk.builtin.sk_method(this.d$def, obj); +}; + +// initialize these classes now that they exist do OneTime initialization only takes care of builtinsdict these are in builtins +const _to_initialize = [ + Sk.builtin.method_descriptor, + Sk.builtin.getset_descriptor, + Sk.builtin.wrapper_descriptor, + Sk.builtin.method_wrapper, + Sk.builtin.classmethod_descriptor, +]; + +for (let i = 0; i < _to_initialize.length; i++) { + const cls = _to_initialize[i]; + Sk.abstr.setUpSlots(cls); + Sk.abstr.setUpMethods(cls); + Sk.abstr.setUpGetSets(cls); +} diff --git a/src/dict.js b/src/dict.js index 61a746d255..213667e748 100644 --- a/src/dict.js +++ b/src/dict.js @@ -1,658 +1,710 @@ + +/** @typedef {Sk.builtin.object} */ var pyObject; +/** @typedef {Sk.builtin.type|Function} */ var typeObject; + /** * @constructor - * @param {Array.} L + * @extends {Sk.builtin.object} + * @param {Array=} L A javascript array of key value pairs - All elements should be pyObjects + * + * @description + * call with an array of key value pairs + * Do not use this function to convert a JS object to a dict + * Instead use {@link Sk.ffi.remapToPy} + * + * */ -Sk.builtin.dict = function dict (L) { - var v; - var it, k; - var i; - if (!(this instanceof Sk.builtin.dict)) { - return new Sk.builtin.dict(L); - } - - - if (L === undefined) { - L = []; - } - - this.size = 0; - this.buckets = {}; +Sk.builtin.dict = Sk.abstr.buildNativeClass("dict", { + constructor: function dict(L) { + // calling new Sk.builtin.dict is an internal method that requires an array of key value pairs + if (L === undefined) { + L = []; + } + Sk.asserts.assert(Array.isArray(L) && this instanceof Sk.builtin.dict, "bad call to dict constructor"); - if (Object.prototype.toString.apply(L) === "[object Array]") { - // Handle dictionary literals - for (i = 0; i < L.length; i += 2) { - this.mp$ass_subscript(L[i], L[i + 1]); + this.size = 0; + this.entries = {}; + this.buckets = {}; + for (let i = 0; i < L.length; i += 2) { + this.set$item(L[i], L[i + 1]); } - } else if (L instanceof Sk.builtin.dict) { - // Handle calls of type "dict(mapping)" from Python code - for (it = Sk.abstr.iter(L), k = it.tp$iternext(); - k !== undefined; - k = it.tp$iternext()) { - v = L.mp$subscript(k); - if (v === undefined) { - //print(k, "had undefined v"); - v = null; + }, + slots: /**@lends {Sk.builtin.dict.prototype}*/{ + tp$getattr: Sk.generic.getAttr, + tp$as_sequence_or_mapping: true, + tp$hash: Sk.builtin.none.none$, + tp$doc: + "dict() -> new empty dictionary\ndict(mapping) -> new dictionary initialized from a mapping object's\n (key, value) pairs\ndict(iterable) -> new dictionary initialized as if via:\n d = {}\n for k, v in iterable:\n d[k] = v\ndict(**kwargs) -> new dictionary initialized with the name=value pairs\n in the keyword argument list. For example: dict(one=1, two=2)", + $r: function () { + const ret = []; + if (this.$entered_repr !== undefined) { + // prevents recursively calling repr; + return new Sk.builtin.str("{...}"); } - this.mp$ass_subscript(k, v); - } - } else if (Sk.builtin.checkIterable(L)) { - // Handle calls of type "dict(iterable)" from Python code - for (it = Sk.abstr.iter(L), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - if (i.mp$subscript) { - this.mp$ass_subscript(i.mp$subscript(0), i.mp$subscript(1)); + this.$entered_repr = true; + // iterate over the keys - we don't use the dict iterator or mp$subscript here + const entries = this.entries; + let item, k, v; + for (let key_hash in entries) { + item = entries[key_hash]; + k = item.lhs; + v = item.rhs; + ret.push(Sk.misceval.objectRepr(k) + ": " + Sk.misceval.objectRepr(v)); + } + this.$entered_repr = undefined; + return new Sk.builtin.str("{" + ret.join(", ") + "}"); + }, + tp$new: Sk.generic.new, + tp$init: function (args, kwargs) { + return this.update$common(args, kwargs, "dict"); + }, + tp$iter: function () { + return new Sk.builtin.dict_iter_(this); + }, + tp$richcompare: function (other, op) { + let res; + if (!(other instanceof Sk.builtin.dict)) { + res = Sk.builtin.NotImplemented.NotImplemented$; + } else if (op == "Eq" || op == "NotEq") { + if (other === this) { + res = true; + } else if (this.size !== other.size) { + res = false; + } else { + let item, k, v, otherv; + const entries = this.entries; + for (let key_hash in entries) { + item = entries[key_hash]; + k = item.lhs; + v = item.rhs; + otherv = other.mp$lookup(k); + if (otherv === undefined) { + res = false; + break; + } + if (!Sk.misceval.richCompareBool(v, otherv, "Eq")) { + res = false; + break; + } + } + res = res === undefined; + } + if (op == "NotEq") { + res = !res; + } } else { - throw new Sk.builtin.TypeError("element " + this.size + " is not a sequence"); + res = Sk.builtin.NotImplemented.NotImplemented$; } - } - } else { - throw new Sk.builtin.TypeError("object is not iterable"); - } - - this.__class__ = Sk.builtin.dict; - this.tp$call = undefined; // Not callable, even though constructor is - - return this; -}; - -Sk.builtin.dict.tp$call = function(args, kw) { - var d, i; - Sk.builtin.pyCheckArgsLen("dict", args, 0, 1); - d = new Sk.builtin.dict(args[0]); - if (kw) { - for (i = 0; i < kw.length; i += 2) { - d.mp$ass_subscript(new Sk.builtin.str(kw[i]), kw[i+1]); - } - } - return d; -}; - -Sk.abstr.setUpInheritance("dict", Sk.builtin.dict, Sk.builtin.object); -Sk.abstr.markUnhashable(Sk.builtin.dict); - -var kf = Sk.builtin.hash; + return res; + }, + // sequence or mapping slots + sq$length: function () { + return this.get$size(); + }, + sq$contains: function (ob) { + return this.mp$lookup(ob) !== undefined; + }, + mp$subscript: function (key) { + const res = this.mp$lookup(key); + if (res !== undefined) { + // Found in dictionary + return res; + } else { + // Not found in dictionary + throw new Sk.builtin.KeyError(Sk.misceval.objectRepr(key)); + } + }, + mp$ass_subscript: function (key, value) { + if (value === undefined) { + this.del$item(key); + } else { + this.set$item(key, value); + } + return Sk.builtin.none.none$; + }, + }, + proto: /**@lends {Sk.builtin.dict.prototype}*/{ + get$size: function () { + // can't be overridden by subclasses so we use this for the dict key iterator + return this.size; + }, + get_dict_hash: function get_dict_hash(key) { + if (key.$savedKeyHash_ !== undefined) { + return key.$savedKeyHash_; + } + let key_hash; + if (key.ob$type === Sk.builtin.str) { + key_hash = "_" + key.$jsstr(); + key.$savedKeyHash_ = key_hash; + return key_hash; + } + key_hash = "#_" + Sk.builtin.hash(key).v; + key.$savedKeyHash_ = key_hash; // this is a base key hash + return key_hash; + }, + }, + methods: /**@lends {Sk.builtin.dict.prototype}*/{ + __reversed__: { + $meth: function () { + return new Sk.builtin.dict_reverse_iter_(this); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Return a reverse iterator over the dict keys.", + }, + get: { + $meth: function (k, d) { + if (d === undefined) { + d = Sk.builtin.none.none$; + } + let ret = this.mp$lookup(k); + if (ret === undefined) { + ret = d; + } + return ret; + }, + $flags: {MinArgs: 1, MaxArgs: 2}, + $textsig: "($self, key, default=None, /)", + $doc: "Return the value for key if key is in the dictionary, else default.", + }, + setdefault: { + $meth: function (key, default_) { + const res = this.mp$lookup(key); + if (res !== undefined) { + return res; + } + default_ = default_ || Sk.builtin.none.none$; + this.set$item(key, default_); + return default_; + }, + $flags: {MinArgs: 1, MaxArgs: 2}, + $textsig: "($self, key, default=None, /)", + $doc: + "Insert key with a value of default if key is not in the dictionary.\n\nReturn the value for key if key is in the dictionary, else default.", + }, + pop: { + $meth: function (key, d) { + const hash = this.get_dict_hash(key); + let item, value, s; + if (hash[0] === "_") { + item = this.entries[hash]; + if (item !== undefined) { + value = item.rhs; + delete this.entries[hash]; + } + } else { + item = this.pop$item_from_bucket(key, hash); + if (item !== undefined) { + value = item.rhs; + } + } + if (value !== undefined) { + this.size -= 1; + return value; + } + // Not found in dictionary + if (d !== undefined) { + return d; + } + throw new Sk.builtin.KeyError(Sk.misceval.objectRepr(key)); + }, + $flags: {MinArgs: 1, MaxArgs: 2}, + $textsig: null, + $doc: + "D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\nIf key is not found, d is returned if given, otherwise KeyError is raised", + }, + popitem: { + $meth: function () { + // not particularly efficent but we get allkeys as an array to iter anyway + if (this.get$size() == 0) { + throw new Sk.builtin.KeyError("popitem(): dictionary is empty"); + } + const all_key_hashes = Object.keys(this.entries); + const youngest_key_hash = all_key_hashes[all_key_hashes.length - 1]; + const key = this.entries[youngest_key_hash].lhs; + const val = this.pop.$meth.call(this, key, Sk.builtin.none.none$); + return new Sk.builtin.tuple([key, val]); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "D.popitem() -> (k, v), remove and return some (key, value) pair as a\n2-tuple; but raise KeyError if D is empty.", + }, + keys: { + $meth: function () { + return new Sk.builtin.dict_keys(this); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "D.keys() -> a set-like object providing a view on D's keys", + }, + items: { + $meth: function () { + return new Sk.builtin.dict_items(this); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "D.items() -> a set-like object providing a view on D's items", + }, + values: { + $meth: function () { + return new Sk.builtin.dict_values(this); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "D.values() -> an object providing a view on D's values", + }, + update: { + $meth: function (args, kwargs) { + return this.update$common(args, kwargs, "update"); + }, + $flags: {FastCall: true}, + $textsig: null, + $doc: + "D.update([E, ]**F) -> None. Update D from dict/iterable E and F.\nIf E is present and has a .keys() method, then does: for k in E: D[k] = E[k]\nIf E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v\nIn either case, this is followed by: for k in F: D[k] = F[k]", + }, + clear: { + $meth: function () { + this.size = 0; + this.entries = {}; + this.buckets = {}; + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "D.clear() -> None. Remove all items from D.", + }, + copy: { + $meth: function () { + const newCopy = new Sk.builtin.dict([]); + newCopy.dict$merge(this); + return newCopy; + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "D.copy() -> a shallow copy of D", + }, + }, + classmethods: /**@lends {Sk.builtin.dict.prototype}*/{ + fromkeys: { + $flags: {MinArgs: 1, MaxArgs: 2}, + $textsig: "($type, iterable, value=None, /)", + $meth: function fromkeys(seq, value) { + const keys = Sk.misceval.arrayFromIterable(seq); + const dict = new Sk.builtin.dict([]); + value = value || Sk.builtin.none.none$; + for (let i = 0; i < keys.length; i++) { + dict.set$item(keys[i], value); + } + return dict; + }, + $doc: "Create a new dictionary with keys from iterable and values set to value.", + }, + }, +}); -Sk.builtin.dict.prototype.key$lookup = function (bucket, key) { - var item; - var eq; - var i; +Sk.exportSymbol("Sk.builtin.dict", Sk.builtin.dict); - for (i = 0; i < bucket.items.length; i++) { - item = bucket.items[i]; - eq = Sk.misceval.richCompareBool(item.lhs, key, "Eq"); - if (eq) { - return item; - } +/** + * @private + * @param {Sk.builtin.str} pyName + * @this {Sk.builtin.dict} + */ +Sk.builtin.dict.prototype.quick$lookup = function (pyName) { + /**@type {string} */ + const key_hash = pyName.$savedKeyHash_; + if (key_hash === undefined) { + return; } - - return null; -}; - -Sk.builtin.dict.prototype.key$pop = function (bucket, key) { - var item; - var eq; - var i; - - for (i = 0; i < bucket.items.length; i++) { - item = bucket.items[i]; - eq = Sk.misceval.richCompareBool(item.lhs, key, "Eq"); - if (eq) { - bucket.items.splice(i, 1); - this.size -= 1; - return item; - } + const item = this.entries[key_hash]; + if (item !== undefined) { + return item.rhs; } - return undefined; + return; }; -// Perform dictionary lookup, either return value or undefined if key not in dictionary -Sk.builtin.dict.prototype.mp$lookup = function (key) { - var k = kf(key); - var bucket = this.buckets[k.v]; - var item; - - // todo; does this need to go through mp$ma_lookup - if (bucket !== undefined) { - item = this.key$lookup(bucket, key); - if (item) { - return item.rhs; - } - } - - // Not found in dictionary - return undefined; -}; - -Sk.builtin.dict.prototype.mp$subscript = function (key) { - Sk.builtin.pyCheckArgsLen("[]", arguments.length, 1, 2, false, false); - var s; - var res = this.mp$lookup(key); +/** + * NB: + * We could put the following methods on the proto in the above object literal + * but they're quite long so we keep them below for readability + * @ignore + */ - if (res !== undefined) { - // Found in dictionary - return res; - } else { - // Not found in dictionary - s = new Sk.builtin.str(key); - throw new Sk.builtin.KeyError(s.v); +/** + * + * @function + * @returns {Array} dict keys as an array + * + * @description + * get the keys as an array - used internally for certain methods. + * @private + */ +Sk.builtin.dict.prototype.sk$asarray = function () { + const entries = this.entries; + const keys = []; + for (let hash in entries) { + keys.push(entries[hash].lhs); } + return keys; }; -Sk.builtin.dict.prototype.sq$contains = function (ob) { - var res = this.mp$lookup(ob); - - return (res !== undefined); -}; - -Sk.builtin.dict.prototype.mp$ass_subscript = function (key, w) { - var k = kf(key); - var bucket = this.buckets[k.v]; - var item; - +/** + * @function + * @param {pyObject} key - key to get item for + * @param {string} base_hash - base_hash from the key + * + * @description + * fast call - if we have a str then we can guarantee that it's in the bucket + * so we compare strings quickly rather than reaching out to richcompareBool + * + * @return {pyObject|undefined} the item if found or undefined if not found + * @private + */ +Sk.builtin.dict.prototype.get$item_from_bucket = function (key, base_hash) { + const bucket = this.buckets[base_hash]; + let stored_key, item; if (bucket === undefined) { - // New bucket - bucket = {$hash: k, items: [ - {lhs: key, rhs: w} - ]}; - this.buckets[k.v] = bucket; - this.size += 1; - return; - } - - item = this.key$lookup(bucket, key); - if (item) { - item.rhs = w; return; } - - // Not found in dictionary - bucket.items.push({lhs: key, rhs: w}); - this.size += 1; -}; - -Sk.builtin.dict.prototype.mp$del_subscript = function (key) { - Sk.builtin.pyCheckArgsLen("del", arguments.length, 1, 1, false, false); - var k = kf(key); - var bucket = this.buckets[k.v]; - var item; - var s; - - // todo; does this need to go through mp$ma_lookup - - if (bucket !== undefined) { - item = this.key$pop(bucket, key); - if (item !== undefined) { - return; + for (let i = 0; i < bucket.length; i++) { + item = bucket[i]; + if (item === undefined) { + continue; } - } - - // Not found in dictionary - s = new Sk.builtin.str(key); - throw new Sk.builtin.KeyError(s.v); -}; - -Sk.builtin.dict.prototype["$r"] = function () { - var v; - var iter, k; - var ret = []; - for (iter = Sk.abstr.iter(this), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - v = this.mp$subscript(k); - if (v === undefined) { - //print(k, "had undefined v"); - v = null; - } - - // we need to check if value is same as object - // otherwise it would cause an stack overflow - if(v === this) { - ret.push(Sk.misceval.objectRepr(k).v + ": {...}"); - } else { - ret.push(Sk.misceval.objectRepr(k).v + ": " + Sk.misceval.objectRepr(v).v); + stored_key = item.lhs; + if (stored_key === key || Sk.misceval.richCompareBool(key, stored_key, "Eq")) { + return item; } } - return new Sk.builtin.str("{" + ret.join(", ") + "}"); -}; - -Sk.builtin.dict.prototype.mp$length = function () { - return this.size; + return; }; -Sk.builtin.dict.prototype["get"] = new Sk.builtin.func(function (self, k, d) { - Sk.builtin.pyCheckArgsLen("get()", arguments.length, 1, 2, false, true); - var ret; - - if (d === undefined) { - d = Sk.builtin.none.none$; - } - - ret = self.mp$lookup(k); - if (ret === undefined) { - ret = d; +/** + * @function + * @param {pyObject} key + * @param {string} base_hash + * + * @return undefined if no key was found + * or the item if the key was in the bucket + * also removes the item from entries + * @private + */ +Sk.builtin.dict.prototype.pop$item_from_bucket = function (key, base_hash) { + const bucket = this.buckets[base_hash]; + let stored_key, item; + if (bucket === undefined) { + return undefined; } - - return ret; -}); - -Sk.builtin.dict.prototype["pop"] = new Sk.builtin.func(function (self, key, d) { - Sk.builtin.pyCheckArgsLen("pop()", arguments.length, 1, 2, false, true); - var k = kf(key); - var bucket = self.buckets[k.v]; - var item; - var s; - - // todo; does this need to go through mp$ma_lookup - if (bucket !== undefined) { - item = self.key$pop(bucket, key); - if (item !== undefined) { - return item.rhs; + for (let i = 0; i < bucket.length; i++) { + item = bucket[i]; + if (item === undefined) { + continue; } - } - - // Not found in dictionary - if (d !== undefined) { - return d; - } - - s = new Sk.builtin.str(key); - throw new Sk.builtin.KeyError(s.v); -}); - -Sk.builtin.dict.prototype["has_key"] = new Sk.builtin.func(function (self, k) { - Sk.builtin.pyCheckArgsLen("has_key()", arguments.length, 1, 1, false, true); - return new Sk.builtin.bool( self.sq$contains(k)); -}); - -Sk.builtin.dict.prototype["items"] = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("items()", arguments.length, 0, 0, false, true); - var v; - var iter, k; - var ret = []; - - for (iter = Sk.abstr.iter(self), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - v = self.mp$subscript(k); - if (v === undefined) { - //print(k, "had undefined v"); - v = null; + stored_key = item.lhs; + if (stored_key === key || Sk.misceval.richCompareBool(key, stored_key, "Eq")) { + const key_hash = "#" + i + base_hash.slice(1); + delete this.entries[key_hash]; + bucket[i] = undefined; + return item; } - ret.push(new Sk.builtin.tuple([k, v])); } - return new Sk.builtin.list(ret); -}); - -Sk.builtin.dict.prototype["keys"] = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("keys()", arguments.length, 0, 0, false, true); - var iter, k; - var ret = []; - - for (iter = Sk.abstr.iter(self), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - ret.push(k); - } - return new Sk.builtin.list(ret); -}); - -Sk.builtin.dict.prototype["values"] = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("values()", arguments.length, 0, 0, false, true); - var v; - var iter, k; - var ret = []; + return; +}; - for (iter = Sk.abstr.iter(self), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - v = self.mp$subscript(k); - if (v === undefined) { - v = null; +/** + * @function + * @param {Sk.builtin.object} key + * @param {Sk.builtin.object} value + * @param {string} base_hash + * + * @description + * given a key and a base_hash will find a free slot or append to the list of slots for a given base_hash + * then will set the item in the entries and return the item + * Note this should only be called and immediately preceded by assigning the value to the rhs + * + * @return {{lhs: Sk.builtin.object, rhs: Sk.builtin.object}} + * @private + */ +Sk.builtin.dict.prototype.insert$item_from_bucket = function (key, value, base_hash) { + let key_hash, + bucket = this.buckets[base_hash]; + const item = {lhs: key, rhs: value}; + if (bucket === undefined) { + bucket = this.buckets[base_hash] = []; + key_hash = "#" + 0 + base_hash.slice(1); + bucket.push(item); + } else { + // we might have a freeslot from deleting an item + const free_slot = bucket.indexOf(undefined); + if (free_slot !== -1) { + key_hash = "#" + free_slot + base_hash.slice(1); + bucket[free_slot] = item; + } else { + key_hash = "#" + bucket.length + base_hash.slice(1); + bucket.push(item); } - ret.push(v); } - return new Sk.builtin.list(ret); -}); - -Sk.builtin.dict.prototype["clear"] = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("clear()", arguments.length, 0, 0, false, true); - var k; - var iter; + this.entries[key_hash] = item; + return item; +}; - for (iter = Sk.abstr.iter(self), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - self.mp$del_subscript(k); +/** + * @function + * @param {Sk.builtin.object} key - want to check if the key is inside the dict + * + * @return undefined if no key was found + * or the item.rhs (value) if the key was found + * @private + */ +Sk.builtin.dict.prototype.mp$lookup = function (key) { + let item; + const hash = this.get_dict_hash(key); + if (hash[0] === "_") { + item = this.entries[hash]; + } else { + // then we have a base hash so this is non string; + item = this.get$item_from_bucket(key, hash); } -}); - -Sk.builtin.dict.prototype["setdefault"] = new Sk.builtin.func(function (self, key, default_) { - try { - return self.mp$subscript(key); - } catch (e) { - if (default_ === undefined) { - default_ = Sk.builtin.none.none$; - } - self.mp$ass_subscript(key, default_); - return default_; + if (item !== undefined) { + return item.rhs; } -}); + // Not found in dictionary + return undefined; +}; -/* - this function mimics the cpython implementation, which is also the reason for the - almost similar code, this may be changed in future -*/ -Sk.builtin.dict.prototype.dict_merge = function(b) { - var iter; - var k, v; - if(b instanceof Sk.builtin.dict) { - // fast way - for (iter = b.tp$iter(), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { - v = b.mp$subscript(k); - if (v === undefined) { - throw new Sk.builtin.AttributeError("cannot get item for key: " + k.v); - } - this.mp$ass_subscript(k, v); +/** + * @function + * + * @param {Sk.builtin.dict} b or dictlike object (anything with a keys method) + * + * @description + * this function mimics the cpython implementation, which is also the reason for the + * almost similar code, this may be changed in future + * + * Note we don't use mp$ass_subscript since that slot might be overridden by a subclass + * Instead we use this.set$item which is the dict implementation of mp$ass_subscript + * @private + */ +Sk.builtin.dict.prototype.dict$merge = function (b) { + // we don't use mp$ass_subscript incase a subclass overrides __setitem__ we just ignore that like Cpython does + // so use this.set$item instead which can't be overridden by a subclass + let k, v, item; + if (b.tp$iter === Sk.builtin.dict.prototype.tp$iter) { + // fast way used + const entries = b.entries; + for (let key_hash in entries) { + item = entries[key_hash]; + k = item.lhs; + v = item.rhs; + this.set$item(k, v); } + return; } else { - // generic slower way - var keys = Sk.misceval.callsimArray(b["keys"], [b]); - for (iter = Sk.abstr.iter(keys), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { - v = b.tp$getitem(k); // get value + // generic slower way for a subclass that has overriden the tp$iter method + // we'll just assume prototypical inheritance here! and sort of support suspensions + const keys = Sk.misceval.callsimArray(b.keys, [b]); + const self = this; + return Sk.misceval.iterFor(Sk.abstr.iter(keys), (key) => { + v = b.mp$subscript(key); // get value (no suspension for keylookup... todo?) if (v === undefined) { - throw new Sk.builtin.AttributeError("cannot get item for key: " + k.v); + throw new Sk.builtin.AttributeError("cannot get item for key: " + Sk.misceval.objectRepr(key)); } - this.mp$ass_subscript(k, v); - } + self.set$item(key, v); + }); } }; /** + * @function + * + * @param {Array} args + * @param {Array} kwargs + * @param {string} func_name for error messages + * + * @description + * * update() accepts either another dictionary object or an iterable of key/value pairs (as tuples or other iterables of length two). * If keyword arguments are specified, the dictionary is then updated with those key/value pairs: d.update(red=1, blue=2). * https://hg.python.org/cpython/file/4ff865976bb9/Objects/dictobject.c + * + * this function is called by both __init__ and update + * We check that there is only 1 arg + * + * if arg is a dict like object we call dict$merge (must have a keys attribute) + * otherwise call dict$merge_from_seq + * + * finally put the kwargs in the dict. + * @private + * */ -var update_f = function (kwargs, self, other) { - // case another dict or obj with keys and getitem has been provided - if(other !== undefined && (other.tp$name === "dict" || other["keys"])) { - self.dict_merge(other); // we merge with override - } else if(other !== undefined && Sk.builtin.checkIterable(other)) { - // 2nd case, we expect an iterable that contains another iterable of length 2 - var iter; - var k, v; - var seq_i = 0; // index of current sequence item - for (iter = Sk.abstr.iter(other), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext(), seq_i++) { - // check if value is iter - if (!Sk.builtin.checkIterable(k)) { - throw new Sk.builtin.TypeError("cannot convert dictionary update sequence element #" + seq_i + " to a sequence"); +Sk.builtin.dict.prototype.update$common = function (args, kwargs, func_name) { + Sk.abstr.checkArgsLen(func_name, args, 0, 1); + const arg = args[0]; + const self = this; + let ret; + if (arg !== undefined) { + if (arg instanceof Sk.builtin.dict) { + ret = this.dict$merge(arg); + } else if (Sk.abstr.lookupSpecial(arg, new Sk.builtin.str("keys")) !== undefined) { + ret = this.dict$merge(arg); + } else { + ret = this.dict$merge_from_seq(arg); + } + } + return Sk.misceval.chain(ret, () => { + if (kwargs) { + for (let i = 0; i < kwargs.length; i += 2) { + self.set$item(new Sk.builtin.str(kwargs[i]), kwargs[i + 1]); } + } + return Sk.builtin.none.none$; + }); +}; - // cpython impl. would transform iterable into sequence - // we just call iternext twice if k has length of 2 - if(k.sq$length() === 2) { - var k_iter = Sk.abstr.iter(k); - var k_key = k_iter.tp$iternext(); - var k_value = k_iter.tp$iternext(); - self.mp$ass_subscript(k_key, k_value); +/** + * @function + * + * @param {pyObject} arg + * + * @description + * iterate over a sequence like object + * check the next value has length 2 + * and then set the key value pair in + * @private + * + */ +Sk.builtin.dict.prototype.dict$merge_from_seq = function (arg) { + let idx = 0; + const self = this; + return Sk.misceval.iterFor(Sk.abstr.iter(arg), (i) => { + try { + // this should really just be a tuple/list of length 2 so no suspension to get the sequence + const seq = Sk.misceval.arrayFromIterable(i); + if (seq.length !== 2) { + throw new Sk.builtin.ValueError("dictionary update sequence element #" + idx + " has length " + seq.length + "; 2 is required"); + } + self.set$item(seq[0], seq[1]); + } catch (e) { + if (e instanceof Sk.builtin.TypeError) { + throw new Sk.builtin.TypeError("cannot convert dictionary update sequence element #" + idx + " to a sequence"); } else { - // throw exception - throw new Sk.builtin.ValueError("dictionary update sequence element #" + seq_i + " has length " + k.sq$length() + "; 2 is required"); + throw e; } } - } else if(other !== undefined) { - // other is not a dict or iterable - throw new Sk.builtin.TypeError("'" +Sk.abstr.typeName(other) + "' object is not iterable"); - } - - // apply all key/value pairs of kwargs - // create here kwargs_dict, there could be exceptions in other cases before - var kwargs_dict = new Sk.builtins.dict(kwargs); - self.dict_merge(kwargs_dict); - - // returns none, when successful or throws exception - return Sk.builtin.none.none$; + idx++; + }); }; -update_f.co_kwargs = true; -Sk.builtin.dict.prototype.update = new Sk.builtin.func(update_f); - -Sk.builtin.dict.prototype.__contains__ = new Sk.builtin.func(function (self, item) { - Sk.builtin.pyCheckArgsLen("__contains__", arguments.length, 2, 2); - return new Sk.builtin.bool(self.sq$contains(item)); -}); - -Sk.builtin.dict.prototype.__cmp__ = new Sk.builtin.func(function (self, other, op) { - // __cmp__ cannot be supported until dict lt/le/gt/ge operations are supported - return Sk.builtin.NotImplemented.NotImplemented$; -}); - -Sk.builtin.dict.prototype.__delitem__ = new Sk.builtin.func(function (self, item) { - Sk.builtin.pyCheckArgsLen("__delitem__", arguments.length, 1, 1, false, true); - return Sk.builtin.dict.prototype.mp$del_subscript.call(self, item); -}); - -Sk.builtin.dict.prototype.__getitem__ = new Sk.builtin.func(function (self, item) { - Sk.builtin.pyCheckArgsLen("__getitem__", arguments.length, 1, 1, false, true); - return Sk.builtin.dict.prototype.mp$subscript.call(self, item); -}); - -Sk.builtin.dict.prototype.__setitem__ = new Sk.builtin.func(function (self, item, value) { - Sk.builtin.pyCheckArgsLen("__setitem__", arguments.length, 2, 2, false, true); - return Sk.builtin.dict.prototype.mp$ass_subscript.call(self, item, value); -}); - -Sk.builtin.dict.prototype.__hash__ = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("__hash__", arguments.length, 0, 0, false, true); - return Sk.builtin.dict.prototype.tp$hash.call(self); -}); - -Sk.builtin.dict.prototype.__len__ = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("__len__", arguments.length, 0, 0, false, true); - return Sk.builtin.dict.prototype.mp$length.call(self); -}); - -Sk.builtin.dict.prototype.__getattribute__ = new Sk.builtin.func(function (self, attr) { - Sk.builtin.pyCheckArgsLen("__getattribute__", arguments.length, 1, 1, false, true); - if (!Sk.builtin.checkString(attr)) { throw new Sk.builtin.TypeError("__getattribute__ requires a string"); } - return Sk.builtin.dict.prototype.tp$getattr.call(self, attr); -}); - -Sk.builtin.dict.prototype.__iter__ = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("__iter__", arguments.length, 0, 0, false, true); - - return new Sk.builtin.dict_iter_(self); -}); - -Sk.builtin.dict.prototype.tp$iter = function () { - return new Sk.builtin.dict_iter_(this); -}; - -Sk.builtin.dict.prototype.__repr__ = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("__repr__", arguments.length, 0, 0, false, true); - return Sk.builtin.dict.prototype["$r"].call(self); -}); - -/* python3 recommends implementing simple ops */ -Sk.builtin.dict.prototype.ob$eq = function (other) { - - var iter, k, v, otherv; - - if (this === other) { - return Sk.builtin.bool.true$; - } - - if (!(other instanceof Sk.builtin.dict)) { - return Sk.builtin.NotImplemented.NotImplemented$; - } - - if (this.size !== other.size) { - return Sk.builtin.bool.false$; - } - - for (iter = this.tp$iter(), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - v = this.mp$subscript(k); - otherv = other.mp$subscript(k); - - if (!Sk.misceval.richCompareBool(v, otherv, "Eq")) { - return Sk.builtin.bool.false$; +/** + * @function + * + * @param {pyObject} key should be a python object + * @param {pyObject} value + * + * @description + * sets the item from a key, value + * @private + * + */ +Sk.builtin.dict.prototype.set$item = function (key, value) { + const hash = this.get_dict_hash(key); + let item; + if (hash[0] === "_") { + // we have a string so pass it to the dictionary + item = this.entries[hash]; + if (item === undefined) { + this.size += 1; + item = this.entries[hash] = {lhs: key, rhs: undefined}; } + item.rhs = value; + return; } - - return Sk.builtin.bool.true$; -}; - -Sk.builtin.dict.prototype.ob$ne = function (other) { - - var isEqual = this.ob$eq(other); - - if (isEqual instanceof Sk.builtin.NotImplemented) { - return isEqual; - } else if (isEqual.v) { - return Sk.builtin.bool.false$; + item = this.get$item_from_bucket(key, hash); + if (item === undefined) { + item = this.insert$item_from_bucket(key, value, hash); + this.size += 1; } else { - return Sk.builtin.bool.true$; + item.rhs = value; } - + return; }; -Sk.builtin.dict.prototype["copy"] = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("copy", arguments.length, 0, 0, false, true); - - var it; // Iterator - var k; // Key of dict item - var v; // Value of dict item - var newCopy = new Sk.builtin.dict([]); - - for (it = Sk.abstr.iter(self), k = it.tp$iternext(); - k !== undefined; - k = it.tp$iternext()) { - v = self.mp$subscript(k); - if (v === undefined) { - v = null; - } - newCopy.mp$ass_subscript(k, v); - } - - return newCopy; -}); - -Sk.builtin.dict.$fromkeys = function fromkeys(self, seq, value) { - var k, iter, val, res, iterable; - - if (self instanceof Sk.builtin.dict) { - // instance call - Sk.builtin.pyCheckArgsLen("fromkeys", arguments.length, 1, 2, false, true); - - res = self; - iterable = seq; - val = value === undefined ? Sk.builtin.none.none$ : value; +/** + * @function + * + * @param {Sk.builtin.object} key + * + * @description + * deletes an item in the dictionary + * @private + * + */ +Sk.builtin.dict.prototype.del$item = function (key) { + const hash = this.get_dict_hash(key); + let item; + if (hash[0] === "_") { + item = this.entries[hash]; + delete this.entries[hash]; } else { - // static call - Sk.builtin.pyCheckArgsLen("fromkeys", arguments.length, 1, 2, false, false); - - res = new Sk.builtin.dict([]); - iterable = self; - val = seq === undefined ? Sk.builtin.none.none$ : seq; - } - - if (!Sk.builtin.checkIterable(iterable)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(iterable) + "' object is not iterable"); + item = this.pop$item_from_bucket(key, hash); } - - for (iter = Sk.abstr.iter(iterable), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - res.mp$ass_subscript(k, val); + if (item !== undefined) { + this.size -= 1; + return; } - - return res; + // Not found in dictionary + throw new Sk.builtin.KeyError(Sk.misceval.objectRepr(key)); }; - -Sk.builtin.dict.prototype["iteritems"] = new Sk.builtin.func(function (self) { - throw new Sk.builtin.NotImplementedError("dict.iteritems is not yet implemented in Skulpt"); -}); - -Sk.builtin.dict.prototype["iterkeys"] = new Sk.builtin.func(function (self) { - throw new Sk.builtin.NotImplementedError("dict.iterkeys is not yet implemented in Skulpt"); -}); - -Sk.builtin.dict.prototype["itervalues"] = new Sk.builtin.func(function (self) { - throw new Sk.builtin.NotImplementedError("dict.itervalues is not yet implemented in Skulpt"); -}); - -Sk.builtin.dict.prototype["popitem"] = new Sk.builtin.func(function (self) { - throw new Sk.builtin.NotImplementedError("dict.popitem is not yet implemented in Skulpt"); -}); - -Sk.builtin.dict.prototype["viewitems"] = new Sk.builtin.func(function (self) { - throw new Sk.builtin.NotImplementedError("dict.viewitems is not yet implemented in Skulpt"); -}); - -Sk.builtin.dict.prototype["viewkeys"] = new Sk.builtin.func(function (self) { - throw new Sk.builtin.NotImplementedError("dict.viewkeys is not yet implemented in Skulpt"); -}); - -Sk.builtin.dict.prototype["viewvalues"] = new Sk.builtin.func(function (self) { - throw new Sk.builtin.NotImplementedError("dict.viewvalues is not yet implemented in Skulpt"); -}); - -Sk.exportSymbol("Sk.builtin.dict", Sk.builtin.dict); - /** - * @constructor - * @param {Object} obj + * Py2 methods + * @private */ -Sk.builtin.dict_iter_ = function (obj) { - var k, i, bucket, allkeys, buckets; - if (!(this instanceof Sk.builtin.dict_iter_)) { - return new Sk.builtin.dict_iter_(obj); - } - this.$index = 0; - this.$obj = obj; - allkeys = []; - buckets = obj.buckets; - for (k in buckets) { - if (buckets.hasOwnProperty(k)) { - bucket = buckets[k]; - if (bucket && bucket.$hash !== undefined && bucket.items !== undefined) { - // skip internal stuff. todo; merge pyobj and this - for (i = 0; i < bucket.items.length; i++) { - allkeys.push(bucket.items[i].lhs); - } +Sk.builtin.dict.py2$methods = { + has_key: { + $name: "has_key", + $flags: {OneArg: true}, + $meth: function (k) { + return new Sk.builtin.bool(this.sq$contains(k)); + }, + $doc: "D.has_key(k) -> True if D has a key k, else False", + }, + keys: { + $name: "keys", + $meth: function () { + return new Sk.builtin.list(this.sk$asarray()); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "D.keys() -> a set-like object providing a view on D's keys", + }, + items: { + $name: "items", + $meth: function () { + const L = []; + const entries = this.entries; + let item; + for (let key_hash in entries) { + item = entries[key_hash]; + L.push(new Sk.builtin.tuple([item.lhs, item.rhs])); } - } - } - this.$keys = allkeys; - this.tp$iter = this; - this.tp$iternext = function () { - // todo; StopIteration - if (this.$index >= this.$keys.length) { - return undefined; - } - return this.$keys[this.$index++]; - // return this.$obj[this.$keys[this.$index++]].lhs; - }; - this.$r = function () { - return new Sk.builtin.str("dictionary-keyiterator"); - }; - return this; -}; - -Sk.abstr.setUpInheritance("dictionary-keyiterator", Sk.builtin.dict_iter_, Sk.builtin.object); - -Sk.builtin.dict_iter_.prototype.__class__ = Sk.builtin.dict_iter_; - -Sk.builtin.dict_iter_.prototype.__iter__ = new Sk.builtin.func(function (self) { - return self; -}); - -Sk.builtin.dict_iter_.prototype.next$ = function (self) { - var ret = self.tp$iternext(); - if (ret === undefined) { - throw new Sk.builtin.StopIteration(); - } - return ret; + return new Sk.builtin.list(L); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "D.items() -> a set-like object providing a view on D's items", + }, + values: { + $name: "values", + $meth: function () { + const L = []; + const entries = this.entries; + for (let key_hash in entries) { + L.push(entries[key_hash].rhs); + } + return new Sk.builtin.list(L); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "D.values() -> an object providing a view on D's values", + }, }; diff --git a/src/dictviews.js b/src/dictviews.js new file mode 100644 index 0000000000..29ece8a176 --- /dev/null +++ b/src/dictviews.js @@ -0,0 +1,203 @@ +const dict$views = { + KEYS: "dict_keys", + VALUES: "dict_values", + ITEMS: "dict_items", +}; + +function buildDictView(typename) { + const dict_view_options = {}; + dict_view_options.constructor = function (dict) { + this.dict = dict; + }; + dict_view_options.slots = { + tp$getattr: Sk.generic.getAttr, + tp$as_number: true, + tp$as_sequence_or_mapping: true, + tp$hash: Sk.builtin.none.none$, + $r: function () { + if (this.$entered_repr !== undefined) { + // prevent recursively calling oneself + return new Sk.builtin.str("..."); + } + this.$entered_repr = true; + const L = Sk.misceval.arrayFromIterable(this); + const res = Sk.misceval.objectRepr(new Sk.builtin.list(L)); + this.$entered_repr = undefined; + return new Sk.builtin.str(Sk.abstr.typeName(this) + "(" + res + ")"); + }, + tp$richcompare: function () { + return Sk.builtin.NotImplemented.NotImplemented$; + }, + tp$iter: function () { + if (this.tp$name === dict$views.KEYS) { + return new Sk.builtin.dict_iter_(this.dict); + } else if (this.tp$name === dict$views.VALUES) { + return new Sk.builtin.dict_valueiter_(this.dict); + } else if (this.tp$name === dict$views.ITEMS) { + return new Sk.builtin.dict_itemiter_(this.dict); + } + }, + nb$subtract: function () { + // TODO + return Sk.builtin.NotImplemented.NotImplemented$; + }, + nb$and: function () { + return Sk.builtin.NotImplemented.NotImplemented$; + }, + nb$or: function () { + return Sk.builtin.NotImplemented.NotImplemented$; + }, + nb$xor: function () { + return Sk.builtin.NotImplemented.NotImplemented$; + }, + + sq$length: function () { + return this.dict.get$size(); + }, + sq$contains: function (item) { + var iter, key, value, pair; + if (this.tp$name === dict$views.KEYS) { + return this.dict.mp$lookup(item) !== undefined; + } else if (this.tp$name === dict$views.VALUES) { + for (iter = Sk.abstr.iter(this.dict), key = iter.tp$iternext(); key !== undefined; key = iter.tp$iternext()) { + value = this.dict.mp$subscript(key); + if (value === undefined) { + value = null; + } + if (Sk.misceval.isTrue(Sk.misceval.richCompareBool(value, item, "Eq"))) { + return true; + } + } + return false; + } else if (this.tp$name === dict$views.ITEMS) { + if (item.mp$subscript && item.sq$length && item.sq$length() === 2) { + key = item.mp$subscript(new Sk.builtin.int_(0)); + value = this.dict.mp$lookup(key); + if (value !== undefined) { + pair = new Sk.builtin.tuple([key, value]); + if (Sk.misceval.isTrue(Sk.misceval.richCompareBool(pair, item, "Eq"))) { + return true; + } + } + } + return false; + } + }, + }; + dict_view_options.methods = { + isdisjoint: { + $meth: function () { + return Sk.builtin.NotImplemented.NotImplemented$; + }, + $flags: {}, + $textsig: null, + $doc: "Return True if the view and the given iterable have a null intersection.", + }, + __reversed__: { + $meth: function () { + if (this.tp$name === dict$views.KEYS) { + return new Sk.builtin.dict_reverse_iter_(this.dict); + } else if (this.tp$name === dict$views.ITEMS) { + return new Sk.builtin.dict_reverse_itemiter_(this.dict); + } else if (this.tp$name === dict$views.VALUES) { + return new Sk.builtin.dict_reverse_valueiter_(this.dict); + } + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Return a reverse iterator over the dict keys.", + }, + }; + dict_view_options.flags = { + sk$acceptable_as_base: false, + }; + + return Sk.abstr.buildNativeClass(typename, dict_view_options); +} + +Sk.builtin.dict_keys = buildDictView("dict_keys"); +Sk.builtin.dict_values = buildDictView("dict_values"); +Sk.builtin.dict_items = buildDictView("dict_items"); + +function dict_iter_constructor(dict) { + this.$index = 0; + this.$seq = dict.sk$asarray(); + this.$orig = dict; +} + +/** + * @param {string} typename + * @param {Function} iternext + * @param {Function=} constructor + */ +function buildDictIterClass(typename, iternext, constructor) { + return Sk.abstr.buildIteratorClass(typename, { + constructor: + constructor || + function (dict) { + dict_iter_constructor.call(this, dict); + }, + iternext: iternext, + methods: { + __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef, + }, + flags: {sk$acceptable_as_base_class: false}, + }); +} + +/** + * @constructor + * @param {Sk.builtin.dict} dict + */ +Sk.builtin.dict_iter_ = buildDictIterClass("dict_keyiterator", Sk.generic.iterNextWithArrayCheckSize); + +function dict_iter_get_value_or_throw() { + const key = Sk.generic.iterNextWithArrayCheckSize.call(this); + if (key === undefined) { + return key; + } + const res = this.$orig.mp$lookup(key); + if (res !== undefined) { + return res; + } + // some what of a hack since we don't dynamically get keys unlike Python + throw new Sk.builtin.RuntimeError(Sk.misceval.objectRepr(key) + " removed during iteration"); +} + +/** + * @constructor + * @param {Sk.builtin.dict} dict + */ +Sk.builtin.dict_valueiter_ = buildDictIterClass("dict_valueiterator", function () { + return dict_iter_get_value_or_throw.call(this); +}); + +/** + * @constructor + * @param {Sk.builtin.dict} dict + */ +Sk.builtin.dict_itemiter_ = buildDictIterClass("dict_itemiterator", function __next__() { + const idx = this.$index; + const val = dict_iter_get_value_or_throw.call(this); + if (val === undefined) { + return val; + } + return new Sk.builtin.tuple([this.$seq[idx], val]); +}); + +function dict_reverse_iter_constructor(dict) { + dict_iter_constructor.call(this, dict); + this.$seq.reverse(); +} + +Sk.builtin.dict_reverse_iter_ = buildDictIterClass("dict_reversekeyiterator", Sk.generic.iterNextWithArrayCheckSize, function (dict) { + dict_reverse_iter_constructor.call(this, dict); +}); + +Sk.builtin.dict_reverse_itemiter_ = buildDictIterClass("dict_reverseitemiterator", Sk.builtin.dict_itemiter_.prototype.tp$iternext, function (dict) { + dict_reverse_iter_constructor.call(this, dict); +}); + +Sk.builtin.dict_reverse_valueiter_ = buildDictIterClass("dict_reversevalueiterator", Sk.builtin.dict_valueiter_.prototype.tp$iternext, function (dict) { + dict_reverse_iter_constructor.call(this, dict); +}); diff --git a/src/env.js b/src/env.js index f2ea21f60d..763d32952b 100644 --- a/src/env.js +++ b/src/env.js @@ -21,12 +21,17 @@ * Any variables that aren't set will be left alone. */ -Sk.bool_check = function(variable, name) { +Sk.bool_check = function (variable, name) { if (variable === undefined || variable === null || typeof variable !== "boolean") { throw new Error("must specify " + name + " and it must be a boolean"); } }; +/** + * Please use python3 flag to control new behavior that is different + * between Python 2/3, rather than adding new flags. + */ + Sk.python2 = { print_function: false, division: false, @@ -34,7 +39,6 @@ Sk.python2 = { unicode_literals: false, // skulpt specific python3: false, - set_repr: false, class_repr: false, inherit_from_object: false, super_args: false, @@ -42,13 +46,11 @@ Sk.python2 = { bankers_rounding: false, python_version: false, dunder_next: false, - dunder_round: false, - list_clear: false, + dunder_round: false, exceptions: false, no_long_type: false, ceil_floor_int: false, - l_suffix: true, - silent_octal_literal: true + silent_octal_literal: true, }; Sk.python3 = { @@ -58,7 +60,6 @@ Sk.python3 = { unicode_literals: true, // skulpt specific python3: true, - set_repr: true, class_repr: true, inherit_from_object: true, super_args: true, @@ -67,12 +68,10 @@ Sk.python3 = { python_version: true, dunder_next: true, dunder_round: true, - list_clear: true, exceptions: true, no_long_type: true, ceil_floor_int: true, - l_suffix: false, - silent_octal_literal: false + silent_octal_literal: false, }; Sk.configure = function (options) { @@ -105,12 +104,11 @@ Sk.configure = function (options) { Sk.sysargv = options["sysargv"] || Sk.sysargv; Sk.asserts.assert(Sk.isArrayLike(Sk.sysargv)); - Sk.__future__ = options["__future__"] || Sk.python2; + Sk.__future__ = options["__future__"] || Sk.python3; Sk.bool_check(Sk.__future__.print_function, "Sk.__future__.print_function"); Sk.bool_check(Sk.__future__.division, "Sk.__future__.division"); Sk.bool_check(Sk.__future__.unicode_literals, "Sk.__future__.unicode_literals"); - Sk.bool_check(Sk.__future__.set_repr, "Sk.__future__.set_repr"); Sk.bool_check(Sk.__future__.class_repr, "Sk.__future__.class_repr"); Sk.bool_check(Sk.__future__.inherit_from_object, "Sk.__future__.inherit_from_object"); Sk.bool_check(Sk.__future__.super_args, "Sk.__future__.super_args"); @@ -119,12 +117,10 @@ Sk.configure = function (options) { Sk.bool_check(Sk.__future__.python_version, "Sk.__future__.python_version"); Sk.bool_check(Sk.__future__.dunder_next, "Sk.__future__.dunder_next"); Sk.bool_check(Sk.__future__.dunder_round, "Sk.__future__.dunder_round"); - Sk.bool_check(Sk.__future__.list_clear, "Sk.__future__.list_clear"); Sk.bool_check(Sk.__future__.exceptions, "Sk.__future__.exceptions"); Sk.bool_check(Sk.__future__.no_long_type, "Sk.__future__.no_long_type"); Sk.bool_check(Sk.__future__.ceil_floor_int, "Sk.__future__.ceil_floor_int"); Sk.bool_check(Sk.__future__.silent_octal_literal, "Sk.__future__.silent_octal_literal"); - Sk.bool_check(Sk.__future__.l_suffix, "Sk.__future__.l_suffix"); // in __future__ add checks for absolute_import @@ -166,22 +162,30 @@ Sk.configure = function (options) { for (var i = 0; i < Sk.signals.listeners.length; i++) { Sk.signals.listeners[i].call(null, signal, data); } - } + }, }; } else { Sk.signals = null; } Sk.asserts.assert(typeof Sk.signals === "object"); - Sk.breakpoints = options["breakpoints"] || function() { return true; }; + Sk.breakpoints = + options["breakpoints"] || + function () { + return true; + }; Sk.asserts.assert(typeof Sk.breakpoints === "function"); Sk.setTimeout = options["setTimeout"]; if (Sk.setTimeout === undefined) { if (typeof setTimeout === "function") { - Sk.setTimeout = function(func, delay) { setTimeout(func, delay); }; + Sk.setTimeout = function (func, delay) { + setTimeout(func, delay); + }; } else { - Sk.setTimeout = function(func, delay) { func(); }; + Sk.setTimeout = function (func, delay) { + func(); + }; } } Sk.asserts.assert(typeof Sk.setTimeout === "function"); @@ -205,30 +209,27 @@ Sk.configure = function (options) { Sk.misceval.softspace_ = false; - Sk.switch_version("round$", Sk.__future__.dunder_round); - Sk.switch_version("next$", Sk.__future__.dunder_next); - Sk.switch_version("clear$", Sk.__future__.list_clear); - - Sk.builtin.lng.tp$name = Sk.__future__.no_long_type ? "int" : "long"; + Sk.switch_version(Sk.__future__.python3); Sk.setupOperators(Sk.__future__.python3); Sk.setupDunderMethods(Sk.__future__.python3); + Sk.setupObjects(Sk.__future__.python3); }; Sk.exportSymbol("Sk.configure", Sk.configure); /* -* Replaceable handler for uncaught exceptions -*/ -Sk.uncaughtException = function(err) { + * Replaceable handler for uncaught exceptions + */ +Sk.uncaughtException = function (err) { throw err; }; /* * Replaceable handler for uncaught exceptions */ -Sk.uncaughtException = function(err) { +Sk.uncaughtException = function (err) { throw err; }; Sk.exportSymbol("Sk.uncaughtException", Sk.uncaughtException); @@ -257,19 +258,15 @@ Sk.yieldLimit = Number.POSITIVE_INFINITY; Sk.output = function (x) { }; -/* By default, you can put any modules you like into this object. */ -Sk.builtinFiles = {}; -Sk.exportSymbol("Sk.builtinFiles", Sk.builtinFiles); - /* - * Replaceable function to load modules with (called via import, etc.). - * May return a suspension. + * Replacable function to load modules with (called via import, etc.) + * todo; this should be an async api */ Sk.read = function (x) { if (Sk.builtinFiles === undefined || Sk.builtinFiles["files"][x] === undefined) { throw "File not found: '" + x + "'"; } - + return Sk.builtinFiles["files"][x]; }; @@ -284,7 +281,6 @@ Sk.getSysArgv = function () { }; Sk.exportSymbol("Sk.getSysArgv", Sk.getSysArgv); - /** * Setable to emulate PYTHONPATH environment variable (for finding modules). * Should be an array of JS strings. @@ -293,7 +289,7 @@ Sk.syspath = []; Sk.inBrowser = Sk.global["document"] !== undefined; -Sk.afterSingleExecution = function(args) { +Sk.afterSingleExecution = function (args) { }; Sk.exportSymbol("Sk.afterSingleExecution", Sk.afterSingleExecution); @@ -322,78 +318,77 @@ Sk.debugout = function (args) { } else if (Sk.global["print"] !== undefined) { Sk.debugout = Sk.global["print"]; } -}()); +})(); Sk.inputfun = function (args) { return window.prompt(args); }; -// Information about method names and their internal functions for -// methods that differ (in visibility or name) between Python 2 and 3. -// -// Format: -// internal function: { -// "classes" : , -// 2 : or null if none -// 3 : or null if none -// }, -// ... - +/** + * currently can't seem to remove these functions without a serious slow down of 2x + */ Sk.setup_method_mappings = function () { - return { - "round$": { - "classes": [Sk.builtin.float_, - Sk.builtin.int_], - 2: null, - 3: "__round__" +}; +Sk.setupDictIterators = function (python3) { +}; + +Sk.switch_version = function (py3) { + const methods_to_map = { + float_: { + method_names: ["__round__"], + 2: [false], + 3: [true], }, - "clear$": { - "classes": [Sk.builtin.list], - 2: null, - 3: "clear" + int_: { + method_names: ["__round__"], + 2: [false], + 3: [true], + }, + list: { + method_names: ["clear", "copy", "sort"], + 2: [false, false, true], + 3: [true, true, true], + }, + dict: { + method_names: ["has_key", "keys", "items", "values"], + 2: [true, true, true, true], + 3: [false, true, true, true], }, - "next$": { - "classes": [Sk.builtin.dict_iter_, - Sk.builtin.list_iter_, - Sk.builtin.set_iter_, - Sk.builtin.str_iter_, - Sk.builtin.tuple_iter_, - Sk.builtin.generator, - Sk.builtin.enumerate, - Sk.builtin.filter_, - Sk.builtin.zip_, - Sk.builtin.map_, - Sk.builtin.iterator], - 2: "next", - 3: "__next__" - } }; -}; - -Sk.switch_version = function (method_to_map, python3) { - var mapping, klass, classes, idx, len, newmeth, oldmeth, mappings; - - mappings = Sk.setup_method_mappings(); - - mapping = mappings[method_to_map]; - if (python3) { - newmeth = mapping[3]; - oldmeth = mapping[2]; - } else { - newmeth = mapping[2]; - oldmeth = mapping[3]; - } - - classes = mapping["classes"]; - len = classes.length; - for (idx = 0; idx < len; idx++) { - klass = classes[idx]; - if (oldmeth && klass.prototype.hasOwnProperty(oldmeth)) { - delete klass.prototype[oldmeth]; + for (let klass_name in methods_to_map) { + const klass = Sk.builtin[klass_name]; + const method_names = methods_to_map[klass_name].method_names; + const in_py3 = methods_to_map[klass_name][3]; + + // if we're not changing to py2 and we have no py3$methods then don't continue since these methods exist by default + if (py3 && klass.py3$methods === undefined) { + return; + } else if (klass.py3$methods === undefined) { + // Set up py3$methods if we haven't done so already + klass.py3$methods = {}; + for (let i = 0; i < method_names.length; i++) { + const method_name = method_names[i]; + if (!in_py3[i]) { + continue; + } + klass.py3$methods[method_name] = klass.prototype[method_name].d$def; + } + } + let in_version, new_methods; + if (py3) { + in_version = in_py3; + new_methods = klass.py3$methods; + } else { + in_version = methods_to_map[klass_name][2]; + new_methods = klass.py2$methods; } - if (newmeth) { - klass.prototype[newmeth] = new Sk.builtin.func(klass.prototype[method_to_map]); + for (let i = 0; i < method_names.length; i++) { + const method_name = method_names[i]; + delete klass.prototype[method_name]; + if (in_version[i]) { + klass.prototype[method_name] = new Sk.builtin.method_descriptor(klass, new_methods[method_name]); + } } } }; diff --git a/src/errors.js b/src/errors.js index bdf56cd597..afe7687bc4 100644 --- a/src/errors.js +++ b/src/errors.js @@ -8,431 +8,288 @@ */ + /** * @constructor - * @param {...Object|null} args + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.BaseException = function (args) { - var i, o; - - if (!(this instanceof Sk.builtin.BaseException)) { - o = Object.create(Sk.builtin.BaseException.prototype); - o.constructor.apply(o, arguments); - return o; - } - - args = Array.prototype.slice.call(arguments); - // hackage to allow shorter throws - for (i = 0; i < args.length; ++i) { - if (typeof args[i] === "string") { - args[i] = new Sk.builtin.str(args[i]); - } - } - this.args = new Sk.builtin.tuple(args); - this.traceback = []; - // TODO: Hack, this exception isn't guaranteed to be thrown! - this.err = Sk.err; - this.__cause__ = Sk.builtin.none.none$; - this.__context__ = Sk.builtin.none.none$; - this.__suppress_context__ = Sk.builtin.none.none$; - //Sk.err = this; - - // For errors occurring during normal execution, the line/col/etc - // of the error are populated by each stack frame of the runtime code, - // but we can seed it with the supplied parameters. - if (this.args.sq$length() >= 3) { - - // if !this.args[1].v, this is an error, and the exception that causes it - // probably needs to be fixed, but we mark as "" for now - this.traceback.push({ - lineno: this.args.v[2], - filename: this.args.v[1].v || "" - }); - } -}; -Sk.abstr.setUpInheritance("BaseException", Sk.builtin.BaseException, Sk.builtin.object); - -Sk.builtin.BaseException.prototype.tp$str = function () { - var i; - var ret = ""; - - ret += this.tp$name; - if (this.args) { - ret += ": " + (this.args.v.length > 0 ? this.args.v[0].v : ""); - } - if (this.traceback.length !== 0) { - var lineno = this.traceback[0].lineno; - ret += " on line "; - if (Sk.builtin.checkInt(lineno)) { - ret += lineno.v !== undefined ? lineno.v : lineno; - } else { - ret += lineno; - } - } else { - ret += " at "; - } - - if (this.args.v.length > 4) { - ret += "\n" + this.args.v[4].v + "\n"; - for (i = 0; i < this.args.v[3]; ++i) { - ret += " "; - } - ret += "^\n"; - } - - /*for (i = 0; i < this.traceback.length; i++) { - ret += "\n at " + this.traceback[i].filename + " line " + this.traceback[i].lineno; - if ("colno" in this.traceback[i]) { - ret += " column " + this.traceback[i].colno; +Sk.builtin.BaseException = Sk.abstr.buildNativeClass("BaseException", { + constructor: function Exception(...args) { + // internally args is either a string + Sk.asserts.assert(this instanceof Sk.builtin.BaseException); + // hackage to allow shorter throws + // let msg = args[0]; + // if (typeof msg === "string" ) { + // msg = new Sk.builtin.str(msg); + // } + this.args = new Sk.builtin.tuple([new Sk.builtin.str(args[0])]); + this.traceback = []; + // TODO: Hack, this exception isn't guaranteed to be thrown! + this.err = Sk.err; + this.__cause__ = Sk.builtin.none.none$; + this.__context__ = Sk.builtin.none.none$; + this.__suppress_context__ = Sk.builtin.none.none$; + //Sk.err = this; + + // For errors occurring during normal execution, the line/col/etc + // of the error are populated by each stack frame of the runtime code, + // but we can seed it with the supplied parameters. + if (args.length >= 3) { + + // if !this.args[1].v, this is an error, and the exception that causes it + // probably needs to be fixed, but we mark as "" for now + this.traceback.push({ + lineno: args[2], + filename: args[1] || "" + }); } - }*/ - - return new Sk.builtin.str(ret); -}; - -Sk.builtin.BaseException.prototype.toString = function () { - return this.tp$str().v; -}; - -// Create a descriptor to return the 'args' of an exception. -// This is a hack to get around a weird mismatch between builtin -// objects and proper types -Sk.builtin.BaseException.prototype.args = { - "tp$descr_get": function(self, clstype) { - return self.args; - }, - "tp$descr_set": function(self, value) { - self.args = value; - } -}; - -Sk.builtin.BaseException.prototype.__cause__ = { - "tp$descr_get": function(self, clstype) { - return self.__cause__; }, - "tp$descr_set": function(self, value) { - self.__cause__ = value; - } -}; - -Sk.builtin.BaseException.prototype.__context__ = { - "tp$descr_get": function(self, clstype) { - return self.__context__; + slots: /**@lends {Sk.builtin.BaseException}*/{ + tp$getattr: Sk.generic.getAttr, + tp$doc: "Common base class for all exceptions", + tp$new: function (args, kwargs) { + if (!this.hp$type) { + // then we have a builtin constructor so just return it as new this + return new this.constructor; + } else { + const instance = new this.constructor; + Sk.builtin.BaseException.call(instance); + return instance; + } + }, + tp$init: function (args, kwargs) { + Sk.abstr.checkNoKwargs(Sk.abstr.typeName(this), kwargs); + if (this.args.v !== args) { + // we only initiate the args if they are not identical to the args from tp$new; + this.args.v = args; + } + return Sk.builtin.none.none$; + }, + $r: function () { + let ret = this.tp$name; + ret += "(" + this.args.v.map((x) => Sk.misceval.objectRepr(x)).join(", ") + ")"; + return new Sk.builtin.str(ret); + }, + tp$str: function () { + if (this.args.v.length <= 1) { + return new Sk.builtin.str(this.args.v[0]); + } + return this.args.$r(); + } }, - "tp$descr_set": function(self, value) { - self.__context__ = value; - } -}; - -Sk.builtin.BaseException.prototype.__suppress_context__ = { - "tp$descr_get": function(self, clstype) { - return self.__suppress_context__; + getsets: /**@lends {Sk.builtin.BaseException}*/{ + args: { + $get: function () { return this.args; }, + $set: function(v) { this.args = v; } + }, + __cause__: { + $get: function () { return this.__cause__; }, + $set: function(v) { this.__cause__ = v; } + }, + __context__: { + $get: function () { return this.__context__; }, + $set: function(v) { this.__context__ = v; } + }, + __suppress_context__: { + $get: function () { return this.__suppress_context__; }, + $set: function(v) { this.__suppress_context__ = v; } + } }, - "tp$descr_set": function(self, value) { - self.__suppress_context__ = value; - } -}; - -/* -Sk.builtin.BaseException.prototype.tp$getattr = function(pyName, canSuspend) { - switch (Sk.ffi.remapToJs(pyName)) { - case "__class__": return this.__class__; - default: return undefined; + proto: /**@lends {Sk.builtin.BaseException}*/{ + toString: function () { + let ret = this.tp$name; + ret += ": " + this.tp$str().v; + + if (this.traceback.length !== 0) { + ret += " on line " + this.traceback[0].lineno; + } else { + ret += " at "; + } + + if (this.args.v.length > 4) { + ret += "\n" + this.args.v[4].v + "\n"; + for (let i = 0; i < this.args.v[3]; ++i) { + ret += " "; + } + ret += "^\n"; + } + + /*for (i = 0; i < this.traceback.length; i++) { + ret += "\n at " + this.traceback[i].filename + " line " + this.traceback[i].lineno; + if ("colno" in this.traceback[i]) { + ret += " column " + this.traceback[i].colno; + } + }*/ + + return ret; + } } -};*/ +}); Sk.exportSymbol("Sk.builtin.BaseException", Sk.builtin.BaseException); /** * @constructor * @extends Sk.builtin.BaseException - * @param {...*} args - */ -Sk.builtin.Exception = function (args) { - var o; - if (!(this instanceof Sk.builtin.Exception)) { - o = Object.create(Sk.builtin.Exception.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.BaseException.apply(this, arguments); + * @param {*=} args Typically called with a single string argument + */ +Sk.builtin.Exception = function (...args) { + Sk.builtin.BaseException.apply(this, args); }; Sk.abstr.setUpInheritance("Exception", Sk.builtin.Exception, Sk.builtin.BaseException); Sk.exportSymbol("Sk.builtin.Exception", Sk.builtin.Exception); -/** - * @constructor - * @extends Sk.builtin.Exception - * @param {...*} args - */ -Sk.builtin.StandardError = function (args) { - var o; - if (!(this instanceof Sk.builtin.StandardError)) { - o = Object.create(Sk.builtin.StandardError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.Exception.apply(this, arguments); -}; -Sk.abstr.setUpInheritance("StandardError", Sk.builtin.StandardError, Sk.builtin.Exception); -Sk.exportSymbol("Sk.builtin.StandardError", Sk.builtin.StandardError); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.AssertionError = function (args) { - var o; - if (!(this instanceof Sk.builtin.AssertionError)) { - o = Object.create(Sk.builtin.AssertionError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.AssertionError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("AssertionError", Sk.builtin.AssertionError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("AssertionError", Sk.builtin.AssertionError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.AssertionError", Sk.builtin.AssertionError); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.AttributeError = function (args) { - var o; - if (!(this instanceof Sk.builtin.AttributeError)) { - o = Object.create(Sk.builtin.AttributeError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.AttributeError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("AttributeError", Sk.builtin.AttributeError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("AttributeError", Sk.builtin.AttributeError, Sk.builtin.Exception); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.ImportError = function (args) { - var o; - if (!(this instanceof Sk.builtin.ImportError)) { - o = Object.create(Sk.builtin.ImportError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.ImportError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("ImportError", Sk.builtin.ImportError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("ImportError", Sk.builtin.ImportError, Sk.builtin.Exception); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.IndentationError = function (args) { - var o; - if (!(this instanceof Sk.builtin.IndentationError)) { - o = Object.create(Sk.builtin.IndentationError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.IndentationError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("IndentationError", Sk.builtin.IndentationError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("IndentationError", Sk.builtin.IndentationError, Sk.builtin.Exception); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.IndexError = function (args) { - var o; - if (!(this instanceof Sk.builtin.IndexError)) { - o = Object.create(Sk.builtin.IndexError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.IndexError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("IndexError", Sk.builtin.IndexError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("IndexError", Sk.builtin.IndexError, Sk.builtin.Exception); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.KeyError = function (args) { - var o; - if (!(this instanceof Sk.builtin.KeyError)) { - o = Object.create(Sk.builtin.KeyError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.KeyError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("KeyError", Sk.builtin.KeyError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("KeyError", Sk.builtin.KeyError, Sk.builtin.Exception); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.NameError = function (args) { - var o; - if (!(this instanceof Sk.builtin.NameError)) { - o = Object.create(Sk.builtin.NameError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.NameError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("NameError", Sk.builtin.NameError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("NameError", Sk.builtin.NameError, Sk.builtin.Exception); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.UnboundLocalError = function (args) { - var o; - if (!(this instanceof Sk.builtin.UnboundLocalError)) { - o = Object.create(Sk.builtin.UnboundLocalError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.UnboundLocalError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("UnboundLocalError", Sk.builtin.UnboundLocalError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("UnboundLocalError", Sk.builtin.UnboundLocalError, Sk.builtin.Exception); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.OverflowError = function (args) { - var o; - if (!(this instanceof Sk.builtin.OverflowError)) { - o = Object.create(Sk.builtin.OverflowError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.OverflowError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("OverflowError", Sk.builtin.OverflowError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("OverflowError", Sk.builtin.OverflowError, Sk.builtin.Exception); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*} args */ -Sk.builtin.SyntaxError = function (args) { - var o; - if (!(this instanceof Sk.builtin.SyntaxError)) { - o = Object.create(Sk.builtin.SyntaxError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.SyntaxError = function (...args) { + Sk.builtin.Exception.apply(this, args); this.text = arguments.length >= 1 ? Sk.ffi.remapToPy(arguments[0]) : Sk.builtin.none.none$; this.msg = this.text; this.filename = arguments.length >= 2 ? Sk.ffi.remapToPy(arguments[1]) : Sk.builtin.none.none$; this.lineno = arguments.length >= 3 ? Sk.ffi.remapToPy(arguments[2]) : Sk.builtin.none.none$; this.offset = arguments.length >= 4 ? Sk.ffi.remapToPy(arguments[3]) : Sk.builtin.none.none$; }; -Sk.abstr.setUpInheritance("SyntaxError", Sk.builtin.SyntaxError, Sk.builtin.StandardError); -Sk.builtin.SyntaxError.prototype.tp$getattr = function (name) { - if (name != null && (Sk.builtin.checkString(name) || typeof name === "string")) { - var _name = name; - - // get javascript string - if (Sk.builtin.checkString(name)) { - _name = Sk.ffi.remapToJs(name); - } - - if (_name === "lineno" || _name === "msg" || _name === "filename" || _name==="offset" || - _name === "text") { - return this[_name]; - } else if (_name === "__name__") { - return Sk.builtin.str("SyntaxError"); - } else if (_name === "__cause__" || _name === "__context__" || _name==="__suppress_context__") { - return Sk.builtin.none.none$; - } - } - - // if we have not returned yet, try the genericgetattr - return Sk.builtin.object.prototype.GenericGetAttr(name); -}; +Sk.abstr.setUpInheritance("SyntaxError", Sk.builtin.SyntaxError, Sk.builtin.Exception); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.RuntimeError = function (args) { - var o; - if (!(this instanceof Sk.builtin.RuntimeError)) { - o = Object.create(Sk.builtin.RuntimeError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.RuntimeError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("RuntimeError", Sk.builtin.RuntimeError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("RuntimeError", Sk.builtin.RuntimeError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.RuntimeError", Sk.builtin.RuntimeError); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.OSError = function (args) { - var o; - if (!(this instanceof Sk.builtin.OSError)) { - o = Object.create(Sk.builtin.OSError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.OSError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("OSError", Sk.builtin.OSError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("OSError", Sk.builtin.OSError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.OSError", Sk.builtin.OSError); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args */ -Sk.builtin.SuspensionError = function (args) { - var o; - if (!(this instanceof Sk.builtin.SuspensionError)) { - o = Object.create(Sk.builtin.SuspensionError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.SuspensionError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("SuspensionError", Sk.builtin.SuspensionError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("SuspensionError", Sk.builtin.SuspensionError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.SuspensionError", Sk.builtin.SuspensionError); /** * @constructor * @extends Sk.builtin.BaseException - * @param {...*} args - */ -Sk.builtin.SystemExit = function (args) { - var o; - if (!(this instanceof Sk.builtin.SystemExit)) { - o = Object.create(Sk.builtin.SystemExit.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.BaseException.apply(this, arguments); + * @param {*=} args Typically called with a single string argument + */ +Sk.builtin.SystemExit = function (...args) { + Sk.builtin.BaseException.apply(this, args); }; Sk.abstr.setUpInheritance("SystemExit", Sk.builtin.SystemExit, Sk.builtin.BaseException); Sk.exportSymbol("Sk.builtin.SystemExit", Sk.builtin.SystemExit); @@ -440,203 +297,163 @@ Sk.exportSymbol("Sk.builtin.SystemExit", Sk.builtin.SystemExit); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.TypeError = function (args) { - var o; - if (!(this instanceof Sk.builtin.TypeError)) { - o = Object.create(Sk.builtin.TypeError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); - this.__class__ = Sk.builtin.TypeError; +Sk.builtin.TypeError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("TypeError", Sk.builtin.TypeError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("TypeError", Sk.builtin.TypeError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.TypeError", Sk.builtin.TypeError); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.ValueError = function (args) { - var o; - if (!(this instanceof Sk.builtin.ValueError)) { - o = Object.create(Sk.builtin.ValueError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.ValueError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("ValueError", Sk.builtin.ValueError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("ValueError", Sk.builtin.ValueError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.ValueError", Sk.builtin.ValueError); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.ZeroDivisionError = function (args) { - var o; - if (!(this instanceof Sk.builtin.ZeroDivisionError)) { - o = Object.create(Sk.builtin.ZeroDivisionError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.ZeroDivisionError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("ZeroDivisionError", Sk.builtin.ZeroDivisionError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("ZeroDivisionError", Sk.builtin.ZeroDivisionError, Sk.builtin.Exception); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.TimeLimitError = function (args) { - var o; - if (!(this instanceof Sk.builtin.TimeLimitError)) { - o = Object.create(Sk.builtin.TimeLimitError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.TimeoutError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("TimeLimitError", Sk.builtin.TimeLimitError, Sk.builtin.StandardError); -Sk.exportSymbol("Sk.builtin.TimeLimitError", Sk.builtin.TimeLimitError); +Sk.abstr.setUpInheritance("TimeoutError", Sk.builtin.TimeoutError, Sk.builtin.Exception); +Sk.exportSymbol("Sk.builtin.TimeoutError", Sk.builtin.TimeoutError); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.IOError = function (args) { - var o; - if (!(this instanceof Sk.builtin.IOError)) { - o = Object.create(Sk.builtin.IOError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.IOError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("IOError", Sk.builtin.IOError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("IOError", Sk.builtin.IOError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.IOError", Sk.builtin.IOError); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.NotImplementedError = function (args) { - var o; - if (!(this instanceof Sk.builtin.NotImplementedError)) { - o = Object.create(Sk.builtin.NotImplementedError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.NotImplementedError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("NotImplementedError", Sk.builtin.NotImplementedError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("NotImplementedError", Sk.builtin.NotImplementedError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.NotImplementedError", Sk.builtin.NotImplementedError); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.NegativePowerError = function (args) { - var o; - if (!(this instanceof Sk.builtin.NegativePowerError)) { - o = Object.create(Sk.builtin.NegativePowerError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.NegativePowerError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("NegativePowerError", Sk.builtin.NegativePowerError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("NegativePowerError", Sk.builtin.NegativePowerError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.NegativePowerError", Sk.builtin.NegativePowerError); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {*} nativeError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args */ -Sk.builtin.ExternalError = function (nativeError, args) { - var o; - if (!(this instanceof Sk.builtin.ExternalError)) { - o = Object.create(Sk.builtin.ExternalError.prototype); - o.constructor.apply(o, arguments); - return o; - } - // Make the first argument a string, so it can be printed in Python without errors, - // but save a reference to the real thing for Javascript consumption - args = Array.prototype.slice.call(arguments); - this.nativeError = args[0]; - if (!(args[0] instanceof Sk.builtin.str)) { - args[0] = ""+args[0]; - } - Sk.builtin.StandardError.apply(this, args); +Sk.builtin.ExternalError = function (...args) { + this.nativeError = args; + const msg = args.toString(); + Sk.builtin.Exception.call(this, msg); }; -Sk.abstr.setUpInheritance("ExternalError", Sk.builtin.ExternalError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("ExternalError", Sk.builtin.ExternalError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.ExternalError", Sk.builtin.ExternalError); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.OperationError = function (args) { - var o; - if (!(this instanceof Sk.builtin.OperationError)) { - o = Object.create(Sk.builtin.OperationError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.OperationError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("OperationError", Sk.builtin.OperationError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("OperationError", Sk.builtin.OperationError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.OperationError", Sk.builtin.OperationError); /** * @constructor - * @extends Sk.builtin.StandardError - * @param {...*} args + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.SystemError = function (args) { - var o; - if (!(this instanceof Sk.builtin.SystemError)) { - o = Object.create(Sk.builtin.SystemError.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.StandardError.apply(this, arguments); +Sk.builtin.SystemError = function (...args) { + Sk.builtin.Exception.apply(this, args); }; -Sk.abstr.setUpInheritance("SystemError", Sk.builtin.SystemError, Sk.builtin.StandardError); +Sk.abstr.setUpInheritance("SystemError", Sk.builtin.SystemError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.SystemError", Sk.builtin.SystemError); /** * @constructor * @extends Sk.builtin.Exception - * @param {...*} args - */ -Sk.builtin.StopIteration = function (args) { - var o; - if (!(this instanceof Sk.builtin.StopIteration)) { - o = Object.create(Sk.builtin.StopIteration.prototype); - o.constructor.apply(o, arguments); - return o; - } - Sk.builtin.Exception.apply(this, arguments); + * @param {*=} args Typically called with a single string argument + */ +Sk.builtin.StopIteration = function (...args) { + Sk.builtin.Exception.apply(this, args); }; Sk.abstr.setUpInheritance("StopIteration", Sk.builtin.StopIteration, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.StopIteration", Sk.builtin.StopIteration); /** * @constructor + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument */ -Sk.builtin.frame = function(trace) { +Sk.builtin.ReferenceError = function (...args) { + Sk.builtin.Exception.apply(this, args); +}; +Sk.abstr.setUpInheritance("ReferenceError", Sk.builtin.ReferenceError, Sk.builtin.Exception); +Sk.exportSymbol("Sk.builtin.ReferenceError", Sk.builtin.ReferenceError); + +/** + * @constructor + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument + */ +Sk.builtin.EOFError = function (...args) { + Sk.builtin.Exception.apply(this, args); +}; +Sk.abstr.setUpInheritance("EOFError", Sk.builtin.EOFError, Sk.builtin.Exception); +Sk.exportSymbol("Sk.builtin.EOFError", Sk.builtin.EOFError); + +/** + * @constructor + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument + */ +Sk.builtin.MemoryError = function (...args) { + Sk.builtin.Exception.apply(this, args); +}; +Sk.abstr.setUpInheritance("MemoryError", Sk.builtin.MemoryError, Sk.builtin.Exception); +Sk.exportSymbol("Sk.builtin.MemoryError", Sk.builtin.MemoryError); + +/** + * @constructor + */ +Sk.builtin.frame = function (trace) { if (!(this instanceof Sk.builtin.frame)) { return new Sk.builtin.frame(trace); } @@ -657,17 +474,28 @@ Sk.builtin.frame.prototype.tp$getattr = function (name) { } switch (_name) { - case "f_back": return Sk.builtin.none.none$; - case "f_builtins": return Sk.builtin.none.none$; - case "f_code": return Sk.builtin.none.none$; - case "f_globals": return Sk.builtin.none.none$; - case "f_lasti": return Sk.builtin.none.none$; - case "f_lineno": return Sk.ffi.remapToPy(this.trace.lineno); - case "f_line": return Sk.ffi.remapToPy(this.trace.source); - case "f_locals": return Sk.builtin.none.none$; - case "f_trace": return Sk.builtin.none.none$; - case "co_filename": return Sk.ffi.remapToPy(this.trace.filename); - case "co_name": return Sk.ffi.remapToPy(this.trace.scope); + case "f_back": + return Sk.builtin.none.none$; + case "f_builtins": + return Sk.builtin.none.none$; + case "f_code": + return Sk.builtin.none.none$; + case "f_globals": + return Sk.builtin.none.none$; + case "f_lasti": + return Sk.builtin.none.none$; + case "f_lineno": + return Sk.ffi.remapToPy(this.trace.lineno); + case "f_line": + return Sk.ffi.remapToPy(this.trace.source); + case "f_locals": + return Sk.builtin.none.none$; + case "f_trace": + return Sk.builtin.none.none$; + case "co_filename": + return Sk.ffi.remapToPy(this.trace.filename); + case "co_name": + return Sk.ffi.remapToPy(this.trace.scope); } } @@ -683,7 +511,7 @@ Sk.exportSymbol("Sk.builtin.frame", Sk.builtin.frame); * @constructor * @param {Object} err */ -Sk.builtin.traceback = function(trace) { +Sk.builtin.traceback = function (trace) { if (!(this instanceof Sk.builtin.traceback)) { return new Sk.builtin.traceback(trace); } @@ -694,19 +522,19 @@ Sk.builtin.traceback = function(trace) { // TODO: Hack, you know this isn't right this.tb_frame = new Sk.builtin.frame(trace); this.tb_source = new Sk.builtin.str(trace.source); - + //tb_frame, tb_lasti, tb_lineno, tb_next - + this.__class__ = Sk.builtin.traceback; return this; }; Sk.abstr.setUpInheritance("traceback", Sk.builtin.traceback, Sk.builtin.object); -Sk.builtin.traceback.fromList = function(traces) { +Sk.builtin.traceback.fromList = function (traces) { var current = Sk.builtin.traceback(traces[0]), first = current; - for (var i=1; i= ret.$lines.length) { - return undefined; + { + tp$iter: function () { + return ret; + }, + $obj: this, + $index: currentLine, + $lines: allLines, + tp$iternext: function () { + if (ret.$index >= ret.$lines.length) { + return undefined; + } + return new Sk.builtin.str(ret.$lines[ret.$index++]); } - return new Sk.builtin.str(ret.$lines[ret.$index++]); - } - }; + }; return ret; }; @@ -128,9 +128,9 @@ Sk.builtin.file.prototype["read"] = new Sk.builtin.func(function read(self, size } ret = new Sk.builtin.str(self.data$.substr(self.pos$, l_size)); - if(size === undefined){ + if (size === undefined) { self.pos$ = len; - }else{ + } else { self.pos$ += Sk.ffi.remapToJs(size); } if (self.pos$ >= len) { @@ -154,7 +154,7 @@ Sk.builtin.file.$readline = function (self, size, prompt) { if (x instanceof Promise) { susp = new Sk.misceval.Suspension(); - susp.resume = function() { + susp.resume = function () { if (susp.data.error) { throw susp.data.error; } @@ -170,7 +170,7 @@ Sk.builtin.file.$readline = function (self, size, prompt) { return susp; } else { - Sk.execPaused = Date.now()-Sk.execPaused; + Sk.execPaused = Date.now() - Sk.execPaused; return new Sk.builtin.str(x); } } else { @@ -201,7 +201,7 @@ Sk.builtin.file.prototype["readlines"] = new Sk.builtin.func(function readlines( }); Sk.builtin.file.prototype["seek"] = new Sk.builtin.func(function seek(self, offset, whence) { - var l_offset = Sk.ffi.remapToJs(offset); + var l_offset = Sk.ffi.remapToJs(offset); if (whence === undefined) { whence = 0; diff --git a/src/filter.js b/src/filter.js index 539de068c6..73c9d7c7c1 100644 --- a/src/filter.js +++ b/src/filter.js @@ -4,7 +4,7 @@ * @extends Sk.builtin.object */ -Sk.builtin.filter_ = function filter_ (fun, iterable) { +Sk.builtin.filter_ = function filter_(fun, iterable) { var it; var getitem; var result; diff --git a/src/float.js b/src/float.js index 34d2cfab90..04fcfc8d78 100644 --- a/src/float.js +++ b/src/float.js @@ -1,110 +1,226 @@ -/** - * @namespace Sk.builtin - */ +/** @typedef {Sk.builtin.object} */ var pyObject; + /** * @constructor - * Sk.builtin.float_ + * @extends {Sk.builtin.object} * - * @description - * Constructor for Python float. Also used for builtin float(). + * @param {number} x only be called with a JS number * - * @extends {Sk.builtin.numtype} - * - * @param {!(Object|number|string)} x Object or number to convert to Python float. * @return {Sk.builtin.float_} Python float */ -Sk.builtin.float_ = function (x) { - var tmp; - if (x === undefined) { - return new Sk.builtin.float_(0.0); - } - - if (!(this instanceof Sk.builtin.float_)) { - return new Sk.builtin.float_(x); - } - - - if (x instanceof Sk.builtin.str) { - return Sk.builtin._str_to_float(x.v); - } - - // Floats are just numbers - if (typeof x === "number" || x instanceof Sk.builtin.int_ || x instanceof Sk.builtin.lng || x instanceof Sk.builtin.float_) { - tmp = Sk.builtin.asnum$(x); - if (typeof tmp === "string") { - return Sk.builtin._str_to_float(tmp); - } - this.v = tmp; - return this; - } - - // Convert booleans - if (x instanceof Sk.builtin.bool) { - this.v = Sk.builtin.asnum$(x); - return this; - } - - // this is a special internal case - if(typeof x === "boolean") { - this.v = x ? 1.0 : 0.0; - return this; - } - - if (typeof x === "string") { - this.v = parseFloat(x); - return this; - } +Sk.builtin.float_ = Sk.abstr.buildNativeClass("float", { + constructor: function float_(x) { + Sk.asserts.assert(this instanceof Sk.builtin.float_, "bad call to float use 'new'"); + if (typeof x === "number") { + this.v = x; + } else if (typeof x === "string") { + // be careful with converting a string as it could result in infinity + this.v = parseFloat(x); + } else if (x === undefined) { + this.v = 0.0; + } else { + Sk.asserts.fail("bad argument to float constructor"); + } + }, + slots: /**@lends {Sk.builtin.float_.prototype} */{ + tp$gettattr: Sk.generic.getAttr, + tp$as_number: true, + tp$doc: "Convert a string or number to a floating point number, if possible.", + tp$hash: function () { + //todo - this hash function causes a lot of collisions - Cpython implementation is different + return this.nb$int_(); + }, + $r: function () { + return new Sk.builtin.str(this.str$(10, true)); + }, + tp$new: function (args, kwargs) { + if (kwargs && kwargs.length) { + throw new Sk.builtin.TypeError("float() takes no keyword arguments"); + } else if (args && args.length > 1) { + throw new Sk.builtin.TypeError("float expected at most 1 arguments, got " + args.length); + } + const arg = args[0]; + let x; + // is args always an empty list? + if (arg === undefined) { + x = new Sk.builtin.float_(0.0); + } else if (arg.nb$float_) { + x = arg.nb$float_(); + } else if (Sk.builtin.checkString(arg)) { + x = _str_to_float(arg.v); + } + if (x === undefined) { + throw new Sk.builtin.TypeError("float() argument must be a string or a number"); + } + if (this === Sk.builtin.float_.prototype) { + return x; + } else { + const instance = new this.constructor(); + instance.v = x.v; + return instance; + } + }, - // try calling __float__ - var special = Sk.abstr.lookupSpecial(x, Sk.builtin.str.$float_); - if (special != null) { - // method on builtin, provide this arg - return Sk.misceval.callsimArray(special, [x]); + // number slots + nb$int_: function () { + let v = this.v; + if (v < 0) { + v = Math.ceil(v); + } else { + v = Math.floor(v); + } + if (!Number.isInteger(v)) { + throw new Sk.builtin.ValueError("cannot convert float " + Sk.misceval.objectRepr(this) + " to integer"); + } + if (Sk.builtin.int_.withinThreshold(v)) { + return new Sk.builtin.int_(v); + } else { + return new Sk.builtin.int_(JSBI.BigInt(v)); + } + }, + nb$float_: cloneSelf, + nb$lng: function () { + return new Sk.builtin.lng(this.nb$int_().v); + }, + nb$add: numberSlot((v, w) => new Sk.builtin.float_(v + w)), + + nb$subtract: numberSlot((v, w) => new Sk.builtin.float_(v - w)), + nb$reflected_subtract: numberSlot((v, w) => new Sk.builtin.float_(w - v)), + + nb$multiply: numberSlot((v, w) => new Sk.builtin.float_(v * w)), + + nb$divide: numberSlot(divide), + nb$reflected_divide: numberSlot((v, w) => divide(w, v)), + + nb$floor_divide: numberSlot(floordivide), + nb$reflected_floor_divide: numberSlot((v, w) => floordivide(w, v)), + + nb$remainder: numberSlot(remainder), + nb$reflected_remainder: numberSlot((v, w) => remainder(w, v)), + + nb$divmod: numberSlot((v, w) => new Sk.builtin.tuple([floordivide(v, w), remainder(v, w)])), + nb$reflected_divmod: numberSlot((v, w) => new Sk.builtin.tuple([floordivide(w, v), remainder(w, v)])), + + nb$power: numberSlot(power), + nb$reflected_power: numberSlot((v, w) => power(w, v)), + + nb$abs: function () { + return new Sk.builtin.float_(Math.abs(this.v)); + }, + nb$negative: function () { + return new Sk.builtin.float_(-this.v); + }, + nb$positive: function () { + return new Sk.builtin.float_(this.v); + }, + nb$bool: function () { + return this.v !== 0; + }, + nb$isnegative: function () { + return this.v < 0; + }, + nb$ispositive: function () { + return this.v >= 0; + }, + ob$eq: numberSlot((v, w) => v == w), + ob$ne: numberSlot((v, w) => v != w), + ob$gt: numberSlot((v, w) => v > w), + ob$ge: numberSlot((v, w) => v >= w), + ob$lt: numberSlot((v, w) => v < w), + ob$le: numberSlot((v, w) => v <= w), + }, + getsets: /**@lends {Sk.builtin.float_.prototype} */{ + real: { + $get: cloneSelf, + }, + imag: { + $get: function () { + return new Sk.builtin.float_(0.0); + }, + }, + }, + methods: /**@lends {Sk.builtin.float_.prototype} */{ + conjugate: { + $meth: cloneSelf, + $flags: {NoArgs: true}, + $textsig: "($self, /)", + $doc: "Return self, the complex conjugate of any float.", + }, + __trunc__: { + $meth: function () { + return this.nb$int_(); + }, + $flags: {NoArgs: true}, + $textsig: "($self, /)", + $doc: "Return the Integral closest to x between 0 and x.", + }, + __round__: { + $meth: function (ndigits) { + return this.round$(ndigits); + }, + $flags: {MinArgs: 0, MaxArgs: 1}, + $textsig: "($self, ndigits=None, /)", + $doc: "Return the Integral closest to x, rounding half toward even.\n\nWhen an argument is passed, work like built-in round(x, ndigits).", + }, + // as_integer_ratio: { + // $meth: methods.as_integer_ratio, + // $flags: { NoArgs: true }, + // $textsig: "($self, /)", + // $doc: + // "Return integer ratio.\n\nReturn a pair of integers, whose ratio is exactly equal to the original float\nand with a positive denominator.\n\nRaise OverflowError on infinities and a ValueError on NaNs.\n\n>>> (10.0).as_integer_ratio()\n(10, 1)\n>>> (0.0).as_integer_ratio()\n(0, 1)\n>>> (-.25).as_integer_ratio()\n(-1, 4)", + // }, + // hex: { + // $meth: methods.hex, + // $flags: { NoArgs: true }, + // $textsig: "($self, /)", + // $doc: + // "Return a hexadecimal representation of a floating-point number.\n\n>>> (-0.1).hex()\n'-0x1.999999999999ap-4'\n>>> 3.14159.hex()\n'0x1.921f9f01b866ep+1'", + // }, + is_integer: { + $meth: function () { + return new Sk.builtin.bool(Number.isInteger(this.v)); + }, + $flags: {NoArgs: true}, + $textsig: "($self, /)", + $doc: "Return True if the float is an integer.", + }, + __getnewargs__: { + $meth: function () { + return new Sk.builtin.tuple([this]); + }, + $flags: {NoArgs: true}, + $textsig: "($self, /)", + $doc: Sk.builtin.none.none$, + }, + __format__: { + $meth: Sk.formatting.mkNumber__format__(true), + $flags: {OneArg: true}, + $textsig: "($self, format_spec, /)", + $doc: Sk.builtin.none.none$, + }, } +}); - throw new Sk.builtin.TypeError("float() argument must be a string or a number"); -}; - -Sk.abstr.setUpInheritance("float", Sk.builtin.float_, Sk.builtin.numtype); - -Sk.builtin._str_to_float = function (str) { - var tmp; - +function _str_to_float(str) { + let ret; if (str.match(/^-inf$/i)) { - tmp = -Infinity; + ret = -Infinity; } else if (str.match(/^[+]?inf$/i)) { - tmp = Infinity; + ret = Infinity; } else if (str.match(/^[-+]?nan$/i)) { - tmp = NaN; + ret = NaN; } else if (!isNaN(str)) { - tmp = parseFloat(str); + ret = parseFloat(str); } else { throw new Sk.builtin.ValueError("float: Argument: " + str + " is not number"); } - return new Sk.builtin.float_(tmp); -}; - -Sk.builtin.float_.prototype.nb$int_ = function () { - var v = this.v; + return new Sk.builtin.float_(ret); +} - if (v < 0) { - v = Math.ceil(v); - } else { - v = Math.floor(v); - } - - // this should take care of int/long fitting - return new Sk.builtin.int_(v); -}; - -Sk.builtin.float_.prototype.nb$float_ = function() { - return this; -}; - -Sk.builtin.float_.prototype.nb$lng = function () { - return new Sk.builtin.lng(this.v); -}; +function cloneSelf() { + return new Sk.builtin.float_(this.v); +} /** * Checks for float subtypes, though skulpt does not allow to @@ -118,99 +234,20 @@ Sk.builtin.float_.PyFloat_Check = function (op) { if (op === undefined) { return false; } - // this is a little bit hacky // ToDo: subclassable builtins do not require this if (Sk.builtin.checkNumber(op)) { return true; } - if (Sk.builtin.checkFloat(op)) { return true; } - - if (Sk.builtin.issubclass(op.ob$type, Sk.builtin.float_)) { + if (op.ob$type.$isSubType(Sk.builtin.float_)) { return true; } - return false; }; -/** - * Checks if ob is a Python float. - * - * This method is just a wrapper, but uses the correct cpython API name. - * - * Javascript function, returns Javascript object. - * @param {Object} op The object to check. - * @return {boolean} true if op is an instance of Sk.builtin.float_, false otherwise - */ -Sk.builtin.float_.PyFloat_Check_Exact = function (op) { - return Sk.builtin.checkFloat(op); -}; - -Sk.builtin.float_.PyFloat_AsDouble = function (op) { - var f; // nb_float; - var fo; // PyFloatObject *fo; - var val; - - // it is a subclass or direct float - if (op && Sk.builtin.float_.PyFloat_Check(op)) { - return Sk.ffi.remapToJs(op); - } - - if (op == null) { - throw new Error("bad argument for internal PyFloat_AsDouble function"); - } - - // check if special method exists (nb_float is not implemented in skulpt, hence we use __float__) - f = Sk.builtin.type.typeLookup(op.ob$type, Sk.builtin.str.$float_); - if (f == null) { - throw new Sk.builtin.TypeError("a float is required"); - } - - // call internal float method - fo = Sk.misceval.callsimArray(f, [op]); - - // return value of __float__ must be a python float - if (!Sk.builtin.float_.PyFloat_Check(fo)) { - throw new Sk.builtin.TypeError("nb_float should return float object"); - } - - val = Sk.ffi.remapToJs(fo); - - return val; -}; - -/** - * Return this instance's Javascript value. - * - * Javascript function, returns Javascript object. - * - * @return {number} This instance's value. - */ -Sk.builtin.float_.prototype.tp$index = function () { - return this.v; -}; - -/** @override */ -Sk.builtin.float_.prototype.tp$hash = function () { - //the hash of all numbers should be an int and since javascript doesn't really - //care every number can be an int. - return this.nb$int_(); -}; - - -/** - * Returns a copy of this instance. - * - * Javascript function, returns Python object. - * - * @return {Sk.builtin.float_} The copy - */ -Sk.builtin.float_.prototype.clone = function () { - return new Sk.builtin.float_(this.v); -}; /** * Returns this instance's value as a string formatted using fixed-point notation. @@ -225,539 +262,125 @@ Sk.builtin.float_.prototype.toFixed = function (x) { return this.v.toFixed(x); }; -/** @override */ -Sk.builtin.float_.prototype.nb$add = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.float_) { - return new Sk.builtin.float_(this.v + other.v); - } else if (other instanceof Sk.builtin.lng) { - return new Sk.builtin.float_(this.v + parseFloat(other.str$(10, true))); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$reflected_add = function (other) { - // Should not automatically call this.nb$add, as nb$add may have - // been overridden by a subclass - return Sk.builtin.float_.prototype.nb$add.call(this, other); -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$subtract = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.float_) { - return new Sk.builtin.float_(this.v - other.v); - } else if (other instanceof Sk.builtin.lng) { - return new Sk.builtin.float_(this.v - parseFloat(other.str$(10, true))); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$reflected_subtract = function (other) { - // Should not automatically call this.nb$add, as nb$add may have - // been overridden by a subclass - var negative_this = this.nb$negative(); - return Sk.builtin.float_.prototype.nb$add.call(negative_this, other); -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$multiply = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.float_) { - return new Sk.builtin.float_(this.v * other.v); - } else if (other instanceof Sk.builtin.lng) { - return new Sk.builtin.float_(this.v * parseFloat(other.str$(10, true))); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$reflected_multiply = function (other) { - // Should not automatically call this.nb$multiply, as nb$multiply may have - // been overridden by a subclass - return Sk.builtin.float_.prototype.nb$multiply.call(this, other); -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$divide = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.float_) { - - if (other.v === 0) { - throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero"); - } - - if (this.v === Infinity) { - if (other.v === Infinity || other.v === -Infinity) { - return new Sk.builtin.float_(NaN); - } else if (other.nb$isnegative()) { - return new Sk.builtin.float_(-Infinity); - } else { - return new Sk.builtin.float_(Infinity); - } - } - if (this.v === -Infinity) { - if (other.v === Infinity || other.v === -Infinity) { - return new Sk.builtin.float_(NaN); - } else if (other.nb$isnegative()) { - return new Sk.builtin.float_(Infinity); - } else { - return new Sk.builtin.float_(-Infinity); - } - } - - return new Sk.builtin.float_(this.v / other.v); - } - - if (other instanceof Sk.builtin.lng) { - if (other.longCompare(Sk.builtin.biginteger.ZERO) === 0) { - throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero"); - } - - if (this.v === Infinity) { - if (other.nb$isnegative()) { - return new Sk.builtin.float_(-Infinity); - } else { - return new Sk.builtin.float_(Infinity); - } - } - if (this.v === -Infinity) { - if (other.nb$isnegative()) { - return new Sk.builtin.float_(Infinity); - } else { - return new Sk.builtin.float_(-Infinity); - } +function numberSlot(f) { + return function (other) { + const v = this.v; + let w = other.v; + if (typeof w === "number") { + // pass + } else if (w instanceof JSBI) { + w = fromBigIntToNumberOrOverflow(w); + } else { + return Sk.builtin.NotImplemented.NotImplemented$; } + return f(v, w); + }; +} - return new Sk.builtin.float_(this.v / parseFloat(other.str$(10, true))); +function divide(v, w) { + if (w === 0) { + throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero"); } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$reflected_divide = function (other) { - if (other instanceof Sk.builtin.int_ || - other instanceof Sk.builtin.lng) { - other = new Sk.builtin.float_(other); - } - - if (other instanceof Sk.builtin.float_) { - return other.nb$divide(this); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$floor_divide = function (other) { - - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.float_) { - - if (this.v === Infinity || this.v === -Infinity) { + if (v === Infinity) { + if (w === Infinity || v === -Infinity) { return new Sk.builtin.float_(NaN); + } else if (w < 0) { + return new Sk.builtin.float_(-Infinity); + } else { + return new Sk.builtin.float_(Infinity); } - - if (other.v === 0) { - throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero"); - } - - if (other.v === Infinity) { - if (this.nb$isnegative()) { - return new Sk.builtin.float_(-1); - } else { - return new Sk.builtin.float_(0); - } - } - if (other.v === -Infinity) { - if (this.nb$isnegative() || !this.nb$nonzero()) { - return new Sk.builtin.float_(0); - } else { - return new Sk.builtin.float_(-1); - } - } - - return new Sk.builtin.float_(Math.floor(this.v / other.v)); } - - if (other instanceof Sk.builtin.lng) { - if (other.longCompare(Sk.builtin.biginteger.ZERO) === 0) { - throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero"); - } - - if (this.v === Infinity || this.v === -Infinity) { + if (v === -Infinity) { + if (w === Infinity || v === -Infinity) { return new Sk.builtin.float_(NaN); + } else if (w < 0) { + return new Sk.builtin.float_(Infinity); + } else { + return new Sk.builtin.float_(-Infinity); } - - return new Sk.builtin.float_(Math.floor(this.v / parseFloat(other.str$(10, true)))); } + return new Sk.builtin.float_(v / w); +} - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$reflected_floor_divide = function (other) { - if (other instanceof Sk.builtin.int_ || - other instanceof Sk.builtin.lng) { - other = new Sk.builtin.float_(other); +function floordivide(v, w) { + if (v === Infinity || v === -Infinity) { + return new Sk.builtin.float_(NaN); } - - if (other instanceof Sk.builtin.float_) { - return other.nb$floor_divide(this); + if (w === 0) { + throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero"); } - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$remainder = function (other) { - var thisAsLong; - var op2; - var tmp; - var result; - - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.float_) { - - if (other.v === 0) { - throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero"); - } - - if (this.v === 0) { - return new Sk.builtin.float_(0); - } - - if (other.v === Infinity) { - if (this.v === Infinity || this.v === -Infinity) { - return new Sk.builtin.float_(NaN); - } else if (this.nb$ispositive()) { - return new Sk.builtin.float_(this.v); - } else { - return new Sk.builtin.float_(Infinity); - } - } - - // Javacript logic on negatives doesn't work for Python... do this instead - tmp = this.v % other.v; - - if (this.v < 0) { - if (other.v > 0 && tmp < 0) { - tmp = tmp + other.v; - } + if (w === Infinity) { + if (v < 0) { + return new Sk.builtin.float_(-1); } else { - if (other.v < 0 && tmp !== 0) { - tmp = tmp + other.v; - } - } - - if (other.v < 0 && tmp === 0) { - tmp = -0.0; // otherwise the sign gets lost by javascript modulo - } else if (tmp === 0 && Infinity/tmp === -Infinity) { - tmp = 0.0; + return new Sk.builtin.float_(0); } - - return new Sk.builtin.float_(tmp); } - - if (other instanceof Sk.builtin.lng) { - if (other.longCompare(Sk.builtin.biginteger.ZERO) === 0) { - throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero"); - } - - if (this.v === 0) { + if (w === -Infinity) { + if (v < 0 || v !== 0) { return new Sk.builtin.float_(0); - } - - op2 = parseFloat(other.str$(10, true)); - tmp = this.v % op2; - - if (tmp < 0) { - if (op2 > 0 && tmp !== 0) { - tmp = tmp + op2; - } } else { - if (op2 < 0 && tmp !== 0) { - tmp = tmp + op2; - } - } - - if (other.nb$isnegative() && tmp === 0) { - tmp = -0.0; // otherwise the sign gets lost by javascript modulo - } else if (tmp === 0 && Infinity/tmp === -Infinity) { - tmp = 0.0; + return new Sk.builtin.float_(-1); } - - return new Sk.builtin.float_(tmp); } + return new Sk.builtin.float_(Math.floor(v / w)); +} - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$reflected_remainder = function (other) { - if (other instanceof Sk.builtin.int_ || - other instanceof Sk.builtin.lng) { - other = new Sk.builtin.float_(other); +function remainder(v, w) { + if (w === 0) { + throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero"); } - - if (other instanceof Sk.builtin.float_) { - return other.nb$remainder(this); + if (v === 0) { + return new Sk.builtin.float_(0); } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$divmod = function (other) { - if (other instanceof Sk.builtin.int_ || - other instanceof Sk.builtin.lng) { - other = new Sk.builtin.float_(other); - } - - if (other instanceof Sk.builtin.float_) { - return new Sk.builtin.tuple([ - this.nb$floor_divide(other), - this.nb$remainder(other) - ]); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$reflected_divmod = function (other) { - if (other instanceof Sk.builtin.int_ || - other instanceof Sk.builtin.lng) { - other = new Sk.builtin.float_(other); - } - - if (other instanceof Sk.builtin.float_) { - return new Sk.builtin.tuple([ - other.nb$floor_divide(this), - other.nb$remainder(this) - ]); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$power = function (other, mod) { - var thisAsLong; - var result; - - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.float_) { - if (this.v < 0 && other.v % 1 !== 0) { - throw new Sk.builtin.NegativePowerError("cannot raise a negative number to a fractional power"); - } - if (this.v === 0 && other.v < 0) { - throw new Sk.builtin.NegativePowerError("cannot raise zero to a negative power"); - } - - result = new Sk.builtin.float_(Math.pow(this.v, other.v)); - - if ((Math.abs(result.v) === Infinity) && - (Math.abs(this.v) !== Infinity) && - (Math.abs(other.v) !== Infinity)) { - throw new Sk.builtin.OverflowError("Numerical result out of range"); - } - return result; - } - - if (other instanceof Sk.builtin.lng) { - if (this.v === 0 && other.longCompare(Sk.builtin.biginteger.ZERO) < 0) { - throw new Sk.builtin.NegativePowerError("cannot raise zero to a negative power"); + if (w === Infinity) { + if (v === Infinity || this.v === -Infinity) { + return new Sk.builtin.float_(NaN); + } else if (v > 0) { + return new Sk.builtin.float_(v); + } else { + return new Sk.builtin.float_(Infinity); } - - return new Sk.builtin.float_(Math.pow(this.v, parseFloat(other.str$(10, true)))); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$reflected_power = function (n, mod) { - if (n instanceof Sk.builtin.int_ || - n instanceof Sk.builtin.lng) { - n = new Sk.builtin.float_(n); } - if (n instanceof Sk.builtin.float_) { - return n.nb$power(this, mod); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$abs = function () { - return new Sk.builtin.float_(Math.abs(this.v)); -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$inplace_add = Sk.builtin.float_.prototype.nb$add; - -/** @override */ -Sk.builtin.float_.prototype.nb$inplace_subtract = Sk.builtin.float_.prototype.nb$subtract; - -/** @override */ -Sk.builtin.float_.prototype.nb$inplace_multiply = Sk.builtin.float_.prototype.nb$multiply; - -/** @override */ -Sk.builtin.float_.prototype.nb$inplace_divide = Sk.builtin.float_.prototype.nb$divide; + // Javacript logic on negatives doesn't work for Python... do this instead + let tmp = v % w; -/** @override */ -Sk.builtin.float_.prototype.nb$inplace_remainder = Sk.builtin.float_.prototype.nb$remainder; - -/** @override */ -Sk.builtin.float_.prototype.nb$inplace_floor_divide = Sk.builtin.float_.prototype.nb$floor_divide; - -/** @override */ -Sk.builtin.float_.prototype.nb$inplace_power = Sk.builtin.float_.prototype.nb$power; - -/** - * @override - * - * @return {Sk.builtin.float_} A copy of this instance with the value negated. - */ -Sk.builtin.float_.prototype.nb$negative = function () { - return new Sk.builtin.float_(-this.v); -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$positive = function () { - return this.clone(); -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$nonzero = function () { - return this.v !== 0; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$isnegative = function () { - return this.v < 0; -}; - -/** @override */ -Sk.builtin.float_.prototype.nb$ispositive = function () { - return this.v >= 0; -}; - -/** - * Compare this instance's value to another Python object's value. - * - * Returns NotImplemented if comparison between float and other type is unsupported. - * - * Javscript function, returns Javascript object or Sk.builtin.NotImplemented. - * - * @return {(number|Sk.builtin.NotImplemented)} negative if this < other, zero if this == other, positive if this > other - */ -Sk.builtin.float_.prototype.numberCompare = function (other) { - var diff; - var tmp; - var thisAsLong; - - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.float_) { - if (this.v == Infinity && other.v == Infinity) { - return 0; + if (v < 0) { + if (w > 0 && tmp < 0) { + tmp = tmp + w; } - if (this.v == -Infinity && other.v == -Infinity) { - return 0; + } else { + if (w < 0 && tmp !== 0) { + tmp = tmp + w; } - return this.v - other.v; } - - if (other instanceof Sk.builtin.lng) { - if (this.v % 1 === 0) { - thisAsLong = new Sk.builtin.lng(this.v); - tmp = thisAsLong.longCompare(other); - return tmp; - } - diff = this.nb$subtract(other); - if (diff instanceof Sk.builtin.float_) { - return diff.v; - } else if (diff instanceof Sk.builtin.lng) { - return diff.longCompare(Sk.builtin.biginteger.ZERO); + if (tmp === 0) { + if (w < 0) { + tmp = -0.0; // otherwise the sign gets lost by javascript modulo + } else if (Infinity / tmp === -Infinity) { + tmp = 0.0; } } + return new Sk.builtin.float_(tmp); +} - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -// Despite what jshint may want us to do, these two functions need to remain -// as == and != Unless you modify the logic of numberCompare do not change -// these. - -/** @override */ -Sk.builtin.float_.prototype.ob$eq = function (other) { - if (other instanceof Sk.builtin.int_ || - other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.numberCompare(other) == 0); //jshint ignore:line - } else if (other instanceof Sk.builtin.none) { - return Sk.builtin.bool.false$; - } else { - return Sk.builtin.NotImplemented.NotImplemented$; +function power(v, w) { + if (v < 0 && w % 1 !== 0) { + throw new Sk.builtin.NegativePowerError("cannot raise a negative number to a fractional power"); } -}; - -/** @override */ -Sk.builtin.float_.prototype.ob$ne = function (other) { - if (other instanceof Sk.builtin.int_ || - other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.numberCompare(other) != 0); //jshint ignore:line - } else if (other instanceof Sk.builtin.none) { - return Sk.builtin.bool.true$; - } else { - return Sk.builtin.NotImplemented.NotImplemented$; + if (v === 0 && w < 0) { + throw new Sk.builtin.NegativePowerError("cannot raise zero to a negative power"); } -}; -/** @override */ -Sk.builtin.float_.prototype.ob$lt = function (other) { - if (other instanceof Sk.builtin.int_ || - other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.numberCompare(other) < 0); - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; + const result = Math.pow(v, w); -/** @override */ -Sk.builtin.float_.prototype.ob$le = function (other) { - if (other instanceof Sk.builtin.int_ || - other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.numberCompare(other) <= 0); - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; - -/** @override */ -Sk.builtin.float_.prototype.ob$gt = function (other) { - if (other instanceof Sk.builtin.int_ || - other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.numberCompare(other) > 0); - } else { - return Sk.builtin.NotImplemented.NotImplemented$; + if (Math.abs(result) === Infinity && Math.abs(v) !== Infinity && Math.abs(w) !== Infinity) { + throw new Sk.builtin.OverflowError("Numerical result out of range"); } -}; - -/** @override */ -Sk.builtin.float_.prototype.ob$ge = function (other) { - if (other instanceof Sk.builtin.int_ || - other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.numberCompare(other) >= 0); - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; + return new Sk.builtin.float_(result); +} /** * Round this instance to a given number of digits, or zero if omitted. @@ -766,20 +389,16 @@ Sk.builtin.float_.prototype.ob$ge = function (other) { * * Javascript function, returns Python object. * - * @param {Sk.builtin.int_} self This instance. - * @param {Object|number=} ndigits The number of digits after the decimal point to which to round. + * @param {pyObject=} ndigits The number of digits after the decimal point to which to round. * @return {Sk.builtin.float_|Sk.builtin.int_} The rounded float. + * */ -Sk.builtin.float_.prototype.round$ = function (self, ndigits) { - Sk.builtin.pyCheckArgsLen("__round__", arguments.length, 1, 2); - +Sk.builtin.float_.prototype.round$ = function (ndigits) { var result, multiplier, number, num10, rounded, bankRound, ndigs; - - if ((ndigits !== undefined) && !Sk.misceval.isIndex(ndigits)) { + if (ndigits !== undefined && !Sk.misceval.isIndex(ndigits)) { throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(ndigits) + "' object cannot be interpreted as an index"); } - - number = Sk.builtin.asnum$(self); + number = Sk.builtin.asnum$(this); if (ndigits === undefined) { ndigs = 0; } else { @@ -789,7 +408,7 @@ Sk.builtin.float_.prototype.round$ = function (self, ndigits) { if (Sk.__future__.bankers_rounding) { num10 = number * Math.pow(10, ndigs); rounded = Math.round(num10); - bankRound = (((((num10>0)?num10:(-num10))%1)===0.5)?(((0===(rounded%2)))?rounded:(rounded-1)):rounded); + bankRound = (num10 > 0 ? num10 : -num10) % 1 === 0.5 ? (0 === rounded % 2 ? rounded : rounded - 1) : rounded; result = bankRound / Math.pow(10, ndigs); if (ndigits === undefined) { return new Sk.builtin.int_(result); @@ -804,47 +423,6 @@ Sk.builtin.float_.prototype.round$ = function (self, ndigits) { } }; -Sk.builtin.float_.prototype.__format__= function (obj, format_spec) { - var formatstr; - Sk.builtin.pyCheckArgsLen("__format__", arguments.length, 2, 2); - - if (!Sk.builtin.checkString(format_spec)) { - if (Sk.__future__.exceptions) { - throw new Sk.builtin.TypeError("format() argument 2 must be str, not " + Sk.abstr.typeName(format_spec)); - } else { - throw new Sk.builtin.TypeError("format expects arg 2 to be string or unicode, not " + Sk.abstr.typeName(format_spec)); - } - } else { - formatstr = Sk.ffi.remapToJs(format_spec); - if (formatstr !== "") { - throw new Sk.builtin.NotImplementedError("format spec is not yet implemented"); - } - } - - return new Sk.builtin.str(obj); -}; - - -Sk.builtin.float_.prototype.conjugate = new Sk.builtin.func(function (self) { - return new Sk.builtin.float_(self.v); -}); - -/** @override */ -Sk.builtin.float_.prototype["$r"] = function () { - return new Sk.builtin.str(this.str$(10, true)); -}; - -/** - * Return the string representation of this instance. - * - * Javascript function, returns Python object. - * - * @return {Sk.builtin.str} The Python string representation of this instance. - */ -Sk.builtin.float_.prototype.tp$str = function () { - return new Sk.builtin.str(this.str$(10, true)); -}; - /** * Convert this instance's value to a Javascript string. * @@ -881,14 +459,12 @@ Sk.builtin.float_.prototype.str$ = function (base, sign) { work = sign ? this.v : Math.abs(this.v); - if (base === undefined || base === 10) { if (Sk.__future__.python3) { tmp = work.toPrecision(16); } else { tmp = work.toPrecision(12); } - // transform fractions with 4 or more leading zeroes into exponents idx = tmp.indexOf("."); @@ -904,10 +480,10 @@ Sk.builtin.float_.prototype.str$ = function (base, sign) { } if (tmp.indexOf("e") < 0 && tmp.indexOf(".") >= 0) { - while (tmp.charAt(tmp.length-1) == "0") { - tmp = tmp.substring(0,tmp.length-1); + while (tmp.charAt(tmp.length - 1) == "0") { + tmp = tmp.substring(0, tmp.length - 1); } - if (tmp.charAt(tmp.length-1) == ".") { + if (tmp.charAt(tmp.length - 1) == ".") { tmp = tmp + "0"; } } @@ -922,7 +498,7 @@ Sk.builtin.float_.prototype.str$ = function (base, sign) { } // restore negative zero sign - if(this.v === 0 && 1/this.v === -Infinity) { + if (this.v === 0 && 1 / this.v === -Infinity) { tmp = "-" + tmp; } @@ -932,3 +508,15 @@ Sk.builtin.float_.prototype.str$ = function (base, sign) { return tmp; }; + + +Sk.builtin.float_.py2$methods = {}; + +function fromBigIntToNumberOrOverflow(big) { + const x = parseFloat(JSBI.toNumber(big)); + if (x == Infinity || x == -Infinity) { + //trying to convert a large js string to a float + throw new Sk.builtin.OverflowError("int too large to convert to float"); + } + return x; +} diff --git a/src/formatting.js b/src/formatting.js index 80d6a8e357..422f74b7af 100644 --- a/src/formatting.js +++ b/src/formatting.js @@ -1,3 +1,276 @@ +// Implement the default "format specification mini-language" +// for numbers and strings +// https://docs.python.org/3.7/library/string.html#formatspec + +const FORMAT_SPEC_REGEX = /^(?:(.)?([<\>\=\^]))?([\+\-\s])?(#)?(0)?(\d+)?(,)?(?:\.(\d+))?([bcdeEfFgGnosxX%])?$/; +const FMT = { + FILL_CHAR: 1, + FILL_ALIGN: 2, + SIGN: 3, + ALT_FORM: 4, + ZERO_PAD: 5, + FIELD_WIDTH: 6, + COMMA: 7, + PRECISION: 8, + CONVERSION_TYPE: 9 +}; + +Sk.formatting = {}; + +let handleWidth = function (m, r, prefix, isNumber) { + // print(prefix); + Sk.asserts.assert(typeof (r) === "string"); + + if (m[FMT.FIELD_WIDTH]) { + let fieldWidth = parseInt(m[FMT.FIELD_WIDTH], 10); + let fillChar = m[FMT.FILL_CHAR] || (m[FMT.ZERO_PAD] ? "0" : " "); + let fillAlign = m[FMT.FILL_ALIGN] || (m[FMT.ZERO_PAD] ? "=" : isNumber ? ">" : "<"); + let nFill = fieldWidth - (r.length + (prefix ? prefix.length : 0)); + + if (nFill <= 0) { + return r; + } + + let fill = fillChar.repeat(nFill); + + switch (fillAlign) { + case "=": + if (m[FMT.CONVERSION_TYPE] === "s") { + throw new Sk.builtin.ValueError("'=' alignment not allowed in string format specifier"); + } + return prefix + fill + r; + case ">": + return fill + prefix + r; + case "<": + return prefix + r + fill; + case "^": + let idx = Math.floor(nFill / 2); + return fill.substring(0, idx) + prefix + r + fill.substring(idx); + } + } + return prefix + r; +}; + +let signForNeg = function (m, neg) { + return neg ? "-" : + (m[FMT.SIGN] === "+") ? "+" : + (m[FMT.SIGN] === " ") ? " " : ""; +}; + +let handleInteger = function (m, n, base) { + // TODO: Do we need to tolerate float inputs for integer conversions? + // Python doesn't, but I'm guessing this is something to do with JS's + // int/float ambiguity + Sk.asserts.assert(n instanceof Sk.builtin.int_ || n instanceof Sk.builtin.lng); + + if (m[FMT.PRECISION]) { + throw new Sk.builtin.ValueError("Precision not allowed in integer format"); + } + + let r = n.str$(base, false); + let neg = n.nb$isnegative(); + + let prefix = signForNeg(m, neg); + + if (m[FMT.ALT_FORM]) { + if (base === 16) { + prefix += "0x"; + } else if (base === 8) { + prefix += "0o"; + } else if (base === 2) { + prefix += "0b"; + } + } + + if (m[FMT.CONVERSION_TYPE] === "X") { + r = r.toUpperCase(); + } + + if (m[FMT.CONVERSION_TYPE] === "n") { + r = (+r).toLocaleString(); + } else if (m[FMT.COMMA]) { + var parts = r.toString().split("."); + parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); + r = parts.join("."); + } + + return handleWidth(m, r, prefix, true); +}; + +// Common implementation of __format__ for Python number objects +let formatNumber = function (num, formatSpec, isFractional) { + if (!formatSpec) { // empty or undefined + return num.str$(10, true); + } + let m = formatSpec.match(FORMAT_SPEC_REGEX); + if (!m) { + throw new Sk.builtin.ValueError("Invalid format specifier"); + } + + let conversionType = m[FMT.CONVERSION_TYPE]; + if (!conversionType) { + conversionType = (isFractional ? "g" : "d"); + } + + let validConversions = isFractional ? "fFeEgG%" : "bcdoxXnfFeEgG%"; + if (validConversions.indexOf(conversionType) == -1) { + throw new Sk.builtin.ValueError("Unknown format code '" + m[FMT.CONVERSION_TYPE] + "' for object of type '" + Sk.abstr.typeName(num) + "'"); + } + + switch (conversionType) { + case "d": + case "n": + return handleInteger(m, num, 10); + case "x": + case "X": + return handleInteger(m, num, 16); + case "o": + return handleInteger(m, num, 8); + case "b": + return handleInteger(m, num, 2); + case "c": { + if (m[FMT.SIGN]) { + throw new Sk.builtin.ValueError("Sign not allowed with integer format specifier 'c'"); + } + if (m[FMT.ALT_FORM]) { + throw new Sk.builtin.ValueError("Alternate form not allowed with integer format specifier 'c'"); + } + if (m[FMT.COMMA]) { + throw new Sk.builtin.ValueError("Cannot specify ',' with 'c'"); + } + if (m[FMT.PRECISION]) { + throw new Sk.builtin.ValueError("Cannot specify ',' with 'c'"); + } + return handleWidth(m, String.fromCodePoint(Sk.builtin.asnum$(num)), "", true); + }; + + case "f": + case "F": + case "e": + case "E": + case "g": + case "G": { + if (m[FMT.ALT_FORM]) { + throw new Sk.builtin.ValueError("Alternate form (#) not allowed in float format specifier"); + } + let convValue = Sk.builtin.asnum$(num); + if (typeof convValue === "string") { + convValue = Number(convValue); + } + if (convValue === Infinity) { + return handleWidth(m, "inf", "", true); + } + if (convValue === -Infinity) { + return handleWidth(m, "inf", "-", true); + } + if (isNaN(convValue)) { + return handleWidth(m, "nan", "", true); + } + let neg = false; + if (convValue < 0) { + convValue = -convValue; + neg = true; + } + let convName = ["toExponential", "toFixed", "toPrecision"]["efg".indexOf(conversionType.toLowerCase())]; + let precision = m[FMT.PRECISION] ? parseInt(m[FMT.PRECISION], 10) : 6; + let result = (convValue)[convName](precision); + if ("EFG".indexOf(conversionType) !== -1) { + result = result.toUpperCase(); + } + // Python's 'g' does not show trailing 0s + if (conversionType.toLowerCase() === "g" || !m[FMT.CONVERSION_TYPE]) { + let trailingZeros = result.match(/\.(\d*[1-9])?(0+)$/); + if (trailingZeros) { + let [_, hasMoreDigits, zs] = trailingZeros; + // Python's default conversion shows at least one trailing zero + result = result.slice(0, hasMoreDigits ? -zs.length : -(zs.length + 1)); + } + if (result.indexOf(".") == -1 && !m[FMT.CONVERSION_TYPE]) { + result += ".0"; + } + } + if (m[FMT.COMMA]) { + var parts = result.toString().split("."); + parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); + result = parts.join("."); + } + + return handleWidth(m, result, signForNeg(m, neg), true); + }; + + case "%": { + if (m[FMT.ALT_FORM]) { + throw new Sk.builtin.ValueError("Alternate form (#) not allowed with format specifier '%'"); + } + let convValue = Sk.builtin.asnum$(num); + if (typeof convValue === "string") { + convValue = Number(convValue); + } + if (convValue === Infinity) { + return handleWidth(m, "inf%", "", true); + } + if (convValue === -Infinity) { + return handleWidth(m, "inf%", "-", true); + } + if (isNaN(convValue)) { + return handleWidth(m, "nan%", "", true); + } + let neg = false; + if (convValue < 0) { + convValue = -convValue; + neg = true; + } + let precision = m[FMT.PRECISION] ? parseInt(m[FMT.PRECISION], 10) : 6; + let result = (convValue * 100.0).toFixed(precision) + "%"; + return handleWidth(m, result, signForNeg(m, neg), true); + }; + + default: + throw new Sk.builtin.ValueError("Unknown format code '" + m[FMT.CONVERSION_TYPE] + "'"); + } +}; + +Sk.formatting.mkNumber__format__ = (isFractional) => function (format_spec) { + if (!Sk.builtin.checkString(format_spec)) { + throw new Sk.builtin.TypeError("format() argument 2 must be str, not " + Sk.abstr.typeName(format_spec)); + } + return new Sk.builtin.str(formatNumber(this, format_spec.$jsstr(), isFractional)); +}; + +let formatString = function (self, format_spec) { + Sk.builtin.pyCheckArgsLen("__format__", arguments.length, 2, 2); + + if (!Sk.builtin.checkString(format_spec)) { + throw new Sk.builtin.TypeError("format() argument 2 must be str, not " + Sk.abstr.typeName(format_spec)); + } + + let m = format_spec.$jsstr().match(FORMAT_SPEC_REGEX); + if (m[FMT.CONVERSION_TYPE] && m[FMT.CONVERSION_TYPE] !== "s") { + throw new Sk.builtin.ValueError("Unknown format code '" + m[FMT.CONVERSION_TYPE] + "' for object of type 'str'"); + } + + if (m[FMT.SIGN]) { + throw new Sk.builtin.ValueError("Sign not allowed in string format specifier"); + } + + if (m[FMT.ALT_FORM]) { + throw new Sk.builtin.ValueError("Alternate form (#) not allowed with string format specifier"); + } + + if (m[FMT.COMMA]) { + throw new Sk.builtin.ValueError("Cannot specify ',' with 's'"); + } + + let value = self.v; + + if (m[FMT.PRECISION]) { + value = value.substring(0, m[FMT.PRECISION]); + } + + return new Sk.builtin.str(handleWidth(m, value, "", false)); +}; + +// str.format() implementation var format = function (kwa) { // following PEP 3101 @@ -17,7 +290,7 @@ var format = function (kwa) { return args.v; } index = 0; - regex = /{(((?:\d+)|(?:\w+))?((?:\.(\w+))|(?:\[((?:\d+)|(?:\w+))\])?))?(?:\!([rs]))?(?:\:((?:(.)?([<\>\=\^]))?([\+\-\s])?(#)?(0)?(\d+)?(,)?(?:\.(\d+))?([bcdeEfFgGnosxX%])?))?}/g; + regex = /{(((?:\d+)|(?:\w+))?((?:\.(\w+))|(?:\[((?:\d+)|(?:\w+))\])?))?(?:\!([rs]))?(?:\:([^}]*))?}/g; // ex: {o.name!r:*^+#030,.9b} // Field 1, Field_name, o.name // Field 2, arg_name, o @@ -26,319 +299,64 @@ var format = function (kwa) { // Field 5, element_index, [0] // Field 6, conversion, r // Field 7, format_spec,*^+#030,.9b - // Field 9, fill_character,* - // Field 10, fill_align, ^ - // Field 11, sign, + - // Field 12, 0x, # - // Filed 13, sign-aware 0 padding, 0 - // Field 14, width, 30 - // Field 15, comma, , - // Field 16, precision, .9 - // Field 17, conversionType, b // Detect empty/int/complex name // retrive field value // hand off format spec // return resulting spec to function - - if(kwargs.size !== 0){ - - var kwItems = Sk.misceval.callsimArray(Sk.builtin.dict.prototype["items"], [kwargs]); - - for (var n in kwItems.v){ - arg_dict[kwItems.v[n].v[0].v] = kwItems.v[n].v[1]; + if (kwargs.size !== 0) { + let iter, k, v; + for (iter = kwargs.tp$iter(), k = iter.tp$iternext(); + k !== undefined; + k = iter.tp$iternext()) { + v = kwargs.mp$lookup(k); + arg_dict[k.v] = v; } } - for(var i in args.v){ - if(i !== "0") { - arg_dict[i-1] = args.v[i]; + for (var i in args.v) { + if (i !== "0") { + arg_dict[i - 1] = args.v[i]; } } - replFunc = function (substring, field_name, arg_name, attr_name, attribute_name, element_index, conversion, format_spec, fill_char, fill_align, sign, zero_pad, sign_aware, fieldWidth, comma, precision, conversionType, offset, str_whole) { - var return_str; - var formatNumber; - var formatFormat; - var result; - var base; - var value; - var handleWidth; - var alternateForm; - var precedeWithSign; - var blankBeforePositive; - var leftAdjust; - var centerAdjust; - var zeroPad; - var convName; - var convValue; - var percent; - var container; - fieldWidth = Sk.builtin.asnum$(fieldWidth); - precision = Sk.builtin.asnum$(precision); - - if(element_index !== undefined && element_index !== ""){ - container = arg_dict[arg_name]; + replFunc = function (substring, field_name, arg_name, attr_name, attribute_name, element_index, conversion, format_spec, offset, str_whole) { + let value; + + if (element_index !== undefined && element_index !== "") { + let container = arg_dict[arg_name]; if (container.constructor === Array) { value = container[element_index]; + } else if (/^\d+$/.test(element_index)) { + value = Sk.abstr.objectGetItem(container, new Sk.builtin.int_(parseInt(element_index, 10)), false); } else { - if (container instanceof Sk.builtin.dict) { - value = Sk.abstr.objectGetItem(container, new Sk.builtin.str(element_index), false); - } else { - value = Sk.abstr.objectGetItem(container, new Sk.builtin.int_(parseInt(element_index, 10)), false); - } + value = Sk.abstr.objectGetItem(container, new Sk.builtin.str(element_index), false); } index++; - } else if(attribute_name !== undefined && attribute_name !== ""){ - value = arg_dict[arg_name][attribute_name]; - index++; - } else if(arg_name !== undefined && arg_name !== ""){ + } else if (attribute_name !== undefined && attribute_name !== "") { + value = Sk.abstr.gattr(arg_dict[arg_name || (index++)], new Sk.builtin.str(attribute_name)); + } else if (arg_name !== undefined && arg_name !== "") { value = arg_dict[arg_name]; + } else if (field_name === undefined || field_name === "") { + value = arg_dict[index]; index++; - } else if(field_name === undefined || field_name === ""){ - return_str = arg_dict[index]; - index++; - value = return_str; - } else if(field_name instanceof Sk.builtin.int_ || - field_name instanceof Sk.builtin.float_ || - field_name instanceof Sk.builtin.lng || !isNaN(parseInt(field_name, 10))){ - return_str = arg_dict[field_name]; + } else if (field_name instanceof Sk.builtin.int_ || + field_name instanceof Sk.builtin.float_ || + field_name instanceof Sk.builtin.lng || /^\d+$/.test(field_name)) { + value = arg_dict[field_name]; index++; - value = return_str; - } - - if (precision === "") { // ff passes '' here aswell causing problems with G,g, etc. - precision = undefined; - } - if(fill_char === undefined || fill_char === ""){ - fill_char = " "; } - zeroPad = false; - leftAdjust = false; - centerAdjust = false; - blankBeforePositive = false; - precedeWithSign = false; - alternateForm = false; - if (format_spec) { - if(sign !== undefined && sign !== ""){ - if ("-".indexOf(sign) !== -1) { - leftAdjust = true; - } else if ("+".indexOf(sign) !== -1) { - precedeWithSign = true; - } else if (" ".indexOf(sign) !== -1) { - blankBeforePositive = true; - } - } - if(zero_pad){ - alternateForm = "#".indexOf(zero_pad) !== -1; - } - if(fieldWidth !== undefined && fieldWidth !== ""){ - if(fill_char === undefined || fill_char === ""){ - fill_char = " "; - } - } - if("%".indexOf(conversionType) !== -1){ - percent = true; - } - } - if (precision) { - precision = parseInt(precision, 10); - } - - formatFormat = function(value){ - var r; - var s; - if(conversion === undefined || conversion === "" || conversion == "s"){ - s = new Sk.builtin.str(value); - return s.v; - } else if(conversion == "r"){ - r = Sk.builtin.repr(value); - return r.v; - } - - }; - - handleWidth = function (prefix, r) { - // print(prefix); - var totLen; - r = Sk.ffi.remapToJs(r); - - var j; - if(percent){ - r = r +"%"; - } - if (fieldWidth !== undefined && fieldWidth !== "") { - fieldWidth = parseInt(fieldWidth, 10); - totLen = r.length + prefix.length; - if (zeroPad) { - for (j = totLen; j < fieldWidth; ++j) { - r = "0" + r; - } - } else if (leftAdjust) { - for (j = totLen; j < fieldWidth; ++j) { - r = r + fill_char; - } - } else if(">".indexOf(fill_align) !== -1){ - for (j = totLen; j < fieldWidth; ++j) { - prefix = fill_char + prefix; - } - } else if("^".indexOf(fill_align) !== -1){ - for (j = totLen; j < fieldWidth; ++j) { - if(j % 2 === 0){ - prefix = fill_char + prefix; - } else if ( j % 2 === 1){ - r = r + fill_char; - } - } - } else if("=".indexOf(fill_align) !== -1){ - for (j = totLen; j < fieldWidth; ++j) { - r = fill_char + r; - } - } else{ - for (j = totLen; j < fieldWidth; ++j) { - r = r + fill_char; - } - } - } - return formatFormat(prefix + r); - }; - - formatNumber = function(n, base){ - var precZeroPadded; - var prefix; - var neg; - var r; - - base = Sk.builtin.asnum$(base); - neg = false; - - if(format_spec === undefined){ - return formatFormat(value); - } - - if (typeof n === "number") { - if (n < 0) { - n = -n; - neg = true; - } - r = n.toString(base); - } else if (n instanceof Sk.builtin.float_) { - r = n.str$(base, false); - if (r.length > 2 && r.substr(-2) === ".0") { - r = r.substr(0, r.length - 2); - } - neg = n.nb$isnegative(); - } else if (n instanceof Sk.builtin.int_) { - r = n.str$(base, false); - neg = n.nb$isnegative(); - } else if (n instanceof Sk.builtin.lng) { - r = n.str$(base, false); - neg = n.nb$isnegative(); // neg = n.size$ < 0; RNL long.js change - } else{ - r = n; - } - - if (precision) { - n = Number(r); - if (n < 0) { - n = -n; - neg = true; - } - r = n.toFixed(precision); - } - - precZeroPadded = false; - prefix = ""; - - if (neg) { - prefix = "-"; - } else if (precedeWithSign) { - prefix = "+" ; - } else if (blankBeforePositive) { - prefix = " " ; - } - - if (alternateForm) { - if (base === 16) { - prefix += "0x"; - } else if (base === 8 && !precZeroPadded && r !== "0") { - prefix += "0o"; - } else if (base === 2 && !precZeroPadded && r !== "0"){ - prefix += "0b"; - } - } - - if(conversionType === "n"){ - r=r.toLocaleString(); - } else if(",".indexOf(comma) !== -1){ - var parts = r.toString().split("."); - parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); - r = parts.join("."); - } - return handleWidth(prefix, r); - }; - - base = 10; - if(conversionType === "d" || conversionType === "n" || conversionType === "" || conversionType === undefined){ - return formatNumber(value, 10); - }else if (conversionType === "b") { - return formatNumber(value, 2); - }else if (conversionType === "o") { - return formatNumber(value, 8); - } else if (conversionType === "x") { - return formatNumber(value, 16); - } else if (conversionType === "X") { - return formatNumber(value, 16).toUpperCase(); - } else if (conversionType === "f" || conversionType === "F" || conversionType === "e" || conversionType === "E" || conversionType === "g" || conversionType === "G") { - if(alternateForm){ - throw new Sk.builtin.ValueError("Alternate form (#) not allowed in float format specifier"); - } - convValue = Sk.builtin.asnum$(value); - if (typeof convValue === "string") { - convValue = Number(convValue); - } - if (convValue === Infinity) { - return handleWidth("","inf"); - } - if (convValue === -Infinity) { - return handleWidth("-","inf"); - } - if (isNaN(convValue)) { - return handleWidth("","nan"); - } - convName = ["toExponential", "toFixed", "toPrecision"]["efg".indexOf(conversionType.toLowerCase())]; - if (precision === undefined || precision === "") { - if (conversionType === "e" || conversionType === "E" || conversionType === "%") { - precision = parseInt(6, 10); - } else if (conversionType === "f" || conversionType === "F") { - precision = parseInt(6, 10); - } - } - result = (convValue)[convName](precision); - if ("EFG".indexOf(conversionType) !== -1) { - result = result.toUpperCase(); - } - return formatNumber(result, 10); - } else if (conversionType === "c") { - if (typeof value === "number") { - return handleWidth("", String.fromCharCode(value)); - } else if (value instanceof Sk.builtin.int_) { - return handleWidth("", String.fromCharCode(value.v)); - } else if (value instanceof Sk.builtin.float_) { - return handleWidth("", String.fromCharCode(value.v)); - } else if (value instanceof Sk.builtin.lng) { - return handleWidth("", String.fromCharCode(value.str$(10, false)[0])); - } else if (value.constructor === Sk.builtin.str) { - return handleWidth("", value.v.substr(0, 1)); - } else { - throw new Sk.builtin.TypeError("an integer is required"); - } - } else if (percent) { - if(precision === undefined){precision = parseInt(7,10);} - return formatNumber(value.nb$multiply(new Sk.builtin.int_(100)), 10); + if (conversion === "s") { + value = new Sk.builtin.str(value); + } else if (conversion === "r") { + value = Sk.builtin.repr(value); + } else if (conversion !== "" && conversion !== undefined) { + throw new Sk.builtin.ValueError("Unknown conversion specifier " + conversion); } + // TODO "!a" I guess? + return Sk.abstr.objectFormat(value, new Sk.builtin.str(format_spec)).$jsstr(); }; ret = args.v[0].v.replace(regex, replFunc); @@ -347,3 +365,4 @@ var format = function (kwa) { format["co_kwargs"] = true; Sk.builtin.str.prototype["format"] = new Sk.builtin.func(format); +Sk.builtin.str.prototype["__format__"] = new Sk.builtin.func(formatString); diff --git a/src/fromcodepoint.js b/src/fromcodepoint.js index 347cc59418..c6f6eab7f4 100644 --- a/src/fromcodepoint.js +++ b/src/fromcodepoint.js @@ -1,7 +1,7 @@ /*! https://mths.be/fromcodepoint v0.2.1 by @mathias */ if (!String.fromCodePoint) { - (function() { - var defineProperty = (function() { + (function () { + var defineProperty = (function () { // IE 8 only supports `Object.defineProperty` on DOM elements var result; try { @@ -13,7 +13,7 @@ if (!String.fromCodePoint) { }()); var stringFromCharCode = String.fromCharCode; var floor = Math.floor; - var fromCodePoint = function(_) { + var fromCodePoint = function (_) { var MAX_SIZE = 0x4000; var codeUnits = []; var highSurrogate; diff --git a/src/function.js b/src/function.js index 20cb150b0a..e4a9c3046e 100644 --- a/src/function.js +++ b/src/function.js @@ -1,211 +1,3 @@ -/** - * @namespace Sk.builtin - */ - - -/** - * Check arguments to Python functions to ensure the correct number of - * arguments are passed. - * - * @param {string} name the name of the function - * @param {Object} args the args passed to the function - * @param {number} minargs the minimum number of allowable arguments - * @param {number=} maxargs optional maximum number of allowable - * arguments (default: Infinity) - * @param {boolean=} kwargs optional true if kwargs, false otherwise - * (default: false) - * @param {boolean=} free optional true if free vars, false otherwise - * (default: false) - */ -Sk.builtin.pyCheckArgs = function (name, args, minargs, maxargs, kwargs, free) { - var nargs = args.length; - var msg = ""; - - if (maxargs === undefined) { - maxargs = Infinity; - } - if (kwargs) { - nargs -= 1; - } - if (free) { - nargs -= 1; - } - if ((nargs < minargs) || (nargs > maxargs)) { - if (minargs === maxargs) { - msg = name + "() takes exactly " + minargs + " arguments"; - } else if (nargs < minargs) { - msg = name + "() takes at least " + minargs + " arguments"; - } else { - msg = name + "() takes at most " + maxargs + " arguments"; - } - msg += " (" + nargs + " given)"; - throw new Sk.builtin.TypeError(msg); - } -}; -Sk.exportSymbol("Sk.builtin.pyCheckArgs", Sk.builtin.pyCheckArgs); - -/** - * Check arguments to Python functions to ensure the correct number of - * arguments are passed. - * - * @param {string} name the name of the function - * @param {number} nargs the args passed to the function - * @param {number} minargs the minimum number of allowable arguments - * @param {number=} maxargs optional maximum number of allowable - * arguments (default: Infinity) - * @param {boolean=} kwargs optional true if kwargs, false otherwise - * (default: false) - * @param {boolean=} free optional true if free vars, false otherwise - * (default: false) - */ -Sk.builtin.pyCheckArgsLen = function (name, nargs, minargs, maxargs, kwargs, free) { - var msg = ""; - - if (maxargs === undefined) { - maxargs = Infinity; - } - if (kwargs) { - nargs -= 1; - } - if (free) { - nargs -= 1; - } - if ((nargs < minargs) || (nargs > maxargs)) { - if (minargs === maxargs) { - msg = name + "() takes exactly " + minargs + " arguments"; - } else if (nargs < minargs) { - msg = name + "() takes at least " + minargs + " arguments"; - } else { - msg = name + "() takes at most " + maxargs + " arguments"; - } - msg += " (" + nargs + " given)"; - throw new Sk.builtin.TypeError(msg); - } -}; - -/** - * Check type of argument to Python functions. - * - * @param {string} name the name of the argument - * @param {string} exptype string of the expected type name - * @param {boolean} check truthy if type check passes, falsy otherwise - */ -Sk.builtin.pyCheckType = function (name, exptype, check) { - if (!check) { - throw new Sk.builtin.TypeError(name + " must be a " + exptype); - } -}; -Sk.exportSymbol("Sk.builtin.pyCheckType", Sk.builtin.pyCheckType); - -Sk.builtin.checkSequence = function (arg) { - return (arg !== null && arg.mp$subscript !== undefined); -}; -Sk.exportSymbol("Sk.builtin.checkSequence", Sk.builtin.checkSequence); - -/** - * Use this to test whether or not a Python object is iterable. You should **not** rely - * on the presence of tp$iter on the object as a good test, as it could be a user defined - * class with `__iter__` defined or ``__getitem__`` This tests for all of those cases - * - * @param arg {Object} A Python object - * @returns {boolean} true if the object is iterable - */ -Sk.builtin.checkIterable = function (arg) { - var ret = false; - if (arg !== null ) { - try { - ret = Sk.abstr.iter(arg); - if (ret) { - return true; - } else { - return false; - } - } catch (e) { - if (e instanceof Sk.builtin.TypeError) { - return false; - } else { - throw e; - } - } - } - return ret; -}; -Sk.exportSymbol("Sk.builtin.checkIterable", Sk.builtin.checkIterable); - -Sk.builtin.checkCallable = function (obj) { - // takes care of builtin functions and methods, builtins - if (typeof obj === "function") { - return true; - } - // takes care of python function, methods and lambdas - if (obj instanceof Sk.builtin.func) { - return true; - } - // takes care of instances of methods - if (obj instanceof Sk.builtin.method) { - return true; - } - // go up the prototype chain to see if the class has a __call__ method - if (Sk.abstr.lookupSpecial(obj, Sk.builtin.str.$call) !== undefined) { - return true; - } - return false; -}; - -Sk.builtin.checkNumber = function (arg) { - return (arg !== null && (typeof arg === "number" || - arg instanceof Sk.builtin.int_ || - arg instanceof Sk.builtin.float_ || - arg instanceof Sk.builtin.lng)); -}; -Sk.exportSymbol("Sk.builtin.checkNumber", Sk.builtin.checkNumber); - -/** - * Checks for complex type, delegates to internal method - * Most skulpt users would search here! - */ -Sk.builtin.checkComplex = function (arg) { - return Sk.builtin.complex._complex_check(arg); -}; -Sk.exportSymbol("Sk.builtin.checkComplex", Sk.builtin.checkComplex); - -Sk.builtin.checkInt = function (arg) { - return (arg !== null) && ((typeof arg === "number" && arg === (arg | 0)) || - arg instanceof Sk.builtin.int_ || - arg instanceof Sk.builtin.lng); -}; -Sk.exportSymbol("Sk.builtin.checkInt", Sk.builtin.checkInt); - -Sk.builtin.checkFloat = function (arg) { - return (arg !== null) && (arg instanceof Sk.builtin.float_); -}; -Sk.exportSymbol("Sk.builtin.checkFloat", Sk.builtin.checkFloat); - -Sk.builtin.checkString = function (arg) { - return (arg !== null && arg.__class__ == Sk.builtin.str); -}; -Sk.exportSymbol("Sk.builtin.checkString", Sk.builtin.checkString); - -Sk.builtin.checkClass = function (arg) { - return (arg !== null && arg.sk$type); -}; -Sk.exportSymbol("Sk.builtin.checkClass", Sk.builtin.checkClass); - -Sk.builtin.checkBool = function (arg) { - return (arg instanceof Sk.builtin.bool); -}; -Sk.exportSymbol("Sk.builtin.checkBool", Sk.builtin.checkBool); - -Sk.builtin.checkNone = function (arg) { - return (arg instanceof Sk.builtin.none); -}; -Sk.exportSymbol("Sk.builtin.checkNone", Sk.builtin.checkNone); - -Sk.builtin.checkFunction = function (arg) { - return (arg !== null && arg.tp$call !== undefined); -}; -Sk.exportSymbol("Sk.builtin.checkFunction", Sk.builtin.checkFunction); - /** * @constructor * Sk.builtin.func @@ -233,79 +25,163 @@ Sk.exportSymbol("Sk.builtin.checkFunction", Sk.builtin.checkFunction); * to access them via dict-style lookup for closure. * */ -Sk.builtin.func = function (code, globals, closure, closure2) { - if (!(this instanceof Sk.builtin.func)) { - // otherwise it assigned .func_code and .func_globals somewhere and in certain - // situations that will cause a lot of strange errors. - throw new Error("builtin func should be called as a class with `new`"); - } - - var k; - this.func_code = code; - this.func_globals = globals || null; - if (closure2 !== undefined) { - // todo; confirm that modification here can't cause problems - for (k in closure2) { - closure[k] = closure2[k]; +Sk.builtin.func = Sk.abstr.buildNativeClass("function", { + constructor: function func(code, globals, closure, closure2) { + Sk.asserts.assert(this instanceof Sk.builtin.func, "builtin func should be called as a class with `new`"); + + this.func_code = code; + this.func_globals = globals || null; + + this.$name = (code.co_name && code.co_name.v) || code.name || ""; + this.$d = Sk.builtin.dict ? new Sk.builtin.dict() : undefined; + this.$doc = code.$doc; + this.$module = (Sk.globals && Sk.globals["__name__"]) || Sk.builtin.none.none$; + this.$qualname = (code.co_qualname && code.co_qualname.v) || this.$name; + + if (closure2 !== undefined) { + // todo; confirm that modification here can't cause problems + for (let k in closure2) { + closure[k] = closure2[k]; + } + } + this.func_closure = closure; + this.$memoiseFlags(); + this.memoised = code.co_fastcall || undefined; + if (code.co_fastcall) { + this.tp$call = code; } - } - - this["$d"] = { - "__name__": code["co_name"], - "__class__": Sk.builtin.func - }; - this.func_closure = closure; - this.tp$name = (this.func_code && this.func_code["co_name"] && this.func_code["co_name"].v) || this.func_code.name || ""; - return this; -}; -Sk.abstr.setUpInheritance("function", Sk.builtin.func, Sk.builtin.object); + }, + slots: { + tp$getattr: Sk.generic.getAttr, + tp$descr_get: function (obj, objtype) { + if (obj === null) { + return this; + } + return new Sk.builtin.method(this, obj); + }, + $r: function () { + return new Sk.builtin.str(""); + }, + tp$call: function (posargs, kw) { + // Property reads from func_code are slooow, but + // the existing external API allows setup first, so as a + // hack we delay this initialisation. + // TODO change the external API to require all the co_vars + // to be supplied at construction time! + if (!this.memoised) { + this.$memoiseFlags(); + this.memoised = true; + } -Sk.exportSymbol("Sk.builtin.func", Sk.builtin.func); + // Fast path for JS-native functions (which should be implemented + // in a separate tp$call, really) + if (this.co_argcount === undefined && this.co_varnames === undefined && !this.co_kwargs && !this.func_closure) { + // It's a JS function with no type info, don't hang around + // resolving anything. + if (kw && kw.length !== 0) { + throw new Sk.builtin.TypeError(this.$name + "() takes no keyword arguments"); + } + return this.func_code.apply(this.func_globals, posargs); + } + // end js fast path -Sk.builtin.func.prototype.tp$name = "function"; + let args = this.$resolveArgs(posargs, kw); + if (this.func_closure) { + args.push(this.func_closure); + } + // note: functions expect 'this' to be globals to avoid having to + // slice/unshift onto the main args + return this.func_code.apply(this.func_globals, args); + }, + }, + getsets: { + __name__: { + $get: function () { + return new Sk.builtin.str(this.$name); + }, + $set: function (value) { + if (!Sk.builtin.checkString(value)) { + throw new Sk.builtin.TypeError("__name__ must be set to a string object"); + } + this.$name = value.$jsstr(); + }, + }, + __qualname__: { + $get: function () { + return new Sk.builtin.str(this.$qualname); + }, + $set: function (value) { + if (!Sk.builtin.checkString(value)) { + throw new Sk.builtin.TypeError("__qualname__ must be set to a string object"); + } + this.$qualname = value.$jsstr(); + }, + }, + __dict__: Sk.generic.getSetDict, + __defaults__: { + $get: function () { + return new Sk.builtin.tuple(this.$defaults); + }, // technically this is a writable property but we'll leave it as read-only for now + }, + __doc__: { + $get: function () { + return new Sk.builtin.str(this.$doc); + }, + }, + }, + proto: { + $memoiseFlags: function () { + this.co_varnames = this.func_code.co_varnames; + this.co_argcount = this.func_code.co_argcount; + if (this.co_argcount === undefined && this.co_varnames) { + this.co_argcount = this.co_argcount = this.co_varnames.length; + } + this.co_kwonlyargcount = this.func_code.co_kwonlyargcount || 0; + this.co_varargs = this.func_code.co_varargs; + this.co_kwargs = this.func_code.co_kwargs; + this.$defaults = this.func_code.$defaults || []; + this.$kwdefs = this.func_code.$kwdefs || []; + }, -Sk.builtin.func.prototype.tp$descr_get = function (obj, objtype) { - Sk.asserts.assert(!(obj === undefined && objtype === undefined)); - if (objtype && objtype.tp$name in Sk.builtin && Sk.builtin[objtype.tp$name] === objtype) { - // it's a builtin - return new Sk.builtin.method(this, obj, objtype, true); } - return new Sk.builtin.method(this, obj, objtype); -}; +}); -Sk.builtin.func.pythonFunctions = ["__get__"]; -Sk.builtin.func.prototype.__get__ = function __get__(self, instance, owner) { - Sk.builtin.pyCheckArgsLen("__get__", arguments.length, 1, 2, false, true); - if (instance === Sk.builtin.none.none$ && owner === Sk.builtin.none.none$) { - throw new Sk.builtin.TypeError("__get__(None, None) is invalid"); - } +Sk.builtin.func.prototype.$resolveArgs = function (posargs, kw) { + // The rest of this function is a logical Javascript port of + // _PyEval_EvalCodeWithName, and follows its logic, + // plus fast-paths imported from _PyFunction_FastCall* as marked - return self.tp$descr_get(instance, owner); -}; + let co_argcount = this.co_argcount; -Sk.builtin.func.prototype.tp$getname = function () { - return (this.func_code && this.func_code["co_name"] && this.func_code["co_name"].v) || this.func_code.name || ""; -}; + if (co_argcount === undefined) { + co_argcount = this.co_varnames ? this.co_varnames.length : posargs.length; + } + let varnames = this.co_varnames || []; + let co_kwonlyargcount = this.co_kwonlyargcount || 0; + let totalArgs = co_argcount + co_kwonlyargcount; -Sk.builtin.func.prototype.tp$call = function (posargs, kw) { - // This function is a logical Javascript port of - // _PyEval_EvalCodeWithName, and follows its logic. + // Fast path from _PyFunction_FastCallDict + if (co_kwonlyargcount === 0 && !this.co_kwargs && (!kw || kw.length === 0) && !this.co_varargs) { + if (posargs.length == co_argcount) { + return posargs; + } else if (posargs.length === 0 && this.$defaults && + this.$defaults.length === co_argcount) { + for (let i = 0; i != this.$defaults.length; i++) { + posargs[i] = this.$defaults[i]; + } + return posargs; + } + } + // end fast path from _PyFunction_FastCallDict - let co_argcount = this.func_code.co_argcount; - if (co_argcount === undefined) { - co_argcount = this.func_code.co_varnames ? this.func_code.co_varnames.length : posargs.length; - } - let varnames = this.func_code.co_varnames || []; - let co_kwonlyargcount = this.func_code.co_kwonlyargcount || 0; - let totalArgs = co_argcount + co_kwonlyargcount; let kwargs; /* Create a NOT-a-dictionary for keyword parameters (**kwags) */ - if (this.func_code.co_kwargs) { + if (this.co_kwargs) { kwargs = []; } @@ -313,34 +189,35 @@ Sk.builtin.func.prototype.tp$call = function (posargs, kw) { let nposargs = posargs.length; let args = (posargs.length <= co_argcount) ? posargs : posargs.slice(0, co_argcount); + /* Pack other positional arguments into the *args argument */ - if (this.func_code.co_varargs) { + if (this.co_varargs) { let vararg = (posargs.length > args.length) ? posargs.slice(args.length) : []; args[totalArgs] = new Sk.builtin.tuple(vararg); } else if (nposargs > co_argcount) { - throw new Sk.builtin.TypeError(this.tp$getname() + "() takes " + co_argcount + " positional argument" + (co_argcount == 1 ? "" : "s") + " but " + nposargs + (nposargs == 1 ? " was " : " were ") + " given"); + throw new Sk.builtin.TypeError(this.$name + "() takes " + co_argcount + " positional argument" + (co_argcount == 1 ? "" : "s") + " but " + nposargs + (nposargs == 1 ? " was " : " were ") + " given"); } /* Handle keyword arguments */ if (kw) { if (this.func_code["no_kw"]) { - throw new Sk.builtin.TypeError(this.tp$getname() + "() takes no keyword arguments"); + throw new Sk.builtin.TypeError(this.$name + "() takes no keyword arguments"); } for (let i = 0; i < kw.length; i += 2) { let name = kw[i]; // JS string - let value = kw[i+1]; // Python value + let value = kw[i + 1]; // Python value let idx = varnames.indexOf(name); if (idx >= 0) { if (args[idx] !== undefined) { - throw new Sk.builtin.TypeError(this.tp$getname() + "() got multiple values for argument '" + name + "'"); + throw new Sk.builtin.TypeError(this.$name + "() got multiple values for argument '" + name + "'"); } args[idx] = value; } else if (kwargs) { kwargs.push(new Sk.builtin.str(name), value); } else { - throw new Sk.builtin.TypeError(this.tp$getname() + "() got an unexpected keyword argument '" + name + "'"); + throw new Sk.builtin.TypeError(this.$name + "() got an unexpected keyword argument '" + name + "'"); } } } @@ -351,7 +228,7 @@ Sk.builtin.func.prototype.tp$call = function (posargs, kw) { /* Add missing positional arguments (copy default values from defs) (also checks for missing args where no defaults) */ { - let defaults = this.func_code.$defaults || []; + let defaults = this.$defaults || []; let i = 0, missing = [], missingUnnamed = false; // Positional args for which we *don't* have a default let defaultStart = co_argcount - defaults.length; @@ -363,8 +240,8 @@ Sk.builtin.func.prototype.tp$call = function (posargs, kw) { } } } - if (missing.length != 0 && (this.func_code.co_argcount || this.func_code.co_varnames)) { - throw new Sk.builtin.TypeError(this.tp$getname() + "() missing " + missing.length + " required argument" + (missing.length==1?"":"s") + (missingUnnamed ? "" : (": " + missing.join(", ")))); + if (missing.length != 0 && (this.co_argcount || this.co_varnames)) { + throw new Sk.builtin.TypeError(this.$name + "() missing " + missing.length + " required argument" + (missing.length == 1 ? "" : "s") + (missingUnnamed ? "" : (": " + missing.join(", ")))); } for (; i < co_argcount; i++) { if (args[i] === undefined) { @@ -377,23 +254,22 @@ Sk.builtin.func.prototype.tp$call = function (posargs, kw) { if (co_kwonlyargcount > 0) { let missing = []; - let kwdefs = this.func_code.$kwdefs; + let kwdefs = this.$kwdefs; for (let i = co_argcount; i < totalArgs; i++) { if (args[i] === undefined) { - if (kwdefs[i-co_argcount] !== undefined) { - args[i] = kwdefs[i-co_argcount]; + if (kwdefs[i - co_argcount] !== undefined) { + args[i] = kwdefs[i - co_argcount]; } else { missing.push(varnames[i]); } } } if (missing.length !== 0) { - throw new Sk.builtin.TypeError(this.tp$getname() + "() missing " + missing.length + " required keyword argument" + (missing.length==1?"":"s") + ": " + missing.join(", ")); + throw new Sk.builtin.TypeError(this.$name + "() missing " + missing.length + " required keyword argument" + (missing.length == 1 ? "" : "s") + ": " + missing.join(", ")); } } - if (this.func_closure) { // todo; OK to modify? if (varnames) { @@ -402,25 +278,12 @@ Sk.builtin.func.prototype.tp$call = function (posargs, kw) { args.push(undefined); } } - - args.push(this.func_closure); } if (kwargs) { args.unshift(kwargs); } - // note: functions expect 'this' to be globals to avoid having to - // slice/unshift onto the main args - return this.func_code.apply(this.func_globals, args); - + return args; }; -Sk.builtin.func.prototype["$r"] = function () { - var name = this.tp$getname(); - if (name in Sk.builtins && this === Sk.builtins[name]) { - return new Sk.builtin.str(""); - } else { - return new Sk.builtin.str(""); - } -}; diff --git a/src/generator.js b/src/generator.js index 0bce2a1ed3..5e02aeebbe 100644 --- a/src/generator.js +++ b/src/generator.js @@ -62,13 +62,13 @@ Sk.builtin.generator.prototype.tp$iternext = function (canSuspend, yielded) { var self = this; this["gi$running"] = true; if (yielded === undefined) { - yielded = null; + yielded = Sk.builtin.none.none$; } this["gi$sentvalue"] = yielded; // note: functions expect 'this' to be globals to avoid having to // slice/unshift onto the main args - args = [ this ]; + args = [this]; if (this.func_closure) { args.push(this.func_closure); } @@ -97,10 +97,6 @@ Sk.builtin.generator.prototype.tp$iternext = function (canSuspend, yielded) { })(ret); }; -Sk.builtin.generator.prototype.next$ = function (self) { - return self.tp$iternext(true); -}; - Sk.builtin.generator.prototype["$r"] = function () { return new Sk.builtin.str(""); }; diff --git a/src/generic.js b/src/generic.js new file mode 100644 index 0000000000..21929c250e --- /dev/null +++ b/src/generic.js @@ -0,0 +1,285 @@ +/** + * @namespace Sk.generic + * + * @description + * Some useful default methods for native classes + * + */ +Sk.generic = {}; + +/** @typedef {Sk.builtin.object} */ var pyObject; +/** @typedef {Sk.builtin.type|Function} */ var typeObject; + +/** + * @method + * + * @param {Sk.builtin.str} pyName Python string name of the attribute + * @param {boolean=} canSuspend Can we return a suspension? + * + * @description + * The default implementation of __getattribute__. This is used by most instances and will be inherited from object. + * + * If undefined is returned by this method then the object has no attribute + * It is the responsibility of the user to throw the error. + * Currently this is thrown in Sk.abstr.gattr or directly in compile code + * + * @return {Sk.builtin.object|undefined} + */ +Sk.generic.getAttr = function __getattribute__(pyName, canSuspend) { + let f; + const type = this.ob$type; + const descr = type.$typeLookup(pyName); + // look in the type for a descriptor + if (descr !== undefined) { + f = descr.tp$descr_get; + if (f && Sk.builtin.checkDataDescr(descr)) { + return f.call(descr, this, type, canSuspend); + } + } + + const dict = this.$d; + + if (dict !== undefined) { + const res = dict.quick$lookup(pyName); + if (res !== undefined) { + return res; + } + } + if (f) { + return f.call(descr, this, type, canSuspend); + } + if (descr != null) { + return descr; + } + return; +}; +Sk.exportSymbol("Sk.generic.getAttr", Sk.generic.getAttr); + +/** + * @method + * + * @description + * The default implementation of __setattr__/__delattr__ used by most instance objects + * There is no return value for this function + * An error will be thrown if no attribute exists + * + * A value=undefined signifies that the attribute is to be deleted + * + * @param {Sk.builtin.str} pyName + * @param {Sk.builtin.object|undefined} value + * @param {boolean=} canSuspend ? can this function suspend + * @return {undefined} + */ +Sk.generic.setAttr = function __setattr__(pyName, value, canSuspend) { + const descr = this.ob$type.$typeLookup(pyName); + // otherwise, look in the type for a descr + if (descr !== undefined && descr !== null) { + const f = descr.tp$descr_set; + // todo; is this the right lookup priority for data descriptors? + if (f) { + return f.call(descr, this, value, canSuspend); + } + } + + const dict = this.$d; + if (dict !== undefined) { + if (dict.mp$ass_subscript) { + if (value !== undefined) { + return dict.mp$ass_subscript(pyName, value); + } else { + try { + return dict.mp$ass_subscript(pyName); + } catch (e) { + if (e instanceof Sk.builtin.KeyError) { + throw new Sk.builtin.AttributeError("'" + Sk.abstr.typeName(this) + "' object has no attribute '" + pyName.$jsstr() + "'"); + } + throw e; + } + } + } else if (typeof dict === "object") { + const jsMangled = pyName.$mangled; + if (value !== undefined) { + dict[jsMangled] = value; + return; + } else if (dict[jsMangled] !== undefined) { + delete dict[jsMangled]; + return; + } + } + } + throw new Sk.builtin.AttributeError("'" + Sk.abstr.typeName(this) + "' object has no attribute '" + pyName.$jsstr() + "'"); +}; +Sk.exportSymbol("Sk.generic.setAttr", Sk.generic.setAttr); + + +/** + * @method + * + * @description + * The default implementation of tp$new for builtin type objects that are mutable + * args and kwargs are ignored + * either a new instance of the builtin is returned or an instance of a subtype + * + * @see {Sk.builtin.type.prototype.tp$new} + * + * @param {typeObject} builtin + */ +Sk.generic.new = function (builtin) { + const genericNew = function __new__(args, kwargs) { + // this = prototype of an sk$type object. + if (this === builtin.prototype) { + return new this.constructor(); + } else { + const instance = new this.constructor(); + // now we want to apply instance to the builtin + builtin.call(instance); + return instance; + } + }; + return genericNew; +}; + +/** + * @method + * + * @description + * method definitaion for __new__ that wraps tp$new + * typically called by subtypes using super().__new__(args, kwargs) + * + * the algorithm follows Cpython + * + * @see {Sk.slots.__new__} + * + */ +Sk.generic.newMethodDef = { + $meth: function (args, kwargs) { + // this = a type object + let this_name, subs_name; + const native_type_proto = this.prototype; + + if (args.length < 1) { + this_name = native_type_proto.tp$name; + throw new Sk.builtin.TypeError(this_name + ".__new__(): not enough arguments"); + } + + const subtype = args.shift(); + + if (subtype.sk$type === undefined) { + this_name = native_type_proto.tp$name; + throw new Sk.builtin.TypeError(this_name + "__new__(X): X is not a type object (" + Sk.abstr.typeName(subtype) + ")"); + } + + if (!subtype.$isSubType(this)) { + this_name = native_type_proto.tp$name; + subs_name = subtype.prototype.tp$name; + throw new Sk.builtin.TypeError(this_name + ".__new__(" + subs_name + "): " + subs_name + " is not a subtype of " + this_name); + } + /* from CPython: Check that the use doesn't do something silly and unsafe like + object.__new__(dict). To do this, we check that the + most derived base that's not a heap type is this type. */ + let static_proto = subtype.prototype; + let is_static_new = static_proto.hasOwnProperty("tp$new") ? static_proto.tp$new.sk$static_new : false; + while (!is_static_new) { + static_proto = static_proto.tp$base.prototype; + is_static_new = static_proto.hasOwnProperty("tp$new") ? static_proto.tp$new.sk$static_new : false; + } + if (static_proto.tp$new !== native_type_proto.tp$new) { + this_name = native_type_proto.tp$name; + subs_name = subtype.prototype.tp$name; + const suitable = static_proto.tp$name; + throw new Sk.builtin.TypeError(this_name + ".__new__(" + subs_name + ") is not safe, use " + suitable + ".__new__()"); + } + return native_type_proto.tp$new.call(subtype.prototype, args, kwargs); + }, + $flags: {FastCall: true}, + $textsig: "($type, *args, **kwargs)", + $name: "__new__", +}; + +/** + * @description + * used by most iterators that return self + * + * @function + */ +Sk.generic.selfIter = function __iter__() { + return this; +}; + +/** + * @method + * + * @description + * the $seq of the iterator must be an array + * $orig must be provided and must have a get$size private method + * note we do not use sq$length since this can be override by subclasses + * + * typically used by mutable iterators like dict_iter_ and set_iter_ + */ +Sk.generic.iterNextWithArrayCheckSize = function __next__() { + if (this.$index >= this.$seq.length) { + return undefined; + } else if (this.$seq.length !== this.$orig.get$size()) { + const error_name = this.tp$name.split("_")[0]; + throw new Sk.builtin.RuntimeError(error_name + " changed size during iteration"); + } + return this.$seq[this.$index++]; +}; + +/** + * @method + * + * @description + * the $seq of the iterator must be an array + */ +Sk.generic.iterNextWithArray = function __next__() { + if (this.$index >= this.$seq.length) { + return undefined; + } + return this.$seq[this.$index++]; +}; + +/** + * @method + * + * @description + * compares the $seq.length to the $index + */ +Sk.generic.iterLengthHintWithArrayMethodDef = { + $meth: function __length_hint__() { + return new Sk.builtin.int_(this.$seq.length - this.$index); + }, + $flags: {NoArgs: true}, +}; + +/** + * @method + * + * @description + * returns the current index + */ +Sk.generic.iterReverseLengthHintMethodDef = { + $meth: function __length_hint__() { + return new Sk.builtin.int_(this.$index); + }, + $flags: {NoArgs: true}, +}; + + +/** + * @description + * typical implementation of `__dict__` for type objects that support it + */ +Sk.generic.getSetDict = { + $get: function () { + return this.$d; + }, + $set: function (value) { + if (!(value instanceof Sk.builtin.dict)) { + throw new Sk.builtin.TypeError("__dict__ must be set to a dictionary, not a '" + Sk.abstr.typeName(value) + "'"); + } + this.$d = value; + }, + $doc: "dictionary for instance variables (if defined)", + $name: "__dict__", +}; diff --git a/src/import.js b/src/import.js index b57697c476..b43ee5a948 100644 --- a/src/import.js +++ b/src/import.js @@ -7,15 +7,6 @@ Sk.sysmodules = new Sk.builtin.dict([]); Sk.realsyspath = undefined; -Sk.getCurrentSysModules = function() { - var sys = Sk.sysmodules.mp$lookup(Sk.builtin.str("sys")); - if (sys === undefined) { - return Sk.sysmodules; - } else { - return sys.tp$getattr(Sk.builtin.str("modules")); - } -}; - /** * @param {string} name to look for * @param {string} ext extension to use (.py or .js) @@ -28,12 +19,12 @@ Sk.importSearchPathForName = function (name, ext, searchPath) { var nameAsPath = name.replace(/\./g, "/"); var it, i; - var tryPathAndBreakOnSuccess = function(filename, packagePath) { + var tryPathAndBreakOnSuccess = function (filename, packagePath) { return Sk.misceval.chain( - Sk.misceval.tryCatch(function() { + Sk.misceval.tryCatch(function () { return Sk.read(filename); }, function(e) { /* Exceptions signal "not found" */ }), - function(code) { + function (code) { if (code !== undefined) { // This will cause the iterFor() to return the specified value return new Sk.misceval.Break({filename: filename, code: code, packagePath: packagePath}); @@ -46,12 +37,12 @@ Sk.importSearchPathForName = function (name, ext, searchPath) { searchPath = Sk.realsyspath; } - return Sk.misceval.iterFor(searchPath.tp$iter(), function(pathStr) { + return Sk.misceval.iterFor(searchPath.tp$iter(), function (pathStr) { // For each element of path, try loading the module, and if that // doesn't work, try the corresponding package. return Sk.misceval.chain( tryPathAndBreakOnSuccess(pathStr.v + "/" + nameAsPath + ext, false), // module - function(r) { + function (r) { return r ? r : tryPathAndBreakOnSuccess(pathStr.v + "/" + nameAsPath + "/__init__" + ext, pathStr.v + "/" + nameAsPath); // package } @@ -63,110 +54,39 @@ Sk.importSearchPathForName = function (name, ext, searchPath) { * Complete any initialization of Python classes which relies on internal * dependencies. * - * This includes making Python classes subclassable and ensuring that the - * {@link Sk.builtin.object} magic methods are wrapped inside Python functions. + * type, object, super, nonetype, notimplemented + * getset_descr, method_descr, wrapper_descr, method_wrapper + * + * __doc__ for the above + classmethod, property, staticmethod * - * @return {undefined} */ Sk.doOneTimeInitialization = function (canSuspend) { - var proto, name, i, x, func, typesWithFunctionsToWrap, builtin_type, j; - - // can't fill these out when making the type because tuple/dict aren't - // defined yet. - Sk.builtin.type.basesStr_ = new Sk.builtin.str("__bases__"); - Sk.builtin.type.mroStr_ = new Sk.builtin.str("__mro__"); - - // Register a Python class with an internal dictionary, which allows it to - // be subclassed - var setUpClass = function (child) { - var parent = child.tp$base; - var bases = []; - var base; - - for (base = parent; base !== undefined; base = base.tp$base) { - bases.push(base); + function setUpClass(klass) { + const proto = klass.prototype; + if (!proto.hasOwnProperty("sk$slots")) { + // sk$slots was set to null during setUpSlots + // if this flag is not set then we setUpSlots using the klass prototype + Sk.abstr.setUpSlots(klass); } - - child.tp$mro = new Sk.builtin.tuple([child]); - if (!child.tp$base){ - child.tp$base = bases[0]; + if (!proto.hasOwnProperty("tp$mro")) { + Sk.abstr.setUpBuiltinMro(klass); } - child["$d"] = new Sk.builtin.dict([]); - child["$d"].mp$ass_subscript(Sk.builtin.type.basesStr_, new Sk.builtin.tuple(bases)); - child["$d"].mp$ass_subscript(Sk.builtin.type.mroStr_, child.tp$mro); - }; - - for (x in Sk.builtin) { - func = Sk.builtin[x]; - if ((func.prototype instanceof Sk.builtin.object || - func === Sk.builtin.object) && !func.sk$abstract) { - setUpClass(func); + if (proto.hasOwnProperty("tp$getsets") && proto.tp$getsets != null) { + Sk.abstr.setUpGetSets(klass); } - } - - // Wrap the inner Javascript code of Sk.builtin.object's Python methods inside - // Sk.builtin.func, as that class was undefined when these functions were declared - typesWithFunctionsToWrap = [Sk.builtin.object, Sk.builtin.type, Sk.builtin.func, Sk.builtin.method]; - - for (i = 0; i < typesWithFunctionsToWrap.length; i++) { - builtin_type = typesWithFunctionsToWrap[i]; - proto = builtin_type.prototype; - for (j = 0; j < builtin_type.pythonFunctions.length; j++) { - name = builtin_type.pythonFunctions[j]; - - if (proto[name] instanceof Sk.builtin.func) { - // If functions have already been initialized, do not wrap again. - break; - } - - proto[name].co_kwargs = null; - proto[name] = new Sk.builtin.func(proto[name]); + if (proto.hasOwnProperty("tp$methods") && proto.tp$methods != null) { + Sk.abstr.setUpMethods(klass); } - } - - // Mock builtin module - var modname = new Sk.builtin.str("builtins"); - Sk.builtins["__builtins__"] = new Sk.builtin.module(); - Sk.builtins["__builtins__"].$d = { - "__name__": modname, - "__doc__":new Sk.builtin.str("Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices."), - "__package__": Sk.builtin.str.$emptystr - }; - Sk.builtins["__builtins__"].overrides = new Sk.builtin.dict([]); - Sk.builtins["__builtins__"].tp$getattr = function(pyName, canSuspend) { - var jsName = Sk.ffi.remapToJs(pyName); - var overrides = this.overrides; - if (jsName === "__dict__") { - return this; - } else if (jsName === "copy") { - return function(k, d) { - return overrides.copy.tp$call([overrides]); - }; - } else if (jsName === "get") { - return function(k, d) { - return overrides.get.tp$call([overrides, k, d]); - }; + if (!proto.hasOwnProperty("__doc__") && proto.hasOwnProperty("tp$doc")) { + // a few klasses had slots setup before str was initialized so we add them here + proto.__doc__ = new Sk.builtin.str(proto.tp$doc); } }; - Sk.builtins["__builtins__"].tp$getitem = function(key) { - var overridden = this.overrides.mp$lookup(key); - if (overridden !== undefined) { - return overridden; - } - var jsName = Sk.ffi.remapToJs(key); - if (Sk.builtins[jsName] !== undefined) { - return Sk.builtins[jsName]; + for (let x in Sk.builtins) { + const obj = Sk.builtins[x]; + if (obj instanceof Sk.builtin.type) { + setUpClass(obj); } - throw new Sk.builtin.NameError("name '" + Sk.unfixReserved(name) + "' is not defined"); - }; - Sk.getCurrentSysModules().mp$ass_subscript(modname, Sk.builtins["__builtins__"]); - - for (var file in Sk.internalPy.files) { - var fileWithoutExtension = file.split(".")[0].split("/")[1]; - var mod = Sk.importBuiltinWithBody(fileWithoutExtension, false, Sk.internalPy.files[file], true); - mod = Sk.misceval.retryOptionalSuspensionOrThrow(mod); - Sk.asserts.assert(mod["$d"][fileWithoutExtension] !== undefined, "Should have imported name " + fileWithoutExtension); - Sk.builtins[fileWithoutExtension] = mod["$d"][fileWithoutExtension]; } }; @@ -204,15 +124,12 @@ Sk.importSetUpPath = function (canSuspend) { * @param {boolean=} canSuspend whether we may return a Suspension object */ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, relativeToPackage, returnUndefinedOnTopLevelNotFound, canSuspend) { - //dumpJS = true; - /* TODO: temporary hack, need to delete! */ - /*if (name === "pedal.sandbox.sandbox") { - suppliedPyBody= "class Sandbox: pass\ndef run(): pass\ndef reset(): pass"; - }*/ + if (name === "pedal.sandbox.timeout") { suppliedPyBody = "def timeout(delay, func, *args, **kwargs):\n return func(*args, **kwargs)"; } - /* End hack */ + + //dumpJS = true; var filename; var prev; var parentModName; @@ -241,19 +158,6 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela modNameSplit = name.split("."); - // if leaf is already in sys.modules, early out - try { - prev = Sk.getCurrentSysModules().mp$subscript(new Sk.builtin.str(modname)); - // if we're a dotted module, return the top level, otherwise ourselves - if (modNameSplit.length > 1) { - return Sk.getCurrentSysModules().mp$subscript(new Sk.builtin.str(absolutePackagePrefix + modNameSplit[0])); - } else { - return prev; - } - } catch (x) { - // not in sys.modules, continue - } - if (modNameSplit.length > 1) { // if we're a module inside a package (i.e. a.b.c), then we'll need to return the // top-level package ('a'). recurse upwards on our parent, importing @@ -263,196 +167,203 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela topLevelModuleToReturn = Sk.importModuleInternal_(parentModName, dumpJS, undefined, undefined, relativeToPackage, returnUndefinedOnTopLevelNotFound, canSuspend); } - ret = Sk.misceval.chain(topLevelModuleToReturn, function(topLevelModuleToReturn_) { - var codeAndPath, co, googClosure; - var searchFileName = name; - var result; - + ret = Sk.misceval.chain(topLevelModuleToReturn, function (topLevelModuleToReturn_) { topLevelModuleToReturn = topLevelModuleToReturn_; - // If we're inside a package, look search using its __path__ - if (modNameSplit.length > 1) { - if (!topLevelModuleToReturn) { - return undefined; - } - parentModule = Sk.getCurrentSysModules().mp$subscript(new Sk.builtin.str(absolutePackagePrefix + parentModName)); - searchFileName = modNameSplit[modNameSplit.length-1]; - searchPath = parentModule.tp$getattr(Sk.builtin.str.$path); + // if leaf is already in sys.modules, early out + try { + prev = Sk.sysmodules.mp$subscript(new Sk.builtin.str(modname)); + // if we're a dotted module, return the top level, otherwise ourselves + return topLevelModuleToReturn || prev; + } catch (x) { + // not in sys.modules, continue } - // otherwise: - // - create module object - // - add module object to sys.modules - // - compile source to (function(){...}); - // - run module and set the module locals returned to the module __dict__ - module = new Sk.builtin.module(); + return Sk.misceval.chain(undefined, function () { + var codeAndPath, co, googClosure; + var searchFileName = name; + var result; - if (suppliedPyBody) { - filename = name + ".py"; - co = Sk.compile(suppliedPyBody, filename, "exec", canSuspend, true); - } else { - co = Sk.misceval.chain(undefined, function() { - // If an onBeforeImport method is supplied, call it and if - // the result is false or a string, prevent the import. - // This allows for a user to conditionally prevent the usage - // of certain libraries. - if (Sk.onBeforeImport && typeof Sk.onBeforeImport === "function") { - return Sk.onBeforeImport(name); + // If we're inside a package, look search using its __path__ + if (modNameSplit.length > 1) { + if (!topLevelModuleToReturn) { + return undefined; } + parentModule = Sk.sysmodules.mp$subscript(new Sk.builtin.str(absolutePackagePrefix + parentModName)); + searchFileName = modNameSplit[modNameSplit.length - 1]; + searchPath = parentModule.tp$getattr(Sk.builtin.str.$path); + } - return; - }, function(result) { - if (result === false) { - throw new Sk.builtin.ImportError("Importing " + name + " is not allowed"); - } else if (typeof result === "string") { - throw new Sk.builtin.ImportError(result); - } + // otherwise: + // - create module object + // - add module object to sys.modules + // - compile source to (function(){...}); + // - run module and set the module locals returned to the module __dict__ + module = new Sk.builtin.module(); - // Try loading as a builtin (i.e. already in JS) module, then try .py files - return Sk.importSearchPathForName(searchFileName, ".js", searchPath); - }, function(codeAndPath) { - if (codeAndPath) { - return { - funcname: "$builtinmodule", code: codeAndPath.code, - filename: codeAndPath.filename, packagePath: codeAndPath.packagePath - }; - } else { - return Sk.misceval.chain(Sk.importSearchPathForName(searchFileName, ".py", searchPath), function(codeAndPath_) { - codeAndPath = codeAndPath_; // We'll want it in a moment - if (codeAndPath) { - return Sk.compile(codeAndPath.code, codeAndPath.filename, "exec", canSuspend, true); - } - }, function(co) { - if (co) { - co.packagePath = codeAndPath.packagePath; - return co; - } - }); - } - }); + if (suppliedPyBody) { + filename = name + ".py"; + co = Sk.compile(suppliedPyBody, filename, "exec", canSuspend, true); + } else { + co = Sk.misceval.chain(undefined, function () { + // If an onBeforeImport method is supplied, call it and if + // the result is false or a string, prevent the import. + // This allows for a user to conditionally prevent the usage + // of certain libraries. + if (Sk.onBeforeImport && typeof Sk.onBeforeImport === "function") { + return Sk.onBeforeImport(name); + } - } - return co; + return; + }, function (result) { + if (result === false) { + throw new Sk.builtin.ImportError("Importing " + name + " is not allowed"); + } else if (typeof result === "string") { + throw new Sk.builtin.ImportError(result); + } - }, function(co) { + // Try loading as a builtin (i.e. already in JS) module, then try .py files + return Sk.importSearchPathForName(searchFileName, ".js", searchPath); + }, function (codeAndPath) { + if (codeAndPath) { + return { + funcname: "$builtinmodule", code: codeAndPath.code, + filename: codeAndPath.filename, packagePath: codeAndPath.packagePath + }; + } else { + return Sk.misceval.chain(Sk.importSearchPathForName(searchFileName, ".py", searchPath), function (codeAndPath_) { + codeAndPath = codeAndPath_; // We'll want it in a moment + if (codeAndPath) { + return Sk.compile(codeAndPath.code, codeAndPath.filename, "exec", canSuspend, true); + } + }, function (co) { + if (co) { + co.packagePath = codeAndPath.packagePath; + return co; + } + }); + } + }); - var finalcode; - var withLineNumbers; - var modscope; + } + return co; - if (!co) { - return undefined; - } + }, function (co) { - // Now we know this module exists, we can add it to the cache - Sk.getCurrentSysModules().mp$ass_subscript(new Sk.builtin.str(modname), module); + var finalcode; + var withLineNumbers; + var modscope; - module.$js = co.code; // todo; only in DEBUG? - finalcode = co.code; + if (!co) { + return undefined; + } - if (filename == null) { - filename = co.filename; - } + // Now we know this module exists, we can add it to the cache + Sk.sysmodules.mp$ass_subscript(new Sk.builtin.str(modname), module); - if (Sk.dateSet == null || !Sk.dateSet) { - finalcode = "Sk.execStart = Sk.lastYield = new Date();Sk.execPaused=0;\n" + co.code; - Sk.dateSet = true; - } + module.$js = co.code; // todo; only in DEBUG? + finalcode = co.code; + + if (filename == null) { + filename = co.filename; + } - // if (!COMPILED) - // { - if (dumpJS) { - withLineNumbers = function (code) { - var j; - var pad; - var width; - var i; - var beaut = Sk.js_beautify(code); - var lines = beaut.split("\n"); - for (i = 1; i <= lines.length; ++i) { - width = ("" + i).length; - pad = ""; - for (j = width; j < 5; ++j) { - pad += " "; + if (Sk.dateSet == null || !Sk.dateSet) { + finalcode = "Sk.execStart = Sk.lastYield = new Date();\n" + co.code; + Sk.dateSet = true; + } + + // if (!COMPILED) + // { + if (dumpJS) { + withLineNumbers = function (code) { + var j; + var pad; + var width; + var i; + var beaut = Sk.js_beautify(code); + var lines = beaut.split("\n"); + for (i = 1; i <= lines.length; ++i) { + width = ("" + i).length; + pad = ""; + for (j = width; j < 5; ++j) { + pad += " "; + } + lines[i - 1] = "/* " + pad + i + " */ " + lines[i - 1]; } - lines[i - 1] = "/* " + pad + i + " */ " + lines[i - 1]; - } - return lines.join("\n"); - }; - finalcode = withLineNumbers(finalcode); - Sk.debugout(finalcode); - } - // } - - finalcode += "\n" + co.funcname + ";"; - - modscope = Sk.global["eval"](finalcode); - - module["$d"] = { - "__name__": new Sk.builtin.str(modname), - "__doc__": Sk.builtin.none.none$, - "__package__": co.packagePath ? new Sk.builtin.str(modname) : - parentModName ? new Sk.builtin.str(absolutePackagePrefix + parentModName) : - relativePackageName ? relativePackageName : Sk.builtin.none.none$ - }; - //module["$d"]["__dict__"] = module["$d"]; - if (co.packagePath) { - module["$d"]["__path__"] = new Sk.builtin.tuple([new Sk.builtin.str(co.packagePath)]); - } + return lines.join("\n"); + }; + finalcode = withLineNumbers(finalcode); + Sk.debugout(finalcode); + } + // } - return modscope(module["$d"]); + finalcode += "\n" + co.funcname + ";"; - }, function (modlocs) { - var i; + modscope = Sk.global["eval"](finalcode); - if (modlocs === undefined) { - if (returnUndefinedOnTopLevelNotFound && !topLevelModuleToReturn) { - return undefined; - } else { - throw new Sk.builtin.ImportError("No module named " + name); + module["$d"] = { + "__name__": new Sk.builtin.str(modname), + "__doc__": Sk.builtin.none.none$, + "__package__": co.packagePath ? new Sk.builtin.str(modname) : + parentModName ? new Sk.builtin.str(absolutePackagePrefix + parentModName) : + relativePackageName ? relativePackageName : Sk.builtin.none.none$ + }; + if (co.packagePath) { + module["$d"]["__path__"] = new Sk.builtin.tuple([new Sk.builtin.str(co.packagePath)]); } - } - // Some builtin modules replace their globals entirely. - // For their benefit, we copy over any of the standard - // dunder-values they didn't supply. - if (modlocs !== module["$d"]) { - for (i in module["$d"]) { - if (!modlocs[i]) { - modlocs[i] = module["$d"][i]; + return modscope(module["$d"]); + + }, function (modlocs) { + var i; + + if (modlocs === undefined) { + if (returnUndefinedOnTopLevelNotFound && !topLevelModuleToReturn) { + return undefined; + } else { + throw new Sk.builtin.ImportError("No module named " + name); } } - module["$d"] = modlocs; - } - // If an onAfterImport method is defined on the global Sk - // then call it now after a library has been successfully imported - // and compiled. - if (Sk.onAfterImport && typeof Sk.onAfterImport === "function") { - try { - Sk.onAfterImport(name); - } catch (e) { + // Some builtin modules replace their globals entirely. + // For their benefit, we copy over any of the standard + // dunder-values they didn't supply. + if (modlocs !== module["$d"]) { + for (i in module["$d"]) { + if (!modlocs[i]) { + modlocs[i] = module["$d"][i]; + } + } + module["$d"] = modlocs; } - } - // TODO: answer.py.instructor_append - // Somehow adding it into the name of the module + // If an onAfterImport method is defined on the global Sk + // then call it now after a library has been successfully imported + // and compiled. + if (Sk.onAfterImport && typeof Sk.onAfterImport === "function") { + try { + Sk.onAfterImport(name); + } catch (e) { + } + } - if (topLevelModuleToReturn) { - // if we were a dotted name, then we want to return the top-most - // package. we store ourselves into our parent as an attribute - parentModule.tp$setattr(new Sk.builtin.str(modNameSplit[modNameSplit.length - 1]), module); - //print("import returning parent module, modname", modname, "__name__", toReturn.tp$getattr("__name__").v); - return topLevelModuleToReturn; - } + if (topLevelModuleToReturn) { + // if we were a dotted name, then we want to return the top-most + // package. we store ourselves into our parent as an attribute + parentModule.tp$setattr(new Sk.builtin.str(modNameSplit[modNameSplit.length - 1]), module); + //print("import returning parent module, modname", modname, "__name__", toReturn.tp$getattr("__name__").v); + return topLevelModuleToReturn; + } - if (relativeToPackage) { - relativeToPackage.tp$setattr(new Sk.builtin.str(name), module); - } + if (relativeToPackage) { + relativeToPackage.tp$setattr(new Sk.builtin.str(name), module); + } - //print("name", name, "modname", modname, "returning leaf"); - // otherwise we return the actual module that we just imported - return module; + //print("name", name, "modname", modname, "returning leaf"); + // otherwise we return the actual module that we just imported + return module; + }); }); return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret); @@ -488,13 +399,18 @@ Sk.importMain = function (name, dumpJS, canSuspend) { * @param dumpJS {boolean} print out the compiled javascript * @param body {string} Python Code * @param canSuspend {boolean} Use Suspensions for async execution + * @param sysmodules {object} An existing sysmodules to reuse. * */ -Sk.importMainWithBody = function (name, dumpJS, body, canSuspend) { +Sk.importMainWithBody = function (name, dumpJS, body, canSuspend, sysmodules) { Sk.dateSet = false; Sk.filesLoaded = false; // Added to reset imports - Sk.sysmodules = new Sk.builtin.dict([]); + if (sysmodules === undefined) { + Sk.sysmodules = new Sk.builtin.dict([]); + } else { + Sk.sysmodules = sysmodules; + } Sk.realsyspath = undefined; Sk.resetCompiler(); @@ -513,21 +429,23 @@ Sk.importMainWithBody = function (name, dumpJS, body, canSuspend) { * */ Sk.importBuiltinWithBody = function (name, dumpJS, body, canSuspend) { - return Sk.importModuleInternal_(name, dumpJS, "__builtin__."+name, body, undefined, false, canSuspend); + return Sk.importModuleInternal_(name, dumpJS, "__builtin__." + name, body, undefined, false, canSuspend); }; Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { - + //print("Importing: ", JSON.stringify(name), JSON.stringify(fromlist), level); //if (name == "") { debugger; } // Save the Sk.globals variable importModuleInternal_ may replace it when it compiles // a Python language module. var saveSk = Sk.globals; + // If passed in a PyString, then turn it into a JS string. if (Sk.builtin.checkString(name)) { name = name.v; } + // If we don't have the globals, then fetch them forth. if (globals === undefined) { globals = Sk.globals; } @@ -549,15 +467,15 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { if (relativeToPackageName && level > 0) { // Trim packages off the end relativeToPackageNames = relativeToPackageName.split("."); - if (level-1 >= relativeToPackageNames.length) { + if (level - 1 >= relativeToPackageNames.length) { throw new Sk.builtin.ValueError("Attempted relative import beyond toplevel package"); } - relativeToPackageNames.length -= level-1; + relativeToPackageNames.length -= level - 1; relativeToPackageName = relativeToPackageNames.join("."); } try { - relativeToPackage = Sk.getCurrentSysModules().mp$subscript(new Sk.builtin.str(relativeToPackageName)); - } catch(e) { + relativeToPackage = Sk.sysmodules.mp$subscript(new Sk.builtin.str(relativeToPackageName)); + } catch (e) { relativeToPackageName = undefined; } } @@ -566,10 +484,11 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { throw new Sk.builtin.ValueError("Attempted relative import in non-package"); } + //console.log("&*(", name); var dottedName = name.split("."); var firstDottedName = dottedName[0]; - return Sk.misceval.chain(undefined, function() { + return Sk.misceval.chain(undefined, function () { // Attempt local load first (and just fall through to global // case if level == -1 and we fail to load the top-level package) if (level !== 0 && relativeToPackage !== undefined) { @@ -577,10 +496,10 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { // "from .. import ..." return relativeToPackage; } else { - return Sk.importModuleInternal_(name, undefined, relativeToPackageName + "." + name, undefined, relativeToPackage, level==-1, true); + return Sk.importModuleInternal_(name, undefined, relativeToPackageName + "." + name, undefined, relativeToPackage, level == -1, true); } } - }, function(ret) { + }, function (ret) { if (ret === undefined) { // Either it was always a global import, or it was an // either-way import that just fell through. @@ -590,7 +509,7 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { } else { return ret; } - }, function(ret) { + }, function (ret) { // We might also have to load modules named by the fromlist. // If there is no fromlist, we have reached the end of the lookup, return if (!fromlist || fromlist.length === 0) { @@ -603,11 +522,10 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { var leafModule; var importChain; - leafModule = Sk.getCurrentSysModules().mp$subscript( + leafModule = Sk.sysmodules.mp$subscript( new Sk.builtin.str((relativeToPackageName || "") + - ((relativeToPackageName && name) ? "." : "") + - name) - ); + ((relativeToPackageName && name) ? "." : "") + + name)); for (i = 0; i < fromlist.length; i++) { fromName = fromlist[i]; @@ -621,7 +539,7 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { } } - return Sk.misceval.chain(importChain, function() { + return Sk.misceval.chain(importChain, function () { // if there's a fromlist we want to return the leaf module // (ret), not the toplevel namespace Sk.asserts.assert(leafModule); @@ -629,7 +547,7 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { }); } - }, function(ret) { + }, function (ret) { if (saveSk !== Sk.globals) { Sk.globals = saveSk; } @@ -638,11 +556,22 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { }; Sk.importStar = function (module, loc, global) { - var i; - var props = Object["getOwnPropertyNames"](module["$d"]); - for (i in props) { - if (props[i].charAt(0) != "_") { - loc[props[i]] = module["$d"][props[i]]; + var __all__ = module.tp$getattr(new Sk.builtin.str("__all__")); + + if (__all__) { + // TODO this does not support naming *modules* in __all__, + // only variables + for (let it = Sk.abstr.iter(__all__), i = it.tp$iternext(); + i !== undefined; i = it.tp$iternext()) { + + loc[i.v] = Sk.abstr.gattr(module, i); + } + } else { + let props = Object["getOwnPropertyNames"](module["$d"]); + for (let i in props) { + if (props[i].charAt(0) != "_") { + loc[props[i]] = module["$d"][props[i]]; + } } } }; @@ -651,5 +580,4 @@ Sk.exportSymbol("Sk.importMain", Sk.importMain); Sk.exportSymbol("Sk.importMainWithBody", Sk.importMainWithBody); Sk.exportSymbol("Sk.importBuiltinWithBody", Sk.importBuiltinWithBody); Sk.exportSymbol("Sk.builtin.__import__", Sk.builtin.__import__); -Sk.exportSymbol("Sk.importStar", Sk.importStar); -Sk.exportSymbol("Sk.getCurrentSysModules", Sk.getCurrentSysModules); \ No newline at end of file +Sk.exportSymbol("Sk.importStar", Sk.importStar); \ No newline at end of file diff --git a/src/int.js b/src/int.js index 623d4f62f5..a1a429213c 100644 --- a/src/int.js +++ b/src/int.js @@ -1,1111 +1,488 @@ -/* jslint nomen: true, bitwise: true */ -/* global Sk: true */ +/**@constructor */ +const JSBI = require("jsbi"); /** - * @namespace Sk.builtin - */ - -/** - * @constructor - * Sk.builtin.int_ - * - * @description - * Constructor for Python int. If provided number is greater than integer threshold, will return a Python long instead. - * - * type int, all integers are created with this method, it is also used - * for the builtin int() - * - * Takes also implemented `__int__` and `__trunc__` methods for x into account - * and tries to use `__index__` and/or `__int__` if base is not a number - * - * @extends {Sk.builtin.numtype} - * - * @param {!(Object|number)} x Python object or Javascript number to convert to Python int - * @param {!(Object|number|Sk.builtin.none)=} base Optional base, can only be used when x is Sk.builtin.str - * @return {(Sk.builtin.int_|Sk.builtin.lng)} Python int (or long, if overflow) - */ -Sk.builtin.int_ = function (x, base) { - var val; - var func; - var ret; // return value - var magicName; // name of magic method - - if (!(this instanceof Sk.builtin.int_)) { - return new Sk.builtin.int_(x, base); - } - - - if (this instanceof Sk.builtin.bool) { - return this; - } - - if (x instanceof Sk.builtin.int_ && base === undefined) { - this.v = x.v; - return this; - } - - // if base is not of type int, try calling .__index__ - if(base !== Sk.builtin.none.none$ && base !== undefined && !Sk.builtin.checkInt(base)) { - if (Sk.builtin.checkFloat(base)) { - throw new Sk.builtin.TypeError("integer argument expected, got " + Sk.abstr.typeName(base)); - } else if (base.__index__) { - base = Sk.misceval.callsimArray(base.__index__, [base]); - } else if(base.__int__) { - base = Sk.misceval.callsimArray(base.__int__, [base]); - } else { - throw new Sk.builtin.AttributeError(Sk.abstr.typeName(base) + " instance has no attribute '__index__' or '__int__'"); - } - } - - if (x instanceof Sk.builtin.str) { - base = Sk.builtin.asnum$(base); - if (base === Sk.builtin.none.none$) { - base = 10; - } - - val = Sk.str2number(x.v, base, parseInt, function (x) { - return -x; - }, "int"); - - if ((val > Sk.builtin.int_.threshold$) || (val < -Sk.builtin.int_.threshold$)) { - // Too big for int, convert to long - return new Sk.builtin.lng(x, base); - } - - this.v = val; - return this; - } - - if (base !== undefined && base !== Sk.builtin.none.none$) { - throw new Sk.builtin.TypeError("int() can't convert non-string with explicit base"); - } - - if (x === undefined || x === Sk.builtin.none) { - x = 0; - } - - /** - * try calling special methods: - * 1. __int__ - * 2. __trunc__ - */ - if(x !== undefined && (x.__class__ && x.__class__.tp$getattr && (func = x.__class__.tp$getattr(Sk.builtin.str.$int_)))) { - // calling a method which contains im_self and im_func - // causes skulpt to automatically map the im_self as first argument - ret = Sk.misceval.callsimArray(func, [x]); - magicName = "__int__"; - } else if(x !== undefined && x.__int__) { - // required for internal types - // __int__ method is on prototype - ret = Sk.misceval.callsimArray(x.__int__, [x]); - magicName = "__int__"; - } else if(x !== undefined && (x.__class__ && x.__class__.tp$getattr && (func = x.__class__.tp$getattr(Sk.builtin.str.$trunc)))) { - ret = Sk.misceval.callsimArray(func, [x]); - magicName = "__trunc__"; - } else if(x !== undefined && x.__trunc__) { - ret = Sk.misceval.callsimArray(x.__trunc__, [x]); - magicName = "__trunc__"; - } - - // check return type of magic methods - if(ret !== undefined && !Sk.builtin.checkInt(ret)) { - throw new Sk.builtin.TypeError(magicName + " returned non-Integral (type " + Sk.abstr.typeName(ret)+")"); - } else if(ret !== undefined){ - x = ret; // valid return value, proceed in function - } - - // check type even without magic numbers - if(!Sk.builtin.checkNumber(x)) { - throw new Sk.builtin.TypeError("int() argument must be a string or a number, not '" + Sk.abstr.typeName(x) + "'"); - } - - x = Sk.builtin.asnum$(x); - if (x > Sk.builtin.int_.threshold$ || x < -Sk.builtin.int_.threshold$) { - return new Sk.builtin.lng(x); - } - if ((x > -1) && (x < 1)) { - x = 0; - } - - this.v = parseInt(x, base); - return this; -}; - -Sk.builtin.int_.$shiftconsts = [0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296, 8589934592, 17179869184, 34359738368, 68719476736, 137438953472, 274877906944, 549755813888, 1099511627776, 2199023255552, 4398046511104, 8796093022208, 17592186044416, 35184372088832, 70368744177664, 140737488355328, 281474976710656, 562949953421312, 1125899906842624, 2251799813685248, 4503599627370496, 9007199254740992]; - -Sk.abstr.setUpInheritance("int", Sk.builtin.int_, Sk.builtin.numtype); - -/* NOTE: See constants used for kwargs in constants.js */ - -Sk.builtin.int_.prototype.nb$int_ = function () { - return this; -}; - -Sk.builtin.int_.prototype.nb$float_ = function() { - return new Sk.builtin.float_(this.v); -}; - -Sk.builtin.int_.prototype.nb$lng = function () { - return new Sk.builtin.lng(this.v); -}; - -/** - * Python wrapper of `__trunc__` dunder method. - * - * @instance - */ -Sk.builtin.int_.prototype.__trunc__ = new Sk.builtin.func(function(self) { - return self; -}); - -/** - * Python wrapper of `__index__` dunder method. - * - * @instance - */ -Sk.builtin.int_.prototype.__index__ = new Sk.builtin.func(function(self) { - return self; -}); - -/** - * Python wrapper of `__complex__` dunder method. - * - * @instance - */ -Sk.builtin.int_.prototype.__complex__ = new Sk.builtin.func(function(self) { - return Sk.builtin.NotImplemented.NotImplemented$; -}); - -/** - * Return this instance's Javascript value. - * - * Javascript function, returns Javascript object. - * - * @return {number} This instance's value. - */ -Sk.builtin.int_.prototype.tp$index = function () { - return this.v; -}; - -/** @override */ -Sk.builtin.int_.prototype.tp$hash = function () { - //the hash of all numbers should be an int and since javascript doesn't really - //care every number can be an int. - return new Sk.builtin.int_(this.v); -}; - -/** - * Threshold to determine when types should be converted to long. - * - * Note: be sure to check against threshold in both positive and negative directions. - * - * @type {number} - */ -Sk.builtin.int_.threshold$ = Math.pow(2, 53) - 1; - -/** - * Returns a copy of this instance. - * - * Javascript function, returns Python object. - * - * @return {Sk.builtin.int_} The copy - */ -Sk.builtin.int_.prototype.clone = function () { - return new Sk.builtin.int_(this.v); -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$add = function (other) { - var thisAsLong, thisAsFloat; - var result; - - if (other instanceof Sk.builtin.int_) { - result = this.v + other.v; - if (result > Sk.builtin.int_.threshold$ || - result < -Sk.builtin.int_.threshold$) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$add(other); - } - return new Sk.builtin.int_(result); - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$add(other); - } - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.v); - return thisAsFloat.nb$add(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$reflected_add = function (other) { - // Should not automatically call this.nb$add, as nb$add may have - // been overridden by a subclass - return Sk.builtin.int_.prototype.nb$add.call(this, other); -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$subtract = function (other) { - var thisAsLong, thisAsFloat; - var result; - - if (other instanceof Sk.builtin.int_) { - result = this.v - other.v; - if (result > Sk.builtin.int_.threshold$ || - result < -Sk.builtin.int_.threshold$) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$subtract(other); - } - return new Sk.builtin.int_(result); - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$subtract(other); - } - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.v); - return thisAsFloat.nb$subtract(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$reflected_subtract = function (other) { - // Should not automatically call this.nb$add, as nb$add may have - // been overridden by a subclass - var negative_this = this.nb$negative(); - return Sk.builtin.int_.prototype.nb$add.call(negative_this, other); -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$multiply = function (other) { - var product, thisAsLong, thisAsFloat; - - if (other instanceof Sk.builtin.int_) { - product = this.v * other.v; - - if (product > Sk.builtin.int_.threshold$ || - product < -Sk.builtin.int_.threshold$) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$multiply(other); - } else { - return new Sk.builtin.int_(product); - } - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$multiply(other); - } - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.v); - return thisAsFloat.nb$multiply(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$reflected_multiply = function (other) { - // Should not automatically call this.nb$multiply, as nb$multiply may have - // been overridden by a subclass - return Sk.builtin.int_.prototype.nb$multiply.call(this, other); -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$divide = function (other) { - var thisAsLong, thisAsFloat; - if (Sk.__future__.division) { - thisAsFloat = new Sk.builtin.float_(this.v); - return thisAsFloat.nb$divide(other); - } - - if (other instanceof Sk.builtin.int_) { - return this.nb$floor_divide(other); - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$divide(other); - } - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.v); - return thisAsFloat.nb$divide(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$reflected_divide = function (other) { - return this.nb$reflected_floor_divide(other); -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$floor_divide = function (other) { - var thisAsLong, thisAsFloat; - - if (other instanceof Sk.builtin.int_) { - - if (other.v === 0) { - throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero"); - } - - return new Sk.builtin.int_(Math.floor(this.v / other.v)); - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$floor_divide(other); - } - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.v); - return thisAsFloat.nb$floor_divide(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$reflected_floor_divide = function (other) { - if (other instanceof Sk.builtin.int_) { - return other.nb$divide(this); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$remainder = function (other) { - var thisAsLong, thisAsFloat; - var tmp; - var divResult; - - if (other instanceof Sk.builtin.int_) { - // Javacript logic on negatives doesn't work for Python... do this instead - divResult = Sk.abstr.numberBinOp(this, other, "FloorDiv"); - tmp = Sk.abstr.numberBinOp(divResult, other, "Mult"); - tmp = Sk.abstr.numberBinOp(this, tmp, "Sub"); - tmp = tmp.v; - - if (other.v < 0 && tmp === 0) { - tmp = -0.0; // otherwise the sign gets lost by javascript modulo - } else if (tmp === 0 && Infinity/tmp === -Infinity) { - tmp = 0.0; - } - - return new Sk.builtin.int_(tmp); - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$remainder(other); - } - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.v); - return thisAsFloat.nb$remainder(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$reflected_remainder = function (other) { - if (other instanceof Sk.builtin.int_) { - return other.nb$remainder(this); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$divmod = function (other) { - var thisAsLong, thisAsFloat; - - if (other instanceof Sk.builtin.int_) { - return new Sk.builtin.tuple([ - this.nb$floor_divide(other), - this.nb$remainder(other) - ]); - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$divmod(other); - } - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.v); - return thisAsFloat.nb$divmod(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$reflected_divmod = function (other) { - if (other instanceof Sk.builtin.int_) { - return new Sk.builtin.tuple([ - other.nb$floor_divide(this), - other.nb$remainder(this) - ]); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$power = function (other, mod) { - var power, ret, thisAsLong, thisAsFloat; - - if (other instanceof Sk.builtin.int_ && (mod === undefined || mod instanceof Sk.builtin.int_)) { - - power = Math.pow(this.v, other.v); - - if (power > Sk.builtin.int_.threshold$ || - power < -Sk.builtin.int_.threshold$) { - thisAsLong = new Sk.builtin.lng(this.v); - ret = thisAsLong.nb$power(other, mod); - } else if (other.v < 0) { - ret = new Sk.builtin.float_(power); - } else { - ret = new Sk.builtin.int_(power); - } - - if (mod !== undefined) { - if (other.v < 0) { - throw new Sk.builtin.TypeError("pow() 2nd argument cannot be negative when 3rd argument specified"); - } - - return ret.nb$remainder(mod); - } else { - return ret; - } - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$power(other); - } - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.v); - return thisAsFloat.nb$power(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$reflected_power = function (other, mod) { - if (other instanceof Sk.builtin.int_) { - return other.nb$power(this, mod); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$abs = function () { - return new Sk.builtin.int_(Math.abs(this.v)); -}; - -/** - * Compute the bitwise AND of this instance and a Python object (i.e. this & other). - * - * Returns NotImplemented if bitwise AND operation between int and other type is unsupported. - * - * Javscript function, returns Python object. - * - * @param {!Sk.builtin.object} other The Python object to AND with this one - * @return {(Sk.builtin.int_|Sk.builtin.lng|Sk.builtin.NotImplemented)} The result of the conjunction - */ -Sk.builtin.int_.prototype.nb$and = function (other) { - var thisAsLong, thisAsFloat; - - if (other instanceof Sk.builtin.int_) { - var tmp; - other = Sk.builtin.asnum$(other); - tmp = this.v & other; - if ((tmp !== undefined) && (tmp < 0)) { - tmp = tmp + 4294967296; // convert back to unsigned - } - - if (tmp !== undefined) { - return new Sk.builtin.int_(tmp); - } - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$and(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.int_.prototype.nb$reflected_and = Sk.builtin.int_.prototype.nb$and; - -/** - * Compute the bitwise OR of this instance and a Python object (i.e. this | other). - * - * Returns NotImplemented if bitwise OR operation between int and other type is unsupported. - * - * Javscript function, returns Python object. - * - * @param {!Sk.builtin.object} other The Python object to OR with this one - * @return {(Sk.builtin.int_|Sk.builtin.lng|Sk.builtin.NotImplemented)} The result of the disjunction - */ -Sk.builtin.int_.prototype.nb$or = function (other) { - var thisAsLong; - - if (other instanceof Sk.builtin.int_) { - var tmp; - other = Sk.builtin.asnum$(other); - tmp = this.v | other; - if ((tmp !== undefined) && (tmp < 0)) { - tmp = tmp + 4294967296; // convert back to unsigned - } - - if (tmp !== undefined) { - return new Sk.builtin.int_(tmp); - } - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$and(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.int_.prototype.nb$reflected_or = Sk.builtin.int_.prototype.nb$or; - -/** - * Compute the bitwise XOR of this instance and a Python object (i.e. this ^ other). - * - * Returns NotImplemented if bitwise XOR operation between int and other type is unsupported. - * - * Javscript function, returns Python object. - * - * @param {!Sk.builtin.object} other The Python object to XOR with this one - * @return {(Sk.builtin.int_|Sk.builtin.lng|Sk.builtin.NotImplemented)} The result of the exclusive disjunction - */ -Sk.builtin.int_.prototype.nb$xor = function (other) { - var thisAsLong; - - if (other instanceof Sk.builtin.int_) { - var tmp; - other = Sk.builtin.asnum$(other); - tmp = this.v ^ other; - if ((tmp !== undefined) && (tmp < 0)) { - tmp = tmp + 4294967296; // convert back to unsigned - } - - if (tmp !== undefined) { - return new Sk.builtin.int_(tmp); - } - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$xor(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.int_.prototype.nb$reflected_xor = Sk.builtin.int_.prototype.nb$xor; - -/** - * Compute the bitwise left shift of this instance by a Python object (i.e. this << other). - * - * Returns NotImplemented if bitwise left shift operation between int and other type is unsupported. - * - * Javscript function, returns Python object. - * - * @param {!Sk.builtin.object} other The Python object by which to left shift - * @return {(Sk.builtin.int_|Sk.builtin.lng|Sk.builtin.NotImplemented)} The result of the left shift - */ -Sk.builtin.int_.prototype.nb$lshift = function (other) { - var thisAsLong; - - if (this.v === 0) { - return this; - } - - if (other instanceof Sk.builtin.int_) { - var tmp; - var shift = Sk.builtin.asnum$(other); - - if (shift !== undefined) { - if (shift < 0) { - throw new Sk.builtin.ValueError("negative shift count"); - } - - if (shift > 53) { - return new Sk.builtin.lng(this.v).nb$lshift(new Sk.builtin.int_(shift)); - } - - tmp = this.v * 2 * Sk.builtin.int_.$shiftconsts[shift]; - if (tmp > Sk.builtin.int_.threshold$ || tmp < -Sk.builtin.int_.threshold$) { - // Fail, recompute with longs - return new Sk.builtin.lng(tmp); - } - } - - if (tmp !== undefined) { - tmp = /** @type {number} */ (tmp); - return new Sk.builtin.int_(tmp); - } - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$lshift(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.int_.prototype.nb$reflected_lshift = function (other) { - if (other instanceof Sk.builtin.int_) { - return other.nb$lshift(this); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** - * Compute the bitwise right shift of this instance by a Python object (i.e. this >> other). * - * Returns NotImplemented if bitwise right shift operation between int and other type is unsupported. + * @constructor + * @extends {Sk.builtin.object} + * @description + * Function should only be called with a JS number|BigInt|String + * If the number is a string then the size will be checked to determined whether it should be a number or BigInt + * It assumed that a number passed it is within `Number.MaxSafeInteger` + * Similarly if a BigInt is passed it is assumed that this is larger than `Number.MaxSafeInteger` + * Internal code like `float.nb$int_` checks the resulting JS instance before calling `new Sk.builtin.int_` * - * Javscript function, returns Python object. + * @param {number|JSBI|string=} x * - * @param {!Sk.builtin.object} other The Python object by which to right shift - * @return {(Sk.builtin.int_|Sk.builtin.lng|Sk.builtin.NotImplemented)} The result of the right shift */ -Sk.builtin.int_.prototype.nb$rshift = function (other) { - var thisAsLong; - - if (other instanceof Sk.builtin.int_) { - var tmp; - var shift = Sk.builtin.asnum$(other); - - if (shift !== undefined) { - if (shift < 0) { - throw new Sk.builtin.ValueError("negative shift count"); +Sk.builtin.int_ = Sk.abstr.buildNativeClass("int", { + constructor: function int_(x) { + Sk.asserts.assert(this instanceof Sk.builtin.int_, "bad call to int use 'new'"); + let v; + if (typeof x === "number" || x instanceof JSBI) { + v = x; + } else if (typeof x === "string") { + v = stringToNumberOrBig(x); + } else if (x === undefined) { + v = 0; + } else { + Sk.asserts.fail("bad argument to int constructor"); + } + /**@type {number|JSBI} */ + this.v = v; + }, + slots: /** @lends {Sk.builtin.int_.prototype}*/{ + tp$as_number: true, + tp$doc: + "int(x=0) -> integer\nint(x, base=10) -> integer\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given. If x is a number, return x.__int__(). For floating point\nnumbers, this truncates towards zero.\n\nIf x is not a number or if base is given, then x must be a string,\nbytes, or bytearray instance representing an integer literal in the\ngiven base. The literal can be preceded by '+' or '-' and be surrounded\nby whitespace. The base defaults to 10. Valid bases are 0 and 2-36.\nBase 0 means to interpret the base from the string as an integer literal.\n>>> int('0b100', base=0)\n4", + $r: function () { + return new Sk.builtin.str(this.v.toString()); + }, + tp$hash: function () { + return new Sk.builtin.int_(this.v); + // todo we shouldn't really have hashes so big for longs... + }, + tp$new: function (args, kwargs) { + let x, base; + if (args.length + (kwargs ? kwargs.length : 0) === 1) { + x = args[0]; + base = Sk.builtin.none.none$; + } else { + args = Sk.abstr.copyKeywordsToNamedArgs("int", [null, "base"], args, kwargs, [new Sk.builtin.int_(0), Sk.builtin.none.none$]); + x = args[0]; + base = args[1]; } - tmp = this.v >> shift; - if ((this.v > 0) && (tmp < 0)) { - // Fix incorrect sign extension - tmp = tmp & (Math.pow(2, 32 - shift) - 1); + x = getInt(x, base); + + if (this === Sk.builtin.int_.prototype) { + return x; + } else { + const instance = new this.constructor(); + instance.v = x.v; + return instance; } - } - - if (tmp !== undefined) { - tmp = /** @type {number} */ (tmp); - return new Sk.builtin.int_(tmp); - } - } - - if (other instanceof Sk.builtin.lng) { - thisAsLong = new Sk.builtin.lng(this.v); - return thisAsLong.nb$rshift(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.int_.prototype.nb$reflected_rshift = function (other) { - if (other instanceof Sk.builtin.int_) { - return other.nb$rshift(this); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** - * Compute the bitwise inverse of this instance (i.e. ~this). - * - * Javscript function, returns Python object. - * - * @return {Sk.builtin.int_} The result of the inversion - */ -Sk.builtin.int_.prototype.nb$invert = function () { - return new Sk.builtin.int_(~this.v); -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$inplace_add = Sk.builtin.int_.prototype.nb$add; - -/** @override */ -Sk.builtin.int_.prototype.nb$inplace_subtract = Sk.builtin.int_.prototype.nb$subtract; - -/** @override */ -Sk.builtin.int_.prototype.nb$inplace_multiply = Sk.builtin.int_.prototype.nb$multiply; - -/** @override */ -Sk.builtin.int_.prototype.nb$inplace_divide = Sk.builtin.int_.prototype.nb$divide; - -/** @override */ -Sk.builtin.int_.prototype.nb$inplace_remainder = Sk.builtin.int_.prototype.nb$remainder; - -/** @override */ -Sk.builtin.int_.prototype.nb$inplace_floor_divide = Sk.builtin.int_.prototype.nb$floor_divide; - -/** @override */ -Sk.builtin.int_.prototype.nb$inplace_power = Sk.builtin.int_.prototype.nb$power; - -/** - * @function - * @name nb$inplace_and - * @memberOf Sk.builtin.int_.prototype - * @description - * Compute the bitwise AND of this instance and a Python object (i.e. this &= other). - * - * Returns NotImplemented if inplace bitwise AND operation between int and other type is unsupported. - * - * Javscript function, returns Python object. - * - * @param {!Sk.builtin.object} other The Python object to AND with this one - * @return {(Sk.builtin.int_|Sk.builtin.lng|Sk.builtin.NotImplemented)} The result of the conjunction - */ -Sk.builtin.int_.prototype.nb$inplace_and = Sk.builtin.int_.prototype.nb$and; - -/** - * @function - * @name nb$inplace_or - * @memberOf Sk.builtin.int_.prototype - * @description - * Compute the bitwise OR of this instance and a Python object (i.e. this |= other). - * - * Returns NotImplemented if inplace bitwise OR operation between int and other type is unsupported. - * - * Javscript function, returns Python object. - * - * @param {!Sk.builtin.object} other The Python object to OR with this one - * @return {(Sk.builtin.int_|Sk.builtin.lng|Sk.builtin.NotImplemented)} The result of the disjunction - */ -Sk.builtin.int_.prototype.nb$inplace_or = Sk.builtin.int_.prototype.nb$or; - -/** - * @function - * @name nb$inplace_xor - * @memberOf Sk.builtin.int_.prototype - * @description - * Compute the bitwise XOR of this instance and a Python object (i.e. this ^= other). - * - * Returns NotImplemented if inplace bitwise XOR operation between int and other type is unsupported. - * - * Javscript function, returns Python object. - * - * @param {!Sk.builtin.object} other The Python object to XOR with this one - * @return {(Sk.builtin.int_|Sk.builtin.lng|Sk.builtin.NotImplemented)} The result of the exclusive disjunction - */ -Sk.builtin.int_.prototype.nb$inplace_xor = Sk.builtin.int_.prototype.nb$xor; - -/** - * @function - * @name nb$inplace_lshift - * @memberOf Sk.builtin.int_.prototype - * @description - * Compute the bitwise left shift of this instance by a Python object (i.e. this <<= other). - * - * Returns NotImplemented if inplace bitwise left shift operation between int and other type is unsupported. - * - * Javscript function, returns Python object. - * - * @param {!Sk.builtin.object} other The Python object by which to left shift - * @return {(Sk.builtin.int_|Sk.builtin.lng|Sk.builtin.NotImplemented)} The result of the left shift - */ -Sk.builtin.int_.prototype.nb$inplace_lshift = Sk.builtin.int_.prototype.nb$lshift; - -/** - * @function - * @name nb$inplace_rshift - * @memberOf Sk.builtin.int_.prototype - * @description - * Compute the bitwise right shift of this instance by a Python object (i.e. this >>= other). - * - * Returns NotImplemented if inplace bitwise right shift operation between int and other type is unsupported. - * - * Javscript function, returns Python object. - * - * @param {!Sk.builtin.object} other The Python object by which to right shift - * @return {(Sk.builtin.int_|Sk.builtin.lng|Sk.builtin.NotImplemented)} The result of the right shift - */ -Sk.builtin.int_.prototype.nb$inplace_rshift = Sk.builtin.int_.prototype.nb$rshift; - -/** - * @override - * - * @return {Sk.builtin.int_} A copy of this instance with the value negated. - */ -Sk.builtin.int_.prototype.nb$negative = function () { - return new Sk.builtin.int_(-this.v); -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$positive = function () { - return this.clone(); -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$nonzero = function () { - return this.v !== 0; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$isnegative = function () { - return this.v < 0; -}; - -/** @override */ -Sk.builtin.int_.prototype.nb$ispositive = function () { - return this.v >= 0; -}; + }, + tp$getattr: Sk.generic.getAttr, + ob$eq: compareSlot((v, w) => v == w, JSBI.equal), + ob$ne: compareSlot((v, w) => v != w, JSBI.notEqual), + ob$gt: compareSlot((v, w) => v > w, JSBI.greaterThan), + ob$ge: compareSlot((v, w) => v >= w, JSBI.greaterThanOrEqual), + ob$lt: compareSlot((v, w) => v < w, JSBI.lessThan), + ob$le: compareSlot((v, w) => v <= w, JSBI.lessThanOrEqual), + + nb$int_: cloneSelf, + nb$index: cloneSelf, + nb$float_: function () { + const v = this.v; + if (typeof v === "number") { + return new Sk.builtin.float_(v); + } else { + const x = parseFloat(JSBI.toNumber(v)); + if (x === Infinity || x === -Infinity) { + throw new Sk.builtin.OverflowError("int too large to convert to float"); + } + return new Sk.builtin.float_(x); + } + }, + nb$isnegative: function () { + const v = this.v; + if (typeof v === "number") { + return v < 0; + } + return v.sign; + }, + nb$ispositive: function () { + const v = this.v; + if (typeof v === "number") { + return v >= 0; + } + return !v.sign; + }, + nb$bool: function () { + return this.v !== 0; // should be fine not to check BigInt here + }, + + nb$positive: cloneSelf, + + nb$negative: numberUnarySlot((v) => -v, JSBI.unaryMinus), + + nb$add: numberSlot( + (v, w) => v + w, + (v, w) => (!(v.sign ^ w.sign) ? JSBI.add(v, w) : convertIfSafe(JSBI.add(v, w))) + ), + nb$subtract: numberSlot( + (v, w) => v - w, + (v, w) => (v.sign ^ w.sign ? JSBI.subtract(v, w) : convertIfSafe(JSBI.subtract(v, w))) + ), + nb$multiply: numberSlot((v, w) => v * w, JSBI.multiply), + nb$divide: function (other) { + if (Sk.__future__.division) { + return this.nb$float_().nb$divide(other); + } + return this.nb$floor_divide(other); + }, + nb$floor_divide: numberDivisionSlot((v, w) => Math.floor(v / w), JSBI.divide), + nb$remainder: numberDivisionSlot((v, w) => v - Math.floor(v / w) * w, JSBI.remainder), + nb$divmod: function (other) { + const floor = this.nb$floor_divide(other); + const remainder = this.nb$remainder(other); + if (floor === Sk.builtin.NotImplemented.NotImplemented$ || remainder === Sk.builtin.NotImplemented.NotImplemented$) { + return Sk.builtin.NotImplemented.NotImplemented$; + } + return new Sk.builtin.tuple([floor, remainder]); + }, + nb$and: numberBitSlot((v, w) => v & w, JSBI.bitwiseAnd), + nb$or: numberBitSlot((v, w) => v | w, JSBI.bitwiseOr), + nb$xor: numberBitSlot((v, w) => v ^ w, JSBI.bitwiseXor), + + nb$abs: numberUnarySlot(Math.abs, (v) => (v.sign ? JSBI.unaryMinus(v) : v)), + + nb$lshift: numberShiftSlot((v, w) => { + if (w < 53) { + const tmp = v * 2 * shiftconsts[w]; + if (numberOrStringWithinThreshold(tmp)) { + return tmp; + } + return; + } + }, JSBI.leftShift), + nb$rshift: numberShiftSlot( + (v, w) => { + const tmp = v >> w; + if (v > 0 && tmp < 0) { + return tmp & (Math.pow(2, 32 - w) - 1); + } + return tmp; + }, + (v, w) => convertIfSafe(JSBI.signedRightShift(v, w)) + ), + + nb$invert: numberUnarySlot((v) => ~v, JSBI.bitwiseNot), + nb$power: function (other, mod) { + let ret; + if (other instanceof Sk.builtin.int_ && (mod === undefined || mod instanceof Sk.builtin.int_)) { + let v = this.v; + let w = other.v; + if (typeof v === "number" && typeof w === "number") { + const power = Math.pow(this.v, other.v); + if (numberOrStringWithinThreshold(power)) { + ret = w < 0 ? new Sk.builtin.float_(power) : new Sk.builtin.int_(power); + } + } + if (ret === undefined) { + v = bigUp(v); + w = bigUp(w); + ret = new Sk.builtin.int_(JSBI.exponentiate(v, w)); + } + if (mod !== undefined) { + if (other.nb$isnegative()) { + throw new Sk.builtin.TypeError("pow() 2nd argument cannot be negative when 3rd argument specified"); + } + return ret.nb$remainder(mod); + } else { + return ret; + } + } + return Sk.builtin.NotImplemented.NotImplemented$; + }, + nb$lng: function () { + return new Sk.builtin.long(this.v); + }, + }, + getsets: /** @lends {Sk.builtin.int_.prototype}*/{ + real: { + $get: cloneSelf, + }, + imag: { + $get: function () { + return new Sk.builtin.int_(0); + }, + }, + }, + methods: /** @lends {Sk.builtin.int_.prototype}*/{ + conjugate: { + $meth: cloneSelf, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Returns self, the complex conjugate of any int.", + }, + bit_length: { + $meth: function () { + return new Sk.builtin.int_(Sk.builtin.bin(this).sq$length() - 2); + }, + $flags: {NoArgs: true}, + $textsig: "($self, /)", + $doc: "Number of bits necessary to represent self in binary.\n\n>>> bin(37)\n'0b100101'\n>>> (37).bit_length()\n6", + }, + to_bytes: { + $meth: function () { + throw new Sk.builtin.NotImplementedError("Not yet implemented in Skulpt"); + }, + $flags: {FastCall: true}, + $textsig: "($self, /, length, byteorder, *, signed=False)", + $doc: + "Return an array of bytes representing an integer.\n\n length\n Length of bytes object to use. An OverflowError is raised if the\n integer is not representable with the given number of bytes.\n byteorder\n The byte order used to represent the integer. If byteorder is 'big',\n the most significant byte is at the beginning of the byte array. If\n byteorder is 'little', the most significant byte is at the end of the\n byte array. To request the native byte order of the host system, use\n `sys.byteorder' as the byte order value.\n signed\n Determines whether two's complement is used to represent the integer.\n If signed is False and a negative integer is given, an OverflowError\n is raised.", + }, + __trunc__: { + $meth: cloneSelf, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Truncating an Integral returns itself.", + }, + __floor__: { + $meth: cloneSelf, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Flooring an Integral returns itself.", + }, + __ceil__: { + $meth: cloneSelf, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Ceiling of an Integral returns itself.", + }, + __round__: { + $meth: function (ndigits) { + return this.round$(ndigits); + }, + $flags: {MinArgs: 0, MaxArgs: 1}, + $textsig: null, + $doc: "Rounding an Integral returns itself.\nRounding with an ndigits argument also returns an integer.", + }, + __getnewargs__: { + $meth: function () { + return new Sk.builtin.tuple([new Sk.builtin.int_(this.v)]); + }, + $flags: {NoArgs: true}, + $textsig: "($self, /)", + $doc: Sk.builtin.none.none$, + }, + __format__: { + $meth: Sk.formatting.mkNumber__format__(false), + $flags: {OneArg: true}, + $textsig: "($self, format_spec, /)", + $doc: Sk.builtin.none.none$, + }, + }, + proto: /** @lends {Sk.builtin.int_.prototype}*/{ + str$: function (base, sign) { + let tmp; + if (base === undefined || base === 10) { + tmp = this.v.toString(); + } else { + tmp = this.v.toString(base); + } + if (sign || sign === undefined) { + return tmp; + } else if (tmp[0] === "-") { + tmp = tmp.substring(1); + } + return tmp; + }, + round$: function (ndigits) { + if (ndigits !== undefined && !Sk.misceval.isIndex(ndigits)) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(ndigits) + "' object cannot be interpreted as an index"); + } + if (ndigits === undefined) { + ndigits = 0; + } else { + ndigits = Sk.misceval.asIndex(ndigits); + } + const v = this.v; + const multiplier = Math.pow(10, -ndigits); + let tmp; + if (ndigits > 0) { + return new Sk.builtin.int_(v); + } + if (typeof v === "number" && Sk.__future__.bankers_rounding) { + const num10 = v / multiplier; + const rounded = Math.round(num10); + const bankRound = (num10 > 0 ? num10 : -num10) % 1 === 0.5 ? (0 === rounded % 2 ? rounded : rounded - 1) : rounded; + const result = bankRound * multiplier; + return new Sk.builtin.int_(result); + } else if (typeof v === "number") { + return new Sk.builtin.int_(Math.round(v / multiplier) * multiplier); + } else { + const BigMultiplier = JSBI.BigInt(multiplier * 10); + const ten = JSBI.BigInt(10); + tmp = JSBI.divide(v, BigMultiplier); + const undecided = JSBI.divide(tmp, ten); + const pt5 = JSBI.subtract(tmp, JSBI.multiply(ten, undecided)); + if (JSBI.toNumber(pt5) < 5) { + tmp = JSBI.multiply(JSBI.multiply(undecided, ten), BigMultiplier); + } else { + JSBI.multiply(JSBI.multiply(JSBI.add(undecided, JSBI.BigInt(1), ten), BigMultiplier)); + } + return new Sk.builtin.int_(tmp); + } + }, + }, +}); +Sk.exportSymbol("Sk.builtin.int_", Sk.builtin.int_); /** - * Compare this instance's value to another Python object's value. + * A function that will return either a number or a BigInt * - * Returns NotImplemented if comparison between int and other type is unsupported. + * There are two functions passed to this slot the quick function where both int values are number + * and the JSBI.BigInt version of the same function + * The fall through case where one or both of the int values is a bigint + * @ignore * - * Javscript function, returns Javascript object or Sk.builtin.NotImplemented. + * @private * - * @return {(number|Sk.builtin.NotImplemented)} negative if this < other, zero if this == other, positive if this > other + * @param {Function} number_func + * @param {Function} bigint_func */ -Sk.builtin.int_.prototype.numberCompare = function (other) { - if (other instanceof Sk.builtin.int_) { - return this.v - other.v; - } - - if (other instanceof Sk.builtin.lng) { - return -other.longCompare(this); - } - - if (other instanceof Sk.builtin.float_) { - return -other.numberCompare(this); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -// Despite what jshint may want us to do, these two functions need to remain -// as == and != Unless you modify the logic of numberCompare do not change -// these. - -/** @override */ -Sk.builtin.int_.prototype.ob$eq = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.numberCompare(other) == 0); //jshint ignore:line - } else if (other instanceof Sk.builtin.none) { - return Sk.builtin.bool.false$; - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; - -/** @override */ -Sk.builtin.int_.prototype.ob$ne = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.numberCompare(other) != 0); //jshint ignore:line - } else if (other instanceof Sk.builtin.none) { - return Sk.builtin.bool.true$; - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; - -/** @override */ -Sk.builtin.int_.prototype.ob$lt = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.numberCompare(other) < 0); - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; - -/** @override */ -Sk.builtin.int_.prototype.ob$le = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.numberCompare(other) <= 0); - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; - -/** @override */ -Sk.builtin.int_.prototype.ob$gt = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.numberCompare(other) > 0); - } else { +function numberSlot(number_func, bigint_func) { + /** + * @this {Sk.builtin.int_} + * + * @param {Sk.builtin.int_|Sk.builtin.object} other + * @return {Sk.builtin.int_|Sk.builtin.NotImplemented} + */ + function doNumberSlot(other) { + if (other instanceof Sk.builtin.int_) { + /**@type {number|JSBI} */ + let v = this.v; + /**@type {number|JSBI} */ + let w = other.v; + if (typeof v === "number" && typeof w === "number") { + const res = number_func(v, w); + if (numberOrStringWithinThreshold(res)) { + return new Sk.builtin.int_(res); + } + } + v = bigUp(v); + w = bigUp(w); + return new Sk.builtin.int_(bigint_func(v, w)); + } return Sk.builtin.NotImplemented.NotImplemented$; - } -}; - -/** @override */ -Sk.builtin.int_.prototype.ob$ge = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.numberCompare(other) >= 0); - } else { + }; + return doNumberSlot; +} + +function compareSlot(number_func, bigint_func) { + return function (other) { + if (other instanceof Sk.builtin.int_) { + let v = this.v; + let w = other.v; + if (typeof v === "number" && typeof w === "number") { + return number_func(v, w); + } + v = bigUp(v); + w = bigUp(w); + return bigint_func(v, w); + } return Sk.builtin.NotImplemented.NotImplemented$; - } -}; + }; +} /** - * Round this instance to a given number of digits, or zero if omitted. * - * Implements `__round__` dunder method. + * @param {function(number): number} number_func + * @param {function(JSBI): JSBI} bigint_func * - * Javascript function, returns Python object. - * - * @param {Sk.builtin.int_} self This instance. - * @param {Object|number=} ndigits The number of digits after the decimal point to which to round. - * @return {Sk.builtin.int_} The rounded integer. */ -Sk.builtin.int_.prototype.round$ = function (self, ndigits) { - Sk.builtin.pyCheckArgsLen("__round__", arguments.length, 1, 2); - - var result, multiplier, number, num10, rounded, bankRound, ndigs; - - if ((ndigits !== undefined) && !Sk.misceval.isIndex(ndigits)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(ndigits) + "' object cannot be interpreted as an index"); - } - - number = Sk.builtin.asnum$(self); - if (ndigits === undefined) { - ndigs = 0; - } else { - ndigs = Sk.misceval.asIndex(ndigits); - } - - if (Sk.__future__.bankers_rounding) { - num10 = number * Math.pow(10, ndigs); - rounded = Math.round(num10); - bankRound = (((((num10>0)?num10:(-num10))%1)===0.5)?(((0===(rounded%2)))?rounded:(rounded-1)):rounded); - result = bankRound / Math.pow(10, ndigs); - return new Sk.builtin.int_(result); - } else { - multiplier = Math.pow(10, ndigs); - result = Math.round(number * multiplier) / multiplier; - - return new Sk.builtin.int_(result); - } -}; - -Sk.builtin.int_.prototype.__format__= function (obj, format_spec) { - var formatstr; - Sk.builtin.pyCheckArgsLen("__format__", arguments.length, 2, 2); +function numberUnarySlot(number_func, bigint_func) { + /** + * @this {Sk.builtin.int_} + * @return {Sk.builtin.int_} + */ + function doUnarySlot() { + const v = this.v; + if (typeof v === "number") { + return new Sk.builtin.int_(number_func(v)); + } + return new Sk.builtin.int_(bigint_func(v)); + }; + return doUnarySlot; +} - if (!Sk.builtin.checkString(format_spec)) { - if (Sk.__future__.exceptions) { - throw new Sk.builtin.TypeError("format() argument 2 must be str, not " + Sk.abstr.typeName(format_spec)); - } else { - throw new Sk.builtin.TypeError("format expects arg 2 to be string or unicode, not " + Sk.abstr.typeName(format_spec)); +function cloneSelf() { + return new Sk.builtin.int_(this.v); +} + +function numberDivisionSlot(number_func, bigint_func) { + return function (other) { + if (other instanceof Sk.builtin.int_) { + let v = this.v; + let w = other.v; + if (w === 0) { + throw new Sk.builtin.ZeroDivisionError("integer division or modulo by zero"); + } + if (typeof v === "number" && typeof w === "number") { + // it's integer division so no need to check if the number got bigger! + return new Sk.builtin.int_(number_func(v, w)); + } + v = bigUp(v); + w = bigUp(w); + return new Sk.builtin.int_(convertIfSafe(bigint_func(v, w))); } - } else { - formatstr = Sk.ffi.remapToJs(format_spec); - if (formatstr !== "") { - throw new Sk.builtin.NotImplementedError("format spec is not yet implemented"); + return Sk.builtin.NotImplemented.NotImplemented$; + }; +} + +function numberShiftSlot(number_func, bigint_func) { + return function (other) { + if (other instanceof Sk.builtin.int_) { + let v = this.v; + let w = other.v; + if (v === 0) { + return this; + } + if (typeof w === "number") { + if (w < 0) { + throw new Sk.builtin.ValueError("negative shift count"); + } + if (typeof v === "number") { + const tmp = number_func(v, w); + if (tmp !== undefined) { + return new Sk.builtin.int_(tmp); + } + } + w = JSBI.BigInt(w); + } else if (JSBI.lessThan(JSBI.BigInt(0))) { + throw new Sk.builtin.ValueError("negative shift count"); + } + v = bigUp(v); + return new Sk.builtin.int_(bigint_func(v, w)); // con't convert if safe for leftshift } - } - - return new Sk.builtin.str(obj); -}; - -Sk.builtin.int_.prototype.conjugate = new Sk.builtin.func(function (self) { - return new Sk.builtin.int_(self.v); -}); - -/** @override */ -Sk.builtin.int_.prototype["$r"] = function () { - return new Sk.builtin.str(this.str$(10, true)); -}; - -/** - * Return the string representation of this instance. - * - * Javascript function, returns Python object. - * - * @return {Sk.builtin.str} The Python string representation of this instance. - */ -Sk.builtin.int_.prototype.tp$str = function () { - return new Sk.builtin.str(this.str$(10, true)); -}; - -/** - * Convert this instance's value to a Javascript string. - * - * Javascript function, returns Javascript object. - * - * @param {number} base The base of the value. - * @param {boolean} sign true if the value should be signed, false otherwise. - * @return {string} The Javascript string representation of this instance. - */ -Sk.builtin.int_.prototype.str$ = function (base, sign) { - var tmp; - var work; - - if (sign === undefined) { - sign = true; - } - - work = sign ? this.v : Math.abs(this.v); - - if (base === undefined || base === 10) { - tmp = work.toString(); - } else { - tmp = work.toString(base); - } - - return tmp; -}; - -Sk.builtin.int_.prototype.bit_length = function () { - return new Sk.builtin.int_(Math.abs(this.v).toString(2).length); -}; + return Sk.builtin.NotImplemented.NotImplemented$; + }; +} + +function numberBitSlot(number_func, bigint_func) { + return function (other) { + if (other instanceof Sk.builtin.int_) { + let v = this.v; + let w = other.v; + if (typeof v === "number" && typeof w === "number") { + let tmp = number_func(v, w); + if (tmp < 0) { + tmp = tmp + 4294967296; // convert back to unsigned + } + return new Sk.builtin.int_(tmp); + } + v = bigUp(v); + w = bigUp(w); + return new Sk.builtin.int_(convertIfSafe(bigint_func(v, w))); + } + return Sk.builtin.NotImplemented.NotImplemented$; + }; +} /** * Takes a JavaScript string and returns a number using the parser and negater * functions (for int/long right now) * @param {string} s Javascript string to convert to a number. - * @param {(number)} base The base of the number. - * @param {function(*, (number|undefined)): number} parser Function which should take - * a string that is a postive number which only contains characters that are - * valid in the given base and a base and return a number. - * @param {function((number|Sk.builtin.biginteger)): number} negater Function which should take a - * number and return its negation - * @param {string} fname The name of the calling function, to be used in error messages - * @return {number} The number equivalent of the string in the given base + * @param {number|string=} base The base of the number. */ -Sk.str2number = function (s, base, parser, negater, fname) { +Sk.str2number = function (s, base) { var origs = s, neg = false, i, ch, val; - // strip whitespace from ends // s = s.trim(); s = s.replace(/^\s+|\s+$/g, ""); @@ -1124,33 +501,35 @@ Sk.str2number = function (s, base, parser, negater, fname) { if (base === null || base === undefined) { base = 10; } // default radix is 10, not dwim - if (base < 2 || base > 36) { if (base !== 0) { - throw new Sk.builtin.ValueError(fname + "() base must be >= 2 and <= 36"); + throw new Sk.builtin.ValueError("int() base must be >= 2 and <= 36"); } } + if (typeof base === "string") { + base = Number(base); // keep closure happy for parseInt + } if (s.substring(0, 2).toLowerCase() === "0x") { if (base === 16 || base === 0) { s = s.substring(2); base = 16; } else if (base < 34) { - throw new Sk.builtin.ValueError("invalid literal for " + fname + "() with base " + base + ": '" + origs + "'"); + throw new Sk.builtin.ValueError("invalid literal for int() with base " + base + ": '" + origs + "'"); } } else if (s.substring(0, 2).toLowerCase() === "0b") { if (base === 2 || base === 0) { s = s.substring(2); base = 2; } else if (base < 12) { - throw new Sk.builtin.ValueError("invalid literal for " + fname + "() with base " + base + ": '" + origs + "'"); + throw new Sk.builtin.ValueError("invalid literal for int() with base " + base + ": '" + origs + "'"); } } else if (s.substring(0, 2).toLowerCase() === "0o") { if (base === 8 || base === 0) { s = s.substring(2); base = 8; } else if (base < 25) { - throw new Sk.builtin.ValueError("invalid literal for " + fname + "() with base " + base + ": '" + origs + "'"); + throw new Sk.builtin.ValueError("invalid literal for int() with base " + base + ": '" + origs + "'"); } } else if (s.charAt(0) === "0") { if (s === "0") { @@ -1166,35 +545,245 @@ Sk.str2number = function (s, base, parser, negater, fname) { } if (s.length === 0) { - throw new Sk.builtin.ValueError("invalid literal for " + fname + "() with base " + base + ": '" + origs + "'"); + throw new Sk.builtin.ValueError("invalid literal for int() with base " + base + ": '" + origs + "'"); } // check all characters are valid for (i = 0; i < s.length; i = i + 1) { ch = s.charCodeAt(i); val = base; - if ((ch >= 48) && (ch <= 57)) { + if (ch >= 48 && ch <= 57) { // 0-9 val = ch - 48; - } else if ((ch >= 65) && (ch <= 90)) { + } else if (ch >= 65 && ch <= 90) { // A-Z val = ch - 65 + 10; - } else if ((ch >= 97) && (ch <= 122)) { + } else if (ch >= 97 && ch <= 122) { // a-z val = ch - 97 + 10; } if (val >= base) { - throw new Sk.builtin.ValueError("invalid literal for " + fname + "() with base " + base + ": '" + origs + "'"); + throw new Sk.builtin.ValueError("invalid literal for int() with base " + base + ": '" + origs + "'"); } } - // parse number - val = parser(s, base); if (neg) { - val = negater(val); + s = "-" + s; } - return val; + val = parseInt(s, base); + if (numberOrStringWithinThreshold(val)) { + return val; // will convert our string to a number + } + return fromStrToBigWithBase(s, base); }; -Sk.exportSymbol("Sk.builtin.int_", Sk.builtin.int_); +Sk.builtin.int_.py2$methods = {}; + +/** + * + * @param {string} s + * @param {number=} base + */ +Sk.longFromStr = function (s, base) { + if (Sk.__future__.python3) { + return new Sk.builtin.int_(stringToNumberOrBig(s)); + } else { + const num = Sk.str2number(s, base); + return new Sk.builtin.lng(num); + } +}; +Sk.exportSymbol("Sk.longFromStr", Sk.longFromStr); + + +function numberOrStringWithinThreshold(v) { + return v <= Number.MAX_SAFE_INTEGER && v >= -Number.MAX_SAFE_INTEGER; +} + +Sk.builtin.int_.withinThreshold = numberOrStringWithinThreshold; + +const MaxSafeBig = JSBI.BigInt(Number.MAX_SAFE_INTEGER); +const MaxSafeBigNeg = JSBI.BigInt(-Number.MAX_SAFE_INTEGER); +function convertIfSafe(v) { + if (JSBI.lessThan(v, MaxSafeBig) && JSBI.greaterThan(v, MaxSafeBigNeg)) { + return JSBI.toNumber(v); + } + return v; +} +function stringToNumberOrBig(s) { + if (s <= Number.MAX_SAFE_INTEGER && s >= -Number.MAX_SAFE_INTEGER) { + return +s; + } + return JSBI.BigInt(s); +} + +Sk.builtin.int_.stringToNumberOrBig = stringToNumberOrBig; +function bigUp(v) { + if (typeof v === "number") { + return JSBI.BigInt(v); + } + return v; +} + + +function getInt(x, base) { + let func, res; + // if base is not of type int, try calling .__index__ + if (base !== Sk.builtin.none.none$) { + base = Sk.misceval.asIndexOrThrow(base); + } else { + base = null; + } + + if (x instanceof Sk.builtin.str) { + if (base === null) { + base = 10; + } + return new Sk.builtin.int_(Sk.str2number(x.v, base)); + } else if (base !== null) { + throw new Sk.builtin.TypeError("int() can't convert non-string with explicit base"); + } else if (x.nb$int_) { + return x.nb$int_(); + } + + if ((func = Sk.abstr.lookupSpecial(x, Sk.builtin.str.$trunc))) { + res = Sk.misceval.callsimArray(func, [x]); + // check return type of magic methods + if (!Sk.builtin.checkInt(res)) { + throw new Sk.builtin.TypeError(Sk.builtin.str.$trunc.$jsstr() + " returned non-Integral (type " + Sk.abstr.typeName(x) + ")"); + } + return new Sk.builtin.int_(res.v); + } + + throw new Sk.builtin.TypeError("int() argument must be a string, a bytes-like object or a number, not '" + Sk.abstr.typeName(x) + "'"); +} + +/** + * + * We don't need to check the string has valid digits since str2number did that for us + * @param {*} s + * @param {*} base + * @ignore + */ +function fromStrToBigWithBase(s, base) { + let neg = false; + if (s[0] === "-") { + neg = true; + s = s.substring(1); + } + base = JSBI.BigInt(base); + let power = JSBI.BigInt(1); + let num = JSBI.BigInt(0); + let toadd, val; + for (let i = s.length - 1; i >= 0; i--) { + val = s.charCodeAt(i); + if (val >= 48 && val <= 57) { + // 0-9 + val = val - 48; + } else if (val >= 65 && val <= 90) { + // A-Z + val = val - 65 + 10; + } else if (val >= 97 && val <= 122) { + // a-z + val = val - 97 + 10; + } + toadd = JSBI.multiply(JSBI.BigInt(val), power); + num = JSBI.add(num, toadd); + power = JSBI.multiply(power, base); + } + if (neg) { + num.sign = true; + } + return num; +} + +const shiftconsts = [ + 0.5, + 1, + 2, + 4, + 8, + 16, + 32, + 64, + 128, + 256, + 512, + 1024, + 2048, + 4096, + 8192, + 16384, + 32768, + 65536, + 131072, + 262144, + 524288, + 1048576, + 2097152, + 4194304, + 8388608, + 16777216, + 33554432, + 67108864, + 134217728, + 268435456, + 536870912, + 1073741824, + 2147483648, + 4294967296, + 8589934592, + 17179869184, + 34359738368, + 68719476736, + 137438953472, + 274877906944, + 549755813888, + 1099511627776, + 2199023255552, + 4398046511104, + 8796093022208, + 17592186044416, + 35184372088832, + 70368744177664, + 140737488355328, + 281474976710656, + 562949953421312, + 1125899906842624, + 2251799813685248, + 4503599627370496, + 9007199254740992, +]; + + +/** + * @constructor + * + * @description + * This is only for backward compatibility with py2. + * We take the approach of using a trivial subclass with int and overriding a few methods + * + * @param {number|string|JSBI} x + * @extends {Sk.builtin.int_} + * @ignore + */ +Sk.builtin.lng = Sk.abstr.buildNativeClass("long", { + base: Sk.builtin.int_, // not technically correct but makes backward compatibility easy + constructor: function lng (x) { + Sk.builtin.int_.call(this, x); + }, + slots: /** @lends {Sk.builtin.lng.prototype} */{ + $r: function () { + return new Sk.builtin.str(this.v.toString() + "L"); + }, + tp$as_number: true, + nb$negative: function () { + return new Sk.builtin.lng(intProto.nb$negative.call(this).v); + }, + nb$positive: function () { + return new Sk.builtin.lng(intProto.nb$positive.call(this).v); + }, + }, +}); + +const intProto = Sk.builtin.int_.prototype; diff --git a/src/iterator.js b/src/iterator.js index cdaa2183aa..2f574b7302 100644 --- a/src/iterator.js +++ b/src/iterator.js @@ -1,16 +1,16 @@ /** - * Builds an iterator that outputs the items from the inputted object - * @constructor - * @param {*} obj must support iter protocol (has __iter__ and next methods), if sentinel defined: - * obj must be callable - * @param {*=} sentinel optional if defined returns an object that makes a call to obj until - * sentinel is reached - * @extends Sk.builtin.object - * - * @description - * Constructor for Python iterator. - * - */ + * Builds an iterator that outputs the items from the inputted object + * @constructor + * @param {*} obj must support iter protocol (has __iter__ and next methods), if sentinel defined: + * obj must be callable + * @param {*=} sentinel optional if defined returns an object that makes a call to obj until + * sentinel is reached + * @extends Sk.builtin.object + * + * @description + * Constructor for Python iterator. + * + */ Sk.builtin.iterator = function (obj, sentinel) { var objit; if (obj instanceof Sk.builtin.generator) { @@ -46,7 +46,7 @@ Sk.builtin.iterator.prototype.__iter__ = new Sk.builtin.func(function (self) { return self.tp$iter(); }); -Sk.builtin.iterator.prototype.tp$iter = function () { +Sk.builtin.iterator.prototype.tp$iter = function () { return this; }; @@ -60,9 +60,9 @@ Sk.builtin.iterator.prototype.tp$iternext = function (canSuspend) { } if (this.getitem) { - r = Sk.misceval.tryCatch(function() { + r = Sk.misceval.tryCatch(function () { return Sk.misceval.callsimOrSuspendArray(self.getitem, [self.obj, Sk.ffi.remapToPy(self.idx++)]); - }, function(e) { + }, function (e) { if (e instanceof Sk.builtin.StopIteration || e instanceof Sk.builtin.IndexError) { return undefined; } else { diff --git a/src/iteratorobjects.js b/src/iteratorobjects.js new file mode 100644 index 0000000000..399eccb8dc --- /dev/null +++ b/src/iteratorobjects.js @@ -0,0 +1,290 @@ +/** @typedef {Sk.builtin.object} */ var pyObject; + +/** + * @constructor + * @param {pyObject} iterable + * @param {number|string=} start + * @extends Sk.builtin.object + */ +Sk.builtin.enumerate = Sk.abstr.buildIteratorClass("enumerate", { + constructor: function enumerate(iterable, start) { + if (!(this instanceof Sk.builtin.enumerate)) { + return new Sk.builtin.enumerate(iterable, start); + } + this.$iterable = iterable; + this.$index = start; + return this; + }, + iternext: function (canSuspend) { + const next = this.$iterable.tp$iternext(canSuspend); + const self = this; + if (next === undefined) { + return undefined; + } + if (next.$isSuspension) { + return Sk.misceval.chain(next, (n) => { + if (n === undefined) { + return undefined; + } + const idx = new Sk.builtin.int_(self.$index++); + return new Sk.builtin.tuple([idx, n]); + }); + } + const idx = new Sk.builtin.int_(this.$index++); + return new Sk.builtin.tuple([idx, next]); + }, + slots: { + tp$doc: + "Return an enumerate object.\n\n iterable\n an object supporting iteration\n\nThe enumerate object yields pairs containing a count (from start, which\ndefaults to zero) and a value yielded by the iterable argument.\n\nenumerate is useful for obtaining an indexed list:\n (0, seq[0]), (1, seq[1]), (2, seq[2]), ...", + tp$new: function (args, kwargs) { + args = Sk.abstr.copyKeywordsToNamedArgs("enumerate", ["iterable", "start"], args, kwargs); + if (args[0] === undefined) { + throw new Sk.builtin.TypeError("__new__() missing 1 required positional argument: 'iterable'"); + } + const iterable = Sk.abstr.iter(args[0]); + let start = args[1]; + if (start !== undefined) { + start = Sk.misceval.asIndexOrThrow(start); + } else { + start = 0; + } + if (this === Sk.builtin.enumerate.prototype) { + return new Sk.builtin.enumerate(iterable, start); + } else { + const instance = new this.constructor(); + Sk.builtin.enumerate.call(instance, iterable, start); + return instance; + } + }, + }, +}); +Sk.exportSymbol("Sk.builtin.enumerate", Sk.builtin.enumerate); + +/** + * @constructor + * @param {pyObject} func + * @param {pyObject} iterable + * @extends Sk.builtin.object + */ +Sk.builtin.filter_ = Sk.abstr.buildIteratorClass("filter", { + constructor: function filter_(func, iterable) { + this.func = func; + this.iterable = iterable; + }, + iternext: function () { + let res, item; + while (res === undefined) { + item = this.iterable.tp$iternext(); + if (item === undefined) { + return undefined; + } + res = this.check$filter(item); + } + return item; + }, + slots: { + tp$doc: + "Return an iterator yielding those items of iterable for which function(item)\nis true. If function is None, return the items that are true.", + tp$new: function (args, kwargs) { + args = Sk.abstr.copyKeywordsToNamedArgs("filter", ["predicate", "iterable"], args, kwargs); + if (args[0] === undefined) { + throw new Sk.builtin.TypeError("__new__() missing 2 required positional arguments: 'predicate' and 'iterable'"); + } else if (args[1] === undefined) { + throw new Sk.builtin.TypeError("__new__() missing 1 required positional argument: 'iterable'"); + } + const func = Sk.builtin.checkNone(args[0]) ? null : args[0]; + const iterable = Sk.abstr.iter(args[1]); + // in theory you could subclass + if (this === Sk.builtin.filter_.prototype) { + return new Sk.builtin.filter_(func, iterable); + } else { + const instance = new this.constructor(); + Sk.builtin.filter_.call(instance, func, iterable); + return instance; + } + }, + }, + proto: { + check$filter: function (item) { + let res; + if (this.func === null) { + res = item; + } else { + res = Sk.misceval.callsimArray(this.func, [item]); + } + if (Sk.misceval.isTrue(res)) { + return res; + } + return undefined; + }, + }, +}); + +Sk.exportSymbol("Sk.builtin.filter_", Sk.builtin.filter_); + +/** + * @constructor + * @param {Object} seq + * @extends Sk.builtin.object + */ +Sk.builtin.reversed = Sk.abstr.buildIteratorClass("reversed", { + constructor: function reversed(seq) { + this.idx = seq.sq$length() - 1; + this.seq = seq; + return this; + }, + iternext: function () { + if (this.idx < 0) { + return undefined; + } + try { + const i = new Sk.builtin.int_(this.idx); + const next = Sk.abstr.objectGetItem(this.seq, i); + this.idx--; + return next; + } catch (e) { + if (e instanceof Sk.builtin.IndexError) { + this.idx = -1; + return undefined; + } else { + throw e; + } + } + }, + slots: { + tp$doc: "Return a reverse iterator over the values of the given sequence.", + tp$new: function (args, kwargs) { + if (this === Sk.builtin.reversed.prototype) { + Sk.abstr.checkNoKwargs("reversed", kwargs); + } + Sk.abstr.checkArgsLen("reversed", args, 1, 1); + let seq = args[0]; + const special = Sk.abstr.lookupSpecial(seq, Sk.builtin.str.$reversed); + if (special !== undefined) { + return Sk.misceval.callsimArray(special, [seq]); + } else if (!Sk.builtin.checkSequence(seq) || Sk.abstr.lookupSpecial(seq, Sk.builtin.str.$len) === undefined) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(seq) + "' object is not a sequence"); + } + if (this === Sk.builtin.reversed.prototype) { + return new Sk.builtin.reversed(seq); + } else { + const instance = new this.constructor(); + Sk.builtin.reversed.call(instance, seq); + return instance; + } + }, + }, + methods: { + __length_hint__: { + $meth: function __length_hint__(self) { + return self.idx >= 0 ? new Sk.builtin.int_(self.idx) : new Sk.builtin.int_(0); + }, + $flags: {NoArgs: true}, + }, + }, +}); + +/** + * @constructor + * @param {Array} JS Array of iterator objects + * @extends Sk.builtin.object + */ +Sk.builtin.zip_ = Sk.abstr.buildIteratorClass("zip", { + constructor: function zip_(iters) { + this.iters = iters; + return this; + }, + iternext: function () { + if (this.iters.length === 0) { + return undefined; + } + const tup = []; + for (let i = 0; i < this.iters.length; i++) { + const next = this.iters[i].tp$iternext(); + if (next === undefined) { + return undefined; + } + tup.push(next); + } + return new Sk.builtin.tuple(tup); + }, + slots: { + tp$doc: + "zip(iter1 [,iter2 [...]]) --> zip object\n\nReturn a zip object whose .__next__() method returns a tuple where\nthe i-th element comes from the i-th iterable argument. The .__next__()\nmethod continues until the shortest iterable in the argument sequence\nis exhausted and then it raises StopIteration.", + tp$new: function (args, kwargs) { + if (this === Sk.builtin.zip_.prototype) { + Sk.abstr.checkNoKwargs("zip", kwargs); + } + const iters = []; + for (let i = 0; i < args.length; i++) { + try { + iters.push(Sk.abstr.iter(args[i])); + } catch (e) { + if (e instanceof Sk.builtin.TypeError) { + throw new Sk.builtin.TypeError("zip argument #" + (i + 1) + " must support iteration"); + } else { + throw e; + } + } + } + if (this === Sk.builtin.zip_.prototype) { + return new Sk.builtin.zip_(iters); + } else { + const instance = new this.constructor(); + Sk.builtin.zip_.call(instance, iters); + return instance; + } + }, + }, +}); +Sk.exportSymbol("Sk.builtin.zip_", Sk.builtin.zip_); + +/** + * @constructor + * @param {Sk.builtin.func} func + * @param {Array} array of iterators + * @extends Sk.builtin.object + */ +Sk.builtin.map_ = Sk.abstr.buildIteratorClass("map", { + constructor: function map_(func, iters) { + this.func = func; + this.iters = iters; + return this; + }, + iternext: function () { + const args = []; + let next; + for (let i = 0; i < this.iters.length; i++) { + next = this.iters[i].tp$iternext(); + if (next === undefined) { + return undefined; + } + args.push(next); + } + return Sk.misceval.callsimArray(this.func, args); + }, + slots: { + tp$doc: + "map(func, *iterables) --> map object\n\nMake an iterator that computes the function using arguments from\neach of the iterables. Stops when the shortest iterable is exhausted.", + tp$new: function (args, kwargs) { + if (this === Sk.builtin.map_.prototype) { + Sk.abstr.checkNoKwargs("map", kwargs); + } + Sk.abstr.checkArgsLen("map", args, 2); + const func = args[0]; + const iters = []; + for (let i = 1; i < args.length; i++) { + iters.push(Sk.abstr.iter(args[i])); + } + if (this === Sk.builtin.map_.prototype) { + return new Sk.builtin.map_(func, iters); + } else { + const instance = new this.constructor(); + Sk.builtin.map_.call(instance, func, iters); + return instance; + } + }, + }, +}); + +Sk.exportSymbol("Sk.builtin.map_", Sk.builtin.map_); diff --git a/src/lib/PIL/__init__.js b/src/lib/PIL/__init__.js index 6b3ca50337..b490e0f3d5 100644 --- a/src/lib/PIL/__init__.js +++ b/src/lib/PIL/__init__.js @@ -1,4 +1,4 @@ -var $builtinmodule = function(name) { +var $builtinmodule = function (name) { var mod, sampleWrapper; mod = {__name__: "PIL"}; @@ -18,30 +18,30 @@ var $builtinmodule = function(name) { // checking FrameManager.willRenderNext() function InstantPromise(err, result) { this.lastResult = result; - this.lastError = err; + this.lastError = err; } - InstantPromise.prototype.then = function(cb) { + InstantPromise.prototype.then = function (cb) { if (this.lastError) { return this; } try { this.lastResult = cb(this.lastResult); - } catch(e) { + } catch (e) { this.lastResult = undefined; - this.lastError = e; + this.lastError = e; } return this.lastResult instanceof Promise ? this.lastResult : this; }; - InstantPromise.prototype.catch = function(cb) { + InstantPromise.prototype.catch = function (cb) { if (this.lastError) { try { this.lastResult = cb(this.lastError); - this.lastError = undefined; - } catch(e) { + this.lastError = undefined; + } catch (e) { this.lastResult = undefined; this.lastError = e; } @@ -50,12 +50,12 @@ var $builtinmodule = function(name) { return this.lastResult instanceof Promise ? this.lastResult : this; }; - var buildImage = function(imageData) { + var buildImage = function (imageData) { }; function getAsset(name) { - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { if (Sk.PIL.assets[name] !== undefined) { //return Sk.PIL.assets[name]; resolve(Sk.PIL.assets[name]); @@ -74,7 +74,7 @@ var $builtinmodule = function(name) { }); } - var image = function($gbl, $loc) { + var image = function ($gbl, $loc) { // open(filename) or open(url) // show() @@ -82,7 +82,7 @@ var $builtinmodule = function(name) { // }); - $loc.open = new Sk.builtin.func(function(self, file_or_url) { + $loc.open = new Sk.builtin.func(function (self, file_or_url) { Sk.builtin.pyCheckArgs("open", arguments, 2, 2); Sk.builtin.pyCheckType("file_or_url", "string", Sk.builtin.checkString(file_or_url)); self.file_or_url = file_or_url; @@ -90,7 +90,7 @@ var $builtinmodule = function(name) { var imagePromise = getAsset(Sk.ffi.remapToJs(file_or_url)); var susp = new Sk.misceval.Suspension(); self.image = Sk.builtin.none.none$; - susp.resume = function() { + susp.resume = function () { if (susp.data["error"]) { //throw new Sk.builtin.IOError(susp.data["error"].message); throw susp.data["error"]; @@ -101,7 +101,7 @@ var $builtinmodule = function(name) { }; susp.data = { type: "Sk.promise", - promise: imagePromise.then(function(value) { + promise: imagePromise.then(function (value) { console.log("PROMISED"); self.image = value; self.canvas = document.createElement("canvas"); @@ -113,7 +113,7 @@ var $builtinmodule = function(name) { self.pixels = self.canvas.getContext("2d").getImageData(0, 0, self.image.width, self.image.height).data; console.log(self.pixels); return value; - }, function(err) { + }, function (err) { self.image = ""; throw err; //return err; @@ -123,7 +123,7 @@ var $builtinmodule = function(name) { return susp; }); - $loc.show = new Sk.builtin.func(function(self) { + $loc.show = new Sk.builtin.func(function (self) { if (Sk.console === undefined) { throw new Sk.builtin.NameError("Can not resolve drawing area. Sk.console is undefined!"); } diff --git a/src/lib/StringIO.py b/src/lib/StringIO.py index 0c4550cd09..1f2d5dcd80 100644 --- a/src/lib/StringIO.py +++ b/src/lib/StringIO.py @@ -1 +1 @@ -from io import StringIO \ No newline at end of file +from io import StringIO diff --git a/src/lib/_thread.py b/src/lib/_thread.py index b10987a39d..2c2b783772 100644 --- a/src/lib/_thread.py +++ b/src/lib/_thread.py @@ -1,2 +1,2 @@ def get_ident(): - return 1 \ No newline at end of file + return 1 diff --git a/src/lib/array.js b/src/lib/array.js index f23a929625..d719b532fb 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -15,75 +15,75 @@ $builtinmodule = function (name) { // 'f' float float 4 // 'd' double float 8 - var typecodes = ['c', 'b', 'B', 'u', 'h', 'H', 'i', 'I', 'l', 'L', 'f', 'd']; + var typecodes = ["c", "b", "B", "u", "h", "H", "i", "I", "l", "L", "f", "d"]; var functions = [ - '__add__', - '__class__', - '__contains__', - '__copy__', - '__deepcopy__', - '__delattr__', - '__delitem__', - '__delslice__', - '__doc__', - '__eq__', - '__format__', - '__ge__', - '__getattribute__', - '__getitem__', - '__getslice__', - '__gt__', - '__hash__', - '__iadd__', - '__imul__', - '__init__', - '__iter__', - '__le__', - '__len__', - '__lt__', - '__mul__', - '__ne__', - '__new__', - '__reduce__', - '__reduce_ex__', - '__repr__', - '__rmul__', - '__setattr__', - '__setitem__', - '__setslice__', - '__sizeof__', - '__str__', - '__subclasshook__', - 'append', - 'buffer_info', - 'byteswap', - 'count', - 'extend', - 'fromfile', - 'fromlist', - 'fromstring', - 'fromunicode', - 'index', - 'insert', - 'itemsize', - 'pop', - 'read', - 'remove', - 'reverse', - 'tofile', - 'tolist', - 'tostring', - 'tounicode', - 'typecode', - 'write']; + "__add__", + "__class__", + "__contains__", + "__copy__", + "__deepcopy__", + "__delattr__", + "__delitem__", + "__delslice__", + "__doc__", + "__eq__", + "__format__", + "__ge__", + "__getattribute__", + "__getitem__", + "__getslice__", + "__gt__", + "__hash__", + "__iadd__", + "__imul__", + "__init__", + "__iter__", + "__le__", + "__len__", + "__lt__", + "__mul__", + "__ne__", + "__new__", + "__reduce__", + "__reduce_ex__", + "__repr__", + "__rmul__", + "__setattr__", + "__setitem__", + "__setslice__", + "__sizeof__", + "__str__", + "__subclasshook__", + "append", + "buffer_info", + "byteswap", + "count", + "extend", + "fromfile", + "fromlist", + "fromstring", + "fromunicode", + "index", + "insert", + "itemsize", + "pop", + "read", + "remove", + "reverse", + "tofile", + "tolist", + "tostring", + "tounicode", + "typecode", + "write"]; var array = function ($gbl, $loc) { $loc.__init__ = new Sk.builtin.func(function (self, typecode, initialiser) { Sk.builtin.pyCheckArgsLen("__init__", arguments.length, 2, 3); if (typecodes.indexOf(Sk.ffi.remapToJs(typecode)) == -1) { - throw new Sk.builtin.ValueError("bad typecode (must be c, b, B, u, h, H, i, I, l, L, f or d)") + throw new Sk.builtin.ValueError("bad typecode (must be c, b, B, u, h, H, i, I, l, L, f or d)"); } if (initialiser && !Sk.builtin.checkIterable(initialiser)) { @@ -103,8 +103,8 @@ $builtinmodule = function (name) { } else { self.internalIterable = new Sk.builtin.list(); for (iter = Sk.abstr.iter(initialiser), item = iter.tp$iternext(); - item !== undefined; - item = iter.tp$iternext()) { + item !== undefined; + item = iter.tp$iternext()) { Sk.misceval.callsimArray(self.internalIterable.append, [self.internalIterable, item]); } @@ -118,7 +118,7 @@ $builtinmodule = function (name) { if (Sk.ffi.remapToJs(self.typecode) == "c") { iterableJs = ", '" + Sk.ffi.remapToJs(self.internalIterable).join("") + "'"; } else { - iterableJs = ", " + Sk.ffi.remapToJs(Sk.misceval.callsimArray(self.internalIterable.__repr__, [self.internalIterable])); + iterableJs = ", " + Sk.ffi.remapToJs(Sk.misceval.callsimArray(self.internalIterable.__repr__, [self.internalIterable])); } } @@ -136,7 +136,7 @@ $builtinmodule = function (name) { return Sk.builtin.none.none$; }); - $loc.extend = new Sk.builtin.func(function(self, iterable) { + $loc.extend = new Sk.builtin.func(function (self, iterable) { Sk.builtin.pyCheckArgsLen("__init__", arguments.length, 2, 2); if (!Sk.builtin.checkIterable(iterable)) { @@ -144,8 +144,8 @@ $builtinmodule = function (name) { } for (iter = Sk.abstr.iter(iterable), item = iter.tp$iternext(); - item !== undefined; - item = iter.tp$iternext()) { + item !== undefined; + item = iter.tp$iternext()) { Sk.misceval.callsimArray(self.internalIterable.append, [self.internalIterable, item]); } @@ -154,7 +154,7 @@ $builtinmodule = function (name) { mod.array = Sk.misceval.buildClass(mod, array, "array", []); - mod.__name__ = new Sk.builtin.str('array'); + mod.__name__ = new Sk.builtin.str("array"); return mod; }; diff --git a/src/lib/ast.js b/src/lib/ast.js index ea508dccb1..0246bca790 100644 --- a/src/lib/ast.js +++ b/src/lib/ast.js @@ -1,19 +1,21 @@ var $builtinmodule = function (name) { - var mod = {__name__: Sk.builtin.str("_ast")}; + var mod = {__name__: new Sk.builtin.str("_ast")}; function mangleAppropriately(name) { switch (name) { - case "name": return "name_$rn$"; - default: return name; + case "name": + return "name_$rn$"; + default: + return name; } } - + /** - * Consumes an AST Node (JS version). Return a list of tuples of + * Consumes an AST Node (JS version). Return a list of tuples of * ``(fieldname, value)`` for each field in ``node._fields`` that is * present on *node*. */ - var iter_fieldsJs = function(node) { + var iter_fieldsJs = function (node) { var fieldList = []; for (var i = 0; i < node._fields.length; i += 2) { var field = node._fields[i]; @@ -23,12 +25,12 @@ var $builtinmodule = function (name) { } return fieldList; }; - - mod.iter_fields = function(node) { + + mod.iter_fields = function (node) { return node._fields; }; - - var convertValue = function(value) { + + var convertValue = function (value) { // acbart: kwarg field for lambdas (and functions perhaps?) can be undefined if (value === null || value === undefined) { return Sk.builtin.none.none$; @@ -53,7 +55,7 @@ var $builtinmodule = function (name) { // No AST nodes have primitive list values, just // lists of AST nodes } - return Sk.builtin.list(subvalues); + return new Sk.builtin.list(subvalues); } else if (isJsAst(value)) { var constructorName = functionName(value.constructor); return Sk.misceval.callsim(mod[constructorName], value); @@ -61,37 +63,63 @@ var $builtinmodule = function (name) { return value; } }; - - var isJsAst = function(jsNode) { + + var isJsAst = function (jsNode) { return jsNode instanceof Object && "_astname" in jsNode; }; - var isSpecialPyAst = function(val) { + var isSpecialPyAst = function (val) { if (typeof val == "function") { switch (functionName(val)) { - case "Add": case "Add": case "Sub": case "Mult": case "Div": - case "Mod": case "Pow": case "LShift": case "RShift": - case "BitOr": case "BitXor": case "BitAnd": case "FloorDiv": - case "Store": case "Load": case "Del": case "Param": - case "And": case "Or": case "Xor": case "Not": - case "Invert": case "UAdd": case "USub": - case "Lt": case "Gt": case "LtE": case "GtE": - case "NotEq": case "Eq": case "Is": case "IsNot": - case "In": case "NotIn": + case "Add": + case "Add": + case "Sub": + case "Mult": + case "Div": + case "Mod": + case "Pow": + case "LShift": + case "RShift": + case "BitOr": + case "BitXor": + case "BitAnd": + case "FloorDiv": + case "Store": + case "Load": + case "Del": + case "Param": + case "And": + case "Or": + case "Xor": + case "Not": + case "Invert": + case "UAdd": + case "USub": + case "Lt": + case "Gt": + case "LtE": + case "GtE": + case "NotEq": + case "Eq": + case "Is": + case "IsNot": + case "In": + case "NotIn": return true; - default: return false; + default: + return false; } } else { return false; } }; - var isPyAst = function(pyValue) { + var isPyAst = function (pyValue) { return Sk.misceval.isTrue(Sk.builtin.isinstance(pyValue, mod.AST)); }; - var isPyList = function(pyValue) { + var isPyList = function (pyValue) { return Sk.misceval.isTrue(Sk.builtin.isinstance(pyValue, Sk.builtin.list)); }; - - var iter_child_nodesJs = function(node) { + + var iter_child_nodesJs = function (node) { var fieldList = iter_fieldsJs(node); var resultList = []; for (var i = 0; i < fieldList.length; i += 1) { @@ -112,13 +140,13 @@ var $builtinmodule = function (name) { } return resultList; }; - + // Python node - mod.iter_child_nodes = function(node) { + mod.iter_child_nodes = function (node) { var fieldList = node._fields.v; var childFields = []; for (var i = 0; i < fieldList.length; i += 1) { - var field = Sk.ffi.remapToJs(fieldList[i].v[0]), + var field = Sk.ffi.remapToJs(fieldList[i].v[0]), value = fieldList[i].v[1]; if (isSpecialPyAst(value)) { childFields.push(value); @@ -133,18 +161,18 @@ var $builtinmodule = function (name) { } } } - return Sk.builtin.list(childFields); + return new Sk.builtin.list(childFields); }; - + /** * Dump the tree in a pretty format - */ - mod.dump = function(node, annotate_fields, include_attributes) { + */ + mod.dump = function (node, annotate_fields, include_attributes) { // Confirm valid arguments Sk.builtin.pyCheckArgs("dump", arguments, 1, 3); // node argument if (!isPyAst(node)) { - throw new Sk.builtin.TypeError("expected AST, got "+Sk.abstr.typeName(node)); + throw new Sk.builtin.TypeError("expected AST, got " + Sk.abstr.typeName(node)); } // annotate_fields argument if (annotate_fields === undefined) { @@ -161,20 +189,20 @@ var $builtinmodule = function (name) { include_attributes = Sk.ffi.remapToJs(include_attributes); } // recursive dump - var _format = function(node) { + var _format = function (node) { //console.log(node.astname, node); if (isSpecialPyAst(node)) { - return functionName(node)+"()"; + return functionName(node) + "()"; } else if (isPyAst(node)) { - var rv = node.jsNode._astname+"("; + var rv = node.jsNode._astname + "("; var fieldList = node._fields.v; var fieldArgs = []; for (var i = 0; i < fieldList.length; i += 1) { - var field = Sk.ffi.remapToJs(fieldList[i].v[0]), + var field = Sk.ffi.remapToJs(fieldList[i].v[0]), value = fieldList[i].v[1]; value = _format(value); if (annotate_fields) { - fieldArgs.push(field+"="+value); + fieldArgs.push(field + "=" + value); } else { fieldArgs.push(value); } @@ -184,7 +212,7 @@ var $builtinmodule = function (name) { for (var i = 0; i < attributeList.length; i += 1) { var field = Sk.ffi.remapToJs(attributeList[i]); var value = Sk.ffi.remapToJs(node.jsNode[field]); - fieldArgs.push(field+"="+value); + fieldArgs.push(field + "=" + value); } } fieldArgs = fieldArgs.join(", "); @@ -192,7 +220,7 @@ var $builtinmodule = function (name) { } else if (isPyList(node)) { var nodes = node.v.map(_format); nodes = nodes.join(", "); - return "["+nodes+"]"; + return "[" + nodes + "]"; } else { return Sk.ffi.remapToJs(node.$r()); } @@ -201,12 +229,12 @@ var $builtinmodule = function (name) { }; var depth = 0; - var NodeVisitor = function($gbl, $loc) { + var NodeVisitor = function ($gbl, $loc) { // Takes in Python Nodes, not JS Nodes - $loc.visit = new Sk.builtin.func(function(self, node) { + $loc.visit = new Sk.builtin.func(function (self, node) { depth += 1; /** Visit a node. **/ - //print(" ".repeat(depth), "VISIT", node.jsNode._astname) + //print(" ".repeat(depth), "VISIT", node.jsNode._astname) var method_name = "visit_" + node.jsNode._astname; //print(" ".repeat(depth), "I'm looking for", method_name) method_name = Sk.ffi.remapToPy(method_name); @@ -216,17 +244,17 @@ var $builtinmodule = function (name) { result = Sk.misceval.callsim(method, node); depth -= 1; return result; - }else { + } else { result = Sk.misceval.callsim(method, self, node); depth -= 1; return result; } - + }); // Takes in Python Nodes, not JS Nodes - $loc.generic_visit = new Sk.builtin.func(function(self, node) { + $loc.generic_visit = new Sk.builtin.func(function (self, node) { /** Called if no explicit visitor function exists for a node. **/ - //print(" ".repeat(depth), "Generically checked", node.astname) + //print(" ".repeat(depth), "Generically checked", node.astname) var fieldList = mod.iter_fields(node).v; for (var i = 0; i < fieldList.length; i += 1) { var field = fieldList[i].v[0].v, value = fieldList[i].v[1]; @@ -251,9 +279,9 @@ var $builtinmodule = function (name) { mod.NodeVisitor = Sk.misceval.buildClass(mod, NodeVisitor, "NodeVisitor", []); // Python node - mod.walk = function(node) { + mod.walk = function (node) { if (isSpecialPyAst(node)) { - return Sk.builtin.list([]); + return new Sk.builtin.list([]); } var resultList = [node]; var childList = mod.iter_child_nodes(node); @@ -262,7 +290,7 @@ var $builtinmodule = function (name) { var children = mod.walk(child); resultList = resultList.concat(children.v); } - return Sk.builtin.list(resultList); + return new Sk.builtin.list(resultList); }; /*NodeVisitor.prototype.visitList = function(nodes) { @@ -284,26 +312,26 @@ var $builtinmodule = function (name) { } return result; }*/ - + var depth = 0; - AST = function($gbl, $loc) { - var copyFromJsNode = function(self, key, jsNode) { + AST = function ($gbl, $loc) { + var copyFromJsNode = function (self, key, jsNode) { if (key in self.jsNode) { - Sk.abstr.sattr(self, Sk.builtin.str(key), Sk.ffi.remapToPy(jsNode[key]), true); - self._attributes.push(Sk.builtin.str(key)); + Sk.abstr.sattr(self, new Sk.builtin.str(key), Sk.ffi.remapToPy(jsNode[key]), true); + self._attributes.push(new Sk.builtin.str(key)); } }; $loc.__init__ = new Sk.builtin.func(function (self, jsNode, partial) { - depth+=1; + depth += 1; if (partial === true) { // Alternative constructor for Skulpt's weird nodes //console.log(" ".repeat(depth)+"S:", jsNode); self.jsNode = {"_astname": jsNode}; self.astname = jsNode; - self._fields = Sk.builtin.list([]); - self._attributes = Sk.builtin.list([]); - Sk.abstr.sattr(self, Sk.builtin.str("_fields"), self._fields, true); - Sk.abstr.sattr(self, Sk.builtin.str("_attributes"), self._attributes, true); + self._fields = new Sk.builtin.list([]); + self._attributes = new Sk.builtin.list([]); + Sk.abstr.sattr(self, new Sk.builtin.str("_fields"), self._fields, true); + Sk.abstr.sattr(self, new Sk.builtin.str("_attributes"), self._attributes, true); //console.log(" ".repeat(depth)+"--", jsNode); } else { //console.log(" ".repeat(depth)+"P:", jsNode._astname); @@ -317,61 +345,64 @@ var $builtinmodule = function (name) { var field = fieldListJs[i][0], value = fieldListJs[i][1]; //console.log(" ".repeat(depth+1)+field, value) if (field === "docstring" && value === undefined) { - value = Sk.builtin.str(""); + value = new Sk.builtin.str(""); } else { value = convertValue(value); } field = mangleAppropriately(field); - Sk.abstr.sattr(self, Sk.builtin.str(field), value, true); + Sk.abstr.sattr(self, new Sk.builtin.str(field), value, true); // TODO: Figure out why name is getting manged, and make it stop! - self._fields.push(Sk.builtin.tuple([Sk.builtin.str(field), value])); + self._fields.push(new Sk.builtin.tuple([new Sk.builtin.str(field), value])); } //console.log(" ".repeat(depth)+"FIELDS") - self._fields = Sk.builtin.list(self._fields); - Sk.abstr.sattr(self, Sk.builtin.str("_fields"), self._fields, true); + self._fields = new Sk.builtin.list(self._fields); + Sk.abstr.sattr(self, new Sk.builtin.str("_fields"), self._fields, true); copyFromJsNode(self, "lineno", self.jsNode); copyFromJsNode(self, "col_offset", self.jsNode); copyFromJsNode(self, "end_lineno", self.jsNode); copyFromJsNode(self, "end_col_offset", self.jsNode); - self._attributes = Sk.builtin.list(self._attributes); - Sk.abstr.sattr(self, Sk.builtin.str("_attributes"), self._attributes, true); + self._attributes = new Sk.builtin.list(self._attributes); + Sk.abstr.sattr(self, new Sk.builtin.str("_attributes"), self._attributes, true); //console.log(" ".repeat(depth)+"--", jsNode._astname); } depth -= 1; + return Sk.builtin.none.none$; }); $loc.__str__ = new Sk.builtin.func(function (self) { - return Sk.builtin.str("<_ast."+self.astname+" object>"); + return new Sk.builtin.str("<_ast." + self.astname + " object>"); }); $loc.__repr__ = $loc.__str__; }; mod.AST = Sk.misceval.buildClass(mod, AST, "AST", []); - + //mod.literal_eval // Implementation wouldn't be hard, but it does require a lot of Skulpting - + mod.parse = function parse(source, filename) { if (!(/\S/.test(source))) { return Sk.misceval.callsim(mod.Module, new Sk.INHERITANCE_MAP.mod[0]([])); } if (filename === undefined) { - filename = Sk.builtin.str(""); + filename = new Sk.builtin.str(""); } var parse = Sk.parse(filename, Sk.ffi.remapToJs(source)); ast = Sk.astFromParse(parse.cst, filename, parse.flags); return Sk.misceval.callsim(mod.Module, ast); // Walk tree and create nodes (lazily?) }; - + /* mod.Module = function ($gbl, $loc) { Sk.abstr.superConstructor(mod.OrderedDict, this, items); }*/ - + function functionName(fun) { let astname = fun.prototype._astname; switch (astname) { - case "arguments": return "arguments_"; - default: return astname; + case "arguments": + return "arguments_"; + default: + return astname; } /* var ret = fun.toString(); @@ -384,17 +415,21 @@ var $builtinmodule = function (name) { } return ret;*/ } - + for (var base in Sk.INHERITANCE_MAP) { - var baseClass = function($gbl, $loc) { return this;}; + var baseClass = function ($gbl, $loc) { + return this; + }; mod[base] = Sk.misceval.buildClass(mod, baseClass, base, [mod.AST]); - for (var i=0; i < Sk.INHERITANCE_MAP[base].length; i++) { + for (var i = 0; i < Sk.INHERITANCE_MAP[base].length; i++) { var nodeType = Sk.INHERITANCE_MAP[base][i]; var nodeName = nodeType.prototype._astname; - var nodeClass = function($gbl, $loc) { return this;}; + var nodeClass = function ($gbl, $loc) { + return this; + }; mod[nodeName] = Sk.misceval.buildClass(mod, nodeClass, nodeName, [mod[base]]); } } - + return mod; }; \ No newline at end of file diff --git a/src/lib/bisect.py b/src/lib/bisect.py index 7732c639e3..49c73b641f 100644 --- a/src/lib/bisect.py +++ b/src/lib/bisect.py @@ -1,5 +1,6 @@ """Bisection algorithms.""" + def insort_right(a, x, lo=0, hi=None): """Insert item x in list a, and keep it sorted assuming a is sorted. @@ -14,11 +15,14 @@ def insort_right(a, x, lo=0, hi=None): if hi is None: hi = len(a) while lo < hi: - mid = (lo+hi)//2 - if x < a[mid]: hi = mid - else: lo = mid+1 + mid = (lo + hi) // 2 + if x < a[mid]: + hi = mid + else: + lo = mid + 1 a.insert(lo, x) + def bisect_right(a, x, lo=0, hi=None): """Return the index where to insert item x in list a, assuming a is sorted. @@ -35,11 +39,14 @@ def bisect_right(a, x, lo=0, hi=None): if hi is None: hi = len(a) while lo < hi: - mid = (lo+hi)//2 - if x < a[mid]: hi = mid - else: lo = mid+1 + mid = (lo + hi) // 2 + if x < a[mid]: + hi = mid + else: + lo = mid + 1 return lo + def insort_left(a, x, lo=0, hi=None): """Insert item x in list a, and keep it sorted assuming a is sorted. @@ -54,9 +61,11 @@ def insort_left(a, x, lo=0, hi=None): if hi is None: hi = len(a) while lo < hi: - mid = (lo+hi)//2 - if a[mid] < x: lo = mid+1 - else: hi = mid + mid = (lo + hi) // 2 + if a[mid] < x: + lo = mid + 1 + else: + hi = mid a.insert(lo, x) @@ -76,11 +85,14 @@ def bisect_left(a, x, lo=0, hi=None): if hi is None: hi = len(a) while lo < hi: - mid = (lo+hi)//2 - if a[mid] < x: lo = mid+1 - else: hi = mid + mid = (lo + hi) // 2 + if a[mid] < x: + lo = mid + 1 + else: + hi = mid return lo + # Overwrite above definitions with a fast C implementation try: from _bisect import * diff --git a/src/lib/cisc108/assertions.py b/src/lib/cisc108/assertions.py index 744ca7ad7d..b3c0072836 100644 --- a/src/lib/cisc108/assertions.py +++ b/src/lib/cisc108/assertions.py @@ -61,7 +61,7 @@ from numbers import Number except: Number = (bool, int, float, complex) - + try: bytes except NameError: @@ -71,7 +71,8 @@ frozenset() except: frozenset = tuple() - + + def make_type_name(value): try: return type(value).__name__ @@ -128,19 +129,23 @@ def _normalize_string(text): MESSAGE_GENERIC_SUCCESS = ( "TEST PASSED{context}") + class StudentTestReport: def __init__(self): self.reset() + def __repr__(self): return str(self) + def __str__(self): return ('' - ).format( + ).format( failures=self.failures, successes=self.successes, tests=self.tests, lines=', '.join(self.lines) ) + def reset(self): self.failures = 0 self.successes = 0 @@ -195,10 +200,12 @@ def assert_equal(x, y, precision=4, exact_strings=False, *args) -> bool: student_tests.successes += 1 return True + # Hack to allow anyone with an assert_equal reference to get the results # since they are global across all calls. Weird strategy! assert_equal.student_tests = student_tests + def _is_equal(x, y, precision, exact_strings, *args): """ _is_equal : thing thing -> boolean @@ -302,6 +309,7 @@ def _are_sets_equal(x, y, precision, exact_strings): return False return True + ################################################################################ # Truthiness stuff @@ -389,7 +397,8 @@ def _is_true(x): return None else: return x - + + ################################################################################ # Type Checking @@ -402,19 +411,22 @@ def _is_true(x): list: 'list' } + def _get_name(value): try: return BETTER_TYPE_NAMES.get(value, value.__name__) except Exception: return str(value)[8:-2] - + + def _make_key_list(values): if not values: return "and there were no keys at all" elif len(values) == 1: return "but there was the key {!r}".format(values[0]) else: - return "but there were the keys "+ (", ".join(sorted(map(repr, values[:-1])))) + " and {!r}".format(values[-1]) + return "but there were the keys " + (", ".join(sorted(map(repr, values[:-1])))) + " and {!r}".format(values[-1]) + WRONG_TYPE_MESSAGE = " was the wrong type. Expected type was {y_type!r}, but actual value was {x} ({x_type!r})." WRONG_KEY_TYPE_MESSAGE = " had a wrong type for a key. Expected type of all keys was {y_type!r}, but there was the key {x} ({x_type!r})." @@ -422,6 +434,7 @@ def _make_key_list(values): EXTRA_KEYS_MESSAGE = " had all the correct keys ({}), but also had these unexpected keys: {}" NOT_A_TYPE_MESSAGE = " was the value {x!r} ({x_type!r}). However, that's not important because the expected type ({y}) doesn't make sense! The type definition should not have literal values like {y} in it, only types (like {y_type}). The literal values go into instances of the type." + def _validate_dictionary_type(value, expected_type, path): if not isinstance(value, dict): return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y_type="dictionary") @@ -430,27 +443,30 @@ def _validate_dictionary_type(value, expected_type, path): if expected_key not in value: return path + MISSING_KEY_MESSAGE.format(expected_key, _make_key_list(list(value.keys()))) reason = _validate_type(value[expected_key], expected_value, - path+"[{!r}]".format(expected_key)) + path + "[{!r}]".format(expected_key)) if reason: return reason elif isinstance(expected_key, type): for k, v in value.items(): - new_path = path+"[{!r}]".format(k) + new_path = path + "[{!r}]".format(k) if not isinstance(k, expected_key): - return path + WRONG_KEY_TYPE_MESSAGE.format(x=repr(k), x_type=_get_name(type(k)), y_type=_get_name(expected_key)) + return path + WRONG_KEY_TYPE_MESSAGE.format(x=repr(k), x_type=_get_name(type(k)), + y_type=_get_name(expected_key)) reason = _validate_type(v, expected_value, new_path) if reason: return reason - break # only support one key/value type in Lookup style + break # only support one key/value type in Lookup style else: if len(expected_type) != len(value): unexpected_keys = set(value.keys()) - set(expected_type.keys()) unexpected_keys = ", ".join(sorted(map(repr, unexpected_keys))) expected_keys = ", ".join(sorted(map(repr, expected_type))) return path + EXTRA_KEYS_MESSAGE.format(expected_keys, unexpected_keys) - + + SIMPLE_TYPES = (int, float, bool, str) + def _validate_type(value, expected_type, path="world"): if isinstance(expected_type, dict): return _validate_dictionary_type(value, expected_type, path) @@ -460,22 +476,27 @@ def _validate_type(value, expected_type, path="world"): if not expected_type and value: return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y_type="empty list") for index, element in enumerate(value): - reason = _validate_type(element, expected_type[0], path+"[{}]".format(index)) + reason = _validate_type(element, expected_type[0], path + "[{}]".format(index)) if reason: return reason elif isinstance(expected_type, SIMPLE_TYPES): - return path + NOT_A_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y=repr(expected_type), y_type=type(expected_type).__name__) + return path + NOT_A_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y=repr(expected_type), + y_type=type(expected_type).__name__) elif expected_type == float: if not isinstance(value, (int, float)) and value is not None: - return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y_type=_get_name(expected_type)) + return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), + y_type=_get_name(expected_type)) elif isinstance(value, bool) and expected_type != bool: - return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y_type=_get_name(expected_type)) + return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), + y_type=_get_name(expected_type)) elif not isinstance(value, expected_type) and value is not None: - return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y_type=_get_name(expected_type)) + return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), + y_type=_get_name(expected_type)) elif value == None and expected_type != None: return path + WRONG_TYPE_MESSAGE.format(x=repr(value), x_type=_get_name(type(value)), y_type=_get_name(expected_type)) + def assert_type(value, expected_type) -> bool: """ Checks that the given value is of the expected_type. @@ -496,20 +517,19 @@ def assert_type(value, expected_type) -> bool: else: context = MESSAGE_LINE_CODE.format(line=line, code=code) student_tests.lines.append(line) - + reason = _validate_type(value, expected_type, "value") student_tests.tests += 1 if reason is not None: student_tests.failures += 1 if isinstance(expected_type, dict): if isinstance(value, dict): - reason = "the "+reason + reason = "the " + reason else: - reason = "the "+reason + reason = "the " + reason print("FAILURE{context},".format(context=context), reason) return False elif not QUIET: print(MESSAGE_GENERIC_SUCCESS.format(context=context)) student_tests.successes += 1 return True - diff --git a/src/lib/cisc108/old_assertions.py b/src/lib/cisc108/old_assertions.py index 8ff53366b2..c84703bae3 100644 --- a/src/lib/cisc108/old_assertions.py +++ b/src/lib/cisc108/old_assertions.py @@ -55,7 +55,7 @@ from numbers import Number except: Number = (bool, int, float, complex) - + try: bytes except NameError: @@ -65,13 +65,15 @@ frozenset() except: frozenset = tuple() - + + def make_type_name(value): try: return type(value).__name__ except Exception: return str(type(value))[8:-2] + def get_line_code(): # Load in extract_stack, or provide shim for environments without it. try: @@ -105,27 +107,33 @@ def get_line_code(): MESSAGE_GENERIC_SUCCESS = ( "TEST PASSED{context}") + class StudentTestReport: def __init__(self): self.reset() + def __repr__(self): return str(self) + def __str__(self): return ('' - ).format( + ).format( failures=self.failures, successes=self.successes, tests=self.tests, lines=', '.join(self.lines) ) + def reset(self): self.failures = 0 self.successes = 0 self.tests = 0 self.lines = [] - + + student_tests = StudentTestReport() + def assert_equal(x, y, precision=4, exact_strings=False, *args): """ Checks an expected value using the _is_equal function. @@ -170,10 +178,12 @@ def assert_equal(x, y, precision=4, exact_strings=False, *args): student_tests.successes += 1 return True + # Hack to allow anyone with an assert_equal reference to get the results # since they are global across all calls. Weird strategy! assert_equal.student_tests = student_tests + def _is_equal(x, y, precision, exact_strings, *args): """ _is_equal : thing thing -> boolean @@ -197,7 +207,7 @@ def _is_equal(x, y, precision, exact_strings, *args): >>> _is_equal(12.3456, 12.34568w5) False """ - + # Check if generators if isinstance(x, LIST_GENERATOR_TYPES): x = list(x) @@ -207,7 +217,7 @@ def _is_equal(x, y, precision, exact_strings, *args): y = list(y) elif isinstance(y, SET_GENERATOR_TYPES): y = set(y) - + if isinstance(x, float) and isinstance(y, float): error = 10 ** (-precision) return abs(x - y) < error diff --git a/src/lib/collections.js b/src/lib/collections.js index 154ec4a3ae..8188a2ddd7 100644 --- a/src/lib/collections.js +++ b/src/lib/collections.js @@ -1,590 +1,1200 @@ -var $builtinmodule = function (name) { - return Sk.misceval.chain(Sk.importModule("keyword", false, true), function(keywds) { - var mod = {}; - - // defaultdict object - - mod.defaultdict = function defaultdict(default_, args) { - if (!(this instanceof mod.defaultdict)) { - return new mod.defaultdict(default_, args); - } - - Sk.abstr.superConstructor(mod.defaultdict, this, args); - - if (default_ === undefined) { - this.default_factory = Sk.builtin.none.none$; - } - else { - if (!Sk.builtin.checkCallable(default_) && !(default_ instanceof Sk.builtin.none)) { +const collections_mod = function (keywds) { + const collections = {}; + + // defaultdict object + const _copy_dd_method_df = { + $meth: function () { + const L = []; + const self = this; + Sk.misceval.iterFor(Sk.abstr.iter(self), function (k) { + L.push(k); + L.push(self.mp$subscript(k)); + }); + return new collections.defaultdict(this.default_factory, L); + }, + $flags: {NoArgs: true}, + }; + + collections.defaultdict = Sk.abstr.buildNativeClass("collections.defaultdict", { + constructor: function defaultdict(default_factory, L) { + this.default_factory = default_factory; + Sk.builtin.dict.call(this, L); + }, + base: Sk.builtin.dict, + methods: { + copy: _copy_dd_method_df, + __copy__: _copy_dd_method_df, + __missing__: { + $meth: function (key) { + if (Sk.builtin.checkNone(this.default_factory)) { + throw new Sk.builtin.KeyError(Sk.misceval.objectRepr(key)); + } else { + const ret = Sk.misceval.callsimArray(this.default_factory, []); + this.mp$ass_subscript(key, ret); + return ret; + } + }, + $flags: {OneArg: true}, + }, + }, + getsets: { + default_factory: { + $get: function () { + return this.default_factory; + }, + $set: function (value) { + this.default_factory = value; + }, + }, + }, + slots: { + tp$doc: + "defaultdict(default_factory[, ...]) --> dict with default factory\n\nThe default factory is called without arguments to produce\na new value when a key is not present, in __getitem__ only.\nA defaultdict compares equal to a dict with the same items.\nAll remaining arguments are treated the same as if they were\npassed to the dict constructor, including keyword arguments.\n", + tp$init: function (args, kwargs) { + const default_ = args.shift(); + if (default_ === undefined) { + this.default_factory = Sk.builtin.none.none$; + } else if (!Sk.builtin.checkCallable(default_) && !Sk.builtin.checkNone(default_)) { throw new Sk.builtin.TypeError("first argument must be callable"); + } else { + this.default_factory = default_; } - this.default_factory = default_; - } - - if (this['$d']) { - this['$d']['default_factory'] = this.default_factory; - } - else { - this['$d'] = {'default_factory': this.default_factory}; - } + return Sk.builtin.dict.prototype.tp$init.call(this, args, kwargs); + }, + $r: function () { + const def_str = Sk.misceval.objectRepr(this.default_factory); + const dict_str = Sk.builtin.dict.prototype.$r.call(this).v; + return new Sk.builtin.str("defaultdict(" + def_str + ", " + dict_str + ")"); + }, + mp$subscript: function (key) { + return this.mp$lookup(key) || Sk.misceval.callsimArray(this.__missing__, [this, key]); + }, + }, + }); - return this; - }; + collections.Counter = Sk.abstr.buildNativeClass("Counter", { + constructor: function Counter() { + this.$d = new Sk.builtin.dict(); + }, + base: Sk.builtin.dict, + methods: { + elements: { + $flags: {NoArgs: true}, + $meth: function () { + const all_elements = []; + for (let iter = this.tp$iter(), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { + for (let i = 0; i < this.mp$subscript(k).v; i++) { + all_elements.push(k); + } + } + if (collections._chain === undefined) { + let itertools = Sk.importModule("itertools", false, true); + return Sk.misceval.chain(itertools, function (i) { + collections._chain = i.$d.chain; + return Sk.misceval.callsimArray(collections._chain, all_elements); + }); + } else { + return Sk.misceval.callsimArray(collections._chain, all_elements); + } + }, + }, + most_common: { + $flags: { + NamedArgs: ["n"], + Defaults: [Sk.builtin.none.none$], + }, + $meth: function (n) { + length = this.sq$length(); + if (Sk.builtin.checkNone(n)) { + n = length; + } else { + if (!Sk.builtin.checkInt(n)) { + if (n instanceof Sk.builtin.float_) { + throw new Sk.builtin.TypeError("integer argument expected, got float"); + } else { + throw new Sk.builtin.TypeError("an integer is required"); + } + } + + n = Sk.builtin.asnum$(n); + n = n <= length ? n : length; + n = n >= 0 ? n : 0; + } - Sk.abstr.setUpInheritance("defaultdict", mod.defaultdict, Sk.builtin.dict); + var most_common_elem = []; + for (var iter = this.tp$iter(), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { + most_common_elem.push([k, this.mp$subscript(k)]); + } - mod.defaultdict.prototype['$r'] = function () { - var def_str = Sk.misceval.objectRepr(this.default_factory).v; - var dict_str = Sk.builtin.dict.prototype['$r'].call(this).v; - return new Sk.builtin.str("defaultdict(" + def_str + ", " + dict_str + ")"); - }; + var sort_func = function (a, b) { + if (a[1].v < b[1].v) { + return 1; + } else if (a[1].v > b[1].v) { + return -1; + } else { + return 0; + } + }; + most_common_elem = most_common_elem.sort(sort_func); + + var ret = []; + for (var i = 0; i < n; i++) { + ret.push(new Sk.builtin.tuple(most_common_elem.shift())); + } - mod.defaultdict.prototype['__copy__'] = function (self) { - var v; - var iter, k; - var ret = []; - - for (iter = Sk.abstr.iter(self), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - v = self.mp$subscript(k); - ret.push(k); - ret.push(v); - } - return new mod.defaultdict(self['$d']['default_factory'], ret); - }; + return new Sk.builtin.list(ret); + }, + }, + update: { + $flags: {FastCall: true}, + $meth: function (args, kwargs) { + Sk.abstr.checkArgsLen("update", args, 0, 1); + let k, iter, count; + const other = args[0]; + if (other !== undefined) { + iter = Sk.abstr.iter(other); + } + if (other instanceof Sk.builtin.dict) { + for (k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { + count = this.mp$subscript(k); + this.mp$ass_subscript(k, count.nb$add(other.mp$subscript(k))); + } + } else if (iter) { + const one = new Sk.builtin.int_(1); + for (k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { + count = this.mp$subscript(k); + this.mp$ass_subscript(k, count.nb$add(one)); + } + } + kwargs = kwargs || []; + for (let i = 0; i < kwargs.length; i += 2) { + k = new Sk.builtin.str(kwargs[i]); + count = this.mp$subscript(k); + this.mp$ass_subscript(k, count.nb$add(kwargs[i + 1])); + } + return Sk.builtin.none.none$; + }, + }, + subtract: { + $flags: {FastCall: true}, + $meth: function (args, kwargs) { + Sk.abstr.checkArgsLen("subtract", args, 0, 1); + let k, iter, count; + const other = args[0]; + if (other !== undefined) { + iter = Sk.abstr.iter(other); + } + if (other instanceof Sk.builtin.dict) { + for (k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { + count = this.mp$subscript(k); + this.mp$ass_subscript(k, count.nb$subtract(other.mp$subscript(k))); + } + } else if (iter) { + const one = new Sk.builtin.int_(1); + for (k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { + count = this.mp$subscript(k); + this.mp$ass_subscript(k, count.nb$subtract(one)); + } + } + kwargs = kwargs || []; + for (let i = 0; i < kwargs.length; i += 2) { + k = new Sk.builtin.str(kwargs[i]); + count = this.mp$subscript(k); + this.mp$ass_subscript(k, count.nb$subtract(kwargs[i + 1])); + } + return Sk.builtin.none.none$; + }, + }, + }, + getsets: { + __dict__: Sk.generic.getSetDict, + }, + slots: { + tp$doc: + "Dict subclass for counting hashable items. Sometimes called a bag\n or multiset. Elements are stored as dictionary keys and their counts\n are stored as dictionary values.\n\n >>> c = Counter('abcdeabcdabcaba') # count elements from a string\n\n >>> c.most_common(3) # three most common elements\n [('a', 5), ('b', 4), ('c', 3)]\n >>> sorted(c) # list all unique elements\n ['a', 'b', 'c', 'd', 'e']\n >>> ''.join(sorted(c.elements())) # list elements with repetitions\n 'aaaaabbbbcccdde'\n >>> sum(c.values()) # total of all counts\n 15\n\n >>> c['a'] # count of letter 'a'\n 5\n >>> for elem in 'shazam': # update counts from an iterable\n ... c[elem] += 1 # by adding 1 to each element's count\n >>> c['a'] # now there are seven 'a'\n 7\n >>> del c['b'] # remove all 'b'\n >>> c['b'] # now there are zero 'b'\n 0\n\n >>> d = Counter('simsalabim') # make another counter\n >>> c.update(d) # add in the second counter\n >>> c['a'] # now there are nine 'a'\n 9\n\n >>> c.clear() # empty the counter\n >>> c\n Counter()\n\n Note: If a count is set to zero or reduced to zero, it will remain\n in the counter until the entry is deleted or the counter is cleared:\n\n >>> c = Counter('aaabbc')\n >>> c['b'] -= 2 # reduce the count of 'b' by two\n >>> c.most_common() # 'b' is still in, but its count is zero\n [('a', 3), ('c', 1), ('b', 0)]\n\n", + tp$init: function (args, kwargs) { + Sk.abstr.checkArgsLen(this.tp$name, args, 0, 1); + return Sk.misceval.callsimArray(this.update, [this, ...args], kwargs); + }, + $r: function () { + var dict_str = this.size > 0 ? Sk.builtin.dict.prototype.$r.call(this).v : ""; + return new Sk.builtin.str("Counter(" + dict_str + ")"); + }, + mp$subscript: function (key) { + return this.mp$lookup(key) || new Sk.builtin.int_(0); + }, + }, + }); - mod.defaultdict.prototype['__missing__'] = function (key) { - Sk.builtin.pyCheckArgsLen('__missing__', arguments.length, 0, 1); - if (key) { - throw new Sk.builtin.KeyError(Sk.misceval.objectRepr(key)); - } - else { - return Sk.misceval.callsimArray(this.default_factory); - } - }; + // OrderedDict + const odict_iter_ = Sk.abstr.buildIteratorClass("odict_iterator", { + constructor: function odict_iter_(odict) { + this.$index = 0; + this.$seq = odict.sk$asarray(); + this.$orig = odict; + }, + iternext: Sk.generic.iterNextWithArrayCheckSize, + flags: {sk$acceptable_as_base_class: false}, + }); - mod.defaultdict.prototype.mp$subscript = function (key) { - try { - return Sk.builtin.dict.prototype.mp$subscript.call(this, key); - } - catch (e) { - if (this.default_factory instanceof Sk.builtin.none) { - return this.__missing__(key); + collections.OrderedDict = Sk.abstr.buildNativeClass("OrderedDict", { + constructor: function OrderedDict() { + this.orderedkeys = []; + Sk.builtin.dict.call(this); + return this; + }, + base: Sk.builtin.dict, + slots: { + tp$as_sequence_or_mapping: true, + tp$init: function (args, kwargs) { + Sk.abstr.checkArgsLen("OrderedDict", args, 0, 1); + args.unshift(this); + res = Sk.misceval.callsimArray(this.update, args, kwargs); + return Sk.builtin.none.none$; + }, + tp$doc: "Dictionary that remembers insertion order", + $r: function () { + let v, pairstr; + const ret = []; + for (let iter = this.tp$iter(), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { + v = this.mp$subscript(k); + if (v === undefined) { + //print(k, "had undefined v"); + v = null; + } + ret.push("(" + Sk.misceval.objectRepr(k) + ", " + Sk.misceval.objectRepr(v) + ")"); } - else { - ret = this.__missing__(); - this.mp$ass_subscript(key, ret); - return ret; + pairstr = ret.join(", "); + if (ret.length > 0) { + pairstr = "[" + pairstr + "]"; } - } - }; - - // Counter object - - mod.Counter = function Counter(iter_or_map) { - if (!(this instanceof mod.Counter)) { - return new mod.Counter(iter_or_map); - } - - - if (iter_or_map instanceof Sk.builtin.dict || iter_or_map === undefined) { - Sk.abstr.superConstructor(mod.Counter, this, iter_or_map); - - } - else { - if (!(Sk.builtin.checkIterable(iter_or_map))) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(iter_or_map) + "' object is not iterable"); + return new Sk.builtin.str("OrderedDict(" + pairstr + ")"); + }, + tp$richcompare: function (other, op) { + if (op != "Eq" && op != "Ne") { + return Sk.builtin.NotImplemented.NotImplemented$; } - - Sk.abstr.superConstructor(mod.Counter, this); - var one = new Sk.builtin.int_(1); - - for (var iter = iter_or_map.tp$iter(), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - var count = this.mp$subscript(k); - count = count.nb$add(one); - this.mp$ass_subscript(k, count); + const $true = op == "Eq" ? true : false; + if (!(other instanceof collections.OrderedDict)) { + return Sk.builtin.dict.prototype.tp$richcompare.call(this, other, op); + } + const l = this.size; + const otherl = other.size; + if (l !== otherl) { + return !$true; } - } - - return this; - }; - - Sk.abstr.setUpInheritance("Counter", mod.Counter, Sk.builtin.dict); - - mod.Counter.prototype['$r'] = function () { - var dict_str = this.size > 0 ? Sk.builtin.dict.prototype['$r'].call(this).v : ''; - return new Sk.builtin.str('Counter(' + dict_str + ')'); - }; - mod.Counter.prototype.mp$subscript = function (key) { - try { - return Sk.builtin.dict.prototype.mp$subscript.call(this, key); - } - catch (e) { - return new Sk.builtin.int_(0); - } - }; + for ( + let iter = this.tp$iter(), otheriter = other.tp$iter(), k = iter.tp$iternext(), otherk = otheriter.tp$iternext(); + k !== undefined; + k = iter.tp$iternext(), otherk = otheriter.tp$iternext() + ) { + if (!Sk.misceval.isTrue(Sk.misceval.richCompareBool(k, otherk, "Eq"))) { + return !$true; + } + const v = this.mp$subscript(k); + const otherv = other.mp$subscript(otherk); - mod.Counter.prototype['elements'] = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen('elements', arguments.length, 1, 1); - var all_elements = []; - for (var iter = self.tp$iter(), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - for (var i = 0; i < self.mp$subscript(k).v; i++) { - all_elements.push(k); + if (!Sk.misceval.isTrue(Sk.misceval.richCompareBool(v, otherv, "Eq"))) { + return !$true; + } } - } - - var ret = - { - tp$iter: function () { - return ret; + return $true; + }, + mp$ass_subscript: function (key, w) { + if (w === undefined) { + this.del$item(key); + } else { + this.set$item(key, w); + } + return Sk.builtin.none.none$; + }, + tp$iter: function () { + return new odict_iter_(this); + }, + }, + methods: { + pop: { + $flags: {NamedArgs: ["key", "default"], Defaults: [null]}, + $meth: function (key, d) { + const idx = this.orderedkeys.indexOf(key); + if (idx != -1) { + this.orderedkeys.splice(idx, 1); + } + if (d === null) { + return Sk.misceval.callsimArray(Sk.builtin.dict.prototype["pop"], [this, key]); + } else { + return Sk.misceval.callsimArray(Sk.builtin.dict.prototype["pop"], [this, key, d]); + } }, - $obj: this, - $index: 0, - $elem: all_elements, - tp$iternext: function () { - if (ret.$index >= ret.$elem.length) { - return undefined; + }, + popitem: { + $flags: {NamedArgs: ["last"], Defaults: [Sk.builtin.bool.true$]}, + $meth: function (last) { + let key, val; + if (this.orderedkeys.length == 0) { + throw new Sk.builtin.KeyError("dictionary is empty"); + } + key = this.orderedkeys[0]; + if (Sk.misceval.isTrue(last)) { + key = this.orderedkeys[this.orderedkeys.length - 1]; } - return ret.$elem[ret.$index++]; + val = Sk.misceval.callsimArray(this["pop"], [this, key]); + return new Sk.builtin.tuple([key, val]); + }, + }, + }, + proto: { + sk$asarray: function () { + return this.orderedkeys.slice(); + }, + set$item: function (key, w) { + const idx = this.orderedkeys.indexOf(key); + if (idx == -1) { + this.orderedkeys.push(key); } - }; - - return ret; - - }); + Sk.builtin.dict.prototype.set$item.call(this, key, w); + }, + del$item: function (key) { + // oops need to edit this as it really doesn't ever get called... or maybe it does by dict; + var idx = this.orderedkeys.indexOf(key); + if (idx != -1) { + this.orderedkeys.splice(idx, 1); + } + Sk.builtin.dict.prototype.del$item.call(this, key); + }, + }, + }); - mod.Counter.prototype['most_common'] = new Sk.builtin.func(function (self, n) { - Sk.builtin.pyCheckArgsLen('most_common', arguments.length, 1, 2); - var length = self.mp$length(); + collections.deque = Sk.abstr.buildNativeClass("collections.deque", { + constructor: function deque(D, maxlen, head, tail, mask) { + this.head = head || 0; + this.tail = tail || 0; + this.mask = mask || 1; + this.maxlen = maxlen; + this.v = D || new Array(2); + }, + slots: { + tp$doc: "deque([iterable[, maxlen]]) --> deque object\n\nA list-like sequence optimized for data accesses near its endpoints.", + tp$hash: Sk.builtin.none.none$, + tp$new: Sk.generic.new, + tp$init: function (args, kwargs) { + [iterable, maxlen] = Sk.abstr.copyKeywordsToNamedArgs("deque", ["iterable", "maxlen"], args, kwargs); + if (maxlen !== undefined && !Sk.builtin.checkNone(maxlen)) { + maxlen = Sk.misceval.asIndexOrThrow(maxlen, "an integer is required"); + if (maxlen < 0) { + throw new Sk.builtin.ValueError("maxlen must be non-negative"); + } else { + this.maxlen = maxlen; + } + } + this.$clear(); + if (iterable !== undefined) { + this.$extend(iterable); + } + return Sk.builtin.none.none$; + }, + tp$getattr: Sk.generic.getAttr, - if (n === undefined) { - n = length; - } - else { - if (!Sk.builtin.checkInt(n)) { - if (n instanceof Sk.builtin.float_) { - throw new Sk.builtin.TypeError("integer argument expected, got float"); + tp$richcompare: function (w, op) { + if (this === w && Sk.misceval.opAllowsEquality(op)) { + return true; + } + // w not a deque + if (!(w instanceof collections.deque)) { + return Sk.builtin.NotImplemented.NotImplemented$; + } + const wd = w; + const v = this.v; + w = w.v; + const vl = (this.tail - this.head) & this.mask; + const wl = (wd.tail - wd.head) & wd.mask; + let k, + i = Math.max(vl, wl); + if (vl === wl) { + for (i = 0; i < vl && i < wl; ++i) { + k = Sk.misceval.richCompareBool(v[(this.head + i) & this.mask], w[(wd.head + i) & wd.mask], "Eq"); + if (!k) { + break; + } } - else { - throw new Sk.builtin.TypeError("an integer is required"); + } + if (i >= vl || i >= wl) { + // no more items to compare, compare sizes + switch (op) { + case "Lt": + return vl < wl; + case "LtE": + return vl <= wl; + case "Eq": + return vl === wl; + case "NotEq": + return vl !== wl; + case "Gt": + return vl > wl; + case "GtE": + return vl >= wl; } } - n = Sk.builtin.asnum$(n); - n = n <= length ? n : length; - n = n >= 0 ? n : 0; - } - - var most_common_elem = []; - for (var iter = self.tp$iter(), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - most_common_elem.push([k, self.mp$subscript(k)]); - } - - var sort_func = function (a, b) { - if (a[1].v < b[1].v) { - return 1; - } else if (a[1].v > b[1].v) { - return -1; + // we have an item that's different + // shortcuts for eq/not + if (op === "Eq") { + return false; + } + if (op === "NotEq") { + return true; + } + // or, compare the differing element using the proper operator + return Sk.misceval.richCompareBool(v[(this.head + i) & this.mask], w[(wd.head + i) & wd.mask], op); + }, + tp$iter: function () { + return new deque_iter_(this); + }, + + $r: function () { + // represetation: deque(['a','b','c'][,maxlen=n]) + const ret = []; + const size = (this.tail - this.head) & this.mask; + if (this.$entered_repr) { + return new Sk.builtin.str("[...]"); + } + this.$entered_repr = true; + for (let i = 0; i < size; i++) { + ret.push(Sk.misceval.objectRepr(this.v[(this.head + i) & this.mask])); + } + const name = Sk.abstr.typeName(this); + if (this.maxlen !== undefined) { + return new Sk.builtin.str(name + "([" + ret.filter(Boolean).join(", ") + "], maxlen=" + this.maxlen + ")"); + } + this.$entered_repr = undefined; + return new Sk.builtin.str(name + "([" + ret.filter(Boolean).join(", ") + "])"); + }, + tp$as_number: true, + nb$bool: function () { + return 0 !== ((this.tail - this.head) & this.mask); + }, + + tp$as_sequence_or_mapping: true, + sq$contains: function (item) { + for (let it = this.tp$iter(), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { + if (Sk.misceval.richCompareBool(i, item, "Eq")) { + return true; + } + } + return false; + }, + sq$concat: function (other) { + // check type + if (!(other instanceof collections.deque)) { + throw new Sk.builtin.TypeError("can only concatenate deque (not '" + Sk.abstr.typeName(other) + "') to deque"); + } + // TODO this can't be the right constructor + const new_deque = this.$copy(); + for (let iter = other.tp$iter(), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { + new_deque.$push(k); + } + return new_deque; + }, + sq$length: function () { + return (this.tail - this.head) & this.mask; + }, + sq$repeat: function (n) { + n = Sk.misceval.asIndexOrThrow(n, "can't multiply sequence by non-int of type '" + Sk.abstr.typeName(n) + "'"); + const size = (this.tail - this.head) & this.mask; + const new_deque = this.$copy(); + let pos; + if (n <= 0) { + new_deque.$clear(); + } + for (let i = 1; i < n; i++) { + for (let j = 0; j < size; j++) { + pos = (this.head + j) & this.mask; + new_deque.$push(this.v[pos]); + } + } + return new_deque; + }, + mp$subscript: function (index) { + index = Sk.misceval.asIndexOrThrow(index); + const size = (this.tail - this.head) & this.mask; + if (index >= size || index < -size) { + throw new Sk.builtin.IndexError("deque index out of range"); + } + const pos = ((index >= 0 ? this.head : this.tail) + index) & this.mask; + return this.v[pos]; + }, + mp$ass_subscript: function (index, val) { + index = Sk.misceval.asIndexOrThrow(index); + const size = (this.tail - this.head) & this.mask; + if (index >= size || index < -size) { + throw new Sk.builtin.IndexError("deque index out of range"); + } + if (val === undefined) { + this.del$item(index); } else { - return 0; + this.set$item(index, val); } - }; - most_common_elem = most_common_elem.sort(sort_func); + return Sk.builtin.none.none$; + }, + nb$inplace_add: function (other) { + this.maxlen = undefined; + for (it = Sk.abstr.iter(other), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { + this.$push(i); + } + return this; + }, + nb$inplace_multiply: function (n) { + n = Sk.misceval.asIndexOrThrow(n, "can't multiply sequence by non-int of type '" + Sk.abstr.typeName(n) + "'"); + if (typeof n !== "number") { + throw new Sk.builtin.OverflowError("cannot fit '" + Sk.abstr.typeName(n) + "' into an index-sized integer"); + } + if (n <= 0) { + this.$clear(); + } + const tmp = this.$copy(); + const size = (this.tail - this.head) & this.mask; + for (let i = 1; i < n; i++) { + for (let j = 0; j < size; j++) { + const pos = (this.head + j) & this.mask; + tmp.$push(this.v[pos]); + } + } + this.v = tmp.v; + this.head = tmp.head; + this.tail = tmp.tail; + this.mask = tmp.mask; + return this; + }, + }, + + methods: { + append: { + $meth: function (value) { + this.$push(value); + return Sk.builtin.none.none$; + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "Add an element to the right side of the deque.", + }, + appendleft: { + $meth: function (value) { + this.$pushLeft(value); + return Sk.builtin.none.none$; + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "Add an element to the left side of the deque.", + }, + clear: { + $meth: function () { + this.$clear(); + return Sk.builtin.none.none$; + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Remove all elements from the deque.", + }, + __copy__: { + $meth: function () { + return this.$copy(); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Return a shallow copy of a deque.", + }, + copy: { + $meth: function () { + return this.$copy(); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Return a shallow copy of a deque.", + }, + count: { + $meth: function (x) { + const size = (this.tail - this.head) & this.mask; + let count = 0; + for (let i = 0; i < size; i++) { + if (Sk.misceval.richCompareBool(this.v[(this.head + i) & this.mask], x, "Eq")) { + count++; + } + } + return new Sk.builtin.int_(count); + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "D.count(value) -> integer -- return number of occurrences of value", + }, + extend: { + $meth: function (iterable) { + this.$extend(iterable); + return Sk.builtin.none.none$; + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "Extend the right side of the deque with elements from the iterable", + }, + extendleft: { + $meth: function (iterable) { + for (it = Sk.abstr.iter(iterable), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { + this.$pushLeft(i); + } + return Sk.builtin.none.none$; + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "Extend the left side of the deque with elements from the iterable", + }, + index: { + $meth: function (x, start, stop) { + const i = this.$index(x, start, stop); + if (i !== undefined) { + return new Sk.builtin.int_(i); + } + throw new Sk.builtin.ValueError(Sk.misceval.objectRepr(x) + " is not in deque"); + }, + $flags: {MinArgs: 1, MaxArgs: 3}, + $textsig: null, + $doc: "D.index(value, [start, [stop]]) -> integer -- return first index of value.\nRaises ValueError if the value is not present.", + }, + insert: { + $meth: function (index, value) { + index = Sk.misceval.asIndexOrThrow(index, "integer argument expected, got " + Sk.abstr.typeName(index)); + const size = (this.tail - this.head) & this.mask; + if (this.maxlen !== undefined && size >= this.maxlen) { + throw new Sk.builtin.IndexError("deque already at its maximum size"); + } + if (index > size) { + index = size; + } + if (index <= -size) { + index = 0; + } - var ret = []; - for (var i = 0; i < n; i++) { - ret.push(new Sk.builtin.tuple(most_common_elem.shift())); - } + const pos = ((index >= 0 ? this.head : this.tail) + index) & this.mask; - return new Sk.builtin.list(ret); - }); + let cur = this.tail; + this.tail = (this.tail + 1) & this.mask; - mod.Counter.prototype['update'] = new Sk.builtin.func(function (self, other) { - Sk.builtin.pyCheckArgsLen('update', arguments.length, 1, 2); + while (cur !== pos) { + const prev = (cur - 1) & this.mask; + this.v[cur] = this.v[prev]; + cur = prev; + } + this.v[pos] = value; + if (this.head === this.tail) { + this.$resize(this.v.length, this.v.length << 1); + } + return Sk.builtin.none.none$; + }, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: null, + $doc: "D.insert(index, object) -- insert object before index", + }, + pop: { + $meth: function () { + return this.$pop(); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Remove and return the rightmost element.", + }, + popleft: { + $meth: function () { + return this.$popLeft(); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Remove and return the leftmost element.", + }, + // __reduce__: { + // $meth: methods.__reduce__, + // $flags: {}, + // $textsig: null, + // $doc: "Return state information for pickling.", + // }, + remove: { + $meth: function (value) { + const index = this.$index(value); + if (index === undefined) { + throw new Sk.builtin.ValueError(Sk.misceval.objectRepr(value) + " is not in deque"); + } + const pos = (this.head + index) & this.mask; + let cur = pos; + while (cur !== this.tail) { + const next = (cur + 1) & this.mask; + this.v[cur] = this.v[next]; + cur = next; + } - if (other instanceof Sk.builtin.dict) { - for (var iter = other.tp$iter(), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - var count = self.mp$subscript(k); - self.mp$ass_subscript(k, count.nb$add(other.mp$subscript(k))); + this.tail = (this.tail - 1) & this.mask; + var size = (this.tail - this.head) & this.mask; + if (size < this.mask >>> 1) { + this.$resize(size, this.v.length >>> 1); + } + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "D.remove(value) -- remove first occurrence of value.", + }, + __reversed__: { + $meth: function () { + return new _deque_reverse_iterator_iter_(this); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "D.__reversed__() -- return a reverse iterator over the deque", + }, + reverse: { + $meth: function () { + const head = this.head; + const tail = this.tail; + const mask = this.mask; + const size = (this.tail - this.head) & this.mask; + for (let i = 0; i < ~~(size / 2); i++) { + const a = (tail - i - 1) & mask; + const b = (head + i) & mask; + const temp = this.v[a]; + this.v[a] = this.v[b]; + this.v[b] = temp; + } + return Sk.builtin.none.none$; + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "D.reverse() -- reverse *IN PLACE*", + }, + rotate: { + $meth: function (num) { + num = num || new Sk.builtin.int_(1); + n = Sk.misceval.asIndexOrThrow(num, "'" + Sk.abstr.typeName(num) + "' object cannot be interpreted as an integer"); + const head = this.head; + const tail = this.tail; + + if (n === 0 || head === tail) { + return this; + } + this.head = (head - n) & this.mask; + this.tail = (tail - n) & this.mask; + if (n > 0) { + for (let i = 1; i <= n; i++) { + const a = (head - i) & this.mask; + const b = (tail - i) & this.mask; + this.v[a] = this.v[b]; + this.v[b] = undefined; + } + } else { + for (let i = 0; i > n; i--) { + const a = (tail - i) & this.mask; + const b = (head - i) & this.mask; + this.v[a] = this.v[b]; + this.v[b] = undefined; + } + } + return Sk.builtin.none.none$; + }, + $flags: {MinArgs: 0, MaxArgs: 1}, + $textsig: null, + $doc: "Rotate the deque n steps to the right (default n=1). If n is negative, rotates left.", + }, + }, + getsets: { + maxlen: { + $get: function () { + return this.maxlen === undefined ? Sk.builtin.none.none$ : new Sk.builtin.int_(this.maxlen); + }, + $doc: "maximum size of a deque or None if unbounded", + }, + }, + proto: { + $clear: function () { + this.head = 0; + this.tail = 0; + this.mask = 1; + this.v = new Array(2); + }, + $copy: function () { + return new collections.deque(this.v.slice(0), this.maxlen, this.head, this.tail, this.mask); + }, + $extend: function (iterable) { + for (it = Sk.abstr.iter(iterable), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { + this.$push(i); } - } - else if (other !== undefined) { - if (!Sk.builtin.checkIterable(other)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(other) + "' object is not iterable"); + }, + set$item: function (index, val) { + const pos = ((index >= 0 ? this.head : this.tail) + index) & this.mask; + this.v[pos] = val; + }, + del$item: function (index) { + const pos = ((index >= 0 ? this.head : this.tail) + index) & this.mask; + let cur = pos; + // Shift items backward 1 to erase position. + while (cur !== this.tail) { + const next = (cur + 1) & this.mask; + this.v[cur] = this.v[next]; + cur = next; } - - var one = new Sk.builtin.int_(1); - for (var iter = other.tp$iter(), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - var count = self.mp$subscript(k); - self.mp$ass_subscript(k, count.nb$add(one)); + // Decrease tail position by 1. + const size = (this.tail - this.head) & this.mask; + this.tail = (this.tail - 1) & this.mask; + if (size < this.mask >>> 1) { + this.$resize(size, this.v.length >>> 1); + } + }, + $push: function (value) { + this.v[this.tail] = value; + this.tail = (this.tail + 1) & this.mask; + if (this.head === this.tail) { + this.$resize(this.v.length, this.v.length << 1); } - } - }); - - mod.Counter.prototype['subtract'] = new Sk.builtin.func(function (self, other) { - Sk.builtin.pyCheckArgsLen('subtract', arguments.length, 1, 2); - if (other instanceof Sk.builtin.dict) { - for (var iter = other.tp$iter(), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - var count = self.mp$subscript(k); - self.mp$ass_subscript(k, count.nb$subtract(other.mp$subscript(k))); + const size = (this.tail - this.head) & this.mask; + if (this.maxlen !== undefined && size > this.maxlen) { + this.$popLeft(); } - } - else if (other !== undefined) { - if (!Sk.builtin.checkIterable(other)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(other) + "' object is not iterable"); + return this; + }, + $pushLeft: function (value) { + this.head = (this.head - 1) & this.mask; + this.v[this.head] = value; + if (this.head === this.tail) { + this.$resize(this.v.length, this.v.length << 1); } - var one = new Sk.builtin.int_(1); - for (var iter = other.tp$iter(), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - var count = self.mp$subscript(k); - self.mp$ass_subscript(k, count.nb$subtract(one)); + const size = (this.tail - this.head) & this.mask; + if (this.maxlen !== undefined && size > this.maxlen) { + this.$pop(); } - } - }); - + return this; + }, + $pop: function () { + if (this.head === this.tail) { + throw new Sk.builtin.IndexError("pop from an empty deque"); + } + this.tail = (this.tail - 1) & this.mask; + const value = this.v[this.tail]; + this.v[this.tail] = undefined; + const size = (this.tail - this.head) & this.mask; + if (size < this.mask >>> 1) { + this.$resize(size, this.v.length >>> 1); + } + return value; + }, + $popLeft: function () { + if (this.head === this.tail) { + throw new Sk.builtin.IndexError("pop from an empty deque"); + } + const value = this.v[this.head]; + this.v[this.head] = undefined; + this.head = (this.head + 1) & this.mask; + const size = (this.tail - this.head) & this.mask; + if (size < this.mask >>> 1) { + this.$resize(size, this.v.length >>> 1); + } + return value; + }, + $resize: function (size, length) { + const head = this.head; + const mask = this.mask; + this.head = 0; + this.tail = size; + this.mask = length - 1; + // Optimize resize when list is already sorted. + if (head === 0) { + this.v.length = length; + return; + } + const sorted = new Array(length); + for (let i = 0; i < size; i++) { + sorted[i] = this.v[(head + i) & mask]; + } + this.v = sorted; + }, + $index: function (x, start, stop) { + const size = (this.tail - this.head) & this.mask; + start = start === undefined ? 0 : Sk.misceval.asIndexOrThrow(start); + stop = stop === undefined ? size : Sk.misceval.asIndexOrThrow(stop); + + const head = this.head; + const mask = this.mask; + const list = this.v; + + const offset = start >= 0 ? start : start < -size ? 0 : size + start; + stop = stop >= 0 ? stop : stop < -size ? 0 : size + stop; + for (let i = offset; i < stop; i++) { + if (list[(head + i) & mask] === x) { + return i; + } + } + }, + sk$asarray: function () { + const ret = []; + const size = (this.tail - this.head) & this.mask; + for (let i = 0; i < size; ++i) { + const pos = (this.head + i) & this.mask; + ret.push(this.v[pos]); + } + return ret; + }, + }, + }); - // OrderedDict - mod.OrderedDict = function OrderedDict(items) - { - if (!(this instanceof mod.OrderedDict)) - { - return new mod.OrderedDict(items); + const deque_iter_ = Sk.abstr.buildIteratorClass("_collections._deque_iterator", { + constructor: function _deque_iterator(dq) { + this.$index = 0; + this.dq = dq.v; + this.$length = (dq.tail - dq.head) & dq.mask; + this.$head = dq.head; + this.$tail = dq.tail; + this.$mask = dq.mask; + }, + iternext: function () { + if (this.$index >= this.$length) { + return undefined; } - - this.orderedkeys = []; - - Sk.abstr.superConstructor(mod.OrderedDict, this, items); - - return this; + const pos = ((this.$index >= 0 ? this.$head : this.$tail) + this.$index) & this.$mask; + this.$index++; + return this.dq[pos]; + }, + methods: { + __length_hint__: { + $meth: function __length_hint__() { + return new Sk.builtin.int_(this.$length - this.$index); + }, + $flags: {NoArgs: true}, + }, } + }); - Sk.abstr.setUpInheritance("OrderedDict", mod.OrderedDict, Sk.builtin.dict); - - mod.OrderedDict.prototype['$r'] = function() - { - var v; - var iter, k; - var ret = []; - var pairstr; - for (iter = this.tp$iter(), k = iter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext()) { - v = this.mp$subscript(k); - if (v === undefined) { - //print(k, "had undefined v"); - v = null; - } - ret.push("(" + Sk.misceval.objectRepr(k).v + ", " + Sk.misceval.objectRepr(v).v + ")"); - } - pairstr = ret.join(", "); - if (ret.length > 0) - { - pairstr = "[" + pairstr + "]"; + const _deque_reverse_iterator_iter_ = Sk.abstr.buildIteratorClass("_collections._deque_reverse_iterator", { + constructor: function _deque_reverse_iterator(dq) { + this.$index = ((dq.tail - dq.head) & dq.mask) - 1; + this.dq = dq.v; + this.$head = dq.head; + this.$mask = dq.mask; + }, + iternext: function () { + if (this.$index < 0) { + return undefined; } - return new Sk.builtin.str("OrderedDict(" + pairstr + ")"); + const pos = (this.$head + this.$index) & this.$mask; + this.$index--; + return this.dq[pos]; + }, + methods: { + __length_hint__: Sk.generic.iterReverseLengthHintMethodDef } + }); - mod.OrderedDict.prototype.mp$ass_subscript = function(key, w) - { - var idx = this.orderedkeys.indexOf(key); - if (idx == -1) - { - this.orderedkeys.push(key); - } + // deque end - return Sk.builtin.dict.prototype.mp$ass_subscript.call(this, key, w); - } + // namedtuple + collections.namedtuples = {}; - mod.OrderedDict.prototype.mp$del_subscript = function(key) - { - var idx = this.orderedkeys.indexOf(key); - if (idx != -1) - { - this.orderedkeys.splice(idx, 1); - } - - return Sk.builtin.dict.prototype.mp$del_subscript.call(this, key); + const namedtuple = function namedtuple(name, fields, rename, defaults, module) { + if (Sk.ffi.remapToJs(Sk.misceval.callsimArray(keywds.$d["iskeyword"], [name]))) { + throw new Sk.builtin.ValueError("Type names and field names cannot be a keyword: '" + Sk.misceval.objectRepr(name) + "'"); } - mod.OrderedDict.prototype.__iter__ = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("__iter__", arguments.length, 0, 0, false, true); - - return mod.OrderedDict.prototype.tp$iter.call(self); - }); - - mod.OrderedDict.prototype.tp$iter = function() - { - var ret; - ret = - { - tp$iter : function () { - return ret; - }, - $obj : this, - $index : 0, - $keys : this.orderedkeys.slice(0), - tp$iternext: function () { - // todo; StopIteration - if (ret.$index >= ret.$keys.length) { - return undefined; - } - return ret.$keys[ret.$index++]; - } - }; - return ret; + const $name = name.$jsstr(); + // regex tests for name and fields + const startsw = new RegExp(/^[0-9].*/); + const startsw2 = new RegExp(/^[0-9_].*/); + const alnum = new RegExp(/^\w*$/); + if (startsw.test($name) || !alnum.test($name) || !$name) { + throw new Sk.builtin.ValueError("Type names and field names must be valid identifiers: '" + $name + "'"); } - mod.OrderedDict.prototype.ob$eq = function (other) { - var l; - var otherl; - var iter; - var k; - var v; - - if (!(other instanceof mod.OrderedDict)) - { - return Sk.builtin.dict.prototype.ob$eq.call(this, other); + let flds; + // fields could be a string or an iterable of strings + if (Sk.builtin.checkString(fields)) { + flds = fields.$jsstr(); + flds = flds.replace(/,/g, " ").split(/\s+/); + if (flds.length == 1 && flds[0] === "") { + flds = []; } - - l = this.mp$length(); - otherl = other.mp$length(); - - if (l !== otherl) { - return Sk.builtin.bool.false$; + } else { + flds = []; + iter = Sk.abstr.iter(fields); + for (i = iter.tp$iternext(); i !== undefined; i = iter.tp$iternext()) { + flds.push(Sk.ffi.remapToJs(i)); } + } - for (iter = this.tp$iter(), otheriter = other.tp$iter(), - k = iter.tp$iternext(), otherk = otheriter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext(), otherk = otheriter.tp$iternext()) - { - if (!Sk.misceval.isTrue(Sk.misceval.richCompareBool(k, otherk, "Eq"))) - { - return Sk.builtin.bool.false$; - } - v = this.mp$subscript(k); - otherv = other.mp$subscript(otherk); - - if (!Sk.misceval.isTrue(Sk.misceval.richCompareBool(v, otherv, "Eq"))) { - return Sk.builtin.bool.false$; + // rename fields + rename = Sk.misceval.isTrue(rename); + if (rename) { + let seen = new Set(); + for (i = 0; i < flds.length; i++) { + if ( + Sk.ffi.remapToJs(Sk.misceval.callsimArray(keywds.$d["iskeyword"], [Sk.ffi.remapToPy(flds[i])])) || + startsw2.test(flds[i]) || + !alnum.test(flds[i]) || + !flds[i] || + seen.has(flds[i]) + ) { + flds[i] = "_" + i; } + seen.add(flds[i]); } + } - return Sk.builtin.bool.true$; - }; - - mod.OrderedDict.prototype.ob$ne = function (other) { - var l; - var otherl; - var iter; - var k; - var v; - - if (!(other instanceof mod.OrderedDict)) - { - return Sk.builtin.dict.prototype.ob$ne.call(this, other); - } - - l = this.size; - otherl = other.size; - - if (l !== otherl) { - return Sk.builtin.bool.true$; + // check the field names + for (i = 0; i < flds.length; i++) { + if (Sk.ffi.remapToJs(Sk.misceval.callsimArray(keywds.$d["iskeyword"], [Sk.ffi.remapToPy(flds[i])]))) { + throw new Sk.builtin.ValueError("Type names and field names cannot be a keyword: '" + flds[i] + "'"); + } else if ((startsw2.test(flds[i]) || !flds[i]) && !rename) { + throw new Sk.builtin.ValueError("Field names cannot start with an underscore: '" + flds[i] + "'"); + } else if (!alnum.test(flds[i])) { + throw new Sk.builtin.ValueError("Type names and field names must be valid identifiers: '" + flds[i] + "'"); } + } - for (iter = this.tp$iter(), otheriter = other.tp$iter(), - k = iter.tp$iternext(), otherk = otheriter.tp$iternext(); - k !== undefined; - k = iter.tp$iternext(), otherk = otheriter.tp$iternext()) - { - if (!Sk.misceval.isTrue(Sk.misceval.richCompareBool(k, otherk, "Eq"))) - { - return Sk.builtin.bool.true$; - } - v = this.mp$subscript(k); - otherv = other.mp$subscript(otherk); - - if (!Sk.misceval.isTrue(Sk.misceval.richCompareBool(v, otherv, "Eq"))) { - return Sk.builtin.bool.true$; - } + // check duplicates + let seen = new Set(); + for (i = 0; i < flds.length; i++) { + if (seen.has(flds[i])) { + throw new Sk.builtin.ValueError("Encountered duplicate field name: '" + flds[i] + "'"); } + seen.add(flds[i]); + } - return Sk.builtin.bool.false$; - }; - - mod.OrderedDict.prototype["pop"] = new Sk.builtin.func(function (self, key, d) { - var s; - var idx; - - Sk.builtin.pyCheckArgsLen('pop', arguments.length, 2, 3); - - idx = self.orderedkeys.indexOf(key); - if (idx != -1) - { - self.orderedkeys.splice(idx, 1); + // create array of default values + const dflts = []; + if (!Sk.builtin.checkNone(defaults)) { + defaults = Sk.abstr.iter(defaults); + for (let i = defaults.tp$iternext(); i !== undefined; i = defaults.tp$iternext()) { + dflts.push(i); } + } + if (dflts.length > flds.length) { + throw new Sk.builtin.TypeError("Got more default values than field names"); + } - return Sk.misceval.callsimArray(Sk.builtin.dict.prototype["pop"], [self, key, d]); - }); - - mod.OrderedDict.prototype["popitem"] = new Sk.builtin.func(function (self, last) { - var key, val; - var s; - - Sk.builtin.pyCheckArgsLen('popitem', arguments.length, 1, 2); - - // Empty dictionary - if (self.orderedkeys.length == 0) - { - s = new Sk.builtin.str('dictionary is empty'); - throw new Sk.builtin.KeyError(s.v); - } + // _field_defaults + const dflts_dict = []; + for (let i = flds.length - dflts.length; i < flds.length; i++) { + dflts_dict.push(new Sk.builtin.str(flds[i])); + dflts_dict.push(dflts[i - (flds.length - dflts.length)]); + } + const _field_defaults = new Sk.builtin.dict(dflts_dict); - key = self.orderedkeys[0]; - if (last === undefined || Sk.misceval.isTrue(last)) - { - key = self.orderedkeys[self.orderedkeys.length - 1]; + // _make + const $make = function _make(_cls, iterable) { + iterable = Sk.abstr.iter(iterable); + values = []; + for (let i = iterable.tp$iternext(); i !== undefined; i = iterable.tp$iternext()) { + values.push(i); } - - val = Sk.misceval.callsimArray(self["pop"], [self, key]); - return Sk.builtin.tuple([key, val]); - }); - - // deque - mod.deque = function deque(iterable, maxlen) { - throw new Sk.builtin.NotImplementedError("deque is not implemented") + return _cls.prototype.tp$new(values); }; + $make.co_varnames = ["_cls", "iterable"]; + const _make = new Sk.builtin.classmethod(new Sk.builtin.func($make)); - // namedtuple - mod.namedtuples = {}; - // should cover most things. Does not: - // * keyword args - // _make - // _replace // _asdict - // _fields - - - var hasDupes = function(a) { - var counts = []; - for(var i = 0; i <= a.length; i++) { - if(counts[a[i]] === undefined) { - counts[a[i]] = 1; - } else { - return true; - } + const $asdict = function _asdict(self) { + const asdict = []; + for (let i = 0; i < self._fields.v.length; i++) { + asdict.push(self._fields.v[i]); + asdict.push(self.v[i]); } - return false; - } - - var Skinherits = function(childCtor, parentCtor) { - /** @constructor */ - function tempCtor() {}; - tempCtor.prototype = parentCtor.prototype; - childCtor.superClass_ = parentCtor.prototype; - childCtor.prototype = new tempCtor(); - /** @override */ - childCtor.prototype.constructor = childCtor; + return new Sk.builtin.dict(asdict); }; + $asdict.co_varnames = ["self"]; + const _asdict = new Sk.builtin.func($asdict); - mod.namedtuple = new Sk.builtin.func(function (name, fields) { - if (Sk.ffi.remapToJs(Sk.misceval.callsimArray(keywds.$d['iskeyword'], [name]))) { - throw new Sk.builtin.ValueError("Type names and field names cannot be a keyword: " + name.v); - } - var nm = Sk.ffi.remapToJs(name); - startsw = new RegExp(/^[0-9].*/); - startsw2 = new RegExp(/^[0-9_].*/); - alnum = new RegExp(/^\w*$/); - if (startsw.test(nm) || (! alnum.test(nm))) { - throw new Sk.builtin.ValueError(" Bad type name " + nm); - } - // fields could be a string or a tuple or list of strings - var flds = Sk.ffi.remapToJs(fields); - - if (typeof(flds) === 'string') { - flds = flds.split(/\s+/); + // _replace + const $replace = function _replace(kwds, _self) { + const kwd_dict = {}; + for (let i = 0; i < kwds.length; i = i + 2) { + kwd_dict[kwds[i].$jsstr()] = kwds[i + 1]; } - // import the keyword module here and use iskeyword - for (i = 0; i < flds.length; i++) { - if (Sk.ffi.remapToJs(Sk.misceval.callsimArray(keywds.$d['iskeyword'], [Sk.ffi.remapToPy(flds[i])])) || - startsw2.test(flds[i]) || (! alnum.test(flds[i])) - ) { - throw new Sk.builtin.ValueError("Type names and field names cannot be a keyword: " + flds[i]); - } + // get the arguments to pass to the contructor + const args = []; + for (let i = 0; i < flds.length; i++) { + const key = flds[i]; + const v = key in kwd_dict ? kwd_dict[key] : _self.v[i]; + args.push(v); + delete kwd_dict[key]; } - if (hasDupes(flds)) { - throw new Sk.builtin.ValueError("Field names must be unique."); + // check if kwd_dict is empty + for (let _ in kwd_dict) { + // if we're here we got an enexpected kwarg + const key_list = Object.keys(kwd_dict).map((x) => "'" + x + "'"); + throw new Sk.builtin.ValueError("Got unexpectd field names: [" + key_list + "]"); } + return nt_klass.prototype.tp$new(args); + }; + $replace.co_kwargs = 1; + $replace.co_varnames = ["_self"]; + const _replace = new Sk.builtin.func($replace); + + // Constructor for namedtuple + const nt_klass = Sk.abstr.buildNativeClass($name, { + constructor: function NamedTuple() { + }, + base: Sk.builtin.tuple, + slots: { + tp$doc: $name + "(" + flds.join(", ") + ")", + tp$new: function (args, kwargs) { + args = Sk.abstr.copyKeywordsToNamedArgs("__new__", flds, args, kwargs, dflts); + const named_tuple_instance = new this.constructor(); + Sk.builtin.tuple.call(named_tuple_instance, args); + return named_tuple_instance; + }, + $r: function () { + const bits = []; + for (let i = 0; i < this.v.length; ++i) { + bits[i] = flds[i] + "=" + Sk.misceval.objectRepr(this.v[i]); + } + const pairs = bits.join(", "); + cls = Sk.abstr.typeName(this); + return new Sk.builtin.str(cls + "(" + pairs + ")"); + }, + }, + proto: { + __module__: Sk.builtin.checkNone(module) ? Sk.globals["__name__"] : module, + __slots__: new Sk.builtin.tuple(), + _fields: new Sk.builtin.tuple(flds.map((x) => new Sk.builtin.str(x))), + _field_defaults: _field_defaults, + _make: _make, + _asdict: _asdict, + _replace: _replace, + }, + }); - var cons = function nametuple_constructor() { - var o; - if (arguments.length !== flds.length ) { - throw new Sk.builtin.TypeError("Number of arguments must match"); - } - if (!(this instanceof mod.namedtuples[nm])) { - o = Object.create(mod.namedtuples[nm].prototype); - o.constructor.apply(o, arguments); - return o; - } - this.__class__ = mod.namedtuples[nm]; - this.v = Array.prototype.slice.call(arguments); - }; - mod.namedtuples[nm] = cons; - - Skinherits(cons, Sk.builtin.tuple); - cons.prototype.tp$name = nm; - cons.prototype.ob$type = Sk.builtin.type.makeIntoTypeObj(nm, mod.namedtuples[nm]); - cons.prototype["$r"] = function () { - var ret; - var i; - var bits; - if (this.v.length === 0) { - return new Sk.builtin.str(nm + "()"); - } - bits = []; - for (i = 0; i < this.v.length; ++i) { - bits[i] = flds[i] + "=" + Sk.misceval.objectRepr(this.v[i]).v; - } - ret = bits.join(", "); - if (this.v.length === 1) { - ret += ","; - } - return new Sk.builtin.str(nm + "(" + ret + ")"); + // create the field properties + for (let i = 0; i < flds.length; i++) { + const fld = Sk.fixReserved(flds[i]); + const fget = function (self) { + Sk.builtin.pyCheckArgs(fld, arguments, 0, 0, false, true); + return self.v[i]; }; + fget.co_name = new Sk.builtin.str(fld); + nt_klass.prototype[fld] = new Sk.builtin.property( + new Sk.builtin.func(fget), + undefined, + undefined, + new Sk.builtin.str("Alias for field number " + i) + ); + } - cons.prototype.tp$getattr = function (pyName) { - var jsName = pyName.$jsstr(); - var i = flds.indexOf(jsName); - if (i >= 0) { - return this.v[i]; - } - return undefined; - }; + collections.namedtuples[$name] = nt_klass; + return nt_klass; + }; - cons.prototype.tp$setattr = function (pyName, value) { - throw new Sk.builtin.AttributeError("can't set attribute"); - }; + namedtuple.co_argcount = 2; + namedtuple.co_kwonlyargcount = 3; + namedtuple.$kwdefs = [Sk.builtin.bool.false$, Sk.builtin.none.none$, Sk.builtin.none.none$]; + namedtuple.co_varnames = ["typename", "field_names", "rename", "defaults", "module"]; - return cons; - }); + collections.namedtuple = new Sk.builtin.func(namedtuple); - return mod; - }); + return collections; +}; + +var $builtinmodule = function (name) { + return Sk.misceval.chain(Sk.importModule("keyword", false, true), collections_mod); }; diff --git a/src/lib/copy.py b/src/lib/copy.py index f1be234ae6..3620ae7b1c 100644 --- a/src/lib/copy.py +++ b/src/lib/copy.py @@ -4,12 +4,19 @@ 2011, 2012, 2013, 2014, 2015 Python Software Foundation; All Rights Reserved """ import types + + class Error(Exception): pass -error = Error + + +error = Error + + class _EmptyClass: pass + def copy(x): cls = type(x) if callable(x): @@ -17,7 +24,7 @@ def copy(x): copier = getattr(cls, "__copy__", None) if copier: return copier(x) - if cls in (type(None), int, float, bool, long, str, tuple, type): + if cls in (type(None), int, float, bool, str, tuple, type): return x if (cls == list) or (cls == dict) or (cls == set) or (cls == slice): return cls(x) @@ -28,7 +35,8 @@ def copy(x): except: reductor = False if getstate or setstate or initargs: - raise NotImplementedError("Skulpt does not yet support copying with user-defined __getstate__, __setstate__ or __getinitargs__()") + raise NotImplementedError( + "Skulpt does not yet support copying with user-defined __getstate__, __setstate__ or __getinitargs__()") reductor = getattr(x, "__reduce_ex__", None) if reductor: rv = reductor(4) @@ -45,6 +53,7 @@ def copy(x): return x return _reconstruct(x, rv, 0) + def _copy_inst(x): if hasattr(x, '__copy__'): return x.__copy__() @@ -64,8 +73,10 @@ def _copy_inst(x): y.__dict__.update(state) return y + d = _deepcopy_dispatch = {} + def deepcopy(x, memo=None, _nil=[]): """Deep copy operation on arbitrary Python objects. See the module's __doc__ string for more info. @@ -84,7 +95,8 @@ def deepcopy(x, memo=None, _nil=[]): except: reductor = False if getstate or setstate or initargs: - raise NotImplementedError("Skulpt does not yet support copying with user-defined __getstate__, __setstate__ or __getinitargs__()") + raise NotImplementedError( + "Skulpt does not yet support copying with user-defined __getstate__, __setstate__ or __getinitargs__()") copier = _deepcopy_dispatch.get(cls) if copier: y = copier(x, memo) @@ -94,7 +106,7 @@ def deepcopy(x, memo=None, _nil=[]): else: try: issc = issubclass(cls, type) - except TypeError: # cls is not a class (old Boost; see SF #502085) + except TypeError: # cls is not a class (old Boost; see SF #502085) issc = 0 if issc: y = _deepcopy_atomic(x, memo) @@ -115,11 +127,14 @@ def deepcopy(x, memo=None, _nil=[]): "un(deep)copyable object of type %s" % cls) y = _reconstruct(x, rv, 1, memo) memo[idx] = y - _keep_alive(x, memo) # Make sure x lives at least as long as d + _keep_alive(x, memo) # Make sure x lives at least as long as d return y + def _deepcopy_atomic(x, memo): return x + + d[type(None)] = _deepcopy_atomic # d[type(Ellipsis)] = _deepcopy_atomic d[type(NotImplemented)] = _deepcopy_atomic @@ -136,6 +151,8 @@ def _deepcopy_atomic(x, memo): d[type] = _deepcopy_atomic # d[types.BuiltinFunctionType] = _deepcopy_atomic d[types.FunctionType] = _deepcopy_atomic + + # d[weakref.ref] = _deepcopy_atomic def _deepcopy_list(x, memo): @@ -144,16 +161,22 @@ def _deepcopy_list(x, memo): for a in x: y.append(deepcopy(a, memo)) return y + + d[list] = _deepcopy_list + def _deepcopy_set(x, memo): result = set([]) # make empty set memo[id(x)] = result # register this set in the memo for loop checking - for a in x: # go through elements of set + for a in x: # go through elements of set result.add(deepcopy(a, memo)) # add the copied elements into the new set - return result # return the new set + return result # return the new set + + d[set] = _deepcopy_set + def _deepcopy_tuple(x, memo): y = [deepcopy(a, memo) for a in x] # We're not going to put the tuple in the memo, but it's still important we @@ -169,14 +192,19 @@ def _deepcopy_tuple(x, memo): else: y = x return y + + d[tuple] = _deepcopy_tuple + def _deepcopy_dict(x, memo): y = {} memo[id(x)] = y for key, value in x.items(): y[deepcopy(key, memo)] = deepcopy(value, memo) return y + + d[dict] = _deepcopy_dict # def _deepcopy_method(x, memo): # Copy instance methods @@ -184,9 +212,10 @@ def _deepcopy_dict(x, memo): # return y d[types.MethodType] = _deepcopy_atomic + def _deepcopy_inst(x, memo): if hasattr(x, '__deepcopy__'): - return x.__deepcopy__(memo) + return x.__deepcopy__(memo) if hasattr(x, '__getinitargs__'): args = x.__getinitargs__() args = deepcopy(args, memo) @@ -205,8 +234,11 @@ def _deepcopy_inst(x, memo): else: y.__dict__.update(state) return y + + d["InstanceType"] = _deepcopy_inst + def _keep_alive(x, memo): """Keeps a reference to the object x in the memo. Because we remember objects by their id, we have @@ -220,7 +252,8 @@ def _keep_alive(x, memo): memo[id(memo)].append(x) except KeyError: # aha, this is the first one :-) - memo[id(memo)]=[x] + memo[id(memo)] = [x] + def _reconstruct(x, info, deep, memo=None): if isinstance(info, str): @@ -277,10 +310,12 @@ def _reconstruct(x, info, deep, memo=None): y[key] = value return y + del d del types + # Helper for instance creation without calling __init__ class _EmptyClass: - pass \ No newline at end of file + pass diff --git a/src/lib/cs1014/dictionaries.py b/src/lib/cs1014/dictionaries.py index d1b747dbfb..705cd43994 100644 --- a/src/lib/cs1014/dictionaries.py +++ b/src/lib/cs1014/dictionaries.py @@ -640,12 +640,12 @@ def key_order_unchained(keys): construct = None find_chain = "" for a_slice in range(len(keys)): - find_chain += "_var{a2}_ = _var{a1}_[__str{a1}__]\n".format(a2=a_slice+1, a1=a_slice) + find_chain += "_var{a2}_ = _var{a1}_[__str{a1}__]\n".format(a2=a_slice + 1, a1=a_slice) if find_match(find_chain): construct = "" count = 0 for key in keys: - construct += "_var{a2}_ = _var{a1}_['{key}']\n".format(a2=count+1, a1=count, key=key) + construct += "_var{a2}_ = _var{a1}_['{key}']\n".format(a2=count + 1, a1=count, key=key) count += 1 if construct: diff --git a/src/lib/cs1014/input_mistakes.py b/src/lib/cs1014/input_mistakes.py index fbe19884b6..586525f8e7 100644 --- a/src/lib/cs1014/input_mistakes.py +++ b/src/lib/cs1014/input_mistakes.py @@ -22,4 +22,3 @@ def unnecessary_cast(needed_casts): if user_cast not in needed_casts and user_cast in known_casts: return explain_r(message.format(user_cast), code, label=tldr) return False - diff --git a/src/lib/cs1014/tests/test_dictionary.py b/src/lib/cs1014/tests/test_dictionary.py index 82594715b4..29a183c573 100644 --- a/src/lib/cs1014/tests/test_dictionary.py +++ b/src/lib/cs1014/tests/test_dictionary.py @@ -223,7 +223,8 @@ def test_dict_out_of_loop(self): def test_wrong_keys(self): # TODO: Check output string - keys = ['Date', "Temperature", "Wind", "Min Temp", "Max Temp", "Avg Temp", "Direction", "Speed", "Month", "Year", + keys = ['Date', "Temperature", "Wind", "Min Temp", "Max Temp", "Avg Temp", "Direction", "Speed", "Month", + "Year", "Week of", "Full", "State", "Code", "City", "Location"] self.to_source("total = 0\n" "for reports in weather_reports:\n" diff --git a/src/lib/datetime.py b/src/lib/datetime.py index ec72aabb14..7b2b04e313 100644 --- a/src/lib/datetime.py +++ b/src/lib/datetime.py @@ -24,14 +24,22 @@ import time as _time import math as _math +# Python 2-vs-3 compat hack +import sys + +unicode = unicode if sys.version_info < (3,) else str + _SENTINEL = object() + def _cmp(x, y): return 0 if x == y else 1 if x > y else -1 + def _round(x): return int(_math.floor(x + 0.5) if x >= 0.0 else _math.ceil(x - 0.5)) + MINYEAR = 1 MAXYEAR = 9999 _MINYEARFMT = 1900 @@ -56,14 +64,17 @@ def _round(x): dbm += dim del dbm, dim + def _is_leap(year): "year -> 1 if leap year, else 0." return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) + def _days_before_year(year): "year -> number of days before January 1st of year." y = year - 1 - return y*365 + y//4 - y//100 + y//400 + return y * 365 + y // 4 - y // 100 + y // 400 + def _days_in_month(year, month): "year, month -> number of days in that month in that year." @@ -72,11 +83,13 @@ def _days_in_month(year, month): return 29 return _DAYS_IN_MONTH[month] + def _days_before_month(year, month): "year, month -> number of days in year preceding first day of month." assert 1 <= month <= 12, 'month must be in 1..12' return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year)) + def _ymd2ord(year, month, day): "year, month, day -> ordinal, considering 01-Jan-0001 as day 1." assert 1 <= month <= 12, 'month must be in 1..12' @@ -86,9 +99,10 @@ def _ymd2ord(year, month, day): _days_before_month(year, month) + day) -_DI400Y = _days_before_year(401) # number of days in 400 years -_DI100Y = _days_before_year(101) # " " " " 100 " -_DI4Y = _days_before_year(5) # " " " " 4 " + +_DI400Y = _days_before_year(401) # number of days in 400 years +_DI100Y = _days_before_year(101) # " " " " 100 " +_DI4Y = _days_before_year(5) # " " " " 4 " # A 4-year cycle has an extra leap day over what we'd get from pasting # together 4 single years. @@ -111,6 +125,7 @@ def _ymd2ord(year, month, day): _US_PER_DAY = 86400000000 _US_PER_WEEK = 604800000000 + def _ord2ymd(n): "ordinal -> (year, month, day), considering 01-Jan-0001 as day 1." @@ -136,7 +151,7 @@ def _ord2ymd(n): # 1 Jan 401 _DI400Y +1 _DI400Y 400-year boundary n -= 1 n400, n = divmod(n, _DI400Y) - year = n400 * 400 + 1 # ..., -399, 1, 401, ... + year = n400 * 400 + 1 # ..., -399, 1, 401, ... # Now n is the (non-negative) offset, in days, from January 1 of year, to # the desired date. Now compute how many 100-year cycles precede n. @@ -155,7 +170,7 @@ def _ord2ymd(n): year += n100 * 100 + n4 * 4 + n1 if n1 == 4 or n100 == 4: assert n == 0 - return year-1, 12, 31 + return year - 1, 12, 31 # Now the year is correct, and n is the offset from January 1. We find # the month via an estimate that's either exact or one too large. @@ -171,11 +186,12 @@ def _ord2ymd(n): # Now the year and month are correct, and n is the offset from the # start of that month: we're done! - return year, month, n+1 + return year, month, n + 1 + # Month and day names. For localized versions, see the calendar module. _MONTHNAMES = [None, "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] _DAYNAMES = [None, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] @@ -184,6 +200,7 @@ def _build_struct_time(y, m, d, hh, mm, ss, dstflag): dnum = _days_before_month(y, m) + d return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag)) + def _format_time(hh, mm, ss, us): # Skip trailing microseconds when us==0. result = "%02d:%02d:%02d" % (hh, mm, ss) @@ -191,6 +208,7 @@ def _format_time(hh, mm, ss, us): result += ".%06d" % us return result + # Correctly substitute for %z and %Z escapes in strftime formats. def _wrap_strftime(object, format, timetuple): year = timetuple[0] @@ -252,12 +270,14 @@ def _wrap_strftime(object, format, timetuple): newformat = "".join(newformat) return _time.strftime(newformat, timetuple) + # Just raise TypeError if the arg isn't None or a string. def _check_tzname(name): if name is not None and not isinstance(name, str): raise TypeError("tzinfo.tzname() must return None or string, " "not '%s'" % type(name)) + # name is the offset-producing method, "utcoffset" or "dst". # offset is what it returned. # If offset isn't None or timedelta, raises TypeError. @@ -285,6 +305,7 @@ def _check_utc_offset(name, offset): raise ValueError("%s()=%d, must be in -1439..1439" % (name, offset)) return offset + def _check_int_field(value): if isinstance(value, int): return int(value) @@ -296,12 +317,11 @@ def _check_int_field(value): else: if isinstance(value, int): return int(value) - elif isinstance(value, long): - return int(long(value)) raise TypeError('__int__ method should return an integer') raise TypeError('an integer is required') raise TypeError('integer argument expected, got float') + def _check_date_fields(year, month, day): year = _check_int_field(year) month = _check_int_field(month) @@ -315,6 +335,7 @@ def _check_date_fields(year, month, day): raise ValueError('day must be in 1..%d' % dim, day) return year, month, day + def _check_time_fields(hour, minute, second, microsecond): hour = _check_int_field(hour) minute = _check_int_field(minute) @@ -330,6 +351,7 @@ def _check_time_fields(hour, minute, second, microsecond): raise ValueError('microsecond must be in 0..999999', microsecond) return hour, minute, second, microsecond + def _check_tzinfo_arg(tz): if tz is not None and not isinstance(tz, tzinfo): raise TypeError("tzinfo argument must be None or of a tzinfo subclass") @@ -360,14 +382,16 @@ def _check_tzinfo_arg(tz): def _cmperror(x, y): raise TypeError("can't compare '%s' to '%s'" % ( - type(x).__name__, type(y).__name__)) + type(x).__name__, type(y).__name__)) + def _normalize_pair(hi, lo, factor): - if not 0 <= lo <= factor-1: + if not 0 <= lo <= factor - 1: inc, lo = divmod(lo, factor) hi += inc return hi, lo + def _normalize_datetime(y, m, d, hh, mm, ss, us, ignore_overflow=False): # Normalize all the inputs, and store the normalized values. ss, us = _normalize_pair(ss, us, 1000000) @@ -377,6 +401,7 @@ def _normalize_datetime(y, m, d, hh, mm, ss, us, ignore_overflow=False): y, m, d = _normalize_date(y, m, d, ignore_overflow) return y, m, d, hh, mm, ss, us + def _normalize_date(year, month, day, ignore_overflow=False): # That was easy. Now it gets muddy: the proper range for day # can't be determined without knowing the correct month and year, @@ -385,7 +410,7 @@ def _normalize_date(year, month, day, ignore_overflow=False): # themselves). # Saying 12 months == 1 year should be non-controversial. if not 1 <= month <= 12: - year, month = _normalize_pair(year, month-1, 12) + year, month = _normalize_pair(year, month - 1, 12) month += 1 assert 1 <= month <= 12 @@ -398,13 +423,13 @@ def _normalize_date(year, month, day, ignore_overflow=False): # Move day-1 days from the first of the month. First try to # get off cheap if we're only one day out of range (adjustments # for timezone alone can't be worse than that). - if day == 0: # move back a day + if day == 0: # move back a day month -= 1 if month > 0: day = _days_in_month(year, month) else: - year, month, day = year-1, 12, 31 - elif day == dim + 1: # move forward a day + year, month, day = year - 1, 12, 31 + elif day == dim + 1: # move forward a day month += 1 day = 1 if month > 12: @@ -418,8 +443,9 @@ def _normalize_date(year, month, day, ignore_overflow=False): raise OverflowError("date value out of range") return year, month, day + def _accum(tag, sofar, num, factor, leftover): - if isinstance(num, (int, long)): + if isinstance(num, (int)): prod = num * factor rsum = sofar + prod return rsum, leftover @@ -429,13 +455,14 @@ def _accum(tag, sofar, num, factor, leftover): rsum = sofar + prod if fracpart == 0.0: return rsum, leftover - assert isinstance(factor, (int, long)) + assert isinstance(factor, (int)) fracpart, intpart = _math.modf(factor * fracpart) rsum += int(intpart) return rsum, leftover + fracpart raise TypeError("unsupported type for timedelta %s component: %s" % (tag, type(num))) + class timedelta(object): """Represent the difference between two datetime objects. @@ -487,7 +514,7 @@ def _from_microseconds(cls, us): def _create(cls, d, s, us, normalize): if normalize: s, us = _normalize_pair(s, us, 1000000) - d, s = _normalize_pair(d, s, 24*3600) + d, s = _normalize_pair(d, s, 24 * 3600) if not -_MAX_DELTA_DAYS <= d <= _MAX_DELTA_DAYS: raise OverflowError("days=%d; must have magnitude <= %d" % (d, _MAX_DELTA_DAYS)) @@ -523,6 +550,7 @@ def __str__(self): if self._days: def plural(n): return n, abs(n) != 1 and "s" or "" + s = ("%d day%s, " % plural(self._days)) + s if self._microseconds: s = s + ".%06d" % self._microseconds @@ -530,7 +558,7 @@ def plural(n): def total_seconds(self): """Total seconds in the duration.""" - return self._to_microseconds() / 10.0**6 + return self._to_microseconds() / 10.0 ** 6 # Read-only field accessors @property @@ -591,7 +619,7 @@ def __abs__(self): return self def __mul__(self, other): - if not isinstance(other, (int, long)): + if not isinstance(other, (int)): return NotImplemented usec = self._to_microseconds() return timedelta._from_microseconds(usec * other) @@ -599,7 +627,7 @@ def __mul__(self, other): __rmul__ = __mul__ def __div__(self, other): - if not isinstance(other, (int, long)): + if not isinstance(other, (int)): return NotImplemented usec = self._to_microseconds() return timedelta._from_microseconds(usec // other) @@ -658,10 +686,12 @@ def __nonzero__(self): self._seconds != 0 or self._microseconds != 0) + timedelta.min = timedelta(-_MAX_DELTA_DAYS) -timedelta.max = timedelta(_MAX_DELTA_DAYS, 24*3600-1, 1000000-1) +timedelta.max = timedelta(_MAX_DELTA_DAYS, 24 * 3600 - 1, 1000000 - 1) timedelta.resolution = timedelta(microseconds=1) + class date(object): """Concrete date type. @@ -951,10 +981,11 @@ def isocalendar(self): week1monday = _isoweek1monday(year) week, day = divmod(today - week1monday, 7) elif week >= 52: - if today >= _isoweek1monday(year+1): + if today >= _isoweek1monday(year + 1): year += 1 week = 0 - return year, week+1, day+1 + return year, week + 1, day + 1 + _date_class = date # so functions w/ args named "date" can get at the class @@ -962,6 +993,7 @@ def isocalendar(self): date.max = date(9999, 12, 31) date.resolution = timedelta(days=1) + class tzinfo(object): """Abstract base class for time zone info classes. @@ -1015,8 +1047,10 @@ def fromutc(self, dt): else: return dt + _tzinfo_class = tzinfo + class time(object): """Time with time zone. @@ -1193,8 +1227,8 @@ def __repr__(self): else: s = "" module = "datetime." if self.__class__ is time else "" - s= "%s(%d, %d%s)" % (module + self.__class__.__name__, - self._hour, self._minute, s) + s = "%s(%d, %d%s)" % (module + self.__class__.__name__, + self._hour, self._minute, s) if self._tzinfo is not None: assert s[-1:] == ")" s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")" @@ -1314,12 +1348,14 @@ def __nonzero__(self): offset = self._utcoffset() or 0 return self.hour * 60 + self.minute != offset + _time_class = time # so functions w/ args named "time" can get at the class time.min = time(0, 0, 0) time.max = time(23, 59, 59, 999999) time.resolution = timedelta(microseconds=1) + class datetime(date): """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]]) @@ -1405,7 +1441,7 @@ def _from_timestamp(cls, converter, timestamp, tzinfo): timestamp += 1 us = 0 y, m, d, hh, mm, ss, weekday, jday, dst = converter(timestamp) - ss = min(ss, 59) # clamp out leap seconds if the platform has them + ss = min(ss, 59) # clamp out leap seconds if the platform has them return cls(y, m, d, hh, mm, ss, us, tzinfo) @classmethod @@ -1708,7 +1744,7 @@ def _cmp(self, other): if myoff is None or otoff is None: raise TypeError("can't compare offset-naive and offset-aware datetimes") # XXX What follows could be done more efficiently... - diff = self - other # this will take offsets into account + diff = self - other # this will take offsets into account if diff.days < 0: return -1 return diff and 1 or 0 @@ -1753,7 +1789,7 @@ def __sub__(self, other): return base if myoff is None or otoff is None: raise TypeError("can't subtract offset-naive and offset-aware datetimes") - return base + timedelta(minutes = otoff-myoff) + return base + timedelta(minutes=otoff - myoff) def __hash__(self): if self._hashcode == -1: @@ -1767,7 +1803,6 @@ def __hash__(self): return self._hashcode - datetime.min = datetime(1, 1, 1) datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999) datetime.resolution = timedelta(microseconds=1) @@ -1784,6 +1819,7 @@ def _isoweek1monday(year): week1monday += 7 return week1monday + """ Some time zone algebra. For a datetime x, let x.n = x stripped of its timezone -- its naive time. diff --git a/src/lib/document.js b/src/lib/document.js index 2128265871..24c8267a11 100644 --- a/src/lib/document.js +++ b/src/lib/document.js @@ -19,12 +19,12 @@ var $builtinmodule = function (name) { mod.getElementsByTagName = new Sk.builtin.func(function (tag) { - var r = document.getElementsByTagName(tag.v) + var r = document.getElementsByTagName(tag.v); var reslist = []; for (var i = r.length - 1; i >= 0; i--) { - reslist.push(Sk.misceval.callsimArray(mod.Element, [r[i]])) + reslist.push(Sk.misceval.callsimArray(mod.Element, [r[i]])); } - return new Sk.builtin.list(reslist) + return new Sk.builtin.list(reslist); }); mod.getElementsByClassName = new Sk.builtin.func(function (cname) { @@ -49,16 +49,15 @@ var $builtinmodule = function (name) { mod.currentDiv = new Sk.builtin.func(function () { if (Sk.divid !== undefined) { - return new Sk.builtin.str(Sk.divid) - } - else { + return new Sk.builtin.str(Sk.divid); + } else { throw new Sk.builtin.AttributeError("There is no value set for divid"); } - }) + }); elementClass = function ($gbl, $loc) { /* - Notes: self['$d'] is the dictionary used by the GenericGetAttr mechanism for an object. + Notes: self['$d'] is the dictionary used by the generic.getAttr mechanism for an object. for various reasons if you create a class in Javascript and have self.xxxx instance variables, you cannot say instance.xxx and get the value of the instance variable unless it is stored in the self['$d'] object. This seems like a duplication of storage to me @@ -68,37 +67,37 @@ var $builtinmodule = function (name) { a method... */ $loc.__init__ = new Sk.builtin.func(function (self, elem) { - self.v = elem - self.innerHTML = elem.innerHTML - self.innerText = elem.innerText + self.v = elem; + self.innerHTML = elem.innerHTML; + self.innerText = elem.innerText; if (elem.value !== undefined) { - self.value = elem.value - Sk.abstr.objectSetItem(self['$d'], new Sk.builtin.str('value'), new Sk.builtin.str(self.value)) + self.value = elem.value; + Sk.abstr.objectSetItem(self["$d"], new Sk.builtin.str("value"), new Sk.builtin.str(self.value)); } if (elem.checked !== undefined) { - self.checked = elem.checked - Sk.abstr.objectSetItem(self['$d'], new Sk.builtin.str('checked'), new Sk.builtin.str(self.checked)) + self.checked = elem.checked; + Sk.abstr.objectSetItem(self["$d"], new Sk.builtin.str("checked"), new Sk.builtin.str(self.checked)); } - Sk.abstr.objectSetItem(self['$d'], new Sk.builtin.str('innerHTML'), new Sk.builtin.str(self.innerHTML)) - Sk.abstr.objectSetItem(self['$d'], new Sk.builtin.str('innerText'), new Sk.builtin.str(self.innerText)) + Sk.abstr.objectSetItem(self["$d"], new Sk.builtin.str("innerHTML"), new Sk.builtin.str(self.innerHTML)); + Sk.abstr.objectSetItem(self["$d"], new Sk.builtin.str("innerText"), new Sk.builtin.str(self.innerText)); - }) + }); - $loc.tp$getattr = Sk.builtin.object.prototype.GenericGetAttr; + $loc.tp$getattr = Sk.generic.getAttr; $loc.__setattr__ = new Sk.builtin.func(function (self, key, value) { key = Sk.ffi.remapToJs(key); - if (key === 'innerHTML') { - self.innerHTML = value - self.v.innerHTML = value.v - Sk.abstr.objectSetItem(self['$d'], new Sk.builtin.str('innerHTML'), value) + if (key === "innerHTML") { + self.innerHTML = value; + self.v.innerHTML = value.v; + Sk.abstr.objectSetItem(self["$d"], new Sk.builtin.str("innerHTML"), value); } - if (key === 'innerText') { - self.innerText = value - self.v.innerText = value.v - Sk.abstr.objectSetItem(self['$d'], new Sk.builtin.str('innerText'), value) + if (key === "innerText") { + self.innerText = value; + self.v.innerText = value.v; + Sk.abstr.objectSetItem(self["$d"], new Sk.builtin.str("innerText"), value); } }); @@ -108,8 +107,8 @@ var $builtinmodule = function (name) { }); $loc.removeChild = new Sk.builtin.func(function (self, node) { - self.v.removeChild(node.v) - }) + self.v.removeChild(node.v); + }); // getCSS @@ -119,27 +118,27 @@ var $builtinmodule = function (name) { $loc.setCSS = new Sk.builtin.func(function (self, attr, value) { - self.v.style[attr.v] = value.v + self.v.style[attr.v] = value.v; - }) + }); $loc.getAttribute = new Sk.builtin.func(function (self, key) { - var res = self.v.getAttribute(key.v) + var res = self.v.getAttribute(key.v); if (res) { - return new Sk.builtin.str(res) + return new Sk.builtin.str(res); } else { return Sk.builtin.none.none$; } }); $loc.setAttribute = new Sk.builtin.func(function (self, attr, value) { - self.v.setAttribute(attr.v, value.v) + self.v.setAttribute(attr.v, value.v); }); $loc.getProperty = new Sk.builtin.func(function (self, key) { - var res = self.v[key.v] + var res = self.v[key.v]; if (res) { - return new Sk.builtin.str(res) + return new Sk.builtin.str(res); } else { return Sk.builtin.none.none$; } @@ -147,18 +146,18 @@ var $builtinmodule = function (name) { $loc.__str__ = new Sk.builtin.func(function (self) { console.log(self.v.tagName); - return new Sk.builtin.str(self.v.tagName) - }) + return new Sk.builtin.str(self.v.tagName); + }); $loc.__repr__ = new Sk.builtin.func(function (self) { - return new Sk.builtin.str('[DOM Element]') - }) + return new Sk.builtin.str("[DOM Element]"); + }); }; - mod.Element = Sk.misceval.buildClass(mod, elementClass, 'Element', []); + mod.Element = Sk.misceval.buildClass(mod, elementClass, "Element", []); return mod; -} +}; diff --git a/src/lib/functools.py b/src/lib/functools.py index 7d4b5f0f02..96cb7a58ce 100644 --- a/src/lib/functools.py +++ b/src/lib/functools.py @@ -1,11 +1,14 @@ from reprlib import recursive_repr + def wraps(wrapped): def make_wrapper(wrapper): wrapper.__name__ = wrapped.__name__ return wrapper + return make_wrapper + # Purely functional, no descriptor behaviour class partial: """New function with partial application of the given arguments @@ -22,7 +25,7 @@ def __new__(cls, func, *args, **keywords): args = func.args + args keywords = keywords.copy() keywords.update(func.keywords) - #keywords = {**func.keywords, **keywords} + # keywords = {**func.keywords, **keywords} func = func.func self = super(partial, cls).__new__(cls) @@ -33,7 +36,7 @@ def __new__(cls, func, *args, **keywords): return self def __call__(self, *args, **keywords): - #keywords = {**self.keywords, **keywords} + # keywords = {**self.keywords, **keywords} keywords = keywords.copy() keywords.update(self.keywords) return self.func(*self.args, *args, **keywords) @@ -43,7 +46,7 @@ def __repr__(self): qualname = type(self).__qualname__ args = [repr(self.func)] args.extend(repr(x) for x in self.args) - args.extend("{k}={v!r}".format(k=k,v=v) for (k, v) in self.keywords.items()) + args.extend("{k}={v!r}".format(k=k, v=v) for (k, v) in self.keywords.items()) if type(self).__module__ == "functools": return "functools.{qualname}({j_args})".format( qualname=qualname, j_args=', '.join(args) @@ -54,7 +57,7 @@ def __repr__(self): def __reduce__(self): return type(self), (self.func,), (self.func, self.args, - self.keywords or None, self.__dict__ or None) + self.keywords or None, self.__dict__ or None) def __setstate__(self, state): if not isinstance(state, tuple): @@ -63,14 +66,14 @@ def __setstate__(self, state): raise TypeError("expected 4 items in state, got {l_state}".format(l_state=len(state))) func, args, kwds, namespace = state if (not callable(func) or not isinstance(args, tuple) or - (kwds is not None and not isinstance(kwds, dict)) or - (namespace is not None and not isinstance(namespace, dict))): + (kwds is not None and not isinstance(kwds, dict)) or + (namespace is not None and not isinstance(namespace, dict))): raise TypeError("invalid partial state") - args = tuple(args) # just in case it's a subclass + args = tuple(args) # just in case it's a subclass if kwds is None: kwds = {} - elif type(kwds) is not dict: # XXX does it need to be *exactly* dict? + elif type(kwds) is not dict: # XXX does it need to be *exactly* dict? kwds = dict(kwds) if namespace is None: namespace = {} @@ -78,4 +81,4 @@ def __setstate__(self, state): self.__dict__ = namespace self.func = func self.args = args - self.keywords = kwds \ No newline at end of file + self.keywords = kwds diff --git a/src/lib/genericpath.py b/src/lib/genericpath.py index ba1f9cee74..700516d0b9 100644 --- a/src/lib/genericpath.py +++ b/src/lib/genericpath.py @@ -82,6 +82,7 @@ def commonprefix(m): return s1[:i] return s1 + # Are two stat buffers (obtained from stat, fstat or lstat) # describing the same file? def samestat(s1, s2): @@ -131,12 +132,13 @@ def _splitext(p, sep, altsep, extsep): # skip all leading dots filenameIndex = sepIndex + 1 while filenameIndex < dotIndex: - if p[filenameIndex:filenameIndex+1] != extsep: + if p[filenameIndex:filenameIndex + 1] != extsep: return p[:dotIndex], p[dotIndex:] filenameIndex += 1 return p, p[:0] + def _check_arg_types(funcname, *args): hasstr = hasbytes = False for s in args: @@ -148,4 +150,4 @@ def _check_arg_types(funcname, *args): raise TypeError('%s() argument must be str or bytes, not %r' % (funcname, s.__class__.__name__)) from None if hasstr and hasbytes: - raise TypeError("Can't mix strings and bytes in path components") from None \ No newline at end of file + raise TypeError("Can't mix strings and bytes in path components") from None diff --git a/src/lib/hashlib.js b/src/lib/hashlib.js index 039435e65d..975d91684e 100644 --- a/src/lib/hashlib.js +++ b/src/lib/hashlib.js @@ -397,6 +397,7 @@ var $builtinmodule = function (name) { var HASH = function ($gbl, $loc) { $loc.__init__ = new Sk.builtin.func(function (self, data) { self.data$ = data.v; + return Sk.builtin.none.none$; }); $loc.__str__ = new Sk.builtin.func(function (self) { return Sk.ffi.remapToPy("<_hashlib.HASH>"); @@ -406,7 +407,7 @@ var $builtinmodule = function (name) { $loc.digest = new Sk.builtin.func(function (self) { var codes = []; - for (var i=0; i < self.data$.length; i++) { + for (var i = 0; i < self.data$.length; i++) { codes.push(new Sk.builtin.int_(self.data$.charCodeAt(i))); } return new Sk.builtin.list(codes); diff --git a/src/lib/hashlib.py b/src/lib/hashlib.py deleted file mode 100644 index abef2e0ddc..0000000000 --- a/src/lib/hashlib.py +++ /dev/null @@ -1 +0,0 @@ -raise NotImplementedError("hashlib is not yet implemented in Skulpt") diff --git a/src/lib/image.js b/src/lib/image.js index 4413fd9003..b2e481c221 100644 --- a/src/lib/image.js +++ b/src/lib/image.js @@ -54,19 +54,19 @@ $builtinmodule = function (name) { susp.data = { type: "Sk.promise", promise: new Promise(function (resolve, reject) { - var newImg = new Image(); - newImg.crossOrigin = ""; - newImg.onerror = function () { - reject(Error("Failed to load URL: " + newImg.src)); - }; - newImg.onload = function () { - self.image = this; - initializeImage(self); - resolve(); - }; - // look for mapping from imagename to url and possible an image proxy server - newImg.src = remapImageIdToURL(imageId); - } + var newImg = new Image(); + newImg.crossOrigin = ""; + newImg.onerror = function () { + reject(Error("Failed to load URL: " + newImg.src)); + }; + newImg.onload = function () { + self.image = this; + initializeImage(self); + resolve(); + }; + // look for mapping from imagename to url and possible an image proxy server + newImg.src = remapImageIdToURL(imageId); + } ) }; return susp; @@ -130,7 +130,7 @@ $builtinmodule = function (name) { for (i = 0; i < self.image.height * self.image.width; i++) { arr[i] = Sk.misceval.callsimArray(self.getPixel, [self, - i % self.image.width, Math.floor(i / self.image.width)]); + i % self.image.width, Math.floor(i / self.image.width)]); } return new Sk.builtin.tuple(arr); }; @@ -197,16 +197,16 @@ $builtinmodule = function (name) { if ((self.updateCount % self.updateInterval) === 0) { if (self.lastx + self.updateInterval >= self.width) { self.lastCtx.putImageData(self.imagedata, self.lastUlx, self.lastUly, - 0, self.lasty, self.width, 2); + 0, self.lasty, self.width, 2); } else if (self.lasty + self.updateInterval >= self.height) { self.lastCtx.putImageData(self.imagedata, self.lastUlx, self.lastUly, - self.lastx, 0, 2, self.height); + self.lastx, 0, 2, self.height); } else { self.lastCtx.putImageData(self.imagedata, self.lastUlx, self.lastUly, - Math.min(x, self.lastx), - Math.min(y, self.lasty), - Math.max(Math.abs(x - self.lastx), 1), - Math.max(Math.abs(y - self.lasty), 1)); + Math.min(x, self.lastx), + Math.min(y, self.lasty), + Math.max(Math.abs(x - self.lastx), 1), + Math.max(Math.abs(y - self.lasty), 1)); } self.lastx = x; self.lasty = y; diff --git a/src/lib/io.py b/src/lib/io.py index 6a336d0692..976cfeafa5 100644 --- a/src/lib/io.py +++ b/src/lib/io.py @@ -31,10 +31,12 @@ __all__ = ["StringIO"] + def _complain_ifclosed(closed): if closed: raise ValueError("I/O operation on closed file") + class StringIO: """class StringIO([buffer]) @@ -47,7 +49,8 @@ class StringIO: cannot be interpreted as 7-bit ASCII (that use the 8th bit) will cause a UnicodeError to be raised when getvalue() is called. """ - def __init__(self, buf = ''): + + def __init__(self, buf=''): # Force self.buf to be a string or unicode if not isinstance(buf, str): buf = str(buf) @@ -89,7 +92,7 @@ def isatty(self): _complain_ifclosed(self.closed) return False - def seek(self, pos, mode = 0): + def seek(self, pos, mode=0): """Set the file's current position. The mode argument is optional and defaults to 0 (absolute file @@ -113,7 +116,7 @@ def tell(self): _complain_ifclosed(self.closed) return self.pos - def read(self, n = -1): + def read(self, n=-1): """Read at most size bytes from the file (less if the read hits EOF before obtaining size bytes). @@ -128,7 +131,7 @@ def read(self, n = -1): if n is None or n < 0: newpos = self.len else: - newpos = min(self.pos+n, self.len) + newpos = min(self.pos + n, self.len) r = self.buf[self.pos:newpos] self.pos = newpos return r @@ -154,7 +157,7 @@ def readline(self, length=None): if i < 0: newpos = self.len else: - newpos = i+1 + newpos = i + 1 if length is not None and length >= 0: if self.pos + length < newpos: newpos = self.pos + length @@ -162,7 +165,7 @@ def readline(self, length=None): self.pos = newpos return r - def readlines(self, sizehint = 0): + def readlines(self, sizehint=0): """Read until EOF using readline() and return a list containing the lines thus read. @@ -219,7 +222,7 @@ def write(self, s): self.len = self.pos = spos + len(s) return if spos > slen: - self.buflist.append('\0'*(spos - slen)) + self.buflist.append('\0' * (spos - slen)) slen = spos newpos = spos + len(s) if spos < slen: diff --git a/src/lib/itertools.js b/src/lib/itertools.js new file mode 100644 index 0000000000..b283775e35 --- /dev/null +++ b/src/lib/itertools.js @@ -0,0 +1,877 @@ +/** + * @todo subclassing incomplete for certain objects + * those that take no kwargs should ignore kwargs if called by a subclass + * probably little need to consider this for now - subclassing an itertools object would be rare... + */ + +var $builtinmodule = function (name) { + var mod = {}; + + mod.accumulate = Sk.abstr.buildIteratorClass("itertools.accumulate", { + constructor: function (iter, func, initial) { + this.iter = iter; + this.func = func; + this.total = initial; + this.first_iter = true; + }, + iternext: function (canSuspend) { + if (this.first_iter !== undefined) { + this.total = Sk.builtin.checkNone(this.total) ? this.iter.tp$iternext() : this.total; + this.first_iter = undefined; + return this.total; + } + let element = this.iter.tp$iternext(); + if (element !== undefined) { + this.total = Sk.misceval.callsimArray(this.func, [this.total, element]); + return this.total; + } + return; + }, + slots: { + tp$doc: + "accumulate(iterable[, func, initial]) --> accumulate object\n\nReturn series of accumulated sums (or other binary function results).", + tp$new: function (args, kwargs) { + let iter, func, initial; + // initial is a keyword only argument; + Sk.abstr.checkArgsLen("accumulate", args, 0, 2); + [iter, func, initial] = Sk.abstr.copyKeywordsToNamedArgs("accumulate", ["iterable", "func", "initial"], args, kwargs, [ + Sk.builtin.none.none$, + Sk.builtin.none.none$, + ]); + iter = Sk.abstr.iter(iter); + func = Sk.builtin.checkNone(func) ? new Sk.builtin.func((a, b) => Sk.abstr.numberBinOp(a, b, "Add")) : func; + if (this === mod.accumulate.prototype) { + return new mod.accumulate(iter, func, initial); + } else { + const instance = new this.constructor(); + mod.accumulate.call(instance, iter, func, initial); + return instance; + } + }, + }, + }); + + mod.chain = Sk.abstr.buildIteratorClass("itertools.chain", { + constructor: function (iterables) { + this.iterables = iterables; + this.current_it = null; + this.i = 0; + }, + iternext: function (canSuspend) { + if (this.current_it === null) { + this.current_it = this.iterables.tp$iternext(); + if (this.current_it === undefined) { + return; + } + this.current_it = Sk.abstr.iter(this.current_it); + } else if (this.current_it === undefined) { + return; + } + + let element; + while (element === undefined) { + element = this.current_it.tp$iternext(); + if (element === undefined) { + this.current_it = this.iterables.tp$iternext(); + if (this.current_it === undefined) { + return; + } + this.current_it = Sk.abstr.iter(this.current_it); + } else { + return element; + } + } + }, + slots: { + tp$doc: + "chain(*iterables) --> chain object\n\nReturn a chain object whose .__next__() method returns elements from the\nfirst iterable until it is exhausted, then elements from the next\niterable, until all of the iterables are exhausted.", + tp$new: function (args, kwargs) { + Sk.abstr.checkNoKwargs("chain", kwargs); + args = new Sk.builtin.tuple(args.slice(0)).tp$iter(); + if (this === mod.chain.prototype) { + return new mod.chain(args); + } else { + const instance = new this.constructor(); + mod.chain.call(instance, args); + return instance; + } + }, + }, + classmethods: { + from_iterable: { + $meth: function (iterable) { + const iterables = Sk.abstr.iter(iterable); + return new mod.chain(iterables); + }, + $flags: {OneArg: true}, + $doc: + "chain.from_iterable(iterable) --> chain object\n\nAlternate chain() constructor taking a single iterable argument\nthat evaluates lazily.", + $textsig: null, + }, + }, + }); + + /** + * Utility function for combinations and combinations_with_replacement + * @param {Object} combinations_proto + * @param {Array} args + * @param {Array|undefined} kwargs + */ + function combinationsNew(combinations_proto, args, kwargs) { + let iterable, r; + [iterable, r] = Sk.abstr.copyKeywordsToNamedArgs(combinations_proto.tp$name, ["iterable", "r"], args, kwargs, []); + const pool = Sk.misceval.arrayFromIterable(iterable); + r = Sk.misceval.asIndexOrThrow(r); + if (r < 0) { + throw new Sk.builtin.ValueError("r must be non-negative"); + } + if (this === combinations_proto) { + return new combinations_proto.constructor(pool, r); + } else { + const instance = new this.constructor(); + combinations_proto.constructor.call(instance, pool, r); + return instance; + } + } + + mod.combinations = Sk.abstr.buildIteratorClass("itertools.combinations", { + constructor: function (pool, r) { + this.pool = pool; + this.r = r; + this.indices = new Array(r).fill().map((_, i) => i); + this.n = pool.length; + this.initial = true; + }, + iternext: function (canSuspend) { + if (this.r > this.n) { + return; + } + if (this.initial !== undefined) { + this.initial = undefined; + return new Sk.builtin.tuple(this.pool.slice(0, this.r)); + } + let i, + found = false; + for (i = this.r - 1; i >= 0; i--) { + if (this.indices[i] != i + this.n - this.r) { + found = true; + break; + } + } + if (!found) { + this.r = 0; + return; + } + this.indices[i]++; + for (let j = i + 1; j < this.r; j++) { + this.indices[j] = this.indices[j - 1] + 1; + } + const res = this.indices.map((i) => this.pool[i]); + return new Sk.builtin.tuple(res); + }, + slots: { + tp$doc: + "combinations(iterable, r) --> combinations object\n\nReturn successive r-length combinations of elements in the iterable.\n\ncombinations(range(4), 3) --> (0,1,2), (0,1,3), (0,2,3), (1,2,3)", + tp$new: function (args, kwargs) { + return combinationsNew.call(this, mod.combinations.prototype, args, kwargs); + }, + }, + }); + + mod.combinations_with_replacement = Sk.abstr.buildIteratorClass("itertools.combinations_with_replacement", { + constructor: function (pool, r) { + this.pool = pool; + this.r = r; + this.indices = new Array(r).fill(0); + this.n = pool.length; + this.initial = true; + }, + iternext: function (canSuspend) { + if (this.r && !this.n) { + return; + } + if (this.initial !== undefined) { + this.initial = undefined; + const res = this.indices.map((i) => this.pool[i]); + return new Sk.builtin.tuple(res); + } + let found = false; + let i; + for (i = this.r - 1; i >= 0; i--) { + if (this.indices[i] != this.n - 1) { + found = true; + break; + } + } + if (!found) { + this.r = 0; + return; + } + const val = this.indices[i] + 1; + for (let j = i; j < this.r; j++) { + this.indices[j] = val; + } + const res = this.indices.map((i) => this.pool[i]); + return new Sk.builtin.tuple(res); + }, + slots: { + tp$doc: + "combinations_with_replacement(iterable, r) --> combinations_with_replacement object\n\nReturn successive r-length combinations of elements in the iterable\nallowing individual elements to have successive repeats.\ncombinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC", + tp$new: function (args, kwargs) { + return combinationsNew.call(this, mod.combinations_with_replacement.prototype, args, kwargs); + }, + }, + }); + + mod.compress = Sk.abstr.buildIteratorClass("itertools.compress", { + constructor: function (data, selectors) { + (this.data = data), (this.selectors = selectors); + }, + iternext: function () { + let d = this.data.tp$iternext(); + let s = this.selectors.tp$iternext(); + while (d !== undefined && s !== undefined) { + if (Sk.misceval.isTrue(s)) { + return d; + } + d = this.data.tp$iternext(); + s = this.selectors.tp$iternext(); + } + }, + slots: { + tp$doc: + "compress(data, selectors) --> iterator over selected data\n\nReturn data elements corresponding to true selector elements.\nForms a shorter iterator from selected data elements using the\nselectors to choose the data elements.", + tp$new: function (args, kwargs) { + let data, selectors; + [data, selectors] = Sk.abstr.copyKeywordsToNamedArgs("compress", ["data", "selectors"], args, kwargs, []); + data = Sk.abstr.iter(data); + selectors = Sk.abstr.iter(selectors); + if (this === mod.count.prototype) { + return new mod.compress(data, selectors); + } else { + const instance = new this.constructor(); + mod.compress.call(instance, data, selectors); + return instance; + } + }, + }, + }); + + mod.count = Sk.abstr.buildIteratorClass("itertools.count", { + constructor: function count(start, step) { + this.start = start; + this.step = step; + }, + iternext: function () { + const tmp = this.start; + this.start = Sk.abstr.numberBinOp(this.start, this.step, "Add"); + return tmp; + }, + slots: { + tp$doc: + "count(start=0, step=1) --> count object\n\nReturn a count object whose .__next__() method returns consecutive values.\nEquivalent to:\n\n def count(firstval=0, step=1):\n x = firstval\n while 1:\n yield x\n x += step\n", + tp$new: function (args, kwargs) { + const [start, step] = Sk.abstr.copyKeywordsToNamedArgs("count", ["start", "step"], args, kwargs, [ + new Sk.builtin.int_(0), + new Sk.builtin.int_(1), + ]); + if (!Sk.builtin.checkNumber(start) && !Sk.builtin.checkComplex(start)) { + throw new Sk.builtin.TypeError("a number is required"); + } + if (!Sk.builtin.checkNumber(step) && !Sk.builtin.checkComplex(step)) { + throw new Sk.builtin.TypeError("a number is required"); + } + if (this === mod.count.prototype) { + return new mod.count(start, step); + } else { + const instance = new this.constructor(); + mod.count.call(instance, start, step); + return instance; + } + }, + $r: function () { + const start_repr = Sk.misceval.objectRepr(this.start); + let step_repr = Sk.misceval.objectRepr(this.step); + step_repr = step_repr === "1" ? "" : ", " + step_repr; + return new Sk.builtin.str(Sk.abstr.typeName(this) + "(" + start_repr + step_repr + ")"); + }, + }, + }); + + mod.cycle = Sk.abstr.buildIteratorClass("itertools.cycle", { + constructor: function cycle(iter) { + this.iter = iter; + this.saved = []; + this.consumed = false; + this.i = 0; + this.length; + }, + iternext: function () { + let element; + if (!this.consumed) { + element = this.iter.tp$iternext(); + if (element !== undefined) { + this.saved.push(element); + return element; + } else { + this.consumed = true; + this.length = this.saved.length; + if (!this.length) { + return; + } + } + } + element = this.saved[this.i]; + this.i = (this.i + 1) % this.length; + return element; + }, + slots: { + tp$doc: + "cycle(iterable) --> cycle object\n\nReturn elements from the iterable until it is exhausted.\nThen repeat the sequence indefinitely.", + tp$new: function (args, kwargs) { + Sk.abstr.checkOneArg("cycle", args, kwargs); + const iter = Sk.abstr.iter(args[0]); + if (this === mod.cycle.prototype) { + return new mod.cycle(iter); + } else { + const instance = new this.constructor(); + mod.cycle.call(instance, iter); + return instance; + } + }, + }, + }); + + mod.dropwhile = Sk.abstr.buildIteratorClass("itertools.dropwhile", { + constructor: function (predicate, iter) { + this.predicate = predicate; + this.iter = iter; + this.passed; + }, + iternext: function () { + let x = this.iter.tp$iternext(); + while (this.passed === undefined && x !== undefined) { + const val = Sk.misceval.callsimArray(this.predicate, [x]); + if (!Sk.misceval.isTrue(val)) { + this.passed = true; + return x; + } + x = this.iter.tp$iternext(); + } + return x; + }, + slots: { + tp$doc: + "dropwhile(predicate, iterable) --> dropwhile object\n\nDrop items from the iterable while predicate(item) is true.\nAfterwards, return every element until the iterable is exhausted.", + tp$new: function (args, kwargs) { + Sk.abstr.checkNoKwargs("dropwhile", kwargs); + Sk.abstr.checkArgsLen("dropwhile", args, 2, 2); + const predicate = args[0]; + const iter = Sk.abstr.iter(args[1]); + if (this === mod.dropwhile.prototype) { + return new mod.dropwhile(predicate, iter); + } else { + const instance = new this.constructor(); + mod.dropwhile.call(instance, predicate, iter); + return instance; + } + }, + }, + }); + + mod.filterfalse = Sk.abstr.buildIteratorClass("itertools.filterfalse", { + constructor: function (predicate, iter) { + this.predicate = predicate; + this.iter = iter; + }, + iternext: function (canSuspend) { + let x = this.iter.tp$iternext(); + if (x === undefined) { + return; + } + let val = Sk.misceval.callsimArray(this.predicate, [x]); + while (Sk.misceval.isTrue(val)) { + x = this.iter.tp$iternext(); + if (x === undefined) { + return; + } + val = Sk.misceval.callsimArray(this.predicate, [x]); + } + return x; + }, + slots: { + tp$doc: + "filterfalse(function or None, sequence) --> filterfalse object\n\nReturn those items of sequence for which function(item) is false.\nIf function is None, return the items that are false.", + tp$new: function (args, kwargs) { + Sk.abstr.checkNoKwargs("filterfalse", kwargs); + Sk.abstr.checkArgsLen("filterfalse", args, 2, 2); + const predicate = Sk.builtin.checkNone(args[0]) ? Sk.builtin.bool : args[0]; + const iter = Sk.abstr.iter(args[1]); + if (this === mod.filterfalse.prototype) { + return new mod.filterfalse(predicate, iter); + } else { + const instance = new this.constructor(); + mod.filterfalse.call(instance, predicate, iter); + return instance; + } + }, + }, + }); + + mod._grouper = Sk.abstr.buildIteratorClass("itertools._grouper", { + constructor: function (groupby, id) { + this.groupby = groupby; + this.tgtkey = groupby.tgtkey; + this.id = groupby.id; + }, + iternext: function (canSuspend) { + const compare = Sk.misceval.richCompareBool(this.groupby.currkey, this.tgtkey, "Eq"); + if (this.groupby.id === this.id && compare) { + let tmp = this.groupby.currval; + this.groupby.currval = this.groupby.iter.tp$iternext(); + if (this.groupby.currval !== undefined) { + this.groupby.currkey = Sk.misceval.callsimArray(this.groupby.keyf, [this.groupby.currval]); + } + return tmp; + } + return; + }, + }); + + mod.groupby = Sk.abstr.buildIteratorClass("itertools.groupby", { + constructor: function (iter, keyf) { + this.iter = iter; + this.keyf = keyf; + this.currval; + this.currkey = this.tgtkey = new Sk.builtin.object(); + this.id; + }, + iternext: function (canSuspend) { + this.id = new Object(); + let compare = Sk.misceval.richCompareBool(this.currkey, this.tgtkey, "Eq"); + while (compare) { + this.currval = this.iter.tp$iternext(); + if (this.currval === undefined) { + return; + } + this.currkey = Sk.misceval.callsimArray(this.keyf, [this.currval]); + compare = Sk.misceval.richCompareBool(this.currkey, this.tgtkey, "Eq"); + } + this.tgtkey = this.currkey; + const grouper = new mod._grouper(this); + return new Sk.builtin.tuple([this.currkey, grouper]); + }, + slots: { + tp$doc: + "groupby(iterable, key=None) -> make an iterator that returns consecutive\nkeys and groups from the iterable. If the key function is not specified or\nis None, the element itself is used for grouping.\n", + tp$new: function (args, kwargs) { + let iter, key; + [iter, key] = Sk.abstr.copyKeywordsToNamedArgs("groupby", ["iterable", "key"], args, kwargs, [Sk.builtin.none.none$]); + iter = Sk.abstr.iter(iter); + key = Sk.builtin.checkNone(key) ? new Sk.builtin.func((x) => x) : key; + if (this === mod.groupby.prototype) { + return new mod.groupby(iter, key); + } else { + const instance = new this.constructor(); + mod.groupby.call(instance, iter, key); + return instance; + } + }, + }, + }); + + mod.islice = Sk.abstr.buildIteratorClass("itertools.islice", { + constructor: function islice(iter, start, stop, step) { + this.iter = iter; + this.previt = start; + this.stop = stop; + this.step = step; + this.initial = true; + }, + iternext: function (canSuspend) { + if (this.initial !== undefined) { + this.initial = undefined; + if (this.previt >= this.stop) { + // consume generator up to stop and return + for (let i = 0; i < this.stop; i++) { + this.iter.tp$iternext(); + } + return; + } else { + //conusme generator up to start and yield + for (let i = 0; i < this.previt; i++) { + this.iter.tp$iternext(); + } + return this.iter.tp$iternext(); + } + } + if (this.previt + this.step >= this.stop) { + // consume generator up to stop and return + for (let i = this.previt + 1; i < this.stop; i++) { + this.previt += this.step; + this.iter.tp$iternext(); + } + return; + } else { + // consume generator up to previt + step and yield + for (let i = this.previt + 1; i < this.previt + this.step; i++) { + this.iter.tp$iternext(); + } + this.previt += this.step; + return this.iter.tp$iternext(); + } + }, + slots: { + tp$doc: + "islice(iterable, stop) --> islice object\nislice(iterable, start, stop[, step]) --> islice object\n\nReturn an iterator whose next() method returns selected values from an\niterable. If start is specified, will skip all preceding elements;\notherwise, start defaults to zero. Step defaults to one. If\nspecified as another value, step determines how many values are \nskipped between successive calls. Works like a slice() on a list\nbut returns an iterator.", + tp$new: function (args, kwargs) { + Sk.abstr.checkNoKwargs("islice", kwargs); + Sk.abstr.checkArgsLen("islice", args, 2, 4); + const iter = Sk.abstr.iter(args[0]); + let start = args[1], + stop = args[2], + step = args[3]; + if (stop === undefined) { + stop = start; + start = Sk.builtin.none.none$; + step = Sk.builtin.none.none$; + } else if (step === undefined) { + step = Sk.builtin.none.none$; + } + + // check stop first + if (!(Sk.builtin.checkNone(stop) || Sk.misceval.isIndex(stop))) { + throw new Sk.builtin.ValueError("Stop for islice() must be None or an integer: 0 <= x <= sys.maxsize."); + } else { + stop = Sk.builtin.checkNone(stop) ? Number.MAX_SAFE_INTEGER : Sk.misceval.asIndex(stop); + if (stop < 0 || stop > Number.MAX_SAFE_INTEGER) { + throw new Sk.builtin.ValueError("Stop for islice() must be None or an integer: 0 <= x <= sys.maxsize."); + } + } + + // check start + if (!(Sk.builtin.checkNone(start) || Sk.misceval.isIndex(start))) { + throw new Sk.builtin.ValueError("Indices for islice() must be None or an integer: 0 <= x <= sys.maxsize."); + } else { + start = Sk.builtin.checkNone(start) ? 0 : Sk.misceval.asIndex(start); + if (start < 0 || start > Number.MAX_SAFE_INTEGER) { + throw new Sk.builtin.ValueError("Indices for islice() must be None or an integer: 0 <= x <= sys.maxsize."); + } + } + + // check step + if (!(Sk.builtin.checkNone(step) || Sk.misceval.isIndex(step))) { + throw new Sk.builtin.ValueError("Step for islice() must be a positive integer or None"); + } else { + step = Sk.builtin.checkNone(step) ? 1 : Sk.misceval.asIndex(step); + if (step <= 0 || step > Number.MAX_SAFE_INTEGER) { + throw new Sk.builtin.ValueError("Step for islice() must be a positive integer or None."); + } + } + + if (this === mod.islice.prototype) { + return new mod.islice(iter, start, stop, step); + } else { + const instance = new this.constructor(); + mod.islice.call(instance, iter, start, stop, step); + return instance; + } + }, + }, + }); + + mod.permutations = Sk.abstr.buildIteratorClass("itertools.permutations", { + constructor: function (pool, r) { + this.pool = pool; + this.r = r; + const n = pool.length; + this.indices = new Array(n).fill().map((_, i) => i); + this.cycles = new Array(r).fill().map((_, i) => n - i); + this.n = n; + this.initial = true; + }, + iternext: function (canSuspend) { + if (this.r > this.n) { + return; + } + if (this.initial !== undefined) { + this.initial = undefined; + return new Sk.builtin.tuple(this.pool.slice(0, this.r)); + } + + for (let i = this.r - 1; i >= 0; i--) { + this.cycles[i]--; + if (this.cycles[i] == 0) { + this.indices.push(this.indices.splice(i, 1)[0]); // push ith element to the end + this.cycles[i] = this.n - i; + } else { + j = this.cycles[i]; + [this.indices[i], this.indices[this.n - j]] = [this.indices[this.n - j], this.indices[i]]; //swap elements; + const res = this.indices.map((i) => this.pool[i]).slice(0, this.r); + return new Sk.builtin.tuple(res); + } + } + this.r = 0; + return; + }, + slots: { + tp$doc: + "permutations(iterable[, r]) --> permutations object\n\nReturn successive r-length permutations of elements in the iterable.\n\npermutations(range(3), 2) --> (0,1), (0,2), (1,0), (1,2), (2,0), (2,1)", + tp$new: function (args, kwargs) { + let iterable, r; + [iterable, r] = Sk.abstr.copyKeywordsToNamedArgs("permutations", ["iterable", "r"], args, kwargs, [Sk.builtin.none.none$]); + const pool = Sk.misceval.arrayFromIterable(iterable); + r = Sk.builtin.checkNone(r) ? pool.length : Sk.misceval.asIndexOrThrow(r); + if (r < 0) { + throw new Sk.builtin.ValueError("r must be non-negative"); + } + if (this === mod.permutations.prototype) { + return new mod.permutations(pool, r); + } else { + const instance = new this.constructor(); + mod.permutations.call(instance, pool, r); + return instance; + } + }, + }, + }); + + mod.product = Sk.abstr.buildIteratorClass("itertools.product", { + constructor: function (pools) { + this.pools = pools; + this.n = pools.length; + this.indices = Array(pools.length).fill(0); + this.pool_sizes = pools.map((x) => x.length); + this.initial = true; + }, + iternext: function (canSuspend) { + if (this.initial !== undefined) { + this.initial = undefined; + const res = this.indices.map((_, i) => this.pools[i][this.indices[i]]); + if (res.some((element) => element === undefined)) { + this.n = 0; // at least one pool arguments is an empty iterator + return; + } + return new Sk.builtin.tuple(res); + } + + let i = this.n - 1; + while (i >= 0 && i < this.n) { + this.indices[i]++; + if (this.indices[i] >= this.pool_sizes[i]) { + this.indices[i] = -1; + i--; + } else { + i++; + } + } + if (!this.n || this.indices.every((index) => index === -1)) { + this.n = 0; // we've done all the iterations + return; + } else { + const res = this.indices.map((_, i) => this.pools[i][this.indices[i]]); + return new Sk.builtin.tuple(res); + } + }, + slots: { + tp$doc: + "product(*iterables, repeat=1) --> product object\n\nCartesian product of input iterables. Equivalent to nested for-loops.\n\nFor example, product(A, B) returns the same as: ((x,y) for x in A for y in B).\nThe leftmost iterators are in the outermost for-loop, so the output tuples\ncycle in a manner similar to an odometer (with the rightmost element changing\non every iteration).\n\nTo compute the product of an iterable with itself, specify the number\nof repetitions with the optional repeat keyword argument. For example,\nproduct(A, repeat=4) means the same as product(A, A, A, A).\n\nproduct('ab', range(3)) --> ('a',0) ('a',1) ('a',2) ('b',0) ('b',1) ('b',2)\nproduct((0,1), (0,1), (0,1)) --> (0,0,0) (0,0,1) (0,1,0) (0,1,1) (1,0,0) ...", + tp$new: function (args, kwargs) { + let [repeat] = Sk.abstr.copyKeywordsToNamedArgs("product", ["repeat"], [], kwargs, [new Sk.builtin.int_(1)]); + repeat = Sk.misceval.asIndexOrThrow(repeat); + if (repeat < 0) { + throw new Sk.builtin.ValueError("repeat argument cannot be negative"); + } + const iterables = []; + for (let i = 0; i < args.length; i++) { + iterables.push(Sk.misceval.arrayFromIterable(args[i])); // want each arg as an array + } + const pools = [].concat(...Array(repeat).fill(iterables)); + if (this === mod.product.prototype) { + return new mod.product(pools); + } else { + const instance = new this.constructor(); + mod.product.call(instance, pools); + return instance; + } + }, + }, + }); + + mod.repeat = Sk.abstr.buildIteratorClass("itertools.repeat", { + constructor: function (object, times) { + this.object = object; + this.times = times; + }, + iternext: function (canSuspend) { + if (this.times === undefined) { + return this.object; + } else if (this.times > 0) { + this.times--; + return this.object; + } else { + return; + } + }, + slots: { + tp$doc: + "repeat(object [,times]) -> create an iterator which returns the object\nfor the specified number of times. If not specified, returns the object\nendlessly.", + tp$new: function (args, kwargs) { + let object, times; + [object, times] = Sk.abstr.copyKeywordsToNamedArgs("repeat", ["object", "times"], args, kwargs, [Sk.builtin.none.none$]); + if (!Sk.builtin.checkNone(times)) { + times = Sk.misceval.asIndexOrThrow(times); + times = times < 0 ? 0 : times; //not important for the algorithm but the repr + } else { + times = undefined; + } + if (this === mod.repeat.prototype) { + return new mod.repeat(object, times); + } else { + const instance = new this.constructor(); + mod.repeat.call(instance, object, times); + return instance; + } + }, + $r: function () { + object_repr = Sk.misceval.objectRepr(this.object); + times_repr = this.times === undefined ? "" : ", " + this.times; + return new Sk.builtin.str(Sk.abstr.typeName(this) + "(" + object_repr + times_repr + ")"); + }, + }, + methods: { + __lenght_hint__: { + $meth: function () { + if (this.times === undefined) { + throw new Sk.builtin.TypeError("len() of unsized object"); + } + return new Sk.builtin.int_(this.times); + }, + $flags: {NoArgs: true}, + $textsig: null, + }, + }, + }); + + mod.starmap = Sk.abstr.buildIteratorClass("itertools.starmap", { + constructor: function (func, iter) { + this.func = func; + this.iter = iter; + }, + iternext: function (canSuspend) { + const args = this.iter.tp$iternext(); + if (args === undefined) { + return; + } + const unpack = Sk.misceval.arrayFromIterable(args); + const val = Sk.misceval.callsimArray(this.func, unpack); + return val; + }, + slots: { + tp$new: function (args, kwargs) { + let func, iter; + [func, iter] = Sk.abstr.copyKeywordsToNamedArgs("starmap", ["func", "iterable"], args, kwargs, []); + iter = Sk.abstr.iter(iter); + func = Sk.builtin.checkNone(func) ? Sk.builtin.bool : func; + if (this === mod.starmap.prototype) { + return new mod.starmap(func, iter); + } else { + const instance = new this.constructor(); + mod.starmap.call(instance, func, iter); + return instance; + } + }, + }, + }); + + mod.takewhile = Sk.abstr.buildIteratorClass("itertools.takewhile", { + constructor: function (predicate, iter) { + this.predicate = predicate; + this.iter = iter; + this.failed; + }, + iternext: function () { + const x = this.iter.tp$iternext(); + if (this.failed === undefined && x !== undefined) { + const val = Sk.misceval.callsimArray(this.predicate, [x]); + if (Sk.misceval.isTrue(val)) { + return x; + } else { + this.failed = true; + } + } + }, + slots: { + tp$doc: + "takewhile(predicate, iterable) --> takewhile object\n\nReturn successive entries from an iterable as long as the \npredicate evaluates to true for each entry.", + tp$new: function (args, kwargs) { + Sk.abstr.checkNoKwargs("takewhile", kwargs); + Sk.abstr.checkArgsLen("takewhile", args, 2, 2); + const predicate = args[0]; + const iter = Sk.abstr.iter(args[1]); + if (this === mod.takewhile.prototype) { + return new mod.takewhile(predicate, iter); + } else { + const instance = new this.constructor(); + mod.takewhile.call(instance, predicate, iter); + return instance; + } + }, + }, + }); + + mod.tee = new Sk.builtin.func(function () { + throw new Sk.builtin.NotImplementedError("tee is not yet implemented in Skulpt"); + }); + + mod.zip_longest = Sk.abstr.buildIteratorClass("itertools.zip_longest", { + constructor: function (iters, fillvalue) { + this.iters = iters; + this.fillvalue = fillvalue; + this.active = this.iters.length; + }, + iternext: function (canSuspend) { + if (!this.active) { + return; + } + let val; + const values = []; + for (let i = 0; i < this.iters.length; i++) { + val = this.iters[i].tp$iternext(); + if (val === undefined) { + this.active--; + if (!this.active) { + return; + } + this.iters[i] = new mod.repeat(this.fillvalue); + val = this.fillvalue; + } + values.push(val); + } + return new Sk.builtin.tuple(values); + }, + slots: { + tp$doc: + "zip_longest(iter1 [,iter2 [...]], [fillvalue=None]) --> zip_longest object\n\nReturn a zip_longest object whose .__next__() method returns a tuple where\nthe i-th element comes from the i-th iterable argument. The .__next__()\nmethod continues until the longest iterable in the argument sequence\nis exhausted and then it raises StopIteration. When the shorter iterables\nare exhausted, the fillvalue is substituted in their place. The fillvalue\ndefaults to None or can be specified by a keyword argument.\n", + tp$new: function (args, kwargs) { + const [fillvalue] = Sk.abstr.copyKeywordsToNamedArgs("zip_longest", ["fillvalue"], [], kwargs, [Sk.builtin.none.none$]); + const iterables = []; + for (let i = 0; i < args.length; i++) { + iterables.push(Sk.abstr.iter(args[i])); + } + if (this === mod.zip_longest.prototype) { + return new mod.zip_longest(iterables, fillvalue); + } else { + const instance = new this.constructor(); + mod.zip_longest.call(instance, iterables, fillvalue); + return instance; + } + }, + }, + }); + + mod.__doc__ = new Sk.builtin.str("An implementation of the python itertools module in Skulpt"); + mod.__package__ = new Sk.builtin.str(""); + + return mod; +}; diff --git a/src/lib/itertools.py b/src/lib/itertools.py deleted file mode 100644 index 3ca951dd75..0000000000 --- a/src/lib/itertools.py +++ /dev/null @@ -1,23 +0,0 @@ -def islice(iterable, limit): - # islice('ABCDEFG', 2) --> A B - # islice('ABCDEFG', 2, 4) --> C D - # islice('ABCDEFG', 2, None) --> C D E F G - # islice('ABCDEFG', 0, None, 2) --> A C E G - start, stop, step = 0, limit, 1 - it = iter(range(start, stop, step)) - try: - nexti = next(it) - except StopIteration: - # Consume *iterable* up to the *start* position. - for i, element in zip(range(start), iterable): - pass - return - try: - for i, element in enumerate(iterable): - if i == nexti: - yield element - nexti = next(it) - except StopIteration: - # Consume to *stop*. - for i, element in zip(range(i + 1, stop), iterable): - pass diff --git a/src/lib/json/__init__.js b/src/lib/json/__init__.js index db35839001..5041472903 100644 --- a/src/lib/json/__init__.js +++ b/src/lib/json/__init__.js @@ -1,116 +1,116 @@ -var $builtinmodule = function(name) { - "use strict"; - var mod = {}; - - // skipkeys=False, - // ensure_ascii=True, - // check_circular=True, - // allow_nan=True, - // cls=None, - // indent=None, - // separators=None, - // encoding="utf-8", - // default=None, - // sort_keys=False, - // **kw - - var dumps_f = function(kwa) { - Sk.builtin.pyCheckArgs("dumps", arguments, 1, Infinity, true, false); - - var args = Array.prototype.slice.call(arguments, 1), - kwargs = new Sk.builtins.dict(kwa), - sort_keys = false, - stringify_opts, default_, jsobj, str; - - // default stringify options - stringify_opts = { - ascii : true, - separators : { - item_separator : ', ', - key_separator : ': ' - } +var $builtinmodule = function (name) { + "use strict"; + var mod = {}; + + // skipkeys=False, + // ensure_ascii=True, + // check_circular=True, + // allow_nan=True, + // cls=None, + // indent=None, + // separators=None, + // encoding="utf-8", + // default=None, + // sort_keys=False, + // **kw + + var dumps_f = function (kwa) { + Sk.builtin.pyCheckArgs("dumps", arguments, 1, Infinity, true, false); + + var args = Array.prototype.slice.call(arguments, 1), + kwargs = new Sk.builtins.dict(kwa), + sort_keys = false, + stringify_opts, default_, jsobj, str; + + // default stringify options + stringify_opts = { + ascii: true, + separators: { + item_separator: ", ", + key_separator: ": " + } + }; + + kwargs = Sk.ffi.remapToJs(kwargs); + jsobj = Sk.ffi.remapToJs(args[0]); + + // TODO: likely need to go through character by character to enable this + if (typeof (kwargs.ensure_ascii) === "boolean" && kwargs.ensure_ascii === false) { + stringify_opts.ascii = false; + } + + // TODO: javascript sort isn't entirely compatible with python's + if (typeof (kwargs.sort_keys) === "boolean" && kwargs.sort_keys) { + sort_keys = true; + } + + if (!sort_keys) { + // don't do any sorting unless sort_keys is true + // if sort_keys use stringify's default sort, which is alphabetical + stringify_opts.cmp = function (a, b) { + return 0; + }; + } + + // item_separator, key_separator) tuple. The default is (', ', ': '). + if (typeof (kwargs.separators) === "object" && kwargs.separators.length == 2) { + stringify_opts.separators.item_separator = kwargs.separators[0]; + stringify_opts.separators.key_separator = kwargs.separators[1]; + } + + // TODO: if indent is 0 it should add newlines + if (kwargs.indent) { + stringify_opts.space = kwargs.indent; + } + + // Sk.ffi.remapToJs doesn't map functions + if (kwargs.default) { + } + + // may need to create a clone of this to have more control/options + str = JSON.stringify(jsobj, stringify_opts, kwargs.indent || 1); + + return new Sk.builtin.str(str); }; - kwargs = Sk.ffi.remapToJs(kwargs); - jsobj = Sk.ffi.remapToJs(args[0]); - - // TODO: likely need to go through character by character to enable this - if (typeof(kwargs.ensure_ascii) === "boolean" && kwargs.ensure_ascii === false) { - stringify_opts.ascii = false; - } - - // TODO: javascript sort isn't entirely compatible with python's - if (typeof(kwargs.sort_keys) === "boolean" && kwargs.sort_keys) { - sort_keys = true; - } - - if (!sort_keys) { - // don't do any sorting unless sort_keys is true - // if sort_keys use stringify's default sort, which is alphabetical - stringify_opts.cmp = function(a, b) { - return 0; - }; - } - - // item_separator, key_separator) tuple. The default is (', ', ': '). - if (typeof(kwargs.separators) === "object" && kwargs.separators.length == 2) { - stringify_opts.separators.item_separator = kwargs.separators[0]; - stringify_opts.separators.key_separator = kwargs.separators[1]; - } - - // TODO: if indent is 0 it should add newlines - if (kwargs.indent) { - stringify_opts.space = kwargs.indent; - } - - // Sk.ffi.remapToJs doesn't map functions - if (kwargs.default) { - } - - // may need to create a clone of this to have more control/options - str = JSON.stringify(jsobj, stringify_opts, kwargs.indent || 1); - - return new Sk.builtin.str(str); - }; - - dumps_f.co_kwargs = true; - mod.dumps = new Sk.builtin.func(dumps_f); - - // encoding[, cls[, object_hook[, parse_float[, parse_int[, parse_constant[, object_pairs_hook[, **kw]]]]]]] - var loads_f = function(kwa) { - Sk.builtin.pyCheckArgs("loads", arguments, 1, Infinity, true, false); - - var args = Array.prototype.slice.call(arguments, 1), - kwargs = new Sk.builtins.dict(kwa), - str, obj; - - kwargs = Sk.ffi.remapToJs(kwargs); - str = args[0].v; - obj = JSON.parse(str); - - return Sk.ffi.remapToPy(obj); - }; - - loads_f.co_kwargs = true; - mod.loads = new Sk.builtin.func(loads_f); - - var load_f = function(kwa) { - Sk.builtin.pyCheckArgs("load", arguments, 1, Infinity, true, false); - - var args = Array.prototype.slice.call(arguments, 1), - kwargs = new Sk.builtins.dict(kwa), - str, obj, file; - - kwargs = Sk.ffi.remapToJs(kwargs); - file = args[0]; - str = Sk.misceval.callsim(Sk.builtin.file.prototype['read'], file).v; - obj = JSON.parse(str); - - return Sk.ffi.remapToPy(obj); - } - - load_f.co_kwargs = true; - mod.load = new Sk.builtin.func(load_f); - - return mod; + dumps_f.co_kwargs = true; + mod.dumps = new Sk.builtin.func(dumps_f); + + // encoding[, cls[, object_hook[, parse_float[, parse_int[, parse_constant[, object_pairs_hook[, **kw]]]]]]] + var loads_f = function (kwa) { + Sk.builtin.pyCheckArgs("loads", arguments, 1, Infinity, true, false); + + var args = Array.prototype.slice.call(arguments, 1), + kwargs = new Sk.builtins.dict(kwa), + str, obj; + + kwargs = Sk.ffi.remapToJs(kwargs); + str = args[0].v; + obj = JSON.parse(str); + + return Sk.ffi.remapToPy(obj); + }; + + loads_f.co_kwargs = true; + mod.loads = new Sk.builtin.func(loads_f); + + var load_f = function (kwa) { + Sk.builtin.pyCheckArgs("load", arguments, 1, Infinity, true, false); + + var args = Array.prototype.slice.call(arguments, 1), + kwargs = new Sk.builtins.dict(kwa), + str, obj, file; + + kwargs = Sk.ffi.remapToJs(kwargs); + file = args[0]; + str = Sk.misceval.callsim(Sk.builtin.file.prototype["read"], file).v; + obj = JSON.parse(str); + + return Sk.ffi.remapToPy(obj); + }; + + load_f.co_kwargs = true; + mod.load = new Sk.builtin.func(load_f); + + return mod; }; \ No newline at end of file diff --git a/src/lib/keyword.py b/src/lib/keyword.py index 8918553e3a..80c480d97b 100644 --- a/src/lib/keyword.py +++ b/src/lib/keyword.py @@ -1,42 +1,39 @@ - __all__ = ["iskeyword", "kwlist"] kwlist = [ -#--start keywords-- - 'and', - 'as', - 'assert', - 'break', - 'class', - 'continue', - 'def', - 'del', - 'elif', - 'else', - 'except', - 'exec', - 'finally', - 'for', - 'from', - 'global', - 'if', - 'import', - 'in', - 'is', - 'lambda', - 'not', - 'or', - 'pass', - 'print', - 'raise', - 'return', - 'try', - 'while', - 'with', - 'yield', -#--end keywords-- - ] - -def iskeyword(wd): - return wd in kwlist + # --start keywords-- + 'and', + 'as', + 'assert', + 'break', + 'class', + 'continue', + 'def', + 'del', + 'elif', + 'else', + 'except', + 'exec', + 'finally', + 'for', + 'from', + 'global', + 'if', + 'import', + 'in', + 'is', + 'lambda', + 'not', + 'or', + 'pass', + 'print', + 'raise', + 'return', + 'try', + 'while', + 'with', + 'yield', + # --end keywords-- +] +iskeyword = frozenset(kwlist).__contains__ diff --git a/src/lib/linecache.py b/src/lib/linecache.py index cafa1c4015..1865954f48 100644 --- a/src/lib/linecache.py +++ b/src/lib/linecache.py @@ -12,10 +12,11 @@ __all__ = ["getline", "clearcache", "checkcache"] + def getline(filename, lineno, module_globals=None): lines = getlines(filename, module_globals) if 1 <= lineno <= len(lines): - return lines[lineno-1] + return lines[lineno - 1] else: return '' @@ -69,7 +70,7 @@ def checkcache(filename=None): continue size, mtime, lines, fullname = entry if mtime is None: - continue # no-op for files loaded via a __loader__ + continue # no-op for files loaded via a __loader__ try: stat = os.stat(fullname) except OSError: @@ -110,7 +111,7 @@ def updatecache(filename, module_globals=None): return [] cache[filename] = ( len(data), None, - [line+'\n' for line in data.splitlines()], fullname + [line + '\n' for line in data.splitlines()], fullname ) return cache[filename][2] @@ -174,4 +175,4 @@ def lazycache(filename, module_globals): get_lines = functools.partial(get_source, name) cache[filename] = (get_lines,) return True - return False \ No newline at end of file + return False diff --git a/src/lib/math.js b/src/lib/math.js index 7a300d2110..b531390270 100644 --- a/src/lib/math.js +++ b/src/lib/math.js @@ -1,338 +1,1094 @@ -var $builtinmodule = function (name) { - var mod = {}; - mod.pi = new Sk.builtin.float_(Math.PI); - mod.e = new Sk.builtin.float_(Math.E); +const $builtinmodule = function (name) { + const math = {}; + + // Mathematical Constants + math.pi = new Sk.builtin.float_(Math.PI); + math.e = new Sk.builtin.float_(Math.E); + math.tau = new Sk.builtin.float_(2 * Math.PI); + math.nan = new Sk.builtin.float_(NaN); + math.inf = new Sk.builtin.float_(Infinity); + + // create all the methods as JS functions + const methods = {}; + + // Number-theoretic and representation functions + methods.ceil = function ceil(x) { + Sk.builtin.pyCheckType("", "real number", Sk.builtin.checkNumber(x)); + const _x = Sk.builtin.asnum$(x); + if (Sk.__future__.ceil_floor_int) { + return new Sk.builtin.int_(Math.ceil(_x)); + } + return new Sk.builtin.float_(Math.ceil(_x)); + }; + + methods.comb = function comb(x, y) { + throw new Sk.builtin.NotImplementedError("methods.comb() is not yet implemented in Skulpt"); + }; + + const get_sign = function (n) { + //deals with signed zeros + // returns -1 or +1 for the sign + if (n) { + n = n < 0 ? -1 : 1; + } else { + n = 1 / n < 0 ? -1 : 1; + } + return n; + }; - mod.fabs = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("fabs", arguments.length, 1, 1); + methods.copysign = function copysign(x, y) { + // returns abs of x with sign y + // does sign x * sign y * x which is equivalent Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + Sk.builtin.pyCheckType("y", "number", Sk.builtin.checkNumber(y)); - return new Sk.builtin.float_(Math.abs(Sk.builtin.asnum$(x))); - }); - - mod.asin = new Sk.builtin.func(function (rad) { - Sk.builtin.pyCheckArgsLen("asin", arguments.length, 1, 1); - Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); - - return new Sk.builtin.float_(Math.asin(Sk.builtin.asnum$(rad))); - }); - - mod.acos = new Sk.builtin.func(function (rad) { - Sk.builtin.pyCheckArgsLen("acos", arguments.length, 1, 1); - Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); + const _y = Sk.builtin.asnum$(y); + const _x = Sk.builtin.asnum$(x); + const sign_x = get_sign(_x); + const sign_y = get_sign(_y); + const sign = sign_x * sign_y; - return new Sk.builtin.float_(Math.acos(Sk.builtin.asnum$(rad))); - }); + return new Sk.builtin.float_(_x * sign); + }; - mod.atan = new Sk.builtin.func(function (rad) { - Sk.builtin.pyCheckArgsLen("atan", arguments.length, 1, 1); - Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); + methods.fabs = function fabs(x) { + Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + let _x = x.v; + if (_x instanceof JSBI) { + _x = x.nb$float().v; //should raise OverflowError for large ints to floats + } + _x = Math.abs(_x); - return new Sk.builtin.float_(Math.atan(Sk.builtin.asnum$(rad))); - }); + return new Sk.builtin.float_(_x); + }; - mod.atan2 = new Sk.builtin.func(function (y, x) { - Sk.builtin.pyCheckArgsLen("atan2", arguments.length, 2, 2); - Sk.builtin.pyCheckType("y", "number", Sk.builtin.checkNumber(y)); + const MAX_SAFE_INTEGER_FACTORIAL = 18; // 19! > Number.MAX_SAFE_INTEGER + methods.factorial = function (x) { Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); - return new Sk.builtin.float_(Math.atan2(Sk.builtin.asnum$(y), Sk.builtin.asnum$(x))); - }); - - mod.sin = new Sk.builtin.func(function (rad) { - Sk.builtin.pyCheckArgsLen("sin", arguments.length, 1, 1); - Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); + let _x = Sk.builtin.asnum$(x); + x = Math.floor(_x); - return new Sk.builtin.float_(Math.sin(Sk.builtin.asnum$(rad))); - }); + if (x != _x) { + throw new Sk.builtin.ValueError("factorial() only accepts integral values"); + } + if (x < 0) { + throw new Sk.builtin.ValueError("factorial() not defined for negative numbers"); + } - mod.cos = new Sk.builtin.func(function (rad) { - Sk.builtin.pyCheckArgsLen("cos", arguments.length, 1, 1); - Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); + let r = 1; + for (let i = 2; i <= x && i <= MAX_SAFE_INTEGER_FACTORIAL; i++) { + r *= i; + } + if (x <= MAX_SAFE_INTEGER_FACTORIAL) { + return new Sk.builtin.int_(r); + } else { + r = JSBI.BigInt(r); + for (let i = MAX_SAFE_INTEGER_FACTORIAL + 1; i <= x; i++) { + r = JSBI.multiply(r, JSBI.BigInt(i)); + } + return new Sk.builtin.int_(r); + } + }; - return new Sk.builtin.float_(Math.cos(Sk.builtin.asnum$(rad))); - }); + methods.floor = function floor(x) { + Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); - mod.tan = new Sk.builtin.func(function (rad) { - Sk.builtin.pyCheckArgsLen("tan", arguments.length, 1, 1); - Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); + if (Sk.__future__.ceil_floor_int) { + return new Sk.builtin.int_(Math.floor(Sk.builtin.asnum$(x))); + } - return new Sk.builtin.float_(Math.tan(Sk.builtin.asnum$(rad))); - }); + return new Sk.builtin.float_(Math.floor(Sk.builtin.asnum$(x))); + }; - mod.asinh = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("asinh", arguments.length, 1, 1); + methods.fmod = function fmod(x, y) { Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + Sk.builtin.pyCheckType("y", "number", Sk.builtin.checkNumber(y)); + let _x = x.v; + let _y = y.v; + if (typeof _x !== "number") { + _x = x.nb$float_().v; + } + if (typeof _y !== "number") { + _y = y.nb$float_().v; + } - x = Sk.builtin.asnum$(x); - - var L = x + Math.sqrt(x * x + 1); - - return new Sk.builtin.float_(Math.log(L)); - }); + if ((_y == Infinity || _y == -Infinity) && isFinite(_x)) { + return new Sk.builtin.float_(_x); + } + const r = _x % _y; + if (isNaN(r)) { + if (!isNaN(_x) && !isNaN(_y)) { + throw new Sk.builtin.ValueError("math domain error"); + } + } + return new Sk.builtin.float_(r); + }; - mod.acosh = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("acosh", arguments.length, 1, 1); + methods.frexp = function frexp(x) { + // algorithm taken from https://locutus.io/c/math/frexp/ Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + const arg = Sk.builtin.asnum$(x); + const res = [arg, 0]; + + if (arg !== 0 && Number.isFinite(arg)) { + const absArg = Math.abs(arg); + let exp = Math.max(-1023, Math.floor(Math.log2(absArg)) + 1); + let m = absArg * Math.pow(2, -exp); + // These while loops compensate for rounding errors that sometimes occur because of ECMAScript's Math.log2's undefined precision + // and also works around the issue of Math.pow(2, -exp) === Infinity when exp <= -1024 + while (m < 0.5) { + m *= 2; + exp--; + } + while (m >= 1) { + m *= 0.5; + exp++; + } + if (arg < 0) { + m = -m; + } + res[0] = m; + res[1] = exp; + } + res[0] = new Sk.builtin.float_(res[0]); + res[1] = new Sk.builtin.int_(res[1]); + return new Sk.builtin.tuple(res); + }; + + methods.fsum = function fsum(iter) { + // algorithm from https://code.activestate.com/recipes/393090/ + // as well as https://github.com/brython-dev/brython/blob/master/www/src/libs/methods.js + if (!Sk.builtin.checkIterable(iter)) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(iter) + "' object is not iterable"); + } - x = Sk.builtin.asnum$(x); - - var L = x + Math.sqrt(x * x - 1); + let partials = []; + iter = Sk.abstr.iter(iter); + let i, hi, lo; + for (let x = iter.tp$iternext(); x !== undefined; x = iter.tp$iternext()) { + Sk.builtin.pyCheckType("", "real number", Sk.builtin.checkNumber(x)); + i = 0; + let _x = x.v; + if (typeof _x !== "number") { + _x = x.nb$float_().v; + } + x = _x; + for (let j = 0, len = partials.length; j < len; j++) { + let y = partials[j]; + if (Math.abs(x) < Math.abs(y)) { + let temp = x; + x = y; + y = temp; + } + hi = x + y; + lo = y - (hi - x); + if (lo) { + partials[i] = lo; + i++; + } + x = hi; + } + partials = partials.slice(0, i).concat([x]); + } + const sum = partials.reduce(function (a, b) { + return a + b; + }, 0); - return new Sk.builtin.float_(Math.log(L)); - }); + return new Sk.builtin.float_(sum); + }; - mod.atanh = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("atanh", arguments.length, 1, 1); - Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + methods.gcd = function gcd(a, b) { + // non ints not allowed in python 3.7.x + Sk.builtin.pyCheckType("a", "integer", Sk.builtin.checkInt(a)); + Sk.builtin.pyCheckType("b", "integer", Sk.builtin.checkInt(b)); - x = Sk.builtin.asnum$(x); + function _gcd(a, b) { + if (b == 0) { + return a; + } + return _gcd(b, a % b); + } - var L = (1 + x) / (1 - x); + const BigZero = JSBI.BigInt(0); - return new Sk.builtin.float_(Math.log(L) / 2); - }); + function _biggcd(a, b) { + if (JSBI.equal(b, BigZero)) { + return a; + } + return _biggcd(b, JSBI.remainder(a, b)); + } - mod.sinh = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("sinh", arguments.length, 1, 1); - Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + let _a = Sk.builtin.asnum$(a); + let _b = Sk.builtin.asnum$(b); + let res; + if (typeof _a === "number" && typeof _b === "number") { + _a = Math.abs(_a); + _b = Math.abs(_b); + res = _gcd(_a, _b); + res = res < 0 ? -res : res; // python only returns positive gcds + return new Sk.builtin.int_(res); + } else { + _a = JSBI.BigInt(_a); + _b = JSBI.BigInt(_b); + res = _biggcd(_a, _b); + res.sign = false; + return new Sk.builtin.int_(res.toString()); // int will convert strings + } + }; + + methods.isclose = function isclose(args, kwargs) { + Sk.abstr.checkArgsLen("isclose", args, 2, 2); + rel_abs_vals = Sk.abstr.copyKeywordsToNamedArgs("isclose", ["rel_tol", "abs_tol"], [], kwargs, [ + new Sk.builtin.float_(1e-9), + new Sk.builtin.float_(0.0), + ]); + + const a = args[0]; + const b = args[1]; + const rel_tol = rel_abs_vals[0]; + const abs_tol = rel_abs_vals[1]; + + Sk.builtin.pyCheckType("a", "number", Sk.builtin.checkNumber(a)); + Sk.builtin.pyCheckType("b", "number", Sk.builtin.checkNumber(b)); + Sk.builtin.pyCheckType("rel_tol", "number", Sk.builtin.checkNumber(rel_tol)); + Sk.builtin.pyCheckType("abs_tol", "number", Sk.builtin.checkNumber(abs_tol)); + + const _a = Sk.builtin.asnum$(a); + const _b = Sk.builtin.asnum$(b); + const _rel_tol = Sk.builtin.asnum$(rel_tol); + const _abs_tol = Sk.builtin.asnum$(abs_tol); + + if (_rel_tol < 0.0 || _abs_tol < 0.0) { + throw new Sk.builtin.ValueError("tolerances must be non-negative"); + } + if (_a == _b) { + return Sk.builtin.bool.true$; + } - x = Sk.builtin.asnum$(x); + if (_a == Infinity || _a == -Infinity || _b == Infinity || _b == -Infinity) { + // same sign infinities were caught in previous test + return Sk.builtin.bool.false$; + } + const diff = Math.abs(_b - _a); + const res = diff <= Math.abs(_rel_tol * _b) || diff <= Math.abs(_rel_tol * _a) || diff <= _abs_tol; + return new Sk.builtin.bool(res); + }; - var e = Math.E; - var p = Math.pow(e, x); - var n = 1 / p; - var result = (p - n) / 2; + methods.isfinite = function isfinite(x) { + Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); - return new Sk.builtin.float_(result); - }); + const _x = Sk.builtin.asnum$(x); + if (Sk.builtin.checkInt(x)) { + return Sk.builtin.bool.true$; //deals with big integers returning False + } else if (isFinite(_x)) { + return Sk.builtin.bool.true$; + } else { + return Sk.builtin.bool.false$; + } + }; - mod.cosh = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("cosh", arguments.length, 1, 1); + methods.isinf = function isinf(x) { + /* Return True if x is infinite or nan, and False otherwise. */ Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); - x = Sk.builtin.asnum$(x); + const _x = Sk.builtin.asnum$(x); + if (Sk.builtin.checkInt(x)) { + return Sk.builtin.bool.false$; //deals with big integers returning True + } else if (isFinite(_x) || isNaN(_x)) { + return Sk.builtin.bool.false$; + } else { + return Sk.builtin.bool.true$; + } + }; - var e = Math.E; - var p = Math.pow(e, x); - var n = 1 / p; - var result = (p + n) / 2; + methods.isnan = function isnan(x) { + // Return True if x is a NaN (not a number), and False otherwise. + Sk.builtin.pyCheckType("x", "float", Sk.builtin.checkFloat(x)); - return new Sk.builtin.float_(result); - }); + const _x = Sk.builtin.asnum$(x); + if (isNaN(_x)) { + return Sk.builtin.bool.true$; + } else { + return Sk.builtin.bool.false$; + } + }; - mod.tanh = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("tanh", arguments.length, 1, 1); - Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + methods.isqrt = function isqrt(x) { + throw new Sk.builtin.NotImplementedError("methods.isqrt() is not yet implemented in Skulpt"); + }; - x = Sk.builtin.asnum$(x); + methods.ldexp = function ldexp(x, i) { + // return x * (2**i) + Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + Sk.builtin.pyCheckType("i", "integer", Sk.builtin.checkInt(i)); - var e = Math.E; - var p = Math.pow(e, x); - var n = 1 / p; - var result = ((p - n) / 2) / ((p + n) / 2); + let _x = x.v; + if (typeof _x !== "number") { + _x = x.nb$float_().v; + } + const _i = Sk.builtin.asnum$(i); - return new Sk.builtin.float_(result); - }); + if (_x == Infinity || _x == -Infinity || _x == 0 || isNaN(_x)) { + return x; + } + const res = _x * Math.pow(2, _i); + if (!isFinite(res)) { + throw new Sk.builtin.OverflowError("math range error"); + } + return new Sk.builtin.float_(res); + }; - mod.ceil = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("ceil", arguments.length, 1, 1); + methods.modf = function modf(x) { Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); - if (Sk.__future__.ceil_floor_int) { - return new Sk.builtin.int_(Math.ceil(Sk.builtin.asnum$(x))); + let _x = Sk.builtin.asnum$(x); + if (!isFinite(_x)) { + //special cases + if (_x == Infinity) { + return new Sk.builtin.tuple([new Sk.builtin.float_(0.0), new Sk.builtin.float_(_x)]); + } else if (_x == -Infinity) { + return new Sk.builtin.tuple([new Sk.builtin.float_(-0.0), new Sk.builtin.float_(_x)]); + } else if (isNaN(_x)) { + return new Sk.builtin.tuple([new Sk.builtin.float_(_x), new Sk.builtin.float_(_x)]); + } } + const sign = get_sign(_x); + _x = Math.abs(_x); + const i = sign * Math.floor(_x); //integer part + const d = sign * (_x - Math.floor(_x)); //decimal part - return new Sk.builtin.float_(Math.ceil(Sk.builtin.asnum$(x))); - }); + return new Sk.builtin.tuple([new Sk.builtin.float_(d), new Sk.builtin.float_(i)]); + }; - // returns y with the sign of x - mod.copysign = new Sk.builtin.func(function (x, y) { - Sk.builtin.pyCheckArgsLen("ceil", arguments.length, 2, 2); - Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); - Sk.builtin.pyCheckType("y", "number", Sk.builtin.checkNumber(y)); + methods.perm = function perm(x) { + throw new Sk.builtin.NotImplementedError("methods.perm() is not yet implemented in Skulpt"); + }; - var _x = Sk.ffi.remapToJs(x); - var _y = Sk.ffi.remapToJs(y); - var res; + methods.prod = function prod(x) { + throw new Sk.builtin.NotImplementedError("methods.prod() is not yet implemented in Skulpt"); + }; - var isNeg_x = _x < 0; - var isNeg_y = _x < 0; + methods.remainder = function remainder(x, y) { + // as per cpython algorithm see cpython for details + Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + Sk.builtin.pyCheckType("y", "number", Sk.builtin.checkNumber(y)); - // special case for floats with negative zero - if(Sk.builtin.checkFloat(x)) { - if(_x === 0) { - isNeg_x = 1/_x === -Infinity ? true : false; - } + let _x = x.v; + let _y = y.v; + if (typeof _x !== "number") { + _x = x.nb$float_().v; } - - if(Sk.builtin.checkFloat(y)) { - if(_y === 0) { - isNeg_y = 1/_y === -Infinity ? true : false; - } + if (typeof _y !== "number") { + _y = y.nb$float_().v; } - // if both signs are equal, just return _y - if((isNeg_x && isNeg_y) || (!isNeg_x && !isNeg_y)) { - res = _y; - } else if((isNeg_x && !isNeg_y) || (!isNeg_x && isNeg_y)) { - // if different, invert sign - if(y === 0) { - // special case for zero - res = isNeg_x ? -0.0 : 0.0; + // deal with most common cases first + if (isFinite(_x) && isFinite(_y)) { + let absx, absy, c, m, r; + if (_y == 0.0) { + throw new Sk.builtin.ValueError("math domain error"); + } + absx = Math.abs(_x); + absy = Math.abs(_y); + m = absx % absy; + c = absy - m; + if (m < c) { + r = m; + } else if (m > c) { + r = -c; } else { - res = _y * -1; + if (m != c) { + throw new Sk.builtin.AssertionError(); + } + r = m - 2.0 * ((0.5 * (absx - m)) % absy); } + return new Sk.builtin.float_(get_sign(_x) * r); } + /* Special values. */ + if (isNaN(_x)) { + return x; + } + if (isNaN(_y)) { + return y; + } + if (_x == Infinity || _x == -Infinity) { + throw new Sk.builtin.ValueError("math domain error"); + } + if (!(_y == Infinity || _y == -Infinity)) { + throw new Sk.builtin.AssertionError(); + } + return new Sk.builtin.float_(_x); + }; - return new Sk.builtin.float_(res); - }); - - mod.floor = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("floor", arguments.length, 1, 1); + methods.trunc = function trunc(x) { Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); - - if (Sk.__future__.ceil_floor_int) { - return new Sk.builtin.int_(Math.floor(Sk.builtin.asnum$(x))); + if (Sk.builtin.checkInt(x)) { + return x; //deals with large ints being passed } + return new Sk.builtin.int_(Sk.builtin.asnum$(x) | 0); + }; - return new Sk.builtin.float_(Math.floor(Sk.builtin.asnum$(x))); - }); - - mod.sqrt = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("sqrt", arguments.length, 1, 1); + // Power and logarithmic functions + methods.exp = function exp(x) { Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + let _x = x.v; + if (typeof _x !== "number") { + _x = x.nb$float_().v; + } + if (_x == Infinity || _x == -Infinity || isNaN(_x)) { + return new Sk.builtin.float_(Math.exp(_x)); + } + const res = Math.exp(_x); + if (!isFinite(res)) { + throw new Sk.builtin.OverflowError("math range error"); + } - return new Sk.builtin.float_(Math.sqrt(Sk.builtin.asnum$(x))); - }); + return new Sk.builtin.float_(res); + }; - mod.trunc = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("trunc", arguments.length, 1, 1); + methods.expm1 = function expm1(x) { + // as per python docs this implements an algorithm for evaluating exp(x) - 1 + // for smaller values of x Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + const _x = Sk.builtin.asnum$(x); - return new Sk.builtin.int_(Sk.builtin.asnum$(x) | 0); - }); + if (Math.abs(_x) < 0.7) { + const _u = Math.exp(_x); + if (_u == 1.0) { + return new Sk.builtin.float_(_x); + } else { + const res = ((_u - 1.0) * _x) / Math.log(_u); + return new Sk.builtin.float_(res); + } + } else { + const res = Math.exp(_x) - 1.0; + return new Sk.builtin.float_(res); + } + }; - mod.log = new Sk.builtin.func(function (x, base) { - Sk.builtin.pyCheckArgsLen("log", arguments.length, 1, 2); + methods.log = function log(x, base) { Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + let _x = Sk.builtin.asnum$(x); + let _base, res; + if (_x <= 0) { + throw new Sk.builtin.ValueError("math domain error"); + } if (base === undefined) { - return new Sk.builtin.float_(Math.log(Sk.builtin.asnum$(x))); + _base = Math.E; } else { Sk.builtin.pyCheckType("base", "number", Sk.builtin.checkNumber(base)); - var ret = Math.log(Sk.builtin.asnum$(x)) / Math.log(Sk.builtin.asnum$(base)); - return new Sk.builtin.float_(ret); + _base = Sk.builtin.asnum$(base); + } + + if (_base <= 0) { + throw new Sk.builtin.ValueError("math domain error"); + } else if (Sk.builtin.checkFloat(x) || _x < Number.MAX_SAFE_INTEGER) { + res = Math.log(_x) / Math.log(_base); + } else { + //int that is larger than max safe integer + // use idea x = 123456789 = .123456789 * 10**9 + // log(x) = 9 * log(10) + log(.123456789) + _x = new Sk.builtin.str(x).$jsstr(); + const digits = _x.length; + const decimal = parseFloat("0." + _x); + res = (digits * Math.log(10) + Math.log(decimal)) / Math.log(_base); } - }); + return new Sk.builtin.float_(res); + }; - mod.log10 = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("log10", arguments.length, 1, 1); + methods.log1p = function log1p(x) { + // as per python docs this is an algorithm for evaluating log 1+x (base e) + // designed to be more accurate close to 0 Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); - var ret = Math.log(Sk.builtin.asnum$(x)) / Math.log(10); - return new Sk.builtin.float_(ret); - }); + let _x = x.v; + if (typeof _x !== "number") { + _x = x.nb$float_().v; + } - /* Return True if x is infinite, and False otherwise. */ - mod.isinf = new Sk.builtin.func(function(x) { - Sk.builtin.pyCheckArgsLen("isinf", arguments.length, 1, 1); + if (_x <= -1.0) { + throw new Sk.builtin.ValueError("math domain error"); + } else if (_x == 0) { + return new Sk.builtin.float_(_x); // respects log1p(-0.0) return -0.0 + } else if (Math.abs(_x) < Number.EPSILON / 2) { + return new Sk.builtin.float_(_x); + } else if (-0.5 <= _x && _x <= 1) { + const _y = 1 + _x; + const res = Math.log(_y) - (_y - 1 - _x) / _y; + return new Sk.builtin.float_(res); + } else { + const res = Math.log(1 + _x); + return new Sk.builtin.float_(res); + } + }; + + methods.log2 = function log2(x) { Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); - var _x = Sk.builtin.asnum$(x); - if(isFinite(_x) && !isNaN(_x)) { - return Sk.builtin.bool.false$; + let _x = Sk.builtin.asnum$(x); + let res; + if (_x < 0) { + throw new Sk.builtin.ValueError("math domain error"); + } else if (Sk.builtin.checkFloat(x) || _x < Number.MAX_SAFE_INTEGER) { + res = Math.log2(_x); } else { - return Sk.builtin.bool.true$ + //int that is larger than max safe integer + // use idea x = 123456789 = .123456789 * 10**9 + // log2(x) = 9 * log2(10) + log2(.123456789) + _x = new Sk.builtin.str(x).$jsstr(); + const digits = _x.length; + const decimal = parseFloat("0." + _x); + res = digits * Math.log2(10) + Math.log2(decimal); } - }); - - /* Return True if x is a NaN (not a number), and False otherwise. */ - mod.isnan = new Sk.builtin.func(function(x) { - Sk.builtin.pyCheckArgsLen("isnan", arguments.length, 1, 1); - Sk.builtin.pyCheckType("x", "float", Sk.builtin.checkFloat(x)); + return new Sk.builtin.float_(res); + }; - var _x = Sk.builtin.asnum$(x); - if(isNaN(_x)) { - return Sk.builtin.bool.true$; + methods.log10 = function log10(x) { + Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + let _x = Sk.builtin.asnum$(x); + let res; + if (_x < 0) { + throw new Sk.builtin.ValueError("math domain error"); + } else if (Sk.builtin.checkFloat(x) || _x < Number.MAX_SAFE_INTEGER) { + res = Math.log10(_x); } else { - return Sk.builtin.bool.false$; + //int that is larger than max safe integer + // use idea x = 123456789 = .123456789 * 10**9 + // log10(x) = 9 + log10(.123456789) + _x = new Sk.builtin.str(x).$jsstr(); + const digits = _x.length; + const decimal = parseFloat("0." + _x); + res = digits + Math.log10(decimal); } - }); + return new Sk.builtin.float_(res); + }; - mod.exp = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("exp", arguments.length, 1, 1); + methods.pow = function pow(x, y) { Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + Sk.builtin.pyCheckType("y", "number", Sk.builtin.checkNumber(y)); - return new Sk.builtin.float_(Math.exp(Sk.builtin.asnum$(x))); - }); + let _x = x.v; + let _y = y.v; + if (typeof _x !== "number") { + _x = x.nb$float_().v; + } + if (typeof _y !== "number") { + _y = y.nb$float_().v; + } + + if (_x == 0 && _y < 0) { + throw new Sk.builtin.ValueError("math domain error"); + } else if (_x == 1) { + return new Sk.builtin.float_(1.0); + } else if (Number.isFinite(_x) && Number.isFinite(_y) && _x < 0 && !Number.isInteger(_y)) { + throw new Sk.builtin.ValueError("math domain error"); + } else if (_x == -1 && (_y == -Infinity || _y == Infinity)) { + return new Sk.builtin.float_(1.0); + } - mod.pow = new Sk.builtin.func(function (x, y) { - Sk.builtin.pyCheckArgsLen("pow", arguments.length, 2, 2); + const res = Math.pow(_x, _y); + if (!Number.isFinite(_x) || !Number.isFinite(_y)) { + return new Sk.builtin.float_(res); + } else if (res == Infinity || res == -Infinity) { + throw new Sk.builtin.OverflowError("math range error"); + } + return new Sk.builtin.float_(res); + }; + + methods.sqrt = function sqrt(x) { Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + + return new Sk.builtin.float_(Math.sqrt(Sk.builtin.asnum$(x))); + }; + + // Trigonometric functions and Hyperbolic + + methods.asin = function asin(rad) { + Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); + + return new Sk.builtin.float_(Math.asin(Sk.builtin.asnum$(rad))); + }; + + methods.acos = function acos(rad) { + Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); + + return new Sk.builtin.float_(Math.acos(Sk.builtin.asnum$(rad))); + }; + + methods.atan = function atan(rad) { + Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); + + return new Sk.builtin.float_(Math.atan(Sk.builtin.asnum$(rad))); + }; + + methods.atan2 = function atan2(y, x) { Sk.builtin.pyCheckType("y", "number", Sk.builtin.checkNumber(y)); + Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + + return new Sk.builtin.float_(Math.atan2(Sk.builtin.asnum$(y), Sk.builtin.asnum$(x))); + }; - return new Sk.builtin.float_(Math.pow(Sk.builtin.asnum$(x), Sk.builtin.asnum$(y))); - }); + methods.sin = function sin(rad) { + Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); - mod.radians = new Sk.builtin.func(function (deg) { - Sk.builtin.pyCheckArgsLen("radians", arguments.length, 1, 1); - Sk.builtin.pyCheckType("deg", "number", Sk.builtin.checkNumber(deg)); + return new Sk.builtin.float_(Math.sin(Sk.builtin.asnum$(rad))); + }; - var ret = Math.PI / 180.0 * Sk.builtin.asnum$(deg); - return new Sk.builtin.float_(ret); - }); + methods.cos = function cos(rad) { + Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); + + return new Sk.builtin.float_(Math.cos(Sk.builtin.asnum$(rad))); + }; - mod.degrees = new Sk.builtin.func(function (rad) { - Sk.builtin.pyCheckArgsLen("degrees", arguments.length, 1, 1); + methods.tan = function tan(rad) { Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); - var ret = 180.0 / Math.PI * Sk.builtin.asnum$(rad); - return new Sk.builtin.float_(ret); - }); + return new Sk.builtin.float_(Math.tan(Sk.builtin.asnum$(rad))); + }; - mod.hypot = new Sk.builtin.func(function (x, y) { - Sk.builtin.pyCheckArgsLen("hypot", arguments.length, 2, 2); + methods.dist = function dist(x) { + throw new Sk.builtin.NotImplementedError("methods.dist() is not yet implemented in Skulpt"); + }; + + methods.hypot = function hypot(x, y) { Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); Sk.builtin.pyCheckType("y", "number", Sk.builtin.checkNumber(y)); x = Sk.builtin.asnum$(x); y = Sk.builtin.asnum$(y); - return new Sk.builtin.float_(Math.sqrt((x * x) + (y * y))); - }); + return new Sk.builtin.float_(Math.sqrt(x * x + y * y)); + }; - var MAX_SAFE_INTEGER_FACTORIAL = 18; // 19! > Number.MAX_SAFE_INTEGER - mod.factorial = new Sk.builtin.func(function (x) { - Sk.builtin.pyCheckArgsLen("factorial", arguments.length, 1, 1); + methods.asinh = function asinh(x) { Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); - x = Math.floor(Sk.builtin.asnum$(x)); - var r = 1; - for (var i = 2; i <= x && i <= MAX_SAFE_INTEGER_FACTORIAL; i++) { - r *= i; - } - if(x<=MAX_SAFE_INTEGER_FACTORIAL){ - return new Sk.builtin.int_(r); - }else{ - // for big numbers (19 and larger) we first calculate 18! above - // and then use bigintegers to continue the process. + x = Sk.builtin.asnum$(x); - // This is inefficient as hell, but it produces correct answers. + const L = x + Math.sqrt(x * x + 1); - // promotes an integer to a biginteger - function bigup(number){ - var n = Sk.builtin.asnum$nofloat(number); - return new Sk.builtin.biginteger(number); - } + return new Sk.builtin.float_(Math.log(L)); + }; - r = bigup(r); - for (var i = MAX_SAFE_INTEGER_FACTORIAL+1; i <= x; i++) { - var i_bigup = bigup(i); - r = r.multiply(i_bigup); - } - return new Sk.builtin.lng(r); + methods.acosh = function acosh(x) { + Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + + x = Sk.builtin.asnum$(x); + + const L = x + Math.sqrt(x * x - 1); + + return new Sk.builtin.float_(Math.log(L)); + }; + + methods.atanh = function atanh(x) { + Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + + x = Sk.builtin.asnum$(x); + + const L = (1 + x) / (1 - x); + + return new Sk.builtin.float_(Math.log(L) / 2); + }; + + methods.sinh = function sinh(x) { + Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + + x = Sk.builtin.asnum$(x); + + const e = Math.E; + const p = Math.pow(e, x); + const n = 1 / p; + const result = (p - n) / 2; + + return new Sk.builtin.float_(result); + }; + + methods.cosh = function cosh(x) { + Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + + x = Sk.builtin.asnum$(x); + + const e = Math.E; + const p = Math.pow(e, x); + const n = 1 / p; + const result = (p + n) / 2; + + return new Sk.builtin.float_(result); + }; + + methods.tanh = function tanh(x) { + Sk.builtin.pyCheckType("x", "number", Sk.builtin.checkNumber(x)); + + const _x = Sk.builtin.asnum$(x); + + if (_x === 0) { + return new Sk.builtin.float_(_x); } - }); - return mod; -} + const e = Math.E; + const p = Math.pow(e, _x); + const n = 1 / p; + const result = (p - n) / 2 / ((p + n) / 2); + + return new Sk.builtin.float_(result); + }; + + // Angular Conversion + methods.radians = function radians(deg) { + Sk.builtin.pyCheckType("deg", "number", Sk.builtin.checkNumber(deg)); + + const ret = (Math.PI / 180.0) * Sk.builtin.asnum$(deg); + return new Sk.builtin.float_(ret); + }; + + methods.degrees = function degrees(rad) { + Sk.builtin.pyCheckType("rad", "number", Sk.builtin.checkNumber(rad)); + + const ret = (180.0 / Math.PI) * Sk.builtin.asnum$(rad); + return new Sk.builtin.float_(ret); + }; + + // Special Functions + methods.erf = function erf(x) { + throw new Sk.builtin.NotImplementedError("methods.erf() is not yet implemented in Skulpt"); + }; + + methods.erfc = function erfc(x) { + throw new Sk.builtin.NotImplementedError("methods.erfc() is not yet implemented in Skulpt"); + }; + + methods.gamma = function gamma(x) { + throw new Sk.builtin.NotImplementedError("methods.gamma() is not yet implemented in Skulpt"); + }; + + methods.lgamma = function lgamma(x) { + throw new Sk.builtin.NotImplementedError("methods.lgamma() is not yet implemented in Skulpt"); + }; + + const method_defs = { + acos: { + $meth: methods.acos, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the arc cosine (measured in radians) of x.", + }, + + acosh: { + $meth: methods.acosh, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the inverse hyperbolic cosine of x.", + }, + + asin: { + $meth: methods.asin, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the arc sine (measured in radians) of x.", + }, + + asinh: { + $meth: methods.asinh, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the inverse hyperbolic sine of x.", + }, + + atan: { + $meth: methods.atan, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the arc tangent (measured in radians) of x.", + }, + + atan2: { + $meth: methods.atan2, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, y, x, /)", + $doc: "Return the arc tangent (measured in radians) of y/x.\n\nUnlike atan(y/x), the signs of both x and y are considered.", + }, + + atanh: { + $meth: methods.atanh, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the inverse hyperbolic tangent of x.", + }, + + ceil: { + $meth: methods.ceil, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the ceiling of x as an Integral.\n\nThis is the smallest integer >= x.", + }, + + copysign: { + $meth: methods.copysign, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, x, y, /)", + $doc: + "Return a float with the magnitude (absolute value) of x but the sign of y.\n\nOn platforms that support signed zeros, copysign(1.0, -0.0)\nreturns -1.0.\n", + }, + + cos: { + $meth: methods.cos, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the cosine of x (measured in radians).", + }, + + cosh: { + $meth: methods.cosh, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the hyperbolic cosine of x.", + }, + + degrees: { + $meth: methods.degrees, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Convert angle x from radians to degrees.", + }, + + erf: { + $meth: methods.erf, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Error function at x.", + }, + + erfc: { + $meth: methods.erfc, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Complementary error function at x.", + }, + + exp: { + $meth: methods.exp, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return e raised to the power of x.", + }, + + expm1: { + $meth: methods.expm1, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return exp(x)-1.\n\nThis function avoids the loss of precision involved in the direct evaluation of exp(x)-1 for small x.", + }, + + fabs: { + $meth: methods.fabs, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the absolute value of the float x.", + }, + + factorial: { + $meth: methods.factorial, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Find x!.\n\nRaise a ValueError if x is negative or non-integral.", + }, + + floor: { + $meth: methods.floor, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the floor of x as an Integral.\n\nThis is the largest integer <= x.", + }, + + fmod: { + $meth: methods.fmod, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, x, y, /)", + $doc: "Return fmod(x, y), according to platform C.\n\nx % y may differ." + }, + + frexp: { + $meth: methods.frexp, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: + "Return the mantissa and exponent of x, as pair (m, e).\n\nm is a float and e is an int, such that x = m * 2.**e.\nIf x is 0, m and e are both 0. Else 0.5 <= abs(m) < 1.0.", + }, + + fsum: { + $meth: methods.fsum, + $flags: {OneArg: true}, + $textsig: "($module, seq, /)", + $doc: "Return an accurate floating point sum of values in the iterable seq.\n\nAssumes IEEE-754 floating point arithmetic.", + }, + + gamma: { + $meth: methods.gamma, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Gamma function at x.", + }, + + gcd: { + $meth: methods.gcd, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, x, y, /)", + $doc: "greatest common divisor of x and y", + }, + + hypot: { + $meth: methods.hypot, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, x, y, /)", + $doc: "Return the Euclidean distance, sqrt(x*x + y*y).", + }, + + isclose: { + $meth: methods.isclose, + $flags: {FastCall: true}, + $textsig: "($module, /, a, b, *, rel_tol=1e-09, abs_tol=0.0)", + $doc: + "Determine whether two floating point numbers are close in value.\n\n rel_tol\n maximum difference for being considered \"close\", relative to the\n magnitude of the input values\n abs_tol\n maximum difference for being considered \"close\", regardless of the\n magnitude of the input values\n\nReturn True if a is close in value to b, and False otherwise.\n\nFor the values to be considered close, the difference between them\nmust be smaller than at least one of the tolerances.\n\n-inf, inf and NaN behave similarly to the IEEE 754 Standard. That\nis, NaN is not close to anything, even itself. inf and -inf are\nonly close to themselves.", + }, + + isfinite: { + $meth: methods.isfinite, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return True if x is neither an infinity nor a NaN, and False otherwise.", + }, + + isinf: { + $meth: methods.isinf, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return True if x is a positive or negative infinity, and False otherwise.", + }, + + isnan: { + $meth: methods.isnan, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return True if x is a NaN (not a number), and False otherwise.", + }, + + ldexp: { + $meth: methods.ldexp, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, x, i, /)", + $doc: "Return x * (2**i).\n\nThis is essentially the inverse of frexp().", + }, + + lgamma: { + $meth: methods.lgamma, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Natural logarithm of absolute value of Gamma function at x.", + }, + + log: { + $meth: methods.log, + $flags: {MinArgs: 1, MaxArgs: 2}, + $textsig: null, + $doc: + "log(x, [base=methods.e])\nReturn the logarithm of x to the given base.\n\nIf the base not specified, returns the natural logarithm (base e) of x.", + }, + + log10: { + $meth: methods.log10, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the base 10 logarithm of x.", + }, + + log1p: { + $meth: methods.log1p, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the natural logarithm of 1+x (base e).\n\nThe result is computed in a way which is accurate for x near zero.", + }, + + log2: { + $meth: methods.log2, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the base 2 logarithm of x.", + }, + + modf: { + $meth: methods.modf, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the fractional and integer parts of x.\n\nBoth results carry the sign of x and are floats." + }, + + pow: { + $meth: methods.pow, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, x, y, /)", + $doc: "Return x**y (x to the power of y).", + }, + + radians: { + $meth: methods.radians, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Convert angle x from degrees to radians.", + }, + + remainder: { + $meth: methods.remainder, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($module, x, y, /)", + $doc: + "Difference between x and the closest integer multiple of y.\n\nReturn x - n*y where n*y is the closest integer multiple of y.\nIn the case where x is exactly halfway between two multiples of\ny, the nearest even value of n is used. The result is always exact.", + }, + + sin: { + $meth: methods.sin, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the sine of x (measured in radians).", + }, + + sinh: { + $meth: methods.sinh, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the hyperbolic sine of x.", + }, + + sqrt: { + $meth: methods.sqrt, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the square root of x.", + }, + + tan: { + $meth: methods.tan, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the tangent of x (measured in radians).", + }, + + tanh: { + $meth: methods.tanh, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Return the hyperbolic tangent of x.", + }, + + trunc: { + $meth: methods.trunc, + $flags: {OneArg: true}, + $textsig: "($module, x, /)", + $doc: "Truncates the Real x to the nearest Integral toward 0.\n\nUses the __trunc__ magic method.", + }, + }; + Sk.abstr.setUpModuleMethods("math", method_defs, math); + delete methods; + + return math; +}; diff --git a/src/lib/matplotlib/__init__.js b/src/lib/matplotlib/__init__.js index fe7981ad62..09e682dc4e 100644 --- a/src/lib/matplotlib/__init__.js +++ b/src/lib/matplotlib/__init__.js @@ -1,6 +1,5 @@ -var $builtinmodule = function(name) -{ - var matplotlib = {}; +var $builtinmodule = function (name) { + var matplotlib = {}; - return matplotlib; + return matplotlib; }; diff --git a/src/lib/matplotlib/pyplot/__init__.js b/src/lib/matplotlib/pyplot/__init__.js index 29929122f9..b98bf436f4 100644 --- a/src/lib/matplotlib/pyplot/__init__.js +++ b/src/lib/matplotlib/pyplot/__init__.js @@ -1,18 +1,18 @@ var jsplotlib = {}; // Skulpt translation -var $builtinmodule = function(name) { +var $builtinmodule = function (name) { var mod = {__name__: "matplotlib.pyplot"}; - + // Unique ID generator for charts var chartCounter = 0; - + var chart; // The aggregate object to hold multiple plots var labels; // Title, X-axis title, Y-axis title var plots; // All the plots to end up drawing var extents; // The highest and lowest values across each axis var colorCycle; - + function resetChart() { chart = null; colorCycle = 0; @@ -24,18 +24,19 @@ var $builtinmodule = function(name) { plots = []; extents = { "xMin": null, - "yMin": null, - "xMax": null, + "yMin": null, + "xMax": null, "yMax": null }; } + resetChart(); - + // Keep track of any plotted values for later checks mod.values = []; // Creates the aggregate chart object that will hold 1 or more plots - var createChart = function(type) { + var createChart = function (type) { if (Sk.console === undefined) { throw new Sk.builtin.NameError( "Can not resolve drawing area. Sk.console is undefined!"); @@ -45,30 +46,30 @@ var $builtinmodule = function(name) { // Create a new chart chartCounter += 1; chart = {}; - + chart.margin = {"top": 20, "right": 30, "bottom": 50, "left": 40}; chart.width = Sk.console.getWidth() - chart.margin.left - chart.margin.right; chart.height = Sk.console.getHeight() - chart.margin.top - chart.margin.bottom; chart.id = "chart" + chartCounter; chart.type = type; - + return chart; } }; - + function updateMinMax(attr, array) { - if (extents[attr+"Min"] === null) { - extents[attr+"Min"] = d3.min(array); + if (extents[attr + "Min"] === null) { + extents[attr + "Min"] = d3.min(array); } else { - extents[attr+"Min"] = Math.min(d3.min(array), extents[attr+"Min"]); + extents[attr + "Min"] = Math.min(d3.min(array), extents[attr + "Min"]); } - if (extents[attr+"Max"] === null) { - extents[attr+"Max"] = d3.max(array); + if (extents[attr + "Max"] === null) { + extents[attr + "Max"] = d3.max(array); } else { - extents[attr+"Max"] = Math.max(d3.max(array), extents[attr+"Max"]); + extents[attr + "Max"] = Math.max(d3.max(array), extents[attr + "Max"]); } } - + function getRandomSubarray(arr, size) { var shuffled = arr.slice(0), i = arr.length, temp, index; while (i--) { @@ -81,16 +82,16 @@ var $builtinmodule = function(name) { } // Main plotting function - var plot_f = function(kwa) { + var plot_f = function (kwa) { // Parse arguments Sk.builtin.pyCheckArgs("plotk", arguments, 1, Infinity, true, false); args = Array.prototype.slice.call(arguments, 1); kwargs = new Sk.builtins.dict(kwa); // is pretty useless for handling kwargs kwargs = Sk.ffi.remapToJs(kwargs); // create a proper dict - + // Keep a backup of the arguments for checker mod.values.push(args); - + // Parse different argument combinations var xdata = null; var ydata = null; @@ -120,17 +121,17 @@ var $builtinmodule = function(name) { xdata.push(i); } } - + // empty canvas from previous plots createChart("line"); // Zip up the data - var actualData = d3.zip(xdata, ydata).map(function(e) { + var actualData = d3.zip(xdata, ydata).map(function (e) { return {"x": e[0], "y": e[1]}; }); // Parse formatting, also keep ColorCycler updated var cycle = jsplotlib.rc["axes.color_cycle"]; - var linestyle = "-", marker= "", + var linestyle = "-", marker = "", color = cycle[colorCycle % cycle.length]; if (stylestring !== null) { var ftm_tuple = jsplotlib._process_plot_format(stylestring); @@ -142,7 +143,7 @@ var $builtinmodule = function(name) { } // Save plots.push({ - "data": actualData, + "data": actualData, "type": "line", "style": { "linestyle": linestyle, @@ -153,7 +154,7 @@ var $builtinmodule = function(name) { // Update min/max updateMinMax("x", xdata); updateMinMax("y", ydata); - + /*if (Sk.console.isMuted()) { return; }*/ @@ -161,12 +162,12 @@ var $builtinmodule = function(name) { plot_f.co_kwargs = true; mod.plot = new Sk.builtin.func(plot_f); - var show_f = function() { + var show_f = function () { /*if (Sk.console.skipDrawing) { Sk.console.printHtml([0], plots); return; }*/ - + if (!chart) { createChart("line"); } @@ -181,25 +182,25 @@ var $builtinmodule = function(name) { if (extents["xMin"] === undefined || extents["yMin"] === undefined) { return; } - + var yAxisBuffer; - + // Establish x/y scalers and axes if (chart.type === "scatter" || chart.type === "line") { - yAxisBuffer = 5*Math.max(extents["yMin"].toLocaleString().length, - extents["yMax"].toLocaleString().length); + yAxisBuffer = 5 * Math.max(extents["yMin"].toLocaleString().length, + extents["yMax"].toLocaleString().length); chart.xScale = d3.scale.linear() .domain([extents["xMin"], extents["xMax"]]) - .range([0, chart.width-yAxisBuffer]); + .range([0, chart.width - yAxisBuffer]); chart.xAxis = d3.svg.axis() .scale(chart.xScale) .orient("bottom"); } else if (chart.type === "hist") { - yAxisBuffer = 5*Math.max(extents["xMin"].toLocaleString().length, - extents["xMax"].toLocaleString().length); + yAxisBuffer = 5 * Math.max(extents["xMin"].toLocaleString().length, + extents["xMax"].toLocaleString().length); chart.xScale = d3.scale.linear() .domain([extents["xMin"], extents["xMax"]]) - .range([0, chart.width-yAxisBuffer]); + .range([0, chart.width - yAxisBuffer]); chart.xAxis = d3.svg.axis() .scale(chart.xScale) .orient("bottom"); @@ -209,24 +210,26 @@ var $builtinmodule = function(name) { 0, bins ]) .range([extents["xMin"], extents["xMax"]]); - var tickArray = d3.range(bins+1) - .map(tempScale).map(function(e) { + var tickArray = d3.range(bins + 1) + .map(tempScale).map(function (e) { return e; }); // TODO: support multiple histograms var histMapper = d3.layout.histogram().bins( tickArray - //chart.xScale.ticks(bins) + //chart.xScale.ticks(bins) )(plots[0]["data"]); } else if (chart.type === "bar") { - yAxisBuffer = 5*Math.max(extents["yMin"].toLocaleString().length, - extents["yMax"].toLocaleString().length); + yAxisBuffer = 5 * Math.max(extents["yMin"].toLocaleString().length, + extents["yMax"].toLocaleString().length); chart.xScale = d3.scale.ordinal() .domain([extents["xMin"], extents["xMax"]]) - .rangeBands([0, chart.width-yAxisBuffer]); + .rangeBands([0, chart.width - yAxisBuffer]); chart.xAxis = d3.svg.axis() .scale(chart.xScale) - .tickFormat(function(d) { return d.index; }) + .tickFormat(function (d) { + return d.index; + }) .orient("bottom"); } // Fix y-axis @@ -236,20 +239,30 @@ var $builtinmodule = function(name) { .range([chart.height, 0]); } else { chart.yScale = d3.scale.linear() - .domain([0, d3.max(histMapper, function(d) { return d.y; })]) + .domain([0, d3.max(histMapper, function (d) { + return d.y; + })]) .range([chart.height, 0]); } chart.yAxis = d3.svg.axis() .scale(chart.yScale) .orient("left"); - - chart.mapX = function(d) {return chart.xScale(d.x);}; - chart.mapY = function(d) {return chart.yScale(d.y);}; + + chart.mapX = function (d) { + return chart.xScale(d.x); + }; + chart.mapY = function (d) { + return chart.yScale(d.y); + }; chart.mapLine = d3.svg.line() - .x(function(d) { return chart.xScale(d.x); }) - .y(function(d) { return chart.yScale(d.y); }) + .x(function (d) { + return chart.xScale(d.x); + }) + .y(function (d) { + return chart.yScale(d.y); + }) .interpolate("linear"); - + // set css classes let outputTarget = Sk.console.plot(plots); chart.svg = d3.select(outputTarget.html[0]).append("div").append("svg"); @@ -258,11 +271,11 @@ var $builtinmodule = function(name) { chart.svg.attr("width", Sk.console.getWidth()); chart.svg.attr("height", Sk.console.getHeight()); chart.svg.attr("chartCount", chartCounter); - + var translation = "translate(" + (chart.margin.left + yAxisBuffer) + "," + chart.margin.top + ")"; chart.canvas = chart.svg.append("g") .attr("transform", translation); - + chart.canvas.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + chart.height + ")") @@ -272,33 +285,33 @@ var $builtinmodule = function(name) { .call(chart.yAxis); chart.canvas.select(".x.axis") .selectAll("text") - .style("font-size","12px"); + .style("font-size", "12px"); chart.canvas.select(".y.axis") .selectAll("text") - .style("font-size","12px"); - translation = "translate(" + ( (chart.width-yAxisBuffer) / 2) + " ," + (chart.height + chart.margin.bottom-14) + ")"; + .style("font-size", "12px"); + translation = "translate(" + ((chart.width - yAxisBuffer) / 2) + " ," + (chart.height + chart.margin.bottom - 14) + ")"; chart.canvas.append("text") // text label for the x axis .attr("transform", translation) .attr("class", "x-axis-label") - .style("font-size", "14px") + .style("font-size", "14px") .text(labels["x-axis"]) .style("text-anchor", "middle"); chart.canvas.append("text") .attr("transform", "rotate(-90)") .attr("class", "y-axis-label") - .attr("y", 0 - chart.margin.left-yAxisBuffer) + .attr("y", 0 - chart.margin.left - yAxisBuffer) .attr("x", 0 - (chart.height / 2)) .attr("dy", "1em") .text(labels["y-axis"]) - .style("font-size", "14px") + .style("font-size", "14px") .style("text-anchor", "middle"); chart.canvas.append("text") - .attr("x", ( (chart.width-yAxisBuffer) / 2)) + .attr("x", ((chart.width - yAxisBuffer) / 2)) .attr("y", 0 - (chart.margin.top / 2)) .attr("class", "title-text") .text(labels["title"]) - .attr("text-anchor", "middle") - .style("font-size", "14px") + .attr("text-anchor", "middle") + .style("font-size", "14px") .style("text-decoration", "underline"); chart.canvas.append("text") .attr("x", 0) @@ -309,12 +322,12 @@ var $builtinmodule = function(name) { chart.svg.insert("defs", ":first-child") .append("style") .attr("type", "text/css") - .text("svg { background-color: white; }\n"+ - ".axis path,.axis line { fill: none; stroke: black; shape-rendering: crispEdges;}\n"+ - ".line { fill: none; stroke-width: 1px;}\n"+ - ".circle { r: 3; shape-rendering: crispEdges; }\n"+ - ".bar { shape-rendering: crispEdges;}\n"); - + .text("svg { background-color: white; }\n" + + ".axis path,.axis line { fill: none; stroke: black; shape-rendering: crispEdges;}\n" + + ".line { fill: none; stroke-width: 1px;}\n" + + ".circle { r: 3; shape-rendering: crispEdges; }\n" + + ".bar { shape-rendering: crispEdges;}\n"); + // Actually draw the chart objects for (var i = 0; i < plots.length; i += 1) { var plot = plots[i]; @@ -343,20 +356,26 @@ var $builtinmodule = function(name) { .attr("class", "bar") .style("fill", plot["style"]["color"]) .style("stroke", "black") - .attr("x", function(d) { return chart.xScale(d.x); }) - .attr("width", (chart.width-yAxisBuffer)/(1+histMapper.length)) - .attr("y", function(d) { return chart.yScale(d.y); }) - .attr("height", function(d) { return chart.height - chart.yScale(d.y); }); + .attr("x", function (d) { + return chart.xScale(d.x); + }) + .attr("width", (chart.width - yAxisBuffer) / (1 + histMapper.length)) + .attr("y", function (d) { + return chart.yScale(d.y); + }) + .attr("height", function (d) { + return chart.height - chart.yScale(d.y); + }); } } - var doctype = '' + "<" + '!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'; + var doctype = "" + "<" + "!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">"; var xml = new XMLSerializer().serializeToString(chart.svg[0][0]); - var blob = new Blob([ doctype + xml], { type: "image/svg+xml" }); + var blob = new Blob([doctype + xml], {type: "image/svg+xml"}); var url = window.URL.createObjectURL(blob); //var data = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(xml))); let anchor = document.createElement("a"); - var img = document.createElement("img"); + var img = document.createElement("img"); img.style.display = "block"; var oldChart = chart; let filename = labels.title; @@ -365,7 +384,7 @@ var $builtinmodule = function(name) { // TODO: Consider using the SVG as the actual image, and using this as the href // surrounding it instead. oldChart.svg[0][0].parentNode.replaceChild(anchor, oldChart.svg[0][0]); - img.onload = function() { + img.onload = function () { img.onload = null; //TODO: Make this capture a class descendant. Cross the D3/Jquery barrier! var canvas = document.createElement("canvas"); @@ -375,13 +394,13 @@ var $builtinmodule = function(name) { ctx.drawImage(img, 0, 0); var canvasUrl = canvas.toDataURL("image/png"); img.setAttribute("src", canvasUrl); - img.setAttribute("title", "Generated plot titled: "+filename); - img.setAttribute("alt", "Generated plot titled: "+filename); + img.setAttribute("title", "Generated plot titled: " + filename); + img.setAttribute("alt", "Generated plot titled: " + filename); anchor.setAttribute("href", canvasUrl); anchor.setAttribute("download", filename); // Snip off this chart, we can now start a new one. }; - img.onerror = function(e) { + img.onerror = function (e) { console.error(e); }; img.setAttribute("src", url); @@ -389,76 +408,76 @@ var $builtinmodule = function(name) { }; mod.show = new Sk.builtin.func(show_f); - var title_f = function(s) { + var title_f = function (s) { Sk.builtin.pyCheckArgs("title", arguments, 1, 1); if (!Sk.builtin.checkString(s)) { throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(s) + "' is not supported for title; should be a string."); } - - labels["title"]= Sk.ffi.remapToJs(s); + + labels["title"] = Sk.ffi.remapToJs(s); }; mod.title = new Sk.builtin.func(title_f); - var xlabel_f = function(s) { + var xlabel_f = function (s) { Sk.builtin.pyCheckArgs("xlabel", arguments, 1, 1); if (!Sk.builtin.checkString(s)) { throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(s) + "' is not supported for xlabel; should be a string."); } - - labels["x-axis"]= Sk.ffi.remapToJs(s); + + labels["x-axis"] = Sk.ffi.remapToJs(s); }; mod.xlabel = new Sk.builtin.func(xlabel_f); - var ylabel_f = function(s) { + var ylabel_f = function (s) { Sk.builtin.pyCheckArgs("ylabel", arguments, 1, 1); if (!Sk.builtin.checkString(s)) { throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(s) + "' is not supported for ylabel; should be a string."); } - - labels["y-axis"]= Sk.ffi.remapToJs(s); + + labels["y-axis"] = Sk.ffi.remapToJs(s); }; mod.ylabel = new Sk.builtin.func(ylabel_f); // Clear the current figure - var clf_f = function() { + var clf_f = function () { chart = null; resetChart(); }; mod.clf = new Sk.builtin.func(clf_f); UNSUPPORTED = ["semilogx", "semilogy", "specgram", "stackplot", "stem", "step", "streamplot", "tricontour", "tricontourf", "tripcolor", "triplot", "vlines", "xcorr", "barbs", "cla", "grid", "table", "text", "annotate", "ticklabel_format", "locator_params", "tick_params", "margins", "autoscale", "autumn", "cool", "copper", "flag", "gray", "hot", "hsv", "jet", "pink", "prism", "spring", "summer", "winter", "spectral", "hlines", "loglog", "magnitude_spectrum", "pcolor", "pcolormesh", "phase_spectrum", "pie", "plot_date", "psd", "quiver", "quiverkey", "findobj", "switch_backend", "isinteractive", "ioff", "ion", "pause", "rc", "rc_context", "rcdefaults", "gci", "sci", "xkcd", "figure", "gcf", "get_fignums", "get_figlabels", "get_current_fig_manager", "connect", "disconnect", "close", "savefig", "ginput", "waitforbuttonpress", "figtext", "suptitle", "figimage", "figlegend", "hold", "ishold", "over", "delaxes", "sca", "gca", "subplot", "subplots", "subplot2grid", "twinx", "twiny", "subplots_adjust", "subplot_tool", "tight_layout", "box", "xlim", "ylim", "xscale", "yscale", "xticks", "yticks", "minorticks_on", "minorticks_off", "rgrids", "thetagrids", "plotting", "get_plot_commands", "colors", "colormaps", "_setup_pyplot_info_docstrings", "colorbar", "clim", "set_cmap", "imread", "imsave", "matshow", "polar", "plotfile", "_autogen_docstring", "acorr", "arrow", "axhline", "axhspan", "axvline", "axvspan", "bar", "barh", "broken_barh", "boxplot", "cohere", "clabel", "contour", "contourf", "csd", "errorbar", "eventplot", "fill", "fill_between", "fill_betweenx", "hexbin", "hist2d", "axis"]; - for (var i = 0; i < UNSUPPORTED.length; i+= 1) { - mod[UNSUPPORTED[i]] = new Sk.builtin.func(function() { - throw new Sk.builtin.NotImplementedError(UNSUPPORTED[i]+" is not yet implemented"); + for (var i = 0; i < UNSUPPORTED.length; i += 1) { + mod[UNSUPPORTED[i]] = new Sk.builtin.func(function () { + throw new Sk.builtin.NotImplementedError(UNSUPPORTED[i] + " is not yet implemented"); }); } - - var legend_f = function() { + + var legend_f = function () { return Sk.builtin.none.none$; }; mod.legend = new Sk.builtin.func(legend_f); - - var hist_f = function(kwa) { + + var hist_f = function (kwa) { // Parse arguments Sk.builtin.pyCheckArgs("hist", arguments, 1, Infinity, true, false); - args = Array.prototype.slice.call(arguments, 1); + args = Array.prototype.slice.call(arguments, 1); kwargs = new Sk.builtins.dict(kwa); // is pretty useless for handling kwargs kwargs = Sk.ffi.remapToJs(kwargs); // create a proper dict - + var bins = 10; if ("bins" in kwargs) { bins = kwargs["bins"]; } - + // Keep a backup of the arguments for checker mod.values.push(args); - + // Parse different argument combinations var data = null; var stylestring = null; @@ -470,13 +489,13 @@ var $builtinmodule = function(name) { data = Sk.ffi.remapToJs(args[0]); stylestring = Sk.ffi.remapToJs(args[1]); } - + // empty canvas from previous plots createChart("hist"); // Parse formatting, also keep ColorCycler updated var cycle = jsplotlib.rc["axes.color_cycle"]; - var linestyle = " ", marker= "o", + var linestyle = " ", marker = "o", color = cycle[colorCycle % cycle.length]; if (stylestring !== null) { var ftm_tuple = jsplotlib._process_plot_format(stylestring); @@ -488,7 +507,7 @@ var $builtinmodule = function(name) { } // Save plots.push({ - "data": data, + "data": data, "type": "hist", "bins": bins, "style": { @@ -501,22 +520,22 @@ var $builtinmodule = function(name) { }; hist_f.co_kwargs = true; mod.hist = new Sk.builtin.func(hist_f); - - var scatter_f = function(kwa) { + + var scatter_f = function (kwa) { // Parse arguments Sk.builtin.pyCheckArgs("scatter", arguments, 1, Infinity, true, false); - args = Array.prototype.slice.call(arguments, 1); + args = Array.prototype.slice.call(arguments, 1); kwargs = new Sk.builtins.dict(kwa); // is pretty useless for handling kwargs kwargs = Sk.ffi.remapToJs(kwargs); // create a proper dict - + var dot_limit = 256; if ("dot_limit" in kwargs) { dot_limit = kwargs["dot_limit"]; } - + // Keep a backup of the arguments for checker mod.values.push(args); - + // Parse different argument combinations var xdata = null; var ydata = null; @@ -531,29 +550,29 @@ var $builtinmodule = function(name) { ydata = Sk.ffi.remapToJs(args[1]); stylestring = Sk.ffi.remapToJs(args[2]); } - + if (xdata && xdata.length > dot_limit) { var xdataSampled = [], ydataSampled = []; var LENGTH = xdata.length, RATE = LENGTH / dot_limit; - for (var i = 0; i < dot_limit; i+= 1) { - var index = Math.floor((i+Math.random())*RATE); + for (var i = 0; i < dot_limit; i += 1) { + var index = Math.floor((i + Math.random()) * RATE); xdataSampled.push(xdata[index]); ydataSampled.push(ydata[index]); } xdata = xdataSampled; ydata = ydataSampled; } - + // empty canvas from previous plots createChart("scatter"); // Zip up the data - var actualData = d3.zip(xdata, ydata).map(function(e) { + var actualData = d3.zip(xdata, ydata).map(function (e) { return {"x": e[0], "y": e[1]}; }); // Parse formatting, also keep ColorCycler updated var cycle = jsplotlib.rc["axes.color_cycle"]; - var linestyle = " ", marker= "o", + var linestyle = " ", marker = "o", color = cycle[colorCycle % cycle.length]; if (stylestring !== null) { var ftm_tuple = jsplotlib._process_plot_format(stylestring); @@ -565,7 +584,7 @@ var $builtinmodule = function(name) { } // Save plots.push({ - "data": actualData, + "data": actualData, "type": "scatter", "style": { "linestyle": linestyle, @@ -579,7 +598,7 @@ var $builtinmodule = function(name) { }; scatter_f.co_kwargs = true; mod.scatter = new Sk.builtin.func(scatter_f); - + return mod; }; @@ -668,7 +687,7 @@ jsplotlib.lineMarkers = { /** Color short keys -**/ + **/ jsplotlib.colors = { "b": "blue", "g": "green", @@ -682,7 +701,7 @@ jsplotlib.colors = { /** Mapping of all possible CSS colors, that are supported by matplotlib -**/ + **/ jsplotlib.cnames = { "aliceblue": "#F0F8FF", "antiquewhite": "#FAEBD7", @@ -829,17 +848,23 @@ jsplotlib.cnames = { "yellowgreen": "#9ACD32" }; -jsplotlib.color_to_hex = function(color) { +jsplotlib.color_to_hex = function (color) { // is color a shortcut? - if (jsplotlib.colors[color]) {color = jsplotlib.colors[color];} + if (jsplotlib.colors[color]) { + color = jsplotlib.colors[color]; + } // is inside cnames array? - if (jsplotlib.cnames[color]) {return jsplotlib.cnames[color];} + if (jsplotlib.cnames[color]) { + return jsplotlib.cnames[color]; + } // check if it is already a hex value if (typeof color == "string") { var match = color.match(/^#(?:[0-9a-fA-F]{3}){1,2}$/); - if (match && match.length === 1) {return match[0];} + if (match && match.length === 1) { + return match[0]; + } } // add rgb colors here @@ -851,12 +876,14 @@ jsplotlib.color_to_hex = function(color) { return jsplotlib.cnames[jsplotlib.rc["lines.color"]]; }; -jsplotlib.get_color = function(cs) { +jsplotlib.get_color = function (cs) { return jsplotlib.colors[cs] ? jsplotlib.colors[cs] : jsplotlib.colors.b; }; -jsplotlib.parse_marker = function(style) { - if (!style) {return "x";} +jsplotlib.parse_marker = function (style) { + if (!style) { + return "x"; + } switch (style) { case ".": return "."; @@ -908,21 +935,21 @@ jsplotlib.parse_marker = function(style) { }; /** -Process a MATLAB style color/line style format string. Return a -(*linestyle*, *color*) tuple as a result of the processing. Default -values are ('-', 'b'). Example format strings include: + Process a MATLAB style color/line style format string. Return a + (*linestyle*, *color*) tuple as a result of the processing. Default + values are ('-', 'b'). Example format strings include: -* 'ko': black circles -* '.b': blue dots -* 'r--': red dashed lines + * 'ko': black circles + * '.b': blue dots + * 'r--': red dashed lines -.. seealso:: + .. seealso:: - :func:`~matplotlib.Line2D.lineStyles` and - :func:`~matplotlib.pyplot.colors` - for all possible styles and color format string. -**/ -jsplotlib._process_plot_format = function(fmt) { + :func:`~matplotlib.Line2D.lineStyles` and + :func:`~matplotlib.pyplot.colors` + for all possible styles and color format string. + **/ +jsplotlib._process_plot_format = function (fmt) { var linestyle = null; var marker = null; var color = null; @@ -937,7 +964,8 @@ jsplotlib._process_plot_format = function(fmt) { "color": color }; } - } catch (e) {} + } catch (e) { + } // handle the multi char special cases and strip them for the string if (fmt.search(/--/) >= 0) { @@ -958,20 +986,20 @@ jsplotlib._process_plot_format = function(fmt) { var c = fmt.charAt(i); if (jsplotlib.lineStyles[c]) { if (linestyle) { - throw new Sk.builtin.ValueError('Illegal format string "' + fmt + - '"; two linestyle symbols'); + throw new Sk.builtin.ValueError("Illegal format string \"" + fmt + + "\"; two linestyle symbols"); } linestyle = c; } else if (jsplotlib.lineMarkers[c]) { if (marker) { - throw new Sk.builtin.ValueError('Illegal format string "' + fmt + - '"; two marker symbols'); + throw new Sk.builtin.ValueError("Illegal format string \"" + fmt + + "\"; two marker symbols"); } marker = c; } else if (jsplotlib.colors[c]) { if (color) { - throw new Sk.builtin.ValueError('Illegal format string "' + fmt + - '"; two color symbols'); + throw new Sk.builtin.ValueError("Illegal format string \"" + fmt + + "\"; two color symbols"); } color = c; } else { @@ -1002,31 +1030,37 @@ jsplotlib._process_plot_format = function(fmt) { https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/colors.py http://matplotlib.org/api/colors_api.html - Returns an *RGB* tuple of three floats from 0-1. + Returns an *RGB* tuple of three floats from 0-1. - *arg* can be an *RGB* or *RGBA* sequence or a string in any of - several forms: + *arg* can be an *RGB* or *RGBA* sequence or a string in any of + several forms: - 1) a letter from the set 'rgbcmykw' - 2) a hex color string, like '#00FFFF' - 3) a standard name, like 'aqua' - 4) a string representation of a float, like '0.4', - indicating gray on a 0-1 scale + 1) a letter from the set 'rgbcmykw' + 2) a hex color string, like '#00FFFF' + 3) a standard name, like 'aqua' + 4) a string representation of a float, like '0.4', + indicating gray on a 0-1 scale - if *arg* is *RGBA*, the *A* will simply be discarded. -**/ -jsplotlib.to_rgb = function(fmt) { - if (!fmt) {return null;} + if *arg* is *RGBA*, the *A* will simply be discarded. + **/ +jsplotlib.to_rgb = function (fmt) { + if (!fmt) { + return null; + } var color = null; if (typeof fmt == "string") { fmt_lower = fmt.toLowerCase(); - if (jsplotlib.colors[fmt_lower]) {return jsplotlib.hex2color(jsplotlib.cnames[jsplotlib.colors[fmt_lower]]);} + if (jsplotlib.colors[fmt_lower]) { + return jsplotlib.hex2color(jsplotlib.cnames[jsplotlib.colors[fmt_lower]]); + } // is inside cnames array? - if (jsplotlib.cnames[fmt_lower]) {return jsplotlib.hex2color(jsplotlib.cnames[fmt_lower]);} + if (jsplotlib.cnames[fmt_lower]) { + return jsplotlib.hex2color(jsplotlib.cnames[fmt_lower]); + } if (fmt_lower.indexOf("#") === 0) { return jsplotlib.hex2color(fmt_lower); @@ -1069,10 +1103,10 @@ jsplotlib.to_rgb = function(fmt) { }; /** - Take a hex string *s* and return the corresponding rgb 3-tuple - Example: #efefef -> (0.93725, 0.93725, 0.93725) -**/ -jsplotlib.hex2color = function(s) { + Take a hex string *s* and return the corresponding rgb 3-tuple + Example: #efefef -> (0.93725, 0.93725, 0.93725) + **/ +jsplotlib.hex2color = function (s) { if (!s || typeof s != "string") { throw new Sk.builtin.TypeError("hex2color requires a string argument"); } @@ -1097,15 +1131,17 @@ jsplotlib.hex2color = function(s) { return color.reverse(); } else { - throw new Sk.builtin.ValueError('invalid hex color string "' + s + '"'); + throw new Sk.builtin.ValueError("invalid hex color string \"" + s + "\""); } }; /** - Expects and rgb tuple with values [0,1] -**/ -jsplotlib.rgb2hex = function(rgb) { - if (!rgb) {return null;} + Expects and rgb tuple with values [0,1] + **/ +jsplotlib.rgb2hex = function (rgb) { + if (!rgb) { + return null; + } if (rgb.length && rgb.length >= 3) { var i; diff --git a/src/lib/numpy/__init__.js b/src/lib/numpy/__init__.js index 3a6994cc75..bda4452bd9 100644 --- a/src/lib/numpy/__init__.js +++ b/src/lib/numpy/__init__.js @@ -1,1134 +1,1148 @@ /** - Made by Michael Ebert for https://github.com/skulpt/skulpt - ndarray implementation inspired by https://github.com/geometryzen/davinci-dev (not compatible with skulpt) + Made by Michael Ebert for https://github.com/skulpt/skulpt + ndarray implementation inspired by https://github.com/geometryzen/davinci-dev (not compatible with skulpt) - Some methods are based on the original numpy implementation. + Some methods are based on the original numpy implementation. - See http://waywaaard.github.io/skulpt/ for more information. -**/ + See http://waywaaard.github.io/skulpt/ for more information. + **/ var numpy = function () { - if (typeof mathjs == 'function') { - // load mathjs instance - this.math = mathjs; - } else { - Sk.debugout("mathjs not included and callable"); - } + if (typeof mathjs == "function") { + // load mathjs instance + this.math = mathjs; + } else { + Sk.debugout("mathjs not included and callable"); + } }; numpy.prototype.wrapasfloats = function (values) { - var i; - for (i = 0; i < values.length; i++) { - values[i] = new Sk.builtin.nmber(values[i], Sk.builtin.nmber.float$); - } + var i; + for (i = 0; i < values.length; i++) { + values[i] = new Sk.builtin.nmber(values[i], Sk.builtin.nmber.float$); + } - return values; + return values; }; numpy.prototype.arange = function (start, stop, step) { - if (step === undefined) - step = 1.0; + if (step === undefined) { + step = 1.0; + } - start *= 1.0; - stop *= 1.0; - step *= 1.0; + start *= 1.0; + stop *= 1.0; + step *= 1.0; - var res = []; - for (var i = start; i < stop; i += step) { - res.push(i); - } + var res = []; + for (var i = start; i < stop; i += step) { + res.push(i); + } - return res; + return res; }; var $builtinmodule = function (name) { - var np = new numpy(); - - var mod = {}; - - /** - Class for numpy.ndarray - **/ - var CLASS_NDARRAY = "numpy.ndarray"; - - function remapToJs_shallow(obj, shallow) { - var _shallow = shallow || true; - if (obj instanceof Sk.builtin.list) { - if (!_shallow) { - var ret = []; - for (var i = 0; i < obj.v.length; ++i) { - ret.push(Sk.ffi.remapToJs(obj.v[i])); + var np = new numpy(); + + var mod = {}; + + /** + Class for numpy.ndarray + **/ + var CLASS_NDARRAY = "numpy.ndarray"; + + function remapToJs_shallow(obj, shallow) { + var _shallow = shallow || true; + if (obj instanceof Sk.builtin.list) { + if (!_shallow) { + var ret = []; + for (var i = 0; i < obj.v.length; ++i) { + ret.push(Sk.ffi.remapToJs(obj.v[i])); + } + return ret; + } else { + return obj.v; + } + } else if (obj instanceof Sk.builtin.float_) { + return Sk.builtin.asnum$nofloat(obj); + } else { + return Sk.ffi.remapToJs(obj); } - return ret; - } else { - return obj.v; - } - } else if (obj instanceof Sk.builtin.float_) { - return Sk.builtin.asnum$nofloat(obj); - } else { - return Sk.ffi.remapToJs(obj); - } - } - - /** - Unpacks in any form fo nested Lists - **/ - function unpack(py_obj, buffer, state) { - if (py_obj instanceof Sk.builtin.list || py_obj instanceof Sk.builtin.tuple) { - var py_items = remapToJs_shallow(py_obj); - state.level += 1; - - if (state.level > state.shape.length) { - state.shape.push(py_items.length); - } - var i; - var len = py_items.length; - for (i = 0; i < len; i++) { - unpack(py_items[i], buffer, state); - } - state.level -= 1; - } else { - buffer.push(py_obj); - } - } - - /** - Computes the strides for columns and rows - **/ - function computeStrides(shape) { - var strides = shape.slice(0); - strides.reverse(); - var prod = 1; - var temp; - for (var i = 0, len = strides.length; i < len; i++) { - temp = strides[i]; - strides[i] = prod; - prod *= temp; } - return strides.reverse(); - } - - /** - Computes the offset for the ndarray for given index and strides - [1, ..., n] - **/ - function computeOffset(strides, index) { - var offset = 0; - for (var k = 0, len = strides.length; k < len; k++) { - offset += strides[k] * index[k]; - } - return offset; - } - - /** - Calculates the size of the ndarray, dummy - **/ - function prod(numbers) { - var size = 1; - var i; - for (i = 0; i < numbers.length; i++) { - size *= numbers[i]; + /** + Unpacks in any form fo nested Lists + **/ + function unpack(py_obj, buffer, state) { + if (py_obj instanceof Sk.builtin.list || py_obj instanceof Sk.builtin.tuple) { + var py_items = remapToJs_shallow(py_obj); + state.level += 1; + + if (state.level > state.shape.length) { + state.shape.push(py_items.length); + } + var i; + var len = py_items.length; + for (i = 0; i < len; i++) { + unpack(py_items[i], buffer, state); + } + state.level -= 1; + } else { + buffer.push(py_obj); + } } - return size; - } - - /** - Creates a string representation for given buffer and shape - buffer is an ndarray - **/ - function stringify(buffer, shape, dtype) { - var emits = shape.map(function (x) { - return 0; - }); - var uBound = shape.length - 1; - var idxLevel = 0; - var str = "["; - var i = 0; - while (idxLevel !== -1) { - if (emits[idxLevel] < shape[idxLevel]) { - if (emits[idxLevel] !== 0) { - str += ", "; + + /** + Computes the strides for columns and rows + **/ + function computeStrides(shape) { + var strides = shape.slice(0); + strides.reverse(); + var prod = 1; + var temp; + for (var i = 0, len = strides.length; i < len; i++) { + temp = strides[i]; + strides[i] = prod; + prod *= temp; } - if (idxLevel < uBound) { - str += "["; - idxLevel += 1; - } else { - if (dtype === Sk.builtin.float_) - str += Sk.ffi.remapToJs(Sk.builtin.str(new Sk.builtin.float_(buffer[ - i++]))); - else - str += Sk.ffi.remapToJs(Sk.builtin.str(buffer[i++])); - emits[idxLevel] += 1; + return strides.reverse(); + } + + /** + Computes the offset for the ndarray for given index and strides + [1, ..., n] + **/ + function computeOffset(strides, index) { + var offset = 0; + for (var k = 0, len = strides.length; k < len; k++) { + offset += strides[k] * index[k]; } - } else { - emits[idxLevel] = 0; - str += "]"; - idxLevel -= 1; - if (idxLevel >= 0) { - emits[idxLevel] += 1; + return offset; + } + + /** + Calculates the size of the ndarray, dummy + **/ + function prod(numbers) { + var size = 1; + var i; + for (i = 0; i < numbers.length; i++) { + size *= numbers[i]; } - } + return size; } - return str; - } - - /* - http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.tolist.html?highlight=tolist#numpy.ndarray.tolist - */ - function tolistrecursive(buffer, shape, strides, startdim, dtype) { - var i, n, stride; - var arr, item; - - /* Base case */ - if (startdim >= shape.length) { - if (dtype && dtype === Sk.builtin.float_) { - return new Sk.builtin.float_(buffer[0]); // handle float special case - } else { - return Sk.ffi.remapToPy(buffer[0]); - } + + /** + Creates a string representation for given buffer and shape + buffer is an ndarray + **/ + function stringify(buffer, shape, dtype) { + var emits = shape.map(function (x) { + return 0; + }); + var uBound = shape.length - 1; + var idxLevel = 0; + var str = "["; + var i = 0; + while (idxLevel !== -1) { + if (emits[idxLevel] < shape[idxLevel]) { + if (emits[idxLevel] !== 0) { + str += ", "; + } + + if (idxLevel < uBound) { + str += "["; + idxLevel += 1; + } else { + if (dtype === Sk.builtin.float_) { + str += Sk.ffi.remapToJs(new Sk.builtin.str(new Sk.builtin.float_(buffer[ + i++]))); + } else { + str += Sk.ffi.remapToJs(new Sk.builtin.str(buffer[i++])); + } + emits[idxLevel] += 1; + } + } else { + emits[idxLevel] = 0; + str += "]"; + idxLevel -= 1; + if (idxLevel >= 0) { + emits[idxLevel] += 1; + } + } + } + return str; } - n = shape[startdim]; - stride = strides[startdim]; + /* + http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.tolist.html?highlight=tolist#numpy.ndarray.tolist + */ + function tolistrecursive(buffer, shape, strides, startdim, dtype) { + var i, n, stride; + var arr, item; + + /* Base case */ + if (startdim >= shape.length) { + if (dtype && dtype === Sk.builtin.float_) { + return new Sk.builtin.float_(buffer[0]); // handle float special case + } else { + return Sk.ffi.remapToPy(buffer[0]); + } + } + + n = shape[startdim]; + stride = strides[startdim]; - arr = []; + arr = []; - for (i = 0; i < n; i++) { - item = tolistrecursive(buffer, shape, strides, startdim + 1, dtype); - arr.push(item); + for (i = 0; i < n; i++) { + item = tolistrecursive(buffer, shape, strides, startdim + 1, dtype); + arr.push(item); - buffer = buffer.slice(stride); + buffer = buffer.slice(stride); + } + + return new Sk.builtin.list(arr); } - return new Sk.builtin.list(arr); - } - - /** - internal tolist interface - **/ - function tolist(buffer, shape, strides, dtype) { - var buffer_copy = buffer.slice(0); - return tolistrecursive(buffer_copy, shape, strides, 0, dtype); - } - - /** - Updates all attributes of the numpy.ndarray - **/ - function updateAttributes(self, ndarrayJs) { - Sk.abstr.sattr(self, 'ndmin', new Sk.builtin.int_(ndarrayJs.shape.length)); - Sk.abstr.sattr(self, 'dtype', ndarrayJs.dtype); - Sk.abstr.sattr(self, 'shape', new Sk.builtin.tuple(ndarrayJs.shape.map( - function (x) { - return new Sk.builtin.int_(x); - }))); - Sk.abstr.sattr(self, 'strides', new Sk.builtin.tuple(ndarrayJs.strides.map( - function (x) { - return new Sk.builtin.int_(x); - }))); - Sk.abstr.sattr(self, 'size', new Sk.builtin.int_(prod(ndarrayJs.shape))); - Sk.abstr.sattr(self, 'data', new Sk.ffi.remapToPy(ndarrayJs.buffer)); - } - - /** - An array object represents a multidimensional, homogeneous array of fixed-size items. - An associated data-type object describes the format of each element in the array - (its byte-order, how many bytes it occupies in memory, whether it is an integer, a - floating point number, or something else, etc.) - - Arrays should be constructed using array, zeros or empty (refer to the See Also - section below). The parameters given here refer to a low-level method (ndarray(...)) for - instantiating an array. - - For more information, refer to the numpy module and examine the the methods and - attributes of an array. - **/ - var ndarray_f = function ($gbl, $loc) { - $loc.__init__ = new Sk.builtin.func(function (self, shape, dtype, buffer, - offset, strides, order) { - var ndarrayJs = {}; // js object holding the actual array - ndarrayJs.shape = Sk.ffi.remapToJs(shape); - - ndarrayJs.strides = computeStrides(ndarrayJs.shape); - ndarrayJs.dtype = dtype || Sk.builtin.none.none$; - - if (buffer && buffer instanceof Sk.builtin.list) { - ndarrayJs.buffer = Sk.ffi.remapToJs(buffer); - } - - self.v = ndarrayJs; // value holding the actual js object and array - self.tp$name = CLASS_NDARRAY; // set class name - - updateAttributes(self, ndarrayJs); - }); + /** + internal tolist interface + **/ + function tolist(buffer, shape, strides, dtype) { + var buffer_copy = buffer.slice(0); + return tolistrecursive(buffer_copy, shape, strides, 0, dtype); + } - $loc.tp$getattr = Sk.builtin.object.prototype.GenericGetAttr; + /** + Updates all attributes of the numpy.ndarray + **/ + function updateAttributes(self, ndarrayJs) { + Sk.abstr.sattr(self, "ndmin", new Sk.builtin.int_(ndarrayJs.shape.length)); + Sk.abstr.sattr(self, "dtype", ndarrayJs.dtype); + Sk.abstr.sattr(self, "shape", new Sk.builtin.tuple(ndarrayJs.shape.map( + function (x) { + return new Sk.builtin.int_(x); + }))); + Sk.abstr.sattr(self, "strides", new Sk.builtin.tuple(ndarrayJs.strides.map( + function (x) { + return new Sk.builtin.int_(x); + }))); + Sk.abstr.sattr(self, "size", new Sk.builtin.int_(prod(ndarrayJs.shape))); + Sk.abstr.sattr(self, "data", new Sk.ffi.remapToPy(ndarrayJs.buffer)); + } - // ToDo: setAttribute should be implemented, change of shape causes resize - // ndmin cannot be set, etc... - $loc.tp$setattr = Sk.builtin.object.prototype.GenericSetAttr; + /** + An array object represents a multidimensional, homogeneous array of fixed-size items. + An associated data-type object describes the format of each element in the array + (its byte-order, how many bytes it occupies in memory, whether it is an integer, a + floating point number, or something else, etc.) + + Arrays should be constructed using array, zeros or empty (refer to the See Also + section below). The parameters given here refer to a low-level method (ndarray(...)) for + instantiating an array. + + For more information, refer to the numpy module and examine the the methods and + attributes of an array. + **/ + var ndarray_f = function ($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self, shape, dtype, buffer, + offset, strides, order) { + var ndarrayJs = {}; // js object holding the actual array + ndarrayJs.shape = Sk.ffi.remapToJs(shape); + + ndarrayJs.strides = computeStrides(ndarrayJs.shape); + ndarrayJs.dtype = dtype || Sk.builtin.none.none$; + + if (buffer && buffer instanceof Sk.builtin.list) { + ndarrayJs.buffer = Sk.ffi.remapToJs(buffer); + } + + self.v = ndarrayJs; // value holding the actual js object and array + self.tp$name = CLASS_NDARRAY; // set class name + + updateAttributes(self, ndarrayJs); + }); - /* - Return the array as a (possibly nested) list. + $loc.tp$getattr = Sk.builtin.object.prototype.GenericGetAttr; - Return a copy of the array data as a (nested) Python list. Data items are - converted to the nearest compatible Python type. - */ - $loc.tolist = new Sk.builtin.func(function (self) { - var ndarrayJs = Sk.ffi.remapToJs(self); - var list = tolist(ndarrayJs.buffer, ndarrayJs.shape, ndarrayJs.strides, - ndarrayJs.dtype); + // ToDo: setAttribute should be implemented, change of shape causes resize + // ndmin cannot be set, etc... + $loc.tp$setattr = Sk.builtin.object.prototype.GenericSetAttr; - return list; - }); + /* + Return the array as a (possibly nested) list. - $loc.reshape = new Sk.builtin.func(function (self, shape, order) { - Sk.builtin.pyCheckArgs("reshape", arguments, 2, 3); - var ndarrayJs = Sk.ffi.remapToJs(self); - return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, ndarrayJs.dtype, - new Sk.builtin.list(ndarrayJs.buffer)); - }); + Return a copy of the array data as a (nested) Python list. Data items are + converted to the nearest compatible Python type. + */ + $loc.tolist = new Sk.builtin.func(function (self) { + var ndarrayJs = Sk.ffi.remapToJs(self); + var list = tolist(ndarrayJs.buffer, ndarrayJs.shape, ndarrayJs.strides, + ndarrayJs.dtype); - $loc.copy = new Sk.builtin.func(function (self, order) { - Sk.builtin.pyCheckArgs("copy", arguments, 1, 2); - var ndarrayJs = Sk.ffi.remapToJs(self); - var buffer = ndarrayJs.buffer.map(function (x) { - return x; - }); - var shape = new Sk.builtin.tuplePy(ndarrayJs.shape.map(function (x) { - return new Sk.builtin.int_(x); - })); - return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, ndarrayJs.dtype, - new Sk.builtin.list(buffer)); - }); + return list; + }); - /** - Fill the array with a scalar value. - Parameters: value: scalar - All elements of a will be assigned this value - **/ - $loc.fill = new Sk.builtin.func(function (self, value) { - Sk.builtin.pyCheckArgs("fill", arguments, 2, 2); - var ndarrayJs = Sk.ffi.remapToJs(self); - var buffer = ndarrayJs.buffer.map(function (x) { - return x; - }); - var i; - for (i = 0; i < ndarrayJs.buffer.length; i++) { - if (ndarrayJs.dtype) { - ndarrayJs.buffer[i] = Sk.misceval.callsim(ndarrayJs.dtype, - value); - } - } - }); + $loc.reshape = new Sk.builtin.func(function (self, shape, order) { + Sk.builtin.pyCheckArgs("reshape", arguments, 2, 3); + var ndarrayJs = Sk.ffi.remapToJs(self); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, ndarrayJs.dtype, + new Sk.builtin.list(ndarrayJs.buffer)); + }); - $loc.__getitem__ = new Sk.builtin.func(function (self, index) { - Sk.builtin.pyCheckArgs("[]", arguments, 2, 2); - var ndarrayJs = Sk.ffi.remapToJs(self); - var _index; // current index - var _buffer; // buffer as python type - var buffer_internal; // buffer als js array - var _stride; // stride - var _shape; // shape as js - var i; - - // single index e.g. [3] - if (Sk.builtin.checkInt(index)) { - var offset = Sk.ffi.remapToJs(index); - - if (ndarrayJs.shape.length > 1) { - _stride = ndarrayJs.strides[0]; - buffer_internal = []; - _index = 0; - - for (i = offset * _stride, ubound = (offset + 1) * _stride; i < - ubound; i++) { - buffer_internal[_index++] = ndarrayJs.buffer[i]; - } - - _buffer = new Sk.builtin.list(buffer_internal); - _shape = new Sk.builtin.tuple(Array.prototype.slice.call( - ndarrayJs.shape, - 1) - .map(function (x) { - return new Sk.builtin.int_(x); + $loc.copy = new Sk.builtin.func(function (self, order) { + Sk.builtin.pyCheckArgs("copy", arguments, 1, 2); + var ndarrayJs = Sk.ffi.remapToJs(self); + var buffer = ndarrayJs.buffer.map(function (x) { + return x; + }); + var shape = new Sk.builtin.tuplePy(ndarrayJs.shape.map(function (x) { + return new Sk.builtin.int_(x); })); - return Sk.misceval.callsim(mod[CLASS_NDARRAY], _shape, - undefined, - _buffer); - } else { - if (offset >= 0 && offset < ndarrayJs.buffer.length) { - return ndarrayJs.buffer[offset]; - } else { - throw new Sk.builtin.IndexError("array index out of range"); - } - } - } else if (index instanceof Sk.builtin.tuple) { - // index like [1,3] - var keyJs = Sk.ffi.remapToJs(index); - return ndarrayJs.buffer[computeOffset(ndarrayJs.strides, keyJs)]; - } else if (index instanceof Sk.builtin.slice) { - // support for slices e.g. [1:4] - var indices = index.indices(); - var start = typeof indices[0] !== 'undefined' ? indices[0] : 0; - var stop = typeof indices[1] !== 'undefined' ? indices[1] : - ndarrayJs - .buffer.length; - stop = stop > ndarrayJs.buffer.length ? ndarrayJs.buffer.length : - stop; - var step = typeof indices[2] !== 'undefined' ? indices[2] : 1; - buffer_internal = []; - _index = 0; - if (step > 0) { - for (i = start; i < stop; i += step) { - buffer_internal[_index++] = ndarrayJs.buffer[i]; - } - } - _buffer = new Sk.builtin.list(buffer_internal); - _shape = new Sk.builtin.tuple([buffer_internal.length].map( - function ( - x) { - return new Sk.builtin.int_(x); - })); - return Sk.misceval.callsim(mod[CLASS_NDARRAY], _shape, undefined, - _buffer); - } else { - throw new Sk.builtin.ValueError('Index "' + index + - '" must be int, slice or tuple'); - } - }); - - $loc.__setitem__ = new Sk.builtin.func(function (self, index, value) { - var ndarrayJs = Sk.ffi.remapToJs(self); - Sk.builtin.pyCheckArgs("[]", arguments, 3, 3); - if (index instanceof Sk.builtin.int_) { - var _offset = Sk.ffi.remapToJs(index); - if (ndarrayJs.shape.length > 1) { - var _value = Sk.ffi.remapToJs(value); - var _stride = ndarrayJs.strides[0]; - var _index = 0; - - var _ubound = (_offset + 1) * _stride; - var i; - for (i = _offset * _stride; i < _ubound; i++) { - ndarrayJs.buffer[i] = _value.buffer[_index++]; - } - } else { - if (_offset >= 0 && _offset < ndarrayJs.buffer.length) { - ndarrayJs.buffer[_offset] = value; - } else { - throw new Sk.builtin.IndexError("array index out of range"); - } - } - } else if (index instanceof Sk.builtin.tuple) { - _key = Sk.ffi.remapToJs(index); - ndarrayJs.buffer[computeOffset(ndarrayJs.strides, _key)] = value; - } else { - throw new Sk.builtin.TypeError( - 'argument "index" must be int or tuple'); - } - }); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, ndarrayJs.dtype, + new Sk.builtin.list(buffer)); + }); - $loc.__len__ = new Sk.builtin.func(function (self) { - var ndarrayJs = Sk.ffi.remapToJs(self); - return new Sk.builtin.int_(ndarrayJs.shape[0]); - }); + /** + Fill the array with a scalar value. + Parameters: value: scalar + All elements of a will be assigned this value + **/ + $loc.fill = new Sk.builtin.func(function (self, value) { + Sk.builtin.pyCheckArgs("fill", arguments, 2, 2); + var ndarrayJs = Sk.ffi.remapToJs(self); + var buffer = ndarrayJs.buffer.map(function (x) { + return x; + }); + var i; + for (i = 0; i < ndarrayJs.buffer.length; i++) { + if (ndarrayJs.dtype) { + ndarrayJs.buffer[i] = Sk.misceval.callsim(ndarrayJs.dtype, + value); + } + } + }); - $loc.__iter__ = new Sk.builtin.func(function (self) { - var ndarrayJs = Sk.ffi.remapToJs(self); - var ret = { - tp$iter: function () { - return ret; - }, - $obj: ndarrayJs, - $index: 0, - tp$iternext: function () { - if (ret.$index >= ret.$obj.buffer.length) return undefined; - return ret.$obj.buffer[ret.$index++]; - } - }; - return ret; - }); + $loc.__getitem__ = new Sk.builtin.func(function (self, index) { + Sk.builtin.pyCheckArgs("[]", arguments, 2, 2); + var ndarrayJs = Sk.ffi.remapToJs(self); + var _index; // current index + var _buffer; // buffer as python type + var buffer_internal; // buffer als js array + var _stride; // stride + var _shape; // shape as js + var i; + + // single index e.g. [3] + if (Sk.builtin.checkInt(index)) { + var offset = Sk.ffi.remapToJs(index); + + if (ndarrayJs.shape.length > 1) { + _stride = ndarrayJs.strides[0]; + buffer_internal = []; + _index = 0; + + for (i = offset * _stride, ubound = (offset + 1) * _stride; i < + ubound; i++) { + buffer_internal[_index++] = ndarrayJs.buffer[i]; + } + + _buffer = new Sk.builtin.list(buffer_internal); + _shape = new Sk.builtin.tuple(Array.prototype.slice.call( + ndarrayJs.shape, + 1) + .map(function (x) { + return new Sk.builtin.int_(x); + })); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], _shape, + undefined, + _buffer); + } else { + if (offset >= 0 && offset < ndarrayJs.buffer.length) { + return ndarrayJs.buffer[offset]; + } else { + throw new Sk.builtin.IndexError("array index out of range"); + } + } + } else if (index instanceof Sk.builtin.tuple) { + // index like [1,3] + var keyJs = Sk.ffi.remapToJs(index); + return ndarrayJs.buffer[computeOffset(ndarrayJs.strides, keyJs)]; + } else if (index instanceof Sk.builtin.slice) { + // support for slices e.g. [1:4] + var indices = index.indices(); + var start = typeof indices[0] !== "undefined" ? indices[0] : 0; + var stop = typeof indices[1] !== "undefined" ? indices[1] : + ndarrayJs + .buffer.length; + stop = stop > ndarrayJs.buffer.length ? ndarrayJs.buffer.length : + stop; + var step = typeof indices[2] !== "undefined" ? indices[2] : 1; + buffer_internal = []; + _index = 0; + if (step > 0) { + for (i = start; i < stop; i += step) { + buffer_internal[_index++] = ndarrayJs.buffer[i]; + } + } + _buffer = new Sk.builtin.list(buffer_internal); + _shape = new Sk.builtin.tuple([buffer_internal.length].map( + function ( + x) { + return new Sk.builtin.int_(x); + })); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], _shape, undefined, + _buffer); + } else { + throw new Sk.builtin.ValueError("Index \"" + index + + "\" must be int, slice or tuple"); + } + }); - $loc.__str__ = new Sk.builtin.func(function (self) { - var ndarrayJs = remapToJs_shallow(self, false); - return new Sk.builtin.str(stringify(ndarrayJs.buffer, - ndarrayJs.shape, ndarrayJs.dtype)); - }); + $loc.__setitem__ = new Sk.builtin.func(function (self, index, value) { + var ndarrayJs = Sk.ffi.remapToJs(self); + Sk.builtin.pyCheckArgs("[]", arguments, 3, 3); + if (index instanceof Sk.builtin.int_) { + var _offset = Sk.ffi.remapToJs(index); + if (ndarrayJs.shape.length > 1) { + var _value = Sk.ffi.remapToJs(value); + var _stride = ndarrayJs.strides[0]; + var _index = 0; + + var _ubound = (_offset + 1) * _stride; + var i; + for (i = _offset * _stride; i < _ubound; i++) { + ndarrayJs.buffer[i] = _value.buffer[_index++]; + } + } else { + if (_offset >= 0 && _offset < ndarrayJs.buffer.length) { + ndarrayJs.buffer[_offset] = value; + } else { + throw new Sk.builtin.IndexError("array index out of range"); + } + } + } else if (index instanceof Sk.builtin.tuple) { + _key = Sk.ffi.remapToJs(index); + ndarrayJs.buffer[computeOffset(ndarrayJs.strides, _key)] = value; + } else { + throw new Sk.builtin.TypeError( + "argument \"index\" must be int or tuple"); + } + }); - $loc.__repr__ = new Sk.builtin.func(function (self) { - var ndarrayJs = Sk.ffi.remapToJs(self); - return new Sk.builtin.str("array(" + stringify(ndarrayJs.buffer, - ndarrayJs.shape, ndarrayJs.dtype) + ")"); - }); + $loc.__len__ = new Sk.builtin.func(function (self) { + var ndarrayJs = Sk.ffi.remapToJs(self); + return new Sk.builtin.int_(ndarrayJs.shape[0]); + }); - /** - Creates left hand side operations for given binary operator - **/ - function makeNumericBinaryOpLhs(operation) { - return function (self, other) { - var lhs; - var rhs; - var buffer; // external - var _buffer; // internal use - var shape; // new shape of returned ndarray - var i; + $loc.__iter__ = new Sk.builtin.func(function (self) { + var ndarrayJs = Sk.ffi.remapToJs(self); + var ret = { + tp$iter: function () { + return ret; + }, + $obj: ndarrayJs, + $index: 0, + tp$iternext: function () { + if (ret.$index >= ret.$obj.buffer.length) { + return undefined; + } + return ret.$obj.buffer[ret.$index++]; + } + }; + return ret; + }); + $loc.__str__ = new Sk.builtin.func(function (self) { + var ndarrayJs = remapToJs_shallow(self, false); + return new Sk.builtin.str(stringify(ndarrayJs.buffer, + ndarrayJs.shape, ndarrayJs.dtype)); + }); - var ndarrayJs = Sk.ffi.remapToJs(self); + $loc.__repr__ = new Sk.builtin.func(function (self) { + var ndarrayJs = Sk.ffi.remapToJs(self); + return new Sk.builtin.str("array(" + stringify(ndarrayJs.buffer, + ndarrayJs.shape, ndarrayJs.dtype) + ")"); + }); - if (Sk.abstr.typeName(other) === CLASS_NDARRAY) { - lhs = ndarrayJs.buffer; - rhs = Sk.ffi.remapToJs(other) - .buffer; - _buffer = []; - for (i = 0, len = lhs.length; i < len; i++) { - //_buffer[i] = operation(lhs[i], rhs[i]); - _buffer[i] = Sk.abstr.binary_op_(lhs[i], rhs[i], operation); - } - } else { - lhs = ndarrayJs.buffer; - _buffer = []; - for (i = 0, len = lhs.length; i < len; i++) { - _buffer[i] = Sk.abstr.numberBinOp(lhs[i], other, operation); - } + /** + Creates left hand side operations for given binary operator + **/ + function makeNumericBinaryOpLhs(operation) { + return function (self, other) { + var lhs; + var rhs; + var buffer; // external + var _buffer; // internal use + var shape; // new shape of returned ndarray + var i; + + + var ndarrayJs = Sk.ffi.remapToJs(self); + + if (Sk.abstr.typeName(other) === CLASS_NDARRAY) { + lhs = ndarrayJs.buffer; + rhs = Sk.ffi.remapToJs(other) + .buffer; + _buffer = []; + for (i = 0, len = lhs.length; i < len; i++) { + //_buffer[i] = operation(lhs[i], rhs[i]); + _buffer[i] = Sk.abstr.binary_op_(lhs[i], rhs[i], operation); + } + } else { + lhs = ndarrayJs.buffer; + _buffer = []; + for (i = 0, len = lhs.length; i < len; i++) { + _buffer[i] = Sk.abstr.numberBinOp(lhs[i], other, operation); + } + } + + // create return ndarray + shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); + buffer = new Sk.builtin.list(_buffer); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, + buffer); + }; } - // create return ndarray - shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { - return new Sk.builtin.int_(x); - })); - buffer = new Sk.builtin.list(_buffer); - return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, - buffer); - }; - } + function makeNumericBinaryOpRhs(operation) { + return function (self, other) { + var ndarrayJs = Sk.ffi.remapToJs(self); + var rhsBuffer = ndarrayJs.buffer; + var _buffer = []; + for (var i = 0, len = rhsBuffer.length; i < len; i++) { + _buffer[i] = Sk.abstr.numberBinOp(other, rhsBuffer[i], operation); + } + var shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); + buffer = new Sk.builtin.list(_buffer); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, buffer); + }; + } - function makeNumericBinaryOpRhs(operation) { - return function (self, other) { - var ndarrayJs = Sk.ffi.remapToJs(self); - var rhsBuffer = ndarrayJs.buffer; - var _buffer = []; - for (var i = 0, len = rhsBuffer.length; i < len; i++) { - _buffer[i] = Sk.abstr.numberBinOp(other, rhsBuffer[i], operation); + /* + Applies given operation on each element of the ndarray. + */ + function makeUnaryOp(operation) { + return function (self) { + var ndarrayJs = Sk.ffi.remapToJs(self); + var _buffer = ndarrayJs.buffer.map(function (value) { + return Sk.abstr.numberUnaryOp(Sk.ffi.remapToPy(value), operation); + }); + var shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); + buffer = new Sk.builtin.list(_buffer); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, buffer); + }; } - var shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { - return new Sk.builtin.int_(x); - })); - buffer = new Sk.builtin.list(_buffer); - return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, buffer); - }; - } - /* - Applies given operation on each element of the ndarray. - */ - function makeUnaryOp(operation) { - return function (self) { - var ndarrayJs = Sk.ffi.remapToJs(self); - var _buffer = ndarrayJs.buffer.map(function (value) { - return Sk.abstr.numberUnaryOp(Sk.ffi.remapToPy(value), operation); - }); - var shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { - return new Sk.builtin.int_(x); - })); - buffer = new Sk.builtin.list(_buffer); - return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, buffer); - }; - } + $loc.__add__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Add")); + $loc.__radd__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Add")); + + $loc.__sub__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Sub")); + $loc.__rsub__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Sub")); - $loc.__add__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Add")); - $loc.__radd__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Add")); + $loc.__mul__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Mult")); + $loc.__rmul__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Mult")); - $loc.__sub__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Sub")); - $loc.__rsub__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Sub")); + $loc.__div__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Div")); + $loc.__rdiv__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Div")); - $loc.__mul__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Mult")); - $loc.__rmul__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Mult")); + $loc.__mod__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Mod")); + $loc.__rmod__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Mod")); - $loc.__div__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Div")); - $loc.__rdiv__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Div")); + $loc.__xor__ = new Sk.builtin.func(makeNumericBinaryOpLhs("BitXor")); + $loc.__rxor__ = new Sk.builtin.func(makeNumericBinaryOpRhs("BitXor")); - $loc.__mod__ = new Sk.builtin.func(makeNumericBinaryOpLhs("Mod")); - $loc.__rmod__ = new Sk.builtin.func(makeNumericBinaryOpRhs("Mod")); + $loc.__lshift__ = new Sk.builtin.func(makeNumericBinaryOpLhs("LShift")); + $loc.__rlshift__ = new Sk.builtin.func(makeNumericBinaryOpRhs("LShift")); - $loc.__xor__ = new Sk.builtin.func(makeNumericBinaryOpLhs("BitXor")); - $loc.__rxor__ = new Sk.builtin.func(makeNumericBinaryOpRhs("BitXor")); + $loc.__rshift__ = new Sk.builtin.func(makeNumericBinaryOpLhs("RShift")); + $loc.__rrshift__ = new Sk.builtin.func(makeNumericBinaryOpRhs("RShift")); - $loc.__lshift__ = new Sk.builtin.func(makeNumericBinaryOpLhs("LShift")); - $loc.__rlshift__ = new Sk.builtin.func(makeNumericBinaryOpRhs("LShift")); + $loc.__pos__ = new Sk.builtin.func(makeUnaryOp("UAdd")); + $loc.__neg__ = new Sk.builtin.func(makeUnaryOp("USub")); + + /** + Simple pow implementation that faciliates the pow builtin + **/ + $loc.__pow__ = new Sk.builtin.func(function (self, other) { + Sk.builtin.pyCheckArgs("__pow__", arguments, 2, 2); + var ndarrayJs = Sk.ffi.remapToJs(self); + var _buffer = ndarrayJs.buffer.map(function (value) { + return Sk.builtin.pow(Sk.ffi.remapToPy(value), other); + }); + var shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); + buffer = new Sk.builtin.list(_buffer); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, buffer); + }); - $loc.__rshift__ = new Sk.builtin.func(makeNumericBinaryOpLhs("RShift")); - $loc.__rrshift__ = new Sk.builtin.func(makeNumericBinaryOpRhs("RShift")); + // end of ndarray_f + }; - $loc.__pos__ = new Sk.builtin.func(makeUnaryOp("UAdd")); - $loc.__neg__ = new Sk.builtin.func(makeUnaryOp("USub")); + mod[CLASS_NDARRAY] = Sk.misceval.buildClass(mod, ndarray_f, + CLASS_NDARRAY, []); /** - Simple pow implementation that faciliates the pow builtin - **/ - $loc.__pow__ = new Sk.builtin.func(function (self, other) { - Sk.builtin.pyCheckArgs("__pow__", arguments, 2, 2); - var ndarrayJs = Sk.ffi.remapToJs(self); - var _buffer = ndarrayJs.buffer.map(function (value) { - return Sk.builtin.pow(Sk.ffi.remapToPy(value), other); - }); - var shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { - return new Sk.builtin.int_(x); - })); - buffer = new Sk.builtin.list(_buffer); - return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, buffer); - }); + Trigonometric functions, all element wise + **/ + mod.pi = Sk.builtin.assk$(np.math ? np.math.PI : Math.PI, Sk.builtin.nmber.float$); + mod.e = Sk.builtin.assk$(np.math ? np.math.E : Math.E, Sk.builtin.nmber.float$); - // end of ndarray_f - }; - - mod[CLASS_NDARRAY] = Sk.misceval.buildClass(mod, ndarray_f, - CLASS_NDARRAY, []); - - /** - Trigonometric functions, all element wise - **/ - mod.pi = Sk.builtin.assk$(np.math ? np.math.PI : Math.PI, Sk.builtin.nmber.float$); - mod.e = Sk.builtin.assk$(np.math ? np.math.E : Math.E, Sk.builtin.nmber.float$); - /** - Trigonometric sine, element-wise. - **/ - - function callTrigonometricFunc(x, op) { - var res; - var num; - if (x instanceof Sk.builtin.list || x instanceof Sk.builtin.tuple) { - x = Sk.misceval.callsim(mod.array, x); - } + /** + Trigonometric sine, element-wise. + **/ + + function callTrigonometricFunc(x, op) { + var res; + var num; + if (x instanceof Sk.builtin.list || x instanceof Sk.builtin.tuple) { + x = Sk.misceval.callsim(mod.array, x); + } - if (Sk.abstr.typeName(x) === CLASS_NDARRAY) { - var ndarrayJs = Sk.ffi.remapToJs(x); - - var _buffer = ndarrayJs.buffer.map(function (value) { - num = Sk.builtin.asnum$(value); - res = op.call(null, num); - return new Sk.builtin.nmber(res, Sk.builtin.nmber - .float$); - }); - - var shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { - return new Sk.builtin.int_(x); - })); - - buffer = new Sk.builtin.list(_buffer); - return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, buffer); - } else if (Sk.builtin.checkNumber(x)) { - num = Sk.builtin.asnum$(x); - res = op.call(null, num); - return new Sk.builtin.nmber(res, Sk.builtin.nmber - .float$); - } + if (Sk.abstr.typeName(x) === CLASS_NDARRAY) { + var ndarrayJs = Sk.ffi.remapToJs(x); - throw new Sk.builtin.TypeError('Unsupported argument type for "x"'); - } - - // Sine, element-wise. - var sin_f = function (x, out) { - Sk.builtin.pyCheckArgs("sin", arguments, 1, 2); - return callTrigonometricFunc(x, np.math ? np.math.sin : Math.sin); - }; - sin_f.co_varnames = ['x', 'out']; - sin_f.$defaults = [0, new Sk.builtin.list([])]; - mod.sin = new Sk.builtin.func(sin_f); - - // Hyperbolic sine, element-wise. - var sinh_f = function (x, out) { - Sk.builtin.pyCheckArgs("sinh", arguments, 1, 2); - if (!np.math) throw new Sk.builtin.OperationError("sinh requires mathjs"); - return callTrigonometricFunc(x, np.math.sinh); - }; - sinh_f.co_varnames = ['x', 'out']; - sinh_f.$defaults = [0, new Sk.builtin.list([])]; - mod.sinh = new Sk.builtin.func(sinh_f); - - // Inverse sine, element-wise. - var arcsin_f = function (x, out) { - Sk.builtin.pyCheckArgs("arcsin", arguments, 1, 2); - return callTrigonometricFunc(x, np.math ? np.math.asin : Math.asin); - }; - arcsin_f.co_varnames = ['x', 'out']; - arcsin_f.$defaults = [0, new Sk.builtin.list([])]; - mod.arcsin = new Sk.builtin.func(arcsin_f); - - // Cosine, element-wise. - var cos_f = function (x, out) { - Sk.builtin.pyCheckArgs("cos", arguments, 1, 2); - return callTrigonometricFunc(x, np.math ? np.math.cos : Math.cos); - }; - cos_f.co_varnames = ['x', 'out']; - cos_f.$defaults = [0, new Sk.builtin.list([])]; - mod.cos = new Sk.builtin.func(cos_f); - - // Hyperbolic cosine, element-wise. - var cosh_f = function (x, out) { - Sk.builtin.pyCheckArgs("cosh", arguments, 1, 2); - if (!np.math) throw new Sk.builtin.OperationError("cosh requires mathjs"); - return callTrigonometricFunc(x, np.math.cosh); - }; - cosh_f.co_varnames = ['x', 'out']; - cosh_f.$defaults = [0, new Sk.builtin.list([])]; - mod.cosh = new Sk.builtin.func(cosh_f); - - // Inverse cosine, element-wise. - var arccos_f = function (x, out) { - Sk.builtin.pyCheckArgs("arccos", arguments, 1, 2); - return callTrigonometricFunc(x, np.math ? np.math.acos : Math.acos); - }; - arccos_f.co_varnames = ['x', 'out']; - arccos_f.$defaults = [0, new Sk.builtin.list([])]; - mod.arccos = new Sk.builtin.func(arccos_f); - - // Inverse tangens, element-wise. - var arctan_f = function (x, out) { - Sk.builtin.pyCheckArgs("arctan", arguments, 1, 2); - return callTrigonometricFunc(x, np.math ? np.math.atan : Math.atan); - }; - arctan_f.co_varnames = ['x', 'out']; - arctan_f.$defaults = [0, new Sk.builtin.list([])]; - mod.arctan = new Sk.builtin.func(arctan_f); - - // Tangens, element-wise. - var tan_f = function (x, out) { - Sk.builtin.pyCheckArgs("tan", arguments, 1, 2); - return callTrigonometricFunc(x, np.math ? np.math.tan : Math.tan); - }; - tan_f.co_varnames = ['x', 'out']; - tan_f.$defaults = [0, new Sk.builtin.list([])]; - mod.tan = new Sk.builtin.func(tan_f); - - // Hyperbolic cosine, element-wise. - var tanh_f = function (x, out) { - Sk.builtin.pyCheckArgs("tanh", arguments, 1, 2); - if (!np.math) throw new Sk.builtin.OperationError("tanh requires mathjs"); - return callTrigonometricFunc(x, np.math.tanh); - }; - tanh_f.co_varnames = ['x', 'out']; - tanh_f.$defaults = [0, new Sk.builtin.list([])]; - mod.tanh = new Sk.builtin.func(tanh_f); - - /* Simple reimplementation of the linspace function - * http://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html - */ - var linspace_f = function (start, stop, num, endpoint, retstep) { - Sk.builtin.pyCheckArgs("linspace", arguments, 3, 5); - Sk.builtin.pyCheckType("start", "number", Sk.builtin.checkNumber( - start)); - Sk.builtin.pyCheckType("stop", "number", Sk.builtin.checkNumber( - stop)); - if (num === undefined) { - num = 50; - } - var num_num = Sk.builtin.asnum$(num); - var endpoint_bool; + var _buffer = ndarrayJs.buffer.map(function (value) { + num = Sk.builtin.asnum$(value); + res = op.call(null, num); + return new Sk.builtin.nmber(res, Sk.builtin.nmber + .float$); + }); - if (endpoint === undefined) { - endpoint_bool = true; - } else if (endpoint.constructor === Sk.builtin.bool) { - endpoint_bool = endpoint.v; - } + var shape = new Sk.builtin.tuple(ndarrayJs.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); + + buffer = new Sk.builtin.list(_buffer); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, undefined, buffer); + } else if (Sk.builtin.checkNumber(x)) { + num = Sk.builtin.asnum$(x); + res = op.call(null, num); + return new Sk.builtin.nmber(res, Sk.builtin.nmber + .float$); + } - var retstep_bool; - if (retstep === undefined) { - retstep_bool = false; - } else if (retstep.constructor === Sk.builtin.bool) { - retstep_bool = retstep.v; + throw new Sk.builtin.TypeError("Unsupported argument type for \"x\""); } - var samples; - var step; + // Sine, element-wise. + var sin_f = function (x, out) { + Sk.builtin.pyCheckArgs("sin", arguments, 1, 2); + return callTrigonometricFunc(x, np.math ? np.math.sin : Math.sin); + }; + sin_f.co_varnames = ["x", "out"]; + sin_f.$defaults = [0, new Sk.builtin.list([])]; + mod.sin = new Sk.builtin.func(sin_f); + + // Hyperbolic sine, element-wise. + var sinh_f = function (x, out) { + Sk.builtin.pyCheckArgs("sinh", arguments, 1, 2); + if (!np.math) { + throw new Sk.builtin.OperationError("sinh requires mathjs"); + } + return callTrigonometricFunc(x, np.math.sinh); + }; + sinh_f.co_varnames = ["x", "out"]; + sinh_f.$defaults = [0, new Sk.builtin.list([])]; + mod.sinh = new Sk.builtin.func(sinh_f); + + // Inverse sine, element-wise. + var arcsin_f = function (x, out) { + Sk.builtin.pyCheckArgs("arcsin", arguments, 1, 2); + return callTrigonometricFunc(x, np.math ? np.math.asin : Math.asin); + }; + arcsin_f.co_varnames = ["x", "out"]; + arcsin_f.$defaults = [0, new Sk.builtin.list([])]; + mod.arcsin = new Sk.builtin.func(arcsin_f); + + // Cosine, element-wise. + var cos_f = function (x, out) { + Sk.builtin.pyCheckArgs("cos", arguments, 1, 2); + return callTrigonometricFunc(x, np.math ? np.math.cos : Math.cos); + }; + cos_f.co_varnames = ["x", "out"]; + cos_f.$defaults = [0, new Sk.builtin.list([])]; + mod.cos = new Sk.builtin.func(cos_f); + + // Hyperbolic cosine, element-wise. + var cosh_f = function (x, out) { + Sk.builtin.pyCheckArgs("cosh", arguments, 1, 2); + if (!np.math) { + throw new Sk.builtin.OperationError("cosh requires mathjs"); + } + return callTrigonometricFunc(x, np.math.cosh); + }; + cosh_f.co_varnames = ["x", "out"]; + cosh_f.$defaults = [0, new Sk.builtin.list([])]; + mod.cosh = new Sk.builtin.func(cosh_f); + + // Inverse cosine, element-wise. + var arccos_f = function (x, out) { + Sk.builtin.pyCheckArgs("arccos", arguments, 1, 2); + return callTrigonometricFunc(x, np.math ? np.math.acos : Math.acos); + }; + arccos_f.co_varnames = ["x", "out"]; + arccos_f.$defaults = [0, new Sk.builtin.list([])]; + mod.arccos = new Sk.builtin.func(arccos_f); + + // Inverse tangens, element-wise. + var arctan_f = function (x, out) { + Sk.builtin.pyCheckArgs("arctan", arguments, 1, 2); + return callTrigonometricFunc(x, np.math ? np.math.atan : Math.atan); + }; + arctan_f.co_varnames = ["x", "out"]; + arctan_f.$defaults = [0, new Sk.builtin.list([])]; + mod.arctan = new Sk.builtin.func(arctan_f); + + // Tangens, element-wise. + var tan_f = function (x, out) { + Sk.builtin.pyCheckArgs("tan", arguments, 1, 2); + return callTrigonometricFunc(x, np.math ? np.math.tan : Math.tan); + }; + tan_f.co_varnames = ["x", "out"]; + tan_f.$defaults = [0, new Sk.builtin.list([])]; + mod.tan = new Sk.builtin.func(tan_f); + + // Hyperbolic cosine, element-wise. + var tanh_f = function (x, out) { + Sk.builtin.pyCheckArgs("tanh", arguments, 1, 2); + if (!np.math) { + throw new Sk.builtin.OperationError("tanh requires mathjs"); + } + return callTrigonometricFunc(x, np.math.tanh); + }; + tanh_f.co_varnames = ["x", "out"]; + tanh_f.$defaults = [0, new Sk.builtin.list([])]; + mod.tanh = new Sk.builtin.func(tanh_f); + + /* Simple reimplementation of the linspace function + * http://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html + */ + var linspace_f = function (start, stop, num, endpoint, retstep) { + Sk.builtin.pyCheckArgs("linspace", arguments, 3, 5); + Sk.builtin.pyCheckType("start", "number", Sk.builtin.checkNumber( + start)); + Sk.builtin.pyCheckType("stop", "number", Sk.builtin.checkNumber( + stop)); + if (num === undefined) { + num = 50; + } + var num_num = Sk.builtin.asnum$(num); + var endpoint_bool; - start_num = Sk.builtin.asnum$(start) * 1.0; - stop_num = Sk.builtin.asnum$(stop) * 1.0; + if (endpoint === undefined) { + endpoint_bool = true; + } else if (endpoint.constructor === Sk.builtin.bool) { + endpoint_bool = endpoint.v; + } - if (num_num <= 0) { - samples = []; - } else { + var retstep_bool; + if (retstep === undefined) { + retstep_bool = false; + } else if (retstep.constructor === Sk.builtin.bool) { + retstep_bool = retstep.v; + } - var samples_array; - if (endpoint_bool) { - if (num_num == 1) { - samples = [start_num]; + var samples; + var step; + + start_num = Sk.builtin.asnum$(start) * 1.0; + stop_num = Sk.builtin.asnum$(stop) * 1.0; + + if (num_num <= 0) { + samples = []; } else { - step = (stop_num - start_num) / (num_num - 1); - samples_array = np.arange(0, num_num); - samples = samples_array.map(function (v) { - return v * step + start_num; - }); - samples[samples.length - 1] = stop_num; - } - } else { - step = (stop_num - start_num) / num_num; - samples_array = np.arange(0, num_num); - samples = samples_array.map(function (v) { - return v * step + start_num; - }); - } - } - //return as ndarray! dtype:float - var dtype = Sk.builtin.float_; - for (i = 0; i < samples.length; i++) { - samples[i] = Sk.misceval.callsim(dtype, samples[i]); - } + var samples_array; + if (endpoint_bool) { + if (num_num == 1) { + samples = [start_num]; + } else { + step = (stop_num - start_num) / (num_num - 1); + samples_array = np.arange(0, num_num); + samples = samples_array.map(function (v) { + return v * step + start_num; + }); + samples[samples.length - 1] = stop_num; + } + } else { + step = (stop_num - start_num) / num_num; + samples_array = np.arange(0, num_num); + samples = samples_array.map(function (v) { + return v * step + start_num; + }); + } + } - var buffer = Sk.builtin.list(samples); - var shape = new Sk.builtin.tuple([samples.length]); - var ndarray = Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, dtype, - buffer); - - if (retstep_bool === true) - return new Sk.builtin.tuple([ndarray, step]); - else - return ndarray; - }; - - // this should allow for named parameters - linspace_f.co_varnames = ['start', 'stop', 'num', 'endpoint', - 'retstep' - ]; - linspace_f.$defaults = [0, 0, 50, true, false]; - mod.linspace = - new Sk.builtin.func(linspace_f); - - /* Simple reimplementation of the arange function - * http://docs.scipy.org/doc/numpy/reference/generated/numpy.arange.html#numpy.arange - */ - var arange_f = function (start, stop, step, dtype) { - Sk.builtin.pyCheckArgs("arange", arguments, 1, 4); - Sk.builtin.pyCheckType("start", "number", Sk.builtin.checkNumber( - start)); - var start_num; - var stop_num; - var step_num; - - if (stop === undefined && step === undefined) { - start_num = Sk.builtin.asnum$(0); - stop_num = Sk.builtin.asnum$(start); - step_num = Sk.builtin.asnum$(1); - } else if (step === undefined) { - start_num = Sk.builtin.asnum$(start); - stop_num = Sk.builtin.asnum$(stop); - step_num = Sk.builtin.asnum$(1); - } else { - start_num = Sk.builtin.asnum$(start); - stop_num = Sk.builtin.asnum$(stop); - step_num = Sk.builtin.asnum$(step); - } + //return as ndarray! dtype:float + var dtype = Sk.builtin.float_; + for (i = 0; i < samples.length; i++) { + samples[i] = Sk.misceval.callsim(dtype, samples[i]); + } - // set to float - if (!dtype || dtype == Sk.builtin.none.none$) { - if (Sk.builtin.checkInt(start)) - dtype = Sk.builtin.int_; - else - dtype = Sk.builtin.float_; - } + var buffer = Sk.builtin.list(samples); + var shape = new Sk.builtin.tuple([samples.length]); + var ndarray = Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, dtype, + buffer); - // return ndarray - var arange_buffer = np.arange(start_num, stop_num, step_num); - // apply dtype casting function, if it has been provided - if (dtype && Sk.builtin.checkClass(dtype)) { - for (i = 0; i < arange_buffer.length; i++) { - arange_buffer[i] = Sk.misceval.callsim(dtype, arange_buffer[i]); - } - } + if (retstep_bool === true) { + return new Sk.builtin.tuple([ndarray, step]); + } else { + return ndarray; + } + }; + + // this should allow for named parameters + linspace_f.co_varnames = ["start", "stop", "num", "endpoint", + "retstep" + ]; + linspace_f.$defaults = [0, 0, 50, true, false]; + mod.linspace = + new Sk.builtin.func(linspace_f); + + /* Simple reimplementation of the arange function + * http://docs.scipy.org/doc/numpy/reference/generated/numpy.arange.html#numpy.arange + */ + var arange_f = function (start, stop, step, dtype) { + Sk.builtin.pyCheckArgs("arange", arguments, 1, 4); + Sk.builtin.pyCheckType("start", "number", Sk.builtin.checkNumber( + start)); + var start_num; + var stop_num; + var step_num; + + if (stop === undefined && step === undefined) { + start_num = Sk.builtin.asnum$(0); + stop_num = Sk.builtin.asnum$(start); + step_num = Sk.builtin.asnum$(1); + } else if (step === undefined) { + start_num = Sk.builtin.asnum$(start); + stop_num = Sk.builtin.asnum$(stop); + step_num = Sk.builtin.asnum$(1); + } else { + start_num = Sk.builtin.asnum$(start); + stop_num = Sk.builtin.asnum$(stop); + step_num = Sk.builtin.asnum$(step); + } - buffer = Sk.builtin.list(arange_buffer); - var shape = new Sk.builtin.tuple([arange_buffer.length]); - return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, dtype, - buffer); - }; + // set to float + if (!dtype || dtype == Sk.builtin.none.none$) { + if (Sk.builtin.checkInt(start)) { + dtype = Sk.builtin.int_; + } else { + dtype = Sk.builtin.float_; + } + } - arange_f.co_varnames = ['start', 'stop', 'step', 'dtype']; - arange_f - .$defaults = [0, 1, 1, Sk.builtin.none.none$]; - mod.arange = new Sk.builtin - .func(arange_f); + // return ndarray + var arange_buffer = np.arange(start_num, stop_num, step_num); + // apply dtype casting function, if it has been provided + if (dtype && Sk.builtin.checkClass(dtype)) { + for (i = 0; i < arange_buffer.length; i++) { + arange_buffer[i] = Sk.misceval.callsim(dtype, arange_buffer[i]); + } + } - /* implementation for numpy.array - ------------------------------------------------------------------------------------------------ - http://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html#numpy.array + buffer = Sk.builtin.list(arange_buffer); + var shape = new Sk.builtin.tuple([arange_buffer.length]); + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, dtype, + buffer); + }; + + arange_f.co_varnames = ["start", "stop", "step", "dtype"]; + arange_f + .$defaults = [0, 1, 1, Sk.builtin.none.none$]; + mod.arange = new Sk.builtin + .func(arange_f); + + /* implementation for numpy.array + ------------------------------------------------------------------------------------------------ + http://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html#numpy.array + + object : array_like + An array, any object exposing the array interface, an object whose __array__ method returns an array, or any (nested) sequence. + + dtype : data-type, optional + The desired data-type for the array. If not given, then the type will be determined as the minimum type required to hold the objects in the sequence. This argument can only be used to ‘upcast’ the array. For downcasting, use the .astype(t) method. + + copy : bool, optional + If true (default), then the object is copied. Otherwise, a copy will only be made if __array__ returns a copy, if obj is a nested sequence, or if a copy is needed to satisfy any of the other requirements (dtype, order, etc.). + + order : {‘C’, ‘F’, ‘A’}, optional + Specify the order of the array. If order is ‘C’ (default), then the array will be in C-contiguous order (last-index varies the fastest). If order is ‘F’, then the returned array will be in Fortran-contiguous order (first-index varies the fastest). If order is ‘A’, then the returned array may be in any order (either C-, Fortran-contiguous, or even discontiguous). + + subok : bool, optional + If True, then sub-classes will be passed-through, otherwise the returned array will be forced to be a base-class array (default). + + ndmin : int, optional + Specifies the minimum number of dimensions that the resulting array should have. Ones will be pre-pended to the shape as needed to meet this requirement. + + Returns : + out : ndarray + An array object satisfying the specified requirements + */ + // https://github.com/geometryzen/davinci-dev/blob/master/src/stdlib/numpy.js + // https://github.com/geometryzen/davinci-dev/blob/master/src/ffh.js + // http://docs.scipy.org/doc/numpy/reference/arrays.html + var array_f = function (object, dtype, copy, order, subok, ndmin) { + Sk.builtin.pyCheckArgs("array", arguments, 1, 6); + + if (object === undefined) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(object) + + "' object is undefined"); + } - object : array_like - An array, any object exposing the array interface, an object whose __array__ method returns an array, or any (nested) sequence. + var elements = []; + var state = {}; + state.level = 0; + state.shape = []; - dtype : data-type, optional - The desired data-type for the array. If not given, then the type will be determined as the minimum type required to hold the objects in the sequence. This argument can only be used to ‘upcast’ the array. For downcasting, use the .astype(t) method. + unpack(object, elements, state); - copy : bool, optional - If true (default), then the object is copied. Otherwise, a copy will only be made if __array__ returns a copy, if obj is a nested sequence, or if a copy is needed to satisfy any of the other requirements (dtype, order, etc.). + var i; + // apply dtype casting function, if it has been provided + if (dtype && Sk.builtin.checkClass(dtype)) { + for (i = 0; i < elements.length; i++) { + elements[i] = Sk.misceval.callsim(dtype, elements[i]); + } + } else { + // check elements and find first usable type + // should be refactored + for (i = 0; i < elements.length; i++) { + if (elements[i] && isNaN(elements[i])) { + dtype = Sk.builtin.float_; + break; + } else if (typeof elements[i] == "string") { + dtype = Sk.builtin.str; + } else { + dtype = Sk.builtin.float_; + } + } + } - order : {‘C’, ‘F’, ‘A’}, optional - Specify the order of the array. If order is ‘C’ (default), then the array will be in C-contiguous order (last-index varies the fastest). If order is ‘F’, then the returned array will be in Fortran-contiguous order (first-index varies the fastest). If order is ‘A’, then the returned array may be in any order (either C-, Fortran-contiguous, or even discontiguous). + // check for ndmin param + if (ndmin) { + if (Sk.builtin.checkNumber(ndmin)) { + var _ndmin = Sk.builtin.asnum$(ndmin); + if (_ndmin >= 0 && _ndmin > state.shape.length) { + var _ndmin_array = []; + for (i = 0; i < _ndmin - state.shape.length; i++) { + _ndmin_array.push(1); + } + state.shape = _ndmin_array.concat(state.shape); + } + } else { + throw new Sk.builtin.TypeError( + "Parameter \"ndmin\" must be of type \"int\""); + } + } - subok : bool, optional - If True, then sub-classes will be passed-through, otherwise the returned array will be forced to be a base-class array (default). + var _shape = new Sk.builtin.tuple(state.shape.map(function (x) { + return new Sk.builtin.int_(x); + })); - ndmin : int, optional - Specifies the minimum number of dimensions that the resulting array should have. Ones will be pre-pended to the shape as needed to meet this requirement. + var _buffer = new Sk.builtin.list(elements); + // create new ndarray instance + return Sk.misceval.callsim(mod[CLASS_NDARRAY], _shape, dtype, + _buffer); + }; - Returns : - out : ndarray - An array object satisfying the specified requirements - */ - // https://github.com/geometryzen/davinci-dev/blob/master/src/stdlib/numpy.js - // https://github.com/geometryzen/davinci-dev/blob/master/src/ffh.js - // http://docs.scipy.org/doc/numpy/reference/arrays.html - var array_f = function (object, dtype, copy, order, subok, ndmin) { - Sk.builtin.pyCheckArgs("array", arguments, 1, 6); + array_f.co_varnames = ["object", "dtype", "copy", "order", + "subok", "ndmin" + ]; + array_f.$defaults = [null, Sk.builtin.none.none$, true, new Sk.builtin.str( + "C"), false, new Sk.builtin.int_(0)]; + mod.array = new Sk.builtin.func(array_f); - if (object === undefined) - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(object) + - "' object is undefined"); + /** + Return a new array of given shape and type, filled with zeros. + **/ + var zeros_f = function (shape, dtype, order) { + Sk.builtin.pyCheckArgs("zeros", arguments, 1, 3); + Sk.builtin.pyCheckType("shape", "tuple", shape instanceof Sk.builtin.tuple); + if (dtype instanceof Sk.builtin.list) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(dtype) + + "' is not supported for dtype."); + } - var elements = []; - var state = {}; - state.level = 0; - state.shape = []; + var _zero = new Sk.builtin.float_(0.0); - unpack(object, elements, state); + return Sk.misceval.callsim(mod.full, shape, _zero, dtype, order); + }; + zeros_f.co_varnames = ["shape", "dtype", "order"]; + zeros_f.$defaults = [ + new Sk.builtin.tuple([]), Sk.builtin.none.none$, new Sk.builtin.str("C") + ]; + mod.zeros = new Sk.builtin.func(zeros_f); - var i; - // apply dtype casting function, if it has been provided - if (dtype && Sk.builtin.checkClass(dtype)) { - for (i = 0; i < elements.length; i++) { - elements[i] = Sk.misceval.callsim(dtype, elements[i]); - } - } else { - // check elements and find first usable type - // should be refactored - for (i = 0; i < elements.length; i++) { - if (elements[i] && isNaN(elements[i])) { - dtype = Sk.builtin.float_; - break; - } else if (typeof elements[i] == 'string') { - dtype = Sk.builtin.str; - } else { - dtype = Sk.builtin.float_; + /** + Return a new array of given shape and type, filled with `fill_value`. + **/ + var full_f = function (shape, fill_value, dtype, order) { + Sk.builtin.pyCheckArgs("full", arguments, 2, 4); + Sk.builtin.pyCheckType("shape", "tuple", shape instanceof Sk.builtin.tuple); + if (dtype instanceof Sk.builtin.list) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(dtype) + + "' is currently not supported for dtype."); } - } - } - // check for ndmin param - if (ndmin) { - if (Sk.builtin.checkNumber(ndmin)) { - var _ndmin = Sk.builtin.asnum$(ndmin); - if (_ndmin >= 0 && _ndmin > state.shape.length) { - var _ndmin_array = []; - for (i = 0; i < _ndmin - state.shape.length; i++) { - _ndmin_array.push(1); - } - state.shape = _ndmin_array.concat(state.shape); + // generate an array of the dimensions for the generic array method + var _shape = Sk.ffi.remapToJs(shape); + var _size = prod(_shape); + var _buffer = []; + var _fill_value = fill_value; + var i; + + for (i = 0; i < _size; i++) { + _buffer[i] = _fill_value; } - } else { - throw new Sk.builtin.TypeError( - 'Parameter "ndmin" must be of type "int"'); - } - } - var _shape = new Sk.builtin.tuple(state.shape.map(function (x) { - return new Sk.builtin.int_(x); - })); - - var _buffer = new Sk.builtin.list(elements); - // create new ndarray instance - return Sk.misceval.callsim(mod[CLASS_NDARRAY], _shape, dtype, - _buffer); - }; - - array_f.co_varnames = ['object', 'dtype', 'copy', 'order', - 'subok', 'ndmin' - ]; - array_f.$defaults = [null, Sk.builtin.none.none$, true, new Sk.builtin.str( - 'C'), false, new Sk.builtin.int_(0)]; - mod.array = new Sk.builtin.func(array_f); - - /** - Return a new array of given shape and type, filled with zeros. - **/ - var zeros_f = function (shape, dtype, order) { - Sk.builtin.pyCheckArgs("zeros", arguments, 1, 3); - Sk.builtin.pyCheckType("shape", "tuple", shape instanceof Sk.builtin.tuple); - if (dtype instanceof Sk.builtin.list) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(dtype) + - "' is not supported for dtype."); - } + // if no dtype given and type of fill_value is numeric, assume float + if (!dtype && Sk.builtin.checkNumber(fill_value)) { + dtype = Sk.builtin.float_; + } - var _zero = new Sk.builtin.float_(0.0); - - return Sk.misceval.callsim(mod.full, shape, _zero, dtype, order); - }; - zeros_f.co_varnames = ['shape', 'dtype', 'order']; - zeros_f.$defaults = [ - new Sk.builtin.tuple([]), Sk.builtin.none.none$, new Sk.builtin.str('C') - ]; - mod.zeros = new Sk.builtin.func(zeros_f); - - /** - Return a new array of given shape and type, filled with `fill_value`. - **/ - var full_f = function (shape, fill_value, dtype, order) { - Sk.builtin.pyCheckArgs("full", arguments, 2, 4); - Sk.builtin.pyCheckType("shape", "tuple", shape instanceof Sk.builtin.tuple); - if (dtype instanceof Sk.builtin.list) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(dtype) + - "' is currently not supported for dtype."); - } + // apply dtype casting function, if it has been provided + if (Sk.builtin.checkClass(dtype)) { + for (i = 0; i < _buffer.length; i++) { + _buffer[i] = Sk.misceval.callsim(dtype, _buffer[i]); + } + } - // generate an array of the dimensions for the generic array method - var _shape = Sk.ffi.remapToJs(shape); - var _size = prod(_shape); - var _buffer = []; - var _fill_value = fill_value; - var i; + return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, dtype, new Sk.builtin + .list( + _buffer)); + }; + full_f.co_varnames = ["shape", "fill_value", "dtype", "order"]; + full_f.$defaults = [ + new Sk.builtin.tuple([]), Sk.builtin.none.none$, Sk.builtin.none.none$, new Sk + .builtin + .str("C") + ]; + mod.full = new Sk.builtin.func(full_f); - for (i = 0; i < _size; i++) { - _buffer[i] = _fill_value; - } - // if no dtype given and type of fill_value is numeric, assume float - if (!dtype && Sk.builtin.checkNumber(fill_value)) { - dtype = Sk.builtin.float_; - } + /** + Return a new array of given shape and type, filled with ones. + **/ + var ones_f = function (shape, dtype, order) { + Sk.builtin.pyCheckArgs("ones", arguments, 1, 3); + Sk.builtin.pyCheckType("shape", "tuple", shape instanceof Sk.builtin.tuple); + if (dtype instanceof Sk.builtin.list) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(dtype) + + "' is not supported for dtype."); + } - // apply dtype casting function, if it has been provided - if (Sk.builtin.checkClass(dtype)) { - for (i = 0; i < _buffer.length; i++) { - _buffer[i] = Sk.misceval.callsim(dtype, _buffer[i]); - } - } + var _one = new Sk.builtin.float_(1.0); + return Sk.misceval.callsim(mod.full, shape, _one, dtype, order); + }; + ones_f.co_varnames = ["shape", "dtype", "order"]; + ones_f.$defaults = [ + new Sk.builtin.tuple([]), Sk.builtin.none.none$, new Sk.builtin.str("C") + ]; + mod.ones = new Sk.builtin.func(ones_f); - return Sk.misceval.callsim(mod[CLASS_NDARRAY], shape, dtype, new Sk.builtin - .list( - _buffer)); - }; - full_f.co_varnames = ['shape', 'fill_value', 'dtype', 'order']; - full_f.$defaults = [ - new Sk.builtin.tuple([]), Sk.builtin.none.none$, Sk.builtin.none.none$, new Sk - .builtin - .str('C') - ]; - mod.full = new Sk.builtin.func(full_f); - - - /** - Return a new array of given shape and type, filled with ones. - **/ - var ones_f = function (shape, dtype, order) { - Sk.builtin.pyCheckArgs("ones", arguments, 1, 3); - Sk.builtin.pyCheckType("shape", "tuple", shape instanceof Sk.builtin.tuple); - if (dtype instanceof Sk.builtin.list) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(dtype) + - "' is not supported for dtype."); - } - var _one = new Sk.builtin.float_(1.0); - return Sk.misceval.callsim(mod.full, shape, _one, dtype, order); - }; - ones_f.co_varnames = ['shape', 'dtype', 'order']; - ones_f.$defaults = [ - new Sk.builtin.tuple([]), Sk.builtin.none.none$, new Sk.builtin.str('C') - ]; - mod.ones = new Sk.builtin.func(ones_f); + /** + Dot product + **/ + var dot_f = function (a, b) { + Sk.builtin.pyCheckArgs("dot", arguments, 2, 2); + // ToDo: add support for ndarray args - /** - Dot product - **/ - var dot_f = function (a, b) { - Sk.builtin.pyCheckArgs("dot", arguments, 2, 2); + if (!(a instanceof Sk.builtin.list) && !Sk.builtin.checkNumber( + a) && (Sk.abstr.typeName(a) !== CLASS_NDARRAY)) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(a) + + "' is not supported for a."); + } - // ToDo: add support for ndarray args + if (!(b instanceof Sk.builtin.list) && !Sk.builtin.checkNumber( + b) && (Sk.abstr.typeName(b) !== CLASS_NDARRAY)) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(b) + + "' is not supported for b."); + } - if (!(a instanceof Sk.builtin.list) && !Sk.builtin.checkNumber( - a) && (Sk.abstr.typeName(a) !== CLASS_NDARRAY)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(a) + - "' is not supported for a."); - } + var res; - if (!(b instanceof Sk.builtin.list) && !Sk.builtin.checkNumber( - b) && (Sk.abstr.typeName(b) !== CLASS_NDARRAY)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(b) + - "' is not supported for b."); - } + var b_matrix; + var a_matrix; - var res; + if (Sk.abstr.typeName(a) === CLASS_NDARRAY) { + a_matrix = a.v.buffer; + a_matrix = a_matrix.map(function (x) { + return Sk.ffi.remapToJs(x); + }); + } else { + a_matrix = Sk.ffi.remapToJs(a); + } - var b_matrix; - var a_matrix; + if (Sk.abstr.typeName(b) === CLASS_NDARRAY) { + b_matrix = b.v.buffer; + b_matrix = b_matrix.map(function (x) { + return Sk.ffi.remapToJs(x); + }); + } else { + b_matrix = Sk.ffi.remapToJs(b); + } - if (Sk.abstr.typeName(a) === CLASS_NDARRAY) { - a_matrix = a.v.buffer; - a_matrix = a_matrix.map(function (x) { - return Sk.ffi.remapToJs(x); - }); - } else { - a_matrix = Sk.ffi.remapToJs(a); - } + var a_size = np.math.size(a_matrix); + var b_size = np.math.size(b_matrix); - if (Sk.abstr.typeName(b) === CLASS_NDARRAY) { - b_matrix = b.v.buffer; - b_matrix = b_matrix.map(function (x) { - return Sk.ffi.remapToJs(x); - }); - } else { - b_matrix = Sk.ffi.remapToJs(b); - } - var a_size = np.math.size(a_matrix); - var b_size = np.math.size(b_matrix); + if (a_size.length >= 1 && b_size.length > 1) { + if (a_size[a_size.length - 1] != b_size[b_size - 2]) { + throw new Sk.builtin.ValueError( + "The last dimension of a is not the same size as the second-to-last dimension of b." + ); + } + } + res = np.math.multiply(a_matrix, b_matrix); - if (a_size.length >= 1 && b_size.length > 1) { - if (a_size[a_size.length - 1] != b_size[b_size - 2]) { - throw new Sk.builtin.ValueError( - "The last dimension of a is not the same size as the second-to-last dimension of b." - ); - } - } + if (!Array.isArray(res)) { // if result + return Sk.ffi.remapToPy(res); + } - res = np.math.multiply(a_matrix, b_matrix); - - if (!Array.isArray(res)) { // if result - return Sk.ffi.remapToPy(res); - } - - // return ndarray - buffer = new Sk.builtin.list(res); - return Sk.misceval.callsim(mod.array, buffer, Sk.builtin.float_); - }; - dot_f.co_varnames = ['a', 'b']; - dot_f.$defaults = [Sk.builtin.none.none$, - Sk.builtin.none.none$ - ]; - mod.dot = new Sk.builtin.func(dot_f); - - /* not implemented methods */ - mod.ones_like = new Sk.builtin.func(function () { - throw new Sk.builtin.NotImplementedError( - "ones_like is not yet implemented"); - }); - mod.empty_like = new Sk.builtin.func(function () { - throw new Sk.builtin.NotImplementedError( - "empty_like is not yet implemented"); - }); - mod.ones_like = new Sk.builtin.func(function () { - throw new Sk.builtin.NotImplementedError( - "ones_like is not yet implemented"); - }); - mod.empty = new Sk.builtin.func(function () { - throw new Sk.builtin.NotImplementedError( - "empty is not yet implemented"); - }); - mod.arctan2 = new Sk.builtin.func(function () { - throw new Sk.builtin.NotImplementedError( - "arctan2 is not yet implemented"); - }); - mod.asarray = new Sk.builtin.func(array_f); - return mod; + // return ndarray + buffer = new Sk.builtin.list(res); + return Sk.misceval.callsim(mod.array, buffer, Sk.builtin.float_); + }; + dot_f.co_varnames = ["a", "b"]; + dot_f.$defaults = [Sk.builtin.none.none$, + Sk.builtin.none.none$ + ]; + mod.dot = new Sk.builtin.func(dot_f); + + /* not implemented methods */ + mod.ones_like = new Sk.builtin.func(function () { + throw new Sk.builtin.NotImplementedError( + "ones_like is not yet implemented"); + }); + mod.empty_like = new Sk.builtin.func(function () { + throw new Sk.builtin.NotImplementedError( + "empty_like is not yet implemented"); + }); + mod.ones_like = new Sk.builtin.func(function () { + throw new Sk.builtin.NotImplementedError( + "ones_like is not yet implemented"); + }); + mod.empty = new Sk.builtin.func(function () { + throw new Sk.builtin.NotImplementedError( + "empty is not yet implemented"); + }); + mod.arctan2 = new Sk.builtin.func(function () { + throw new Sk.builtin.NotImplementedError( + "arctan2 is not yet implemented"); + }); + mod.asarray = new Sk.builtin.func(array_f); + return mod; }; diff --git a/src/lib/operator.js b/src/lib/operator.js index 60359b4a75..5c184597e5 100644 --- a/src/lib/operator.js +++ b/src/lib/operator.js @@ -8,32 +8,32 @@ var $builtinmodule = function (name) { var mod = {}; mod.lt = new Sk.builtin.func(function (a, b) { - return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, 'Lt')); + return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, "Lt")); }); mod.__lt__ = mod.lt; mod.le = new Sk.builtin.func(function (a, b) { - return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, 'LtE')); + return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, "LtE")); }); mod.__le__ = mod.le; mod.eq = new Sk.builtin.func(function (a, b) { - return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, 'Eq')); + return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, "Eq")); }); mod.__eq__ = mod.eq; mod.ne = new Sk.builtin.func(function (a, b) { - return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, 'NotEq')); + return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, "NotEq")); }); mod.__ne__ = mod.ne; mod.ge = new Sk.builtin.func(function (a, b) { - return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, 'GtE')); + return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, "GtE")); }); mod.__ge__ = mod.ge; mod.gt = new Sk.builtin.func(function (a, b) { - return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, 'Gt')); + return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, "Gt")); }); mod.__gt__ = mod.gt; @@ -46,15 +46,15 @@ var $builtinmodule = function (name) { }); mod.is_ = new Sk.builtin.func(function (a, b) { - return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, 'Is')); + return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, "Is")); }); mod.is_not = new Sk.builtin.func(function (a, b) { - return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, 'IsNot')); + return Sk.builtin.bool(Sk.misceval.richCompareBool(a, b, "IsNot")); }); mod.abs = new Sk.builtin.func(function (obj) { - return Sk.misceval.callsimArray(Sk.builtin.abs, [obj]); + return Sk.builtin.abs(obj); }); mod.__abs__ = mod.abs; @@ -90,7 +90,7 @@ var $builtinmodule = function (name) { // I've gone ahead and created this function for completeness' sake, but expect any use of it to // result in an error. mod.inv = new Sk.builtin.func(function (obj) { - return Sk.abstr.numberUnaryOp(obj, 'Invert'); + return Sk.abstr.numberUnaryOp(obj, "Invert"); }); mod.__inv__ = mod.inv; mod.invert = mod.inv; diff --git a/src/lib/os.py b/src/lib/os.py index 0adef78d92..91b9254a27 100644 --- a/src/lib/os.py +++ b/src/lib/os.py @@ -1,6 +1,7 @@ import sys import posixpath as path + sys.modules['os.path'] = path -#raise NotImplementedError("os is not yet implemented in Skulpt") +# raise NotImplementedError("os is not yet implemented in Skulpt") diff --git a/src/lib/parking/__init__.js b/src/lib/parking/__init__.js index 5232113422..15bf18ba6a 100644 --- a/src/lib/parking/__init__.js +++ b/src/lib/parking/__init__.js @@ -1,24 +1,24 @@ -var $builtinmodule = function(name) { +var $builtinmodule = function (name) { var WEEKDAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]; var FULL_DAYS = { "mon": "Monday", "tue": "Tuesday", "wed": "Wednesday", "thu": "Thursday", "fri": "Friday", "sat": "Saturday", "sun": "Sunday" }; - - var convert_day = function(day) { + + var convert_day = function (day) { return WEEKDAYS.indexOf(day.name.v); }; - var convert_day_string = function(day) { + var convert_day_string = function (day) { return WEEKDAYS.indexOf(day.v.toLowerCase().slice(0, 3)); }; - var convert_time = function(hour, minute, meridian) { - return hour*60 + minute + (meridian.toLowerCase() === "pm" ? 12*60 : 0); + var convert_time = function (hour, minute, meridian) { + return hour * 60 + minute + (meridian.toLowerCase() === "pm" ? 12 * 60 : 0); }; - + var mod = {}; - - var time = function($gbl, $loc) { + + var time = function ($gbl, $loc) { $loc.__init__ = new Sk.builtin.func(function (self, hour, minute, meridian) { Sk.builtin.pyCheckArgs("__init__", arguments, 4, 4); Sk.builtin.pyCheckType("hour", "int", Sk.builtin.checkInt(hour)); @@ -30,19 +30,19 @@ var $builtinmodule = function(name) { self.meridian.v = self.meridian.v.toLowerCase(); }); $loc.__str__ = new Sk.builtin.func(function (self) { - return Sk.ffi.remapToPy("<"+(self.hour.v || 12) +":"+ - (self.minute.v < 10 ? "0"+self.minute.v : self.minute.v)+ - self.meridian.v+">"); + return Sk.ffi.remapToPy("<" + (self.hour.v || 12) + ":" + + (self.minute.v < 10 ? "0" + self.minute.v : self.minute.v) + + self.meridian.v + ">"); }); $loc.__repr__ = new Sk.builtin.func(function (self) { - return Sk.ffi.remapToPy("<"+(self.hour.v || 12)+":"+ - (self.minute.v < 10 ? "0"+self.minute.v : self.minute.v)+ - self.meridian.v+">"); + return Sk.ffi.remapToPy("<" + (self.hour.v || 12) + ":" + + (self.minute.v < 10 ? "0" + self.minute.v : self.minute.v) + + self.meridian.v + ">"); }); var comparison = function (operation, self, other) { if (Sk.builtin.isinstance(other, mod.Time).v) { - if (operation(convert_time(self.hour.v % 12, self.minute.v, self.meridian.v), - convert_time(other.hour.v % 12, other.minute.v, other.meridian.v))) { + if (operation(convert_time(self.hour.v % 12, self.minute.v, self.meridian.v), + convert_time(other.hour.v % 12, other.minute.v, other.meridian.v))) { return Sk.ffi.remapToPy(true); } else { return Sk.ffi.remapToPy(false); @@ -53,50 +53,62 @@ var $builtinmodule = function(name) { }; $loc.__eq__ = new Sk.builtin.func(function (self, other) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); - return comparison(function(l,r) {return l==r;}, self, other); + return comparison(function (l, r) { + return l == r; + }, self, other); }); - + $loc.__ne__ = new Sk.builtin.func(function (self, other) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); if (!Sk.builtin.isinstance(other, mod.Time).v) { return Sk.builtin.bool.true$; } - return comparison(function(l,r) {return l!=r;}, self, other); + return comparison(function (l, r) { + return l != r; + }, self, other); }); - + $loc.__lt__ = new Sk.builtin.func(function (self, other) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); - return comparison(function(l,r) {return l < r;}, self, other); + return comparison(function (l, r) { + return l < r; + }, self, other); }); - + $loc.__gt__ = new Sk.builtin.func(function (self, other) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); - return comparison(function(l,r) {return l > r;}, self, other); + return comparison(function (l, r) { + return l > r; + }, self, other); }); - + $loc.__le__ = new Sk.builtin.func(function (self, other) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); - return comparison(function(l,r) {return l <= r;}, self, other); + return comparison(function (l, r) { + return l <= r; + }, self, other); }); - + $loc.__ge__ = new Sk.builtin.func(function (self, other) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); - return comparison(function(l,r) {return l >= r;}, self, other); + return comparison(function (l, r) { + return l >= r; + }, self, other); }); }; - - var day = function($gbl, $loc) { + + var day = function ($gbl, $loc) { $loc.__init__ = new Sk.builtin.func(function (self, name) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name)); self.name = name; - self.name.v = self.name.v.toLowerCase().slice(0,3); + self.name.v = self.name.v.toLowerCase().slice(0, 3); }); $loc.__str__ = new Sk.builtin.func(function (self) { - return Sk.ffi.remapToPy("<"+FULL_DAYS[self.name.v]+">"); + return Sk.ffi.remapToPy("<" + FULL_DAYS[self.name.v] + ">"); }); $loc.__repr__ = new Sk.builtin.func(function (self) { - return Sk.ffi.remapToPy("<"+FULL_DAYS[self.name.v]+">"); + return Sk.ffi.remapToPy("<" + FULL_DAYS[self.name.v] + ">"); }); var comparison = function (operation, self, other) { if (Sk.builtin.isinstance(other, mod.Day).v) { @@ -111,53 +123,65 @@ var $builtinmodule = function(name) { }; $loc.__eq__ = new Sk.builtin.func(function (self, other) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); - return comparison(function(l,r) {return l==r;}, self, other); + return comparison(function (l, r) { + return l == r; + }, self, other); }); - + $loc.__ne__ = new Sk.builtin.func(function (self, other) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); if (!Sk.builtin.isinstance(other, mod.Day).v) { return Sk.builtin.bool.true$; } - return comparison(function(l,r) {return l!=r;}, self, other); + return comparison(function (l, r) { + return l != r; + }, self, other); }); - + $loc.__lt__ = new Sk.builtin.func(function (self, other) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); - return comparison(function(l,r) {return l < r;}, self, other); + return comparison(function (l, r) { + return l < r; + }, self, other); }); - + $loc.__gt__ = new Sk.builtin.func(function (self, other) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); - return comparison(function(l,r) {return l > r;}, self, other); + return comparison(function (l, r) { + return l > r; + }, self, other); }); - + $loc.__le__ = new Sk.builtin.func(function (self, other) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); - return comparison(function(l,r) {return l <= r;}, self, other); + return comparison(function (l, r) { + return l <= r; + }, self, other); }); - + $loc.__ge__ = new Sk.builtin.func(function (self, other) { Sk.builtin.pyCheckArgs("__init__", arguments, 2, 2); - return comparison(function(l,r) {return l >= r;}, self, other); + return comparison(function (l, r) { + return l >= r; + }, self, other); }); }; - + mod.Day = Sk.misceval.buildClass(mod, day, "Day", []); mod.Time = Sk.misceval.buildClass(mod, time, "Time", []); - + mod._today = undefined; mod._hour = undefined; mod._minute = undefined; mod._meridian = undefined; - - mod.today = new Sk.builtin.func(function() { + + mod.today = new Sk.builtin.func(function () { var t = ((new Date).getDay() + 6) % 7; // would be -1, but % is broken for negatives in JS t = Sk.today || mod._today || Sk.ffi.remapToPy(WEEKDAYS[t]); return Sk.misceval.callsim(mod.Day, t); }); - - mod.now = new Sk.builtin.func(function() { + + mod.now = new Sk.builtin.func(function () { var d = new Date(); var hour = d.getHours() % 12, minute = d.getMinutes(), @@ -167,8 +191,8 @@ var $builtinmodule = function(name) { meridian = Sk._meridian || mod._meridian || Sk.ffi.remapToPy(meridian); return Sk.misceval.callsim(mod.Time, hour, minute, meridian); }); - - mod.day_compare = new Sk.builtin.func(function(comparison, value, day) { + + mod.day_compare = new Sk.builtin.func(function (comparison, value, day) { Sk.builtin.pyCheckArgs("day_compare", arguments, 3, 3); Sk.builtin.pyCheckType("comparison", "string", Sk.builtin.checkString(comparison)); Sk.builtin.pyCheckType("value", "Day", Sk.builtin.isinstance(value, mod.Day).v); @@ -176,17 +200,24 @@ var $builtinmodule = function(name) { var day_n = convert_day_string(day), value_n = convert_day(value); switch (comparison.v) { - case "IS": return Sk.ffi.remapToPy(value_n == day_n); - case "BEFORE_EQUAL": return Sk.ffi.remapToPy(value_n <= day_n); - case "AFTER_EQUAL": return Sk.ffi.remapToPy(value_n >= day_n); - case "BEFORE": return Sk.ffi.remapToPy(value_n < day_n); - case "AFTER": return Sk.ffi.remapToPy(value_n > day_n); - case "IS_NOT": return Sk.ffi.remapToPy(value_n != day_n); - default: throw new Sk.builtins.ValueError("Unknown comparison"); + case "IS": + return Sk.ffi.remapToPy(value_n == day_n); + case "BEFORE_EQUAL": + return Sk.ffi.remapToPy(value_n <= day_n); + case "AFTER_EQUAL": + return Sk.ffi.remapToPy(value_n >= day_n); + case "BEFORE": + return Sk.ffi.remapToPy(value_n < day_n); + case "AFTER": + return Sk.ffi.remapToPy(value_n > day_n); + case "IS_NOT": + return Sk.ffi.remapToPy(value_n != day_n); + default: + throw new Sk.builtins.ValueError("Unknown comparison"); } }); - - mod.time_compare = new Sk.builtin.func(function(comparison, left, hour, minute, meridian) { + + mod.time_compare = new Sk.builtin.func(function (comparison, left, hour, minute, meridian) { Sk.builtin.pyCheckArgs("time_compare", arguments, 5, 5); Sk.builtin.pyCheckType("comparison", "string", Sk.builtin.checkString(comparison)); Sk.builtin.pyCheckType("left", "Time", Sk.builtin.isinstance(left, mod.Time).v); @@ -196,13 +227,20 @@ var $builtinmodule = function(name) { var right_time = convert_time(hour.v % 12, minute.v, meridian.v), left_time = convert_time(left.hour.v % 12, left.minute.v, left.meridian.v); switch (comparison.v) { - case "IS": return Sk.ffi.remapToPy(left_time == right_time); - case "BEFORE_EQUAL": return Sk.ffi.remapToPy(left_time <= right_time); - case "AFTER_EQUAL": return Sk.ffi.remapToPy(left_time >= right_time); - case "BEFORE": return Sk.ffi.remapToPy(left_time < right_time); - case "AFTER": return Sk.ffi.remapToPy(left_time > right_time); - case "IS_NOT": return Sk.ffi.remapToPy(left_time != right_time); - default: throw new Sk.builtins.ValueError("Unknown comparison"); + case "IS": + return Sk.ffi.remapToPy(left_time == right_time); + case "BEFORE_EQUAL": + return Sk.ffi.remapToPy(left_time <= right_time); + case "AFTER_EQUAL": + return Sk.ffi.remapToPy(left_time >= right_time); + case "BEFORE": + return Sk.ffi.remapToPy(left_time < right_time); + case "AFTER": + return Sk.ffi.remapToPy(left_time > right_time); + case "IS_NOT": + return Sk.ffi.remapToPy(left_time != right_time); + default: + throw new Sk.builtins.ValueError("Unknown comparison"); } }); diff --git a/src/lib/pedal/assertions/__init__.py b/src/lib/pedal/assertions/__init__.py index a4fc75249d..cbb49e6c91 100644 --- a/src/lib/pedal/assertions/__init__.py +++ b/src/lib/pedal/assertions/__init__.py @@ -4,9 +4,10 @@ from pedal.assertions.assertions import * from pedal.assertions.organizers import * + def set_assertion_mode(exceptions=True, report=None): if report is None: report = MAIN_REPORT _setup_assertions(report) - + report['assertions']['exceptions'] = exceptions diff --git a/src/lib/pedal/assertions/organizers.py b/src/lib/pedal/assertions/organizers.py index 074b748e6a..84f00667c4 100644 --- a/src/lib/pedal/assertions/organizers.py +++ b/src/lib/pedal/assertions/organizers.py @@ -31,12 +31,12 @@ ''' - from pedal.report.imperative import MAIN_REPORT from pedal.assertions.setup import (_setup_assertions, AssertionException, _add_relationships, _add_phase) from functools import wraps + def contextualize_calls(): pass @@ -76,15 +76,18 @@ def finish_section(number, *functions, next_section=False): print("\tNEXT SECTION") return result + def section(*args): ''' TODO: Deprecate? ''' _setup_assertions(MAIN_REPORT) + def wrap(f): _add_phase(phase_name, _handle_entry) MAIN_REPORT['assertions']['phases'].append((section_number, f)) return f + section_number = -1 if len(args) >= 1 and callable(args[0]): if len(args) >= 2: @@ -94,6 +97,7 @@ def wrap(f): section_number = args[0] return wrap + def phase(phase_name, before=None, after=None): ''' @@ -105,6 +109,7 @@ def phase(phase_name, before=None, after=None): should be after. ''' _setup_assertions(MAIN_REPORT) + def wrap(f): @wraps(f) def _handle_entry(*args, **kwargs): @@ -113,14 +118,18 @@ def _handle_entry(*args, **kwargs): value = f(*args, **kwargs) MAIN_REPORT['assertions']['exceptions'] = old_exception_state return value + _add_phase(phase_name, _handle_entry) _add_relationships(phase_name, before) _add_relationships(after, phase_name) return _handle_entry + return wrap - + + def stop_on_failure(f): _setup_assertions(MAIN_REPORT) + @wraps(f) def wrapped(*args, **kwargs): old_exception_state = MAIN_REPORT['assertions']['exceptions'] @@ -132,11 +141,13 @@ def wrapped(*args, **kwargs): pass MAIN_REPORT['assertions']['exceptions'] = old_exception_state return value + return wrapped def try_all(): _setup_assertions(MAIN_REPORT) + @wraps(f) def wrapped(*args, **kwargs): old_exception_state = MAIN_REPORT['assertions']['exceptions'] @@ -144,6 +155,7 @@ def wrapped(*args, **kwargs): value = f(*args, **kwargs) MAIN_REPORT['assertions']['exceptions'] = old_exception_state return value + return wrapped diff --git a/src/lib/pedal/assertions/setup.py b/src/lib/pedal/assertions/setup.py index e282034c22..5df3a598cc 100644 --- a/src/lib/pedal/assertions/setup.py +++ b/src/lib/pedal/assertions/setup.py @@ -3,14 +3,16 @@ from pedal.report.imperative import MAIN_REPORT from pedal.sandbox.exceptions import SandboxStudentCodeException + class AssertionException(Exception): def __str__(self): return self.args[0] + def _topological_sort(names, orderings): visited = set() stack = [] - + def dfs(name): visited.add(name) if name in orderings: @@ -18,12 +20,12 @@ def dfs(name): if neighbor not in visited: dfs(neighbor) stack.insert(0, name) - + for name in names[::-1]: if name not in visited: dfs(name) return stack - + def resolve_all(set_success=False, report=None): from pprint import pprint @@ -34,7 +36,7 @@ def resolve_all(set_success=False, report=None): phase_functions = report['assertions']['phase_functions'] phase_names = report['assertions']['phases'] phase_names = _topological_sort(phase_names, orderings) - #pprint(orderings) + # pprint(orderings) phase_success = False for phase_name in phase_names: phase_success = True @@ -47,14 +49,15 @@ def resolve_all(set_success=False, report=None): phase_success = False if not phase_success: break - - #for f in report.feedback: + + # for f in report.feedback: # print("\t", f, f.mistake, f.misconception) if not report['assertions']['failures'] and phase_success and set_success: report.set_success() - + _reset_phases(report) - + + def _add_phase(phase_name, function, report=None): if report is None: report = MAIN_REPORT @@ -64,7 +67,8 @@ def _add_phase(phase_name, function, report=None): phase_functions[phase_name] = [] phases.append(phase_name) phase_functions[phase_name].append(function) - + + def _add_relationships(befores, afters, report=None): if report is None: report = MAIN_REPORT @@ -84,8 +88,8 @@ def _add_relationships(befores, afters, report=None): if not isinstance(after, str): after = after.__name__ relationships[before].append(after) - - + + def _reset_phases(report=None): if report is None: report = MAIN_REPORT diff --git a/src/lib/pedal/cait/ast_map.py b/src/lib/pedal/cait/ast_map.py index 002583cc03..940f54876e 100644 --- a/src/lib/pedal/cait/ast_map.py +++ b/src/lib/pedal/cait/ast_map.py @@ -95,7 +95,7 @@ def add_func_to_sym_table(self, ins_node, std_node): value = AstSymbol(std_node.astNode.name, std_node) else: # TODO: Little skulpt artifact that doesn't raise Attribute Errors... raise AttributeError -# value = AstSymbol(std_node.astNode.name, std_node) + # value = AstSymbol(std_node.astNode.name, std_node) except AttributeError: node = std_node if type(node.astNode).__name__ != "Call": diff --git a/src/lib/pedal/mistakes/iteration_context.py b/src/lib/pedal/mistakes/iteration_context.py index d06626bc39..4df295854b 100644 --- a/src/lib/pedal/mistakes/iteration_context.py +++ b/src/lib/pedal/mistakes/iteration_context.py @@ -65,6 +65,7 @@ def list_all_zeros_8_2(): return explain_r(message, code, label=tldr) return False + # ################8.2 End####################### diff --git a/src/lib/pedal/plugins/__init__.py b/src/lib/pedal/plugins/__init__.py index 552e872a5b..cc1889972d 100644 --- a/src/lib/pedal/plugins/__init__.py +++ b/src/lib/pedal/plugins/__init__.py @@ -1,4 +1,3 @@ - ''' def default_pipeline(tifa=False, cait=True, sandbox=True): next_section() diff --git a/src/lib/pedal/plugins/cmd_line.py b/src/lib/pedal/plugins/cmd_line.py index 029ab48689..65d7ef1a1e 100644 --- a/src/lib/pedal/plugins/cmd_line.py +++ b/src/lib/pedal/plugins/cmd_line.py @@ -59,7 +59,7 @@ def process(file, module, ins_code, report): # feedback_suffix = "prequiz.py" # assignment_id = 409 feedback_suffix = "postquiz1.py" - assignment_id = 410 # Pass Count = 1 + assignment_id = 410 # Pass Count = 1 # feedback_suffix = "postquiz2-1.py" # assignment_id = 411 # Pass Count = 2 # feedback_suffix = "postquiz2-2.py" diff --git a/src/lib/pedal/plugins/test_reference_solution.py b/src/lib/pedal/plugins/test_reference_solution.py index bc093709c6..1cbb7bd432 100644 --- a/src/lib/pedal/plugins/test_reference_solution.py +++ b/src/lib/pedal/plugins/test_reference_solution.py @@ -37,6 +37,7 @@ def add_test(class_, name, python_file, grader_code, grader_path, grader_args, student_path): seed = find_seed(python_file) grader_args = [substitute_args(arg, student_path, seed) for arg in grader_args] + def _inner_test(self): captured_output = StringIO() with redirect_stdout(captured_output): @@ -48,7 +49,7 @@ def _inner_test(self): clear_report() grader_exec = compile(grader_code, grader_path, 'exec') exec(grader_exec, globals()) - #print(repr(MAIN_REPORT.feedback[0].mistake['error'])) + # print(repr(MAIN_REPORT.feedback[0].mistake['error'])) actual_output = captured_output.getvalue() if expected_output is None: print("File not found:", expected_output_path) @@ -57,8 +58,10 @@ def _inner_test(self): print("\tCreated missing file with current output") else: self.assertEqual(actual_output, expected_output) + setattr(class_, 'test_' + name, _inner_test) + def find_seed(python_code): try: ast = parse_program(python_code) @@ -76,6 +79,7 @@ def find_seed(python_code): return 0 return 0 + # Load reference solutions def add_all_tests(grader_path, reference_solutions_dir, grader_args, limit): # Load grader file @@ -95,7 +99,7 @@ def add_all_tests(grader_path, reference_solutions_dir, grader_args, limit): else: output = None add_test(TestReferenceSolutions, filename[:-3], python, - text_path, output, + text_path, output, grader_code, grader_path, grader_args, path) @@ -116,20 +120,20 @@ def run_tests(): default='test_reference_solution.py,$_STUDENT_MAIN,$_STUDENT_NAME') parser.add_argument('--limit', '-l', help='Limit to a specific file.', default=None) args = parser.parse_args() - + # Turn the reference solutions path into an absolute filename if os.path.isabs(args.path): reference_solutions_path = args.path else: reference_solutions_path = os.path.join(os.path.dirname(args.grader), args.path) - + # If no reference solutions folder, let's make it if not os.path.exists(reference_solutions_path): os.mkdir(reference_solutions_path) - + # Fix up the passed in args grader_args = args.args.split(",") - + # Check that we actually have some files to try out if not os.listdir(reference_solutions_path): print("No reference solutions found") diff --git a/src/lib/pedal/questions/__init__.py b/src/lib/pedal/questions/__init__.py index cd64bd039b..ce0380282b 100644 --- a/src/lib/pedal/questions/__init__.py +++ b/src/lib/pedal/questions/__init__.py @@ -21,13 +21,15 @@ class QuestionGrader: def _get_functions_with_filter(self, filter='grade_'): return [getattr(self, method_name) for method_name in dir(self) - if method_name.startswith(filter) and - callable(getattr(self, method_name))] + if method_name.startswith(filter) and + callable(getattr(self, method_name))] + def _test(self, question): methods = self._get_functions_with_filter() for method in methods: method(question) + class Question: def __init__(self, name, instructions, tests, seed=None, report=None): self.name = name @@ -38,10 +40,10 @@ def __init__(self, name, instructions, tests, seed=None, report=None): report = MAIN_REPORT self.report = report self.answered = False - + def answer(self): self.answered = True - + def ask(self): if isinstance(self.tests, QuestionGrader): self.tests._test(self) @@ -56,7 +58,7 @@ def show_question(instructions, report=None): if report is None: report = MAIN_REPORT report.attach('Question', category='Instructions', tool='Questions', - group=report.group, priority='instructions', hint=instructions) + group=report.group, priority='instructions', hint=instructions) class Pool: @@ -80,14 +82,14 @@ def choose(self, force=None): if self.seed is None: force = self.report['questions']['seed'] if isinstance(force, str): - force = _name_hash(force+self.name) + force = _name_hash(force + self.name) # Assume iterable; could be check that throws better error if not isinstance(force, int): force = force[self.position] else: force = self.seed return self.choices[force % len(self.choices)] - + @property def answered(self): for choice in self.choices: diff --git a/src/lib/pedal/questions/graders.py b/src/lib/pedal/questions/graders.py index b720226100..608b550cdd 100644 --- a/src/lib/pedal/questions/graders.py +++ b/src/lib/pedal/questions/graders.py @@ -5,6 +5,7 @@ from pedal.assertions.assertions import * from pedal.toolkit.functions import * + class FunctionGrader(QuestionGrader): MAX_POINTS = 10 DEFINITION_POINTS = 3 @@ -15,52 +16,52 @@ class FunctionGrader(QuestionGrader): UNIT_TEST_TOTAL_POINTS = 5 UNIT_TEST_TYPE_RATIO = .5 UNIT_TEST_COMPLETION_POINTS = 2 - + def __init__(self, function_name, signature, tests): super().__init__() self.function_name = function_name self.signature = signature self.tests = tests self.points = 0 - + def _test(self, question): defined = self.grade_definition(question) - + if not defined: return self.report_status(question) - + self.grade_components(question) - + passed_tests = self.grade_unit_tests(question) if not passed_tests: return self.report_status(question) - + self.report_success(question) - + def report_status(self, question): pass - + def report_success(self, question): question.answer() - + def grade_definition(self, question): self.student = run(report_exceptions=True, context=False) - self.student.report_exceptions_mode=False - + self.student.report_exceptions_mode = False + self.definition = match_signature_muted(self.function_name, *self.signature) if not assertGenerally(self.definition): gently("Function not defined") return False - + if self.student.exception: return False if not assertHasFunction(self.student, self.function_name): gently("Function defined incorrectly") return False - + self.points += self.DEFINITION_POINTS return True - + def grade_components(self, question): self.component_points = 0 components = self._get_functions_with_filter('grade_component_') @@ -68,10 +69,10 @@ def grade_components(self, question): component(question) self.component_points = min(self.component_points, self.MAX_COMPONENTS_POINTS) self.points += self.component_points - + def assertEqual(self, *parameters): return assertEqual(*parameters) - + def grade_unit_tests(self, question): all_good = True if self.UNIT_TEST_TOTAL_POINTS is None: @@ -79,13 +80,13 @@ def grade_unit_tests(self, question): VALUE_POINT_ADD = self.UNIT_TEST_VALUE_POINTS else: ratio = self.UNIT_TEST_TYPE_RATIO - TYPE_POINT_ADD = (self.UNIT_TEST_TOTAL_POINTS/len(self.tests) * (ratio)) - VALUE_POINT_ADD = (self.UNIT_TEST_TOTAL_POINTS/len(self.tests) * (1-ratio)) + TYPE_POINT_ADD = (self.UNIT_TEST_TOTAL_POINTS / len(self.tests) * (ratio)) + VALUE_POINT_ADD = (self.UNIT_TEST_TOTAL_POINTS / len(self.tests) * (1 - ratio)) for arguments, expected in self.tests: - #import sys - #print(repr(arguments), file=sys.stderr) + # import sys + # print(repr(arguments), file=sys.stderr) result = self.student.call(self.function_name, *arguments, context=False) - #print(repr(self.student.exception), file=sys.stderr) + # print(repr(self.student.exception), file=sys.stderr) if self.student.exception: all_good = False continue diff --git a/src/lib/pedal/questions/loader.py b/src/lib/pedal/questions/loader.py index b502a6c6a4..603c1de2f1 100644 --- a/src/lib/pedal/questions/loader.py +++ b/src/lib/pedal/questions/loader.py @@ -193,6 +193,7 @@ def check_function_value(function, values, settings): class TestCase: CASE_COUNT = 0 + def __init__(self, function_name, case_name): self.function_name = function_name if case_name is None: @@ -246,6 +247,7 @@ def add_prints_returns(self, prints, returns): def fail(self): self.success = False + def check_case(function, case, student_function): """ @@ -330,12 +332,12 @@ def make_table(cases): body.append(" ") body = "\n".join(body) return TEST_TABLE_TEMPLATE.format(body=body) - #if ((any(args) and any(inputs)) or + # if ((any(args) and any(inputs)) or # (any(expected_outputs) and any(expected_returns)) or # (any(actual_outputs) and any(actual_returns))): # # Complex cells # pass - #else: + # else: # Simple table # Make header @@ -381,7 +383,7 @@ def check_cases(function, student_function, settings): table = make_table(test_cases) raise FeedbackException("toolkit", "failed_test_cases", function_name=function_name, - cases_count=len(cases), failure_count=len(cases)-success_cases, + cases_count=len(cases), failure_count=len(cases) - success_cases, table=table) else: raise FeedbackException("toolkit", "failed_test_cases_count", @@ -432,7 +434,7 @@ def load_question(data): check_cases(function, student_function, settings) except FeedbackException as fe: success_ratio = (1.0 - fe.fields['failure_count'] / fe.fields['cases_count']) - function_points += function_rubric.get('cases', 80*success_ratio) + function_points += function_rubric.get('cases', 80 * success_ratio) yield fe.as_message(), fe.label else: function_points += function_rubric.get('cases', 80) diff --git a/src/lib/pedal/questions/setup.py b/src/lib/pedal/questions/setup.py index b261b6bc27..d5308872a9 100644 --- a/src/lib/pedal/questions/setup.py +++ b/src/lib/pedal/questions/setup.py @@ -2,9 +2,11 @@ import hashlib + def _name_hash(name): return hashlib.md5(name.encode('utf8')).digest()[0] + def _setup_questions(report): ''' Initialize any necessary fields for the report's question tool. @@ -17,6 +19,7 @@ def _setup_questions(report): 'seed': 0 } + def set_seed(seed_value, report=None): ''' Sets the seed that will be used in selecting questions. diff --git a/src/lib/pedal/resolvers/sectional.py b/src/lib/pedal/resolvers/sectional.py index 4150be96be..c3fae486d2 100644 --- a/src/lib/pedal/resolvers/sectional.py +++ b/src/lib/pedal/resolvers/sectional.py @@ -38,7 +38,7 @@ def resolve(report=None, priority_key=None): final_success = success or final_success final_score += partial if message is not None: - #print("RESETING GROUP", group, message[:20], found_failure, feedback.priority) + # print("RESETING GROUP", group, message[:20], found_failure, feedback.priority) if group not in finals: finals[group] = [] found_failure = False @@ -63,8 +63,8 @@ def resolve(report=None, priority_key=None): finals[group].insert(0, entry) else: finals[group].append(entry) - #from pprint import pprint - #pprint(finals) + # from pprint import pprint + # pprint(finals) final_hide_correctness = suppressions.get('success', False) if not finals: finals[0] = [{ diff --git a/src/lib/pedal/sandbox/exceptions.py b/src/lib/pedal/sandbox/exceptions.py index 57aab77239..ccae0bc6cc 100644 --- a/src/lib/pedal/sandbox/exceptions.py +++ b/src/lib/pedal/sandbox/exceptions.py @@ -8,18 +8,22 @@ class TimeoutError(Exception): pass + class SandboxException(Exception): """ Generic base exception for sandbox errors. """ + class SandboxStudentCodeException(SandboxException): """ Caused by an error in student code """ + def __init__(self, actual): self.actual = actual + class SandboxPreventModule(Exception): """ Caused by student attempting to load a module that they shouldn't. @@ -86,7 +90,11 @@ def _add_context_to_error(e, message): elif hasattr(e, 'args') and e.args: e.args = tuple([e.args[0] + message]) return e -x=sys.stdout + + +x = sys.stdout + + class SandboxTraceback: """ Class for reformatting tracebacks to have more pertinent information. @@ -133,8 +141,8 @@ def format_exception(self, preamble=""): for frame in tb_e.stack: if frame.filename == os.path.basename(self.student_filename): frame.lineno += self.line_offset - if frame.lineno-1 < len(self.original_code_lines): - frame._line = self.original_code_lines[frame.lineno-1] + if frame.lineno - 1 < len(self.original_code_lines): + frame._line = self.original_code_lines[frame.lineno - 1] else: frame._line = "*line missing*" lines = [self._clean_traceback_line(line) diff --git a/src/lib/pedal/sandbox/messages.py b/src/lib/pedal/sandbox/messages.py index 7cb4df5294..350d8e43e3 100644 --- a/src/lib/pedal/sandbox/messages.py +++ b/src/lib/pedal/sandbox/messages.py @@ -58,4 +58,4 @@ class TimeLimitError(Exception): KeyError: "A dictionary has a bunch of keys that you can use to get data. This error is caused by you trying to refer to a key that does not exist.
    Suggestion: The most common reason you get this exception is that you have a typo in your dictionary access. Check your spelling. Also double check that the key definitely exists.", MemoryError: "Somehow, you have run out of memory.
    Suggestion: Make sure you are filtering your dataset! Alternatively, bring your code to an instructor.", OSError: "It's hard to say what an OSError is without deep checking. Many things can cause it.
    Suggestion: Bring your code to an instructor. ", - TimeLimitError: "A TimeLimit error means that BlockPy wasn't able to process your program fast enough. Typically, this means that you're iterating through too many elements."} + TimeoutError: "A TimeLimit error means that BlockPy wasn't able to process your program fast enough. Typically, this means that you're iterating through too many elements."} diff --git a/src/lib/pedal/sandbox/mocked.py b/src/lib/pedal/sandbox/mocked.py index 930aa101e4..da841dd62b 100644 --- a/src/lib/pedal/sandbox/mocked.py +++ b/src/lib/pedal/sandbox/mocked.py @@ -54,12 +54,14 @@ class FunctionNotAllowed(Exception): def disabled_builtin(name): def _disabled_version(*args, **kwargs): raise FunctionNotAllowed("You are not allowed to call '{}'.".format(name)) + return _disabled_version _OPEN_FORBIDDEN_NAMES = re.compile(r"(^[./])|(\.py$)") _OPEN_FORBIDDEN_MODES = re.compile(r"[wa+]") + # TODO: Turn this into a function that lets us more elegantly specify valid and # invalid filenames/paths @@ -72,6 +74,7 @@ def _restricted_open(name, mode='r', buffering=-1): else: return _original_builtins['open'](name, mode, buffering) + # TODO: Allow this to be flexible @@ -201,6 +204,7 @@ class MockTurtle(MockModule): # TODO: it'd be awesome to have a way to construct a representation # of the drawing result that we could autograde! """ + def __init__(self): super().__init__() diff --git a/src/lib/pedal/sandbox/sandbox.py b/src/lib/pedal/sandbox/sandbox.py index 17ac77f7a3..156890bbe3 100644 --- a/src/lib/pedal/sandbox/sandbox.py +++ b/src/lib/pedal/sandbox/sandbox.py @@ -92,6 +92,7 @@ def var(self): def __repr__(self): return "" + class Sandbox(DataSandbox): """ @@ -556,13 +557,13 @@ def _capture_exception(self, exception, exc_info, report_exceptions, contexts = self.call_contexts[self.call_id] if context is not None: contexts.append(context) - context = '\n'.join(contexts)#[1:]) + context = '\n'.join(contexts) # [1:]) if context.strip(): context = self.CONTEXT_MESSAGE.format(context=context) inputs = self.input_contexts[self.call_id] if inputs is not None and inputs: inputs = "\n".join(inputs) - context += "\n"+self.INPUT_CONTEXT_MESSAGE.format(inputs=inputs) + context += "\n" + self.INPUT_CONTEXT_MESSAGE.format(inputs=inputs) else: context = self.FILE_CONTEXT_MESSAGE.format(filename=self.report['source']['filename']) self.exception = _add_context_to_error(self.exception, context) @@ -636,7 +637,7 @@ def run(self, code, as_filename=None, modules=None, inputs=None, context, keep_context, as_filename, code) return self - + if as_filename is None: as_filename = os.path.basename(self.report['source']['filename']) # todo: earlier version of inputs being made? diff --git a/src/lib/pedal/sandbox/timeout.py b/src/lib/pedal/sandbox/timeout.py index be83a578cb..cb88f6de05 100644 --- a/src/lib/pedal/sandbox/timeout.py +++ b/src/lib/pedal/sandbox/timeout.py @@ -1,155 +1,2 @@ -""" -A module that exposes a useful method (`timeout`) that can execute a -function asynchronously and terminiate if it exceeds a given `duration`. -""" - -import sys -import time - -try: - import threading -except BaseException: - threading = None -try: - import ctypes -except BaseException: - ctypes = None - - -class InterruptableThread(threading.Thread): - ''' - A thread that can be interrupted. - ''' - - def __init__(self, func, args, kwargs): - threading.Thread.__init__(self) - self.func, self.args, self.kwargs = func, args, kwargs - self.daemon = True - self.result = None - self.exc_info = (None, None, None) - - def run(self): - ''' - Begin thread execution, calling the `func` that was originally - passed in. - ''' - try: - self.result = self.func(*self.args, **self.kwargs) - except Exception: - self.exc_info = sys.exc_info() - - @staticmethod - def _async_raise(thread_id, exception): - ''' - Static method to raise an error asychronously using the ctypes module. - ''' - # Cache the function for convenience - RaiseAsyncException = ctypes.pythonapi.PyThreadState_SetAsyncExc - - states_modified = RaiseAsyncException(ctypes.c_long(thread_id), - ctypes.py_object(exception)) - if states_modified == 0: - raise ValueError("nonexistent thread id") - elif states_modified > 1: - RaiseAsyncException(thread_id, 0) - raise SystemError("PyThreadState_SetAsyncExc failed") - - def raise_exception(self, exception): - ''' - Trigger a thread ending exception! - ''' - assert self.is_alive(), "thread must be started" - for thread_id, thread in threading._active.items(): - if thread is self: - InterruptableThread._async_raise(thread_id, exception) - return - - def terminate(self): - self.exc_info = sys.exc_info() - self.raise_exception(SystemExit) - - -def timeout(duration, func, *args, **kwargs): - """ - Executes a function and kills it (throwing an exception) if it runs for - longer than the specified duration, in seconds. - """ - - # If libraries are not available, then we execute normally - if None in (threading, ctypes): - return func(*args, **kwargs) - - target_thread = InterruptableThread(func, args, kwargs) - target_thread.start() - target_thread.join(duration) - - if target_thread.is_alive(): - target_thread.terminate() - timeout_exception = TimeoutError('Your code took too long to run ' - '(it was given {} seconds); ' - 'maybe you have an infinite loop?'.format(duration)) - raise timeout_exception - else: - if target_thread.exc_info[0] is not None: - ei = target_thread.exc_info - # Python 2 had the three-argument raise statement; thanks to PEP - # 3109 for showing how to convert that to valid Python 3 statements. - e = ei[0](ei[1]) - e.__traceback__ = ei[2] - e.exc_info = target_thread.exc_info - raise e - - -# ========================================================================= - - -class _TimeoutData: - """ - Port of Craig Estep's AdaptiveTimeout JUnit rule from the VTCS student - library. - """ - - # ------------------------------------------------------------- - def __init__(self, ceiling): - self.ceiling = ceiling # sec - self.maximum = ceiling * 2 # sec - self.minimum = 0.25 # sec - self.threshold = 0.6 - self.rampup = 1.4 - self.rampdown = 0.5 - self.start = self.end = 0 - self.non_terminating_methods = 0 - - # ------------------------------------------------------------- - - def before_test(self): - """ - Call this before a test case runs in order to reset the timer. - """ - self.start = time.time() - - # ------------------------------------------------------------- - - def after_test(self): - """ - Call this after a test case runs. This will examine how long it took - the test to execute, and if it required an amount of time greater than - the current ceiling, it will adaptively adjust the allowed time for - the next test. - """ - self.end = time.time() - diff = self.end - self.start - - if diff > self.ceiling: - self.non_terminating_methods += 1 - - if self.non_terminating_methods >= 2: - if self.ceiling * self.rampdown < self.minimum: - self.ceiling = self.minimum - else: - self.ceiling = (self.ceiling * self.rampdown) - elif diff > self.ceiling * self.threshold: - if self.ceiling * self.rampup > self.maximum: - self.ceiling = self.maximum - else: - self.ceiling = (self.ceiling * self.rampup) +def timeout(delay, func, *args, **kwargs): + return func(*args, **kwargs) \ No newline at end of file diff --git a/src/lib/pedal/sandbox/tracer.py b/src/lib/pedal/sandbox/tracer.py index 6547204939..f63d306306 100644 --- a/src/lib/pedal/sandbox/tracer.py +++ b/src/lib/pedal/sandbox/tracer.py @@ -78,7 +78,7 @@ def __exit__(self, exc_type, exc_val, traceback): self.pc_covered = analysis.numbers.pc_covered self.missing = analysis.missing self.lines = analysis.statements - analysis.missing - + @property def percent_covered(self): return self.pc_covered diff --git a/src/lib/pedal/source/__init__.py b/src/lib/pedal/source/__init__.py index 54a9f902b5..4033ae3e05 100644 --- a/src/lib/pedal/source/__init__.py +++ b/src/lib/pedal/source/__init__.py @@ -91,6 +91,7 @@ def get_program(report=None): report = MAIN_REPORT return report['source']['code'] + def set_source_file(filename, sections=False, independent=False, report=None): if report is None: report = MAIN_REPORT diff --git a/src/lib/pedal/source/sections.py b/src/lib/pedal/source/sections.py index b81205b539..a4b9a4bbf2 100644 --- a/src/lib/pedal/source/sections.py +++ b/src/lib/pedal/source/sections.py @@ -2,18 +2,19 @@ import ast -#def move_to_section(section_number, name, report=None): +# def move_to_section(section_number, name, report=None): # pass def _calculate_section_number(section_index): - return int((section_index+1)/2) + return int((section_index + 1) / 2) + def next_section(name="", report=None): if report is None: report = MAIN_REPORT report.execute_hooks('source.next_section.before') source = report['source'] - #if not report['source']['success']: + # if not report['source']['success']: # return False source['section'] += 2 section_index = source['section'] @@ -24,7 +25,7 @@ def next_section(name="", report=None): if source['independent']: source['code'] = ''.join(sections[section_index]) old_code = ''.join(sections[:section_index]) - source['line_offset'] = len(old_code.split("\n"))-1 + source['line_offset'] = len(old_code.split("\n")) - 1 else: source['code'] = ''.join(sections[:section_index + 1]) report.group = section_index @@ -35,6 +36,7 @@ def next_section(name="", report=None): "{count}, but there were only {found} sections." ).format(count=section_number, found=found)) + def check_section_exists(section_number, report=None): """ Checks that the right number of sections exist. The prologue before the @@ -59,7 +61,7 @@ def verify_section(report=None): if report is None: report = MAIN_REPORT source = report['source'] - #if not source['success']: + # if not source['success']: # return False code = source['code'] try: @@ -69,7 +71,7 @@ def verify_section(report=None): report.attach('Syntax error', category='Syntax', tool='Source', group=source['section'], mistake={'message': "Invalid syntax on line " - + str(e.lineno+source['line_offset'])+"\n", + + str(e.lineno + source['line_offset']) + "\n", 'error': e, 'position': {"line": e.lineno}}) source['success'] = False diff --git a/src/lib/pedal/tifa/builtin_definitions.py b/src/lib/pedal/tifa/builtin_definitions.py index 78aee1588b..9ffe0d6742 100644 --- a/src/lib/pedal/tifa/builtin_definitions.py +++ b/src/lib/pedal/tifa/builtin_definitions.py @@ -5,6 +5,7 @@ SetType, DayType, TimeType, ClassType, LiteralNum) + def get_builtin_module(name): if name == 'matplotlib': return ModuleType('matplotlib', @@ -208,4 +209,4 @@ def get_builtin_function(name): elif name in ("classmethod", "staticmethod"): return FunctionType(name=name, returns='identity') elif name in ("__name__",): - return StrType() \ No newline at end of file + return StrType() diff --git a/src/lib/pedal/tifa/tifa.py b/src/lib/pedal/tifa/tifa.py index 8afec2bc73..8fadd48c0c 100644 --- a/src/lib/pedal/tifa/tifa.py +++ b/src/lib/pedal/tifa/tifa.py @@ -8,7 +8,7 @@ NumType, NoneType, BoolType, TupleType, ListType, StrType, GeneratorType, DictType, ModuleType, SetType, - # FileType, DayType, TimeType, + # FileType, DayType, TimeType, type_from_json, type_to_literal, get_tifa_type, LiteralNum, LiteralBool, LiteralNone, LiteralStr, @@ -118,7 +118,7 @@ def process_code(self, code, filename="__main__"): self.report['tifa']['error'] = error self.report.attach('tifa_error', category='Analyzer', tool='TIFA', mistake={ - 'message': "Could not process code: "+str(error), + 'message': "Could not process code: " + str(error), 'error': error }) return self.report['tifa'] @@ -332,7 +332,7 @@ def _walk_target(self, target, type): if potential_name is not None and result is None: result = potential_name return result - + def visit_AnnAssign(self, node): """ TODO: Implement! @@ -670,7 +670,7 @@ def definition(tifa, call_type, call_name, parameters, call_position): return_state = self.load_variable("*return", call_position) return_value = return_state.type if node.returns: - #self.visit(node.returns) + # self.visit(node.returns) returns = get_tifa_type(node.returns, self) if not are_types_equal(return_value, returns, True): self.report_issue("Multiple Return Types", @@ -794,7 +794,6 @@ def visit_ListComp(self, node): self.visit(generator) return ListType(self.visit(node.elt)) - def visit_NameConstant(self, node): value = node.value if isinstance(value, bool): @@ -802,7 +801,6 @@ def visit_NameConstant(self, node): else: return NoneType() - def visit_Name(self, node): name = node.id if name == "___": diff --git a/src/lib/pedal/tifa/type_definitions.py b/src/lib/pedal/tifa/type_definitions.py index 9b5756b839..8c6d8ce2f6 100644 --- a/src/lib/pedal/tifa/type_definitions.py +++ b/src/lib/pedal/tifa/type_definitions.py @@ -418,7 +418,6 @@ def update_key(self, literal_key, type): self.literals.append(literal_key) self.values.append(type) - def load_attr(self, attr, tifa, callee=None, callee_position=None): if attr == 'items': def _items(tifa, function_type, callee, args, position): @@ -562,7 +561,7 @@ def type_to_literal(type): def get_tifa_type_from_str(value, self): - #if value in custom_types: + # if value in custom_types: # return custom_types[value] if value.lower() in TYPE_STRINGS: return TYPE_STRINGS[value.lower()]() @@ -571,7 +570,7 @@ def get_tifa_type_from_str(value, self): if variable.exists: state = self.load_variable(value) return state.type - #custom_types.add(value) + # custom_types.add(value) return UnknownType() # TODO: handle custom types diff --git a/src/lib/pedal/tifa/type_operations.py b/src/lib/pedal/tifa/type_operations.py index 2faa7d0d83..5fef8fde5a 100644 --- a/src/lib/pedal/tifa/type_operations.py +++ b/src/lib/pedal/tifa/type_operations.py @@ -112,14 +112,14 @@ def are_types_equal(left, right, formal=False): return False return True elif isinstance(left, DictType): - #print(left.empty, left.keys, left.literals, right) + # print(left.empty, left.keys, left.literals, right) if not left.keys and not left.literals: return isinstance(right, DictType) - #print("L", [literal.value for literal in left.literals], [v.singular_name + # print("L", [literal.value for literal in left.literals], [v.singular_name # if not formal and not isinstance(v, FunctionType) # else TYPE_STRINGS[v.name]().singular_name # for v in left.values]) - #print("R", [literal.value for literal in right.literals], [v.singular_name for v in right.values]) + # print("R", [literal.value for literal in right.literals], [v.singular_name for v in right.values]) if left.empty or right.empty: return True elif left.literals is not None and right.literals is not None: diff --git a/src/lib/pedal/toolkit/functions.py b/src/lib/pedal/toolkit/functions.py index 5135d9aec7..9dad896dfa 100644 --- a/src/lib/pedal/toolkit/functions.py +++ b/src/lib/pedal/toolkit/functions.py @@ -136,10 +136,10 @@ def match_signature(name, length, *parameters): TEST_TABLE_HEADER = "
    ArgumentsReturnedExpected
    " + tip + "
    " + tip + "
    " -TEST_TABLE_OUTPUT = TEST_TABLE_HEADER+( +TEST_TABLE_OUTPUT = TEST_TABLE_HEADER + ( "" ) -TEST_TABLE_UNITS = TEST_TABLE_HEADER+( +TEST_TABLE_UNITS = TEST_TABLE_HEADER + ( "" ) GREEN_CHECK = "" @@ -354,7 +354,7 @@ def check_coverage(report=None): visitor.visit(student_ast) lines_in_code = set(visitor.lines) if lines_executed < lines_in_code: - return lines_in_code - lines_executed, len(lines_executed)/len(lines_in_code) + return lines_in_code - lines_executed, len(lines_executed) / len(lines_in_code) else: return False, 1 diff --git a/src/lib/pedal/toolkit/plotting.py b/src/lib/pedal/toolkit/plotting.py index e1263a700b..a7b4b726db 100644 --- a/src/lib/pedal/toolkit/plotting.py +++ b/src/lib/pedal/toolkit/plotting.py @@ -173,7 +173,8 @@ def check_for_plot_r(plt_type, data): "code": "wrong_plt_data", "label": "Plot Data Incorrect"} elif data_found: - return {"message": "You have plotted the right data, but you appear to have not plotted it as a {}.".format(plt_type), + return {"message": "You have plotted the right data, but you appear to have not plotted it as a {}.".format( + plt_type), "code": "wrong_plt_type", "label": "Wrong Plot Type" } diff --git a/src/lib/pedal/toolkit/records.py b/src/lib/pedal/toolkit/records.py index 1f076f832d..de58f567f1 100644 --- a/src/lib/pedal/toolkit/records.py +++ b/src/lib/pedal/toolkit/records.py @@ -29,4 +29,4 @@ def check_record_instance(record_instance, record_type, instance_identifier, typ if len(record_type) != len(record_instance): explain("{} had extra keys that it should not have.".format(instance_identifier)) return False - return True \ No newline at end of file + return True diff --git a/src/lib/pedal/toolkit/signatures.py b/src/lib/pedal/toolkit/signatures.py index 3f814080db..94c0975dd9 100644 --- a/src/lib/pedal/toolkit/signatures.py +++ b/src/lib/pedal/toolkit/signatures.py @@ -108,8 +108,8 @@ def parse_type(node): elif node.ast_name == "List": return "[{}]".format(", ".join([parse_type(n) for n in node.elts])) elif node.ast_name == "Dict": - return "{"+(", ".join(["{}: {}".format(parse_type(k), parse_type(v)) - for k,v in zip(node.keys, node.values)]))+"}" + return "{" + (", ".join(["{}: {}".format(parse_type(k), parse_type(v)) + for k, v in zip(node.keys, node.values)])) + "}" elif node.ast_name == "Subscript": return parse_type(node.value) + "[{}]".format(parse_type_slice(node.slice)) elif node.ast_name == "BoolOp": @@ -138,7 +138,7 @@ def parse_type_value(value, parse_strings=False): return "()" else: return "({})".format("".join(["{}, ".format(parse_type_value(v)) - for v in value])) + for v in value])) elif isinstance(value, dict): if value == {}: return "{}" @@ -286,7 +286,8 @@ def type_check(left, right): left = normalize_type(left) right = normalize_type(right) return check_piece(left, right) - + + def find_colon(str): parens_stack = [] for i, character in enumerate(str): @@ -297,11 +298,14 @@ def find_colon(str): elif character == ':' and not parens_stack: return i return 0 - + + ARGS = ('args:', 'arg:', 'argument:', 'arguments:', 'parameters:', 'params:', 'parameter:', 'param:') ARG_PATTERN = r'(.+)\s*\((.+)\)\s*:(.+)' RETURNS = ('returns:', 'return:') + + def parse_docstring(doc): # First line's indentation may be different from rest - trust first # non empty line after the first one. @@ -354,6 +358,7 @@ def parse_docstring(doc): returns.append(return_type.strip()) return body, args, ' or '.join(returns) + def function_signature(function_name, returns=None, yields=None, prints=None, raises=None, report=None, root=None, **kwargs): @@ -398,7 +403,7 @@ def function_signature(function_name, returns=None, yields=None, return failing_parameters, type_check(parsed_returns, returns) else: return failing_parameters, False - + def class_signature(class_name, report=None, root=None, **attributes): """ diff --git a/src/lib/pedal/toolkit/utilities.py b/src/lib/pedal/toolkit/utilities.py index 04c85dae16..7e5b4fe23a 100644 --- a/src/lib/pedal/toolkit/utilities.py +++ b/src/lib/pedal/toolkit/utilities.py @@ -2,6 +2,7 @@ from pedal.report.imperative import gently, explain from pedal.report.imperative import gently_r, explain_r + def is_top_level(ast_node): ast = parse_program() for element in ast.body: @@ -144,7 +145,7 @@ def prevent_literal(*literals): str_values = [s.s for s in ast.find_all("Str")] num_values = [n.n for n in ast.find_all("Num")] negative_values = find_negatives(ast) - name_values = ([name.id for name in ast.find_all("Name")]+ + name_values = ([name.id for name in ast.find_all("Name")] + [name.value for name in ast.find_all("NameConstant")]) for literal in literals: if isinstance(literal, (int, float)): @@ -181,7 +182,7 @@ def ensure_literal(*literals): str_values = [s.s for s in ast.find_all("Str")] num_values = [n.n for n in ast.find_all("Num")] negative_values = find_negatives(ast) - name_values = ([str(name.id) for name in ast.find_all("Name")]+ + name_values = ([str(name.id) for name in ast.find_all("Name")] + [str(name.value) for name in ast.find_all("NameConstant")]) for literal in literals: if literal in (True, False, None): diff --git a/src/lib/platform.js b/src/lib/platform.js index 8b4410b381..0c4fe173ba 100644 --- a/src/lib/platform.js +++ b/src/lib/platform.js @@ -1,77 +1,73 @@ -var $builtinmodule = function(name){ +var $builtinmodule = function (name) { var mod = {}; var inBrowser = (typeof window != "undefined") && (typeof window.navigator != "undefined"); - mod.python_implementation = new Sk.builtin.func(function() { + mod.python_implementation = new Sk.builtin.func(function () { Sk.builtin.pyCheckArgsLen("python_implementation", arguments.length, 0, 0); return new Sk.builtin.str("Skulpt"); }); - mod.node = new Sk.builtin.func(function() { + mod.node = new Sk.builtin.func(function () { Sk.builtin.pyCheckArgsLen("node", arguments.length, 0, 0); return new Sk.builtin.str(""); }); - mod.version = new Sk.builtin.func(function() { + mod.version = new Sk.builtin.func(function () { Sk.builtin.pyCheckArgsLen("version", arguments.length, 0, 0); return new Sk.builtin.str(""); }); - mod.python_version = new Sk.builtin.func(function() { + mod.python_version = new Sk.builtin.func(function () { var vers; Sk.builtin.pyCheckArgsLen("python_version", arguments.length, 0, 0); if (Sk.__future__.python_version) { vers = "3.2.0"; - } - else { + } else { vers = "2.7.0"; } return new Sk.builtin.str(vers); }); - mod.system = new Sk.builtin.func(function() { + mod.system = new Sk.builtin.func(function () { var sys; Sk.builtin.pyCheckArgsLen("system", arguments.length, 0, 0); if (inBrowser) { sys = window.navigator.appCodeName; - } - else { + } else { sys = ""; } return new Sk.builtin.str(sys); }); - mod.machine = new Sk.builtin.func(function() { + mod.machine = new Sk.builtin.func(function () { var plat; Sk.builtin.pyCheckArgsLen("machine", arguments.length, 0, 0); if (inBrowser) { plat = window.navigator.platform; - } - else { + } else { plat = ""; } return new Sk.builtin.str(plat); }); - mod.release = new Sk.builtin.func(function() { + mod.release = new Sk.builtin.func(function () { var appVers; Sk.builtin.pyCheckArgsLen("release", arguments.length, 0, 0); if (inBrowser) { appVers = window.navigator.appVersion; - } - else { + } else { appVers = ""; } return new Sk.builtin.str(appVers); }); - mod.architecture = new Sk.builtin.func(function() { + mod.architecture = new Sk.builtin.func(function () { Sk.builtin.pyCheckArgsLen("architecture", arguments.length, 0, 0); return new Sk.builtin.tuple([new Sk.builtin.str("64bit"), - new Sk.builtin.str("")]); + new Sk.builtin.str("")]); }); - mod.processor = new Sk.builtin.func(function() { + mod.processor = new Sk.builtin.func(function () { Sk.builtin.pyCheckArgsLen("processor", arguments.length, 0, 0); return new Sk.builtin.str(""); }); diff --git a/src/lib/posixpath.py b/src/lib/posixpath.py index 91bfc77ce1..e48dc38340 100644 --- a/src/lib/posixpath.py +++ b/src/lib/posixpath.py @@ -30,13 +30,13 @@ import genericpath from genericpath import * -__all__ = ["normcase","isabs","join","splitdrive","split","splitext", - "basename","dirname","commonprefix","getsize","getmtime", - "getatime","getctime","islink","exists","lexists","isdir","isfile", - "ismount", "expanduser","expandvars","normpath","abspath", - "samefile","sameopenfile","samestat", - "curdir","pardir","sep","pathsep","defpath","altsep","extsep", - "devnull","realpath","supports_unicode_filenames","relpath", +__all__ = ["normcase", "isabs", "join", "splitdrive", "split", "splitext", + "basename", "dirname", "commonprefix", "getsize", "getmtime", + "getatime", "getctime", "islink", "exists", "lexists", "isdir", "isfile", + "ismount", "expanduser", "expandvars", "normpath", "abspath", + "samefile", "sameopenfile", "samestat", + "curdir", "pardir", "sep", "pathsep", "defpath", "altsep", "extsep", + "devnull", "realpath", "supports_unicode_filenames", "relpath", "commonpath"] @@ -49,10 +49,11 @@ def fspath(val): def _get_sep(path): if isinstance(path, str): - return '/' #'/' + return '/' # '/' else: return '/' + # Normalize the case of a pathname. Trivial in Posix, string.lower on Mac. # On MS-DOS this may also turn slashes into backslashes; however, other # normalizations (such as optimizing '../' away) are not allowed @@ -87,7 +88,7 @@ def join(a, *p): path = a try: if not p: - path[:0] + sep #23780: Ensure compatible data type even if p is null. + path[:0] + sep # 23780: Ensure compatible data type even if p is null. for b in map(fspath, p): if b.startswith(sep): path = b @@ -113,7 +114,7 @@ def split(p): sep = _get_sep(p) i = p.rfind(sep) + 1 head, tail = p[:i], p[i:] - if head and head != sep*len(head): + if head and head != sep * len(head): head = head.rstrip(sep) return head, tail @@ -132,7 +133,9 @@ def splitext(p): sep = '/' extsep = '.' return genericpath._splitext(p, sep, None, extsep) -#splitext.__doc__ = genericpath._splitext.__doc__ + + +# splitext.__doc__ = genericpath._splitext.__doc__ # Split a pathname into a drive specification and the rest of the # path. Useful on DOS/Windows/NT; on Unix, the drive is always empty. @@ -162,7 +165,7 @@ def dirname(p): sep = _get_sep(p) i = p.rfind(sep) + 1 head = p[:i] - if head and head != sep*len(head): + if head and head != sep * len(head): head = head.rstrip(sep) return head @@ -174,6 +177,7 @@ def islink(path): """Test whether a path is a symbolic link""" return False + # Being true for dangling symbolic links is also useful. def lexists(path): @@ -213,11 +217,11 @@ def ismount(path): dev1 = s1.st_dev dev2 = s2.st_dev if dev1 != dev2: - return True # path/.. on a different device as path + return True # path/.. on a different device as path ino1 = s1.st_ino ino2 = s2.st_ino if ino1 == ino2: - return True # path/.. is the same i-node as path + return True # path/.. is the same i-node as path return False @@ -283,6 +287,7 @@ def expanduser(path): _varprog = None _varprogb = None + def expandvars(path): """Expand shell variables of form $var and ${var}. Unknown variables are left unchanged.""" @@ -355,7 +360,7 @@ def normpath(path): # POSIX allows one or two initial slashes, but treats three or more # as single slash. if (initial_slashes and - path.startswith(sep*2) and not path.startswith(sep*3)): + path.startswith(sep * 2) and not path.startswith(sep * 3)): initial_slashes = 2 comps = path.split(sep) new_comps = [] @@ -363,14 +368,14 @@ def normpath(path): if comp in (empty, dot): continue if (comp != dotdot or (not initial_slashes and not new_comps) or - (new_comps and new_comps[-1] == dotdot)): + (new_comps and new_comps[-1] == dotdot)): new_comps.append(comp) elif new_comps: new_comps.pop() comps = new_comps path = sep.join(comps) if initial_slashes: - path = sep*initial_slashes + path + path = sep * initial_slashes + path return path or dot @@ -393,6 +398,7 @@ def realpath(filename): path, ok = _joinrealpath(filename[:0], filename, {}) return abspath(path) + # Join two paths, normalizing and eliminating any symbolic links # encountered in the second path. def _joinrealpath(path, rest, seen): @@ -437,17 +443,18 @@ def _joinrealpath(path, rest, seen): # The symlink is not resolved, so we must have a symlink loop. # Return already resolved part + rest of the path unchanged. return join(newpath, rest), False - seen[newpath] = None # not resolved symlink + seen[newpath] = None # not resolved symlink path, ok = _joinrealpath(path, os.readlink(newpath), seen) if not ok: return join(path, rest), False - seen[newpath] = path # resolved symlink + seen[newpath] = path # resolved symlink return path, True supports_unicode_filenames = (sys.platform == 'darwin') + def relpath(path, start=None): """Return a relative version of a path""" @@ -475,7 +482,7 @@ def relpath(path, start=None): # Work out how much of the filepath is shared by start and path. i = len(commonprefix([start_list, path_list])) - rel_list = [pardir] * (len(start_list)-i) + path_list[i:] + rel_list = [pardir] * (len(start_list) - i) + path_list[i:] if not rel_list: return curdir return join(*rel_list) @@ -524,4 +531,4 @@ def commonpath(paths): return prefix + sep.join(common) except (TypeError, AttributeError): genericpath._check_arg_types('commonpath', *paths) - raise \ No newline at end of file + raise diff --git a/src/lib/pprint.py b/src/lib/pprint.py index f852e95d51..65999191bb 100644 --- a/src/lib/pprint.py +++ b/src/lib/pprint.py @@ -1,4 +1,5 @@ import json + def pprint(obj, indent=1): - print(json.dumps(obj)) \ No newline at end of file + print(json.dumps(obj)) diff --git a/src/lib/processing.js b/src/lib/processing.js index 13e43d96e2..5db2a05591 100644 --- a/src/lib/processing.js +++ b/src/lib/processing.js @@ -20,7 +20,7 @@ var $builtinmodule = function (name) { var environmentClass; var keyboardClass; var mouseClass; - var vectorClass + var vectorClass; var mod = {}; var imList = []; @@ -41,91 +41,91 @@ var $builtinmodule = function (name) { mod.Y = new Sk.builtin.int_(1); mod.Z = new Sk.builtin.int_(2); - mod.R = new Sk.builtin.int_( 3); - mod.G = new Sk.builtin.int_( 4); - mod.B = new Sk.builtin.int_( 5); - mod.A = new Sk.builtin.int_( 6); - - mod.U = new Sk.builtin.int_( 7); - mod.V = new Sk.builtin.int_( 8); - - mod.NX = new Sk.builtin.int_( 9); - mod.NY = new Sk.builtin.int_( 10); - mod.NZ = new Sk.builtin.int_( 11); - - mod.EDGE = new Sk.builtin.int_( 12); - + mod.R = new Sk.builtin.int_(3); + mod.G = new Sk.builtin.int_(4); + mod.B = new Sk.builtin.int_(5); + mod.A = new Sk.builtin.int_(6); + + mod.U = new Sk.builtin.int_(7); + mod.V = new Sk.builtin.int_(8); + + mod.NX = new Sk.builtin.int_(9); + mod.NY = new Sk.builtin.int_(10); + mod.NZ = new Sk.builtin.int_(11); + + mod.EDGE = new Sk.builtin.int_(12); + // Stroke - mod.SR = new Sk.builtin.int_( 13); - mod.SG = new Sk.builtin.int_( 14); - mod.SB = new Sk.builtin.int_( 15); - mod.SA = new Sk.builtin.int_( 16); - - mod.SW = new Sk.builtin.int_( 17); - + mod.SR = new Sk.builtin.int_(13); + mod.SG = new Sk.builtin.int_(14); + mod.SB = new Sk.builtin.int_(15); + mod.SA = new Sk.builtin.int_(16); + + mod.SW = new Sk.builtin.int_(17); + // Transformations (2D and 3D) - mod.TX = new Sk.builtin.int_( 18); - mod.TY = new Sk.builtin.int_( 19); - mod.TZ = new Sk.builtin.int_( 20); - - mod.VX = new Sk.builtin.int_( 21); - mod.VY = new Sk.builtin.int_( 22); - mod.VZ = new Sk.builtin.int_( 23); - mod.VW = new Sk.builtin.int_( 24); - + mod.TX = new Sk.builtin.int_(18); + mod.TY = new Sk.builtin.int_(19); + mod.TZ = new Sk.builtin.int_(20); + + mod.VX = new Sk.builtin.int_(21); + mod.VY = new Sk.builtin.int_(22); + mod.VZ = new Sk.builtin.int_(23); + mod.VW = new Sk.builtin.int_(24); + // Material properties - mod.AR = new Sk.builtin.int_( 25); - mod.AG = new Sk.builtin.int_( 26); - mod.AB = new Sk.builtin.int_( 27); - - mod.DR = new Sk.builtin.int_( 3); - mod.DG = new Sk.builtin.int_( 4); - mod.DB = new Sk.builtin.int_( 5); - mod.DA = new Sk.builtin.int_( 6); - - mod.SPR = new Sk.builtin.int_( 28); - mod.SPG = new Sk.builtin.int_( 29); - mod.SPB = new Sk.builtin.int_( 30); - - mod.SHINE = new Sk.builtin.int_( 31); - - mod.ER = new Sk.builtin.int_( 32); - mod.EG = new Sk.builtin.int_( 33); - mod.EB = new Sk.builtin.int_( 34); - - mod.BEEN_LIT = new Sk.builtin.int_( 35); - - mod.VERTEX_FIELD_COUNT = new Sk.builtin.int_( 36); - + mod.AR = new Sk.builtin.int_(25); + mod.AG = new Sk.builtin.int_(26); + mod.AB = new Sk.builtin.int_(27); + + mod.DR = new Sk.builtin.int_(3); + mod.DG = new Sk.builtin.int_(4); + mod.DB = new Sk.builtin.int_(5); + mod.DA = new Sk.builtin.int_(6); + + mod.SPR = new Sk.builtin.int_(28); + mod.SPG = new Sk.builtin.int_(29); + mod.SPB = new Sk.builtin.int_(30); + + mod.SHINE = new Sk.builtin.int_(31); + + mod.ER = new Sk.builtin.int_(32); + mod.EG = new Sk.builtin.int_(33); + mod.EB = new Sk.builtin.int_(34); + + mod.BEEN_LIT = new Sk.builtin.int_(35); + + mod.VERTEX_FIELD_COUNT = new Sk.builtin.int_(36); + // Shape drawing modes mod.CENTER = new Sk.builtin.int_(3); mod.RADIUS = new Sk.builtin.int_(2); mod.CORNERS = new Sk.builtin.int_(1); mod.CORNER = new Sk.builtin.int_(0); mod.DIAMETER = new Sk.builtin.int_(3); - + // Text vertical alignment modes // Default vertical alignment for text placement - mod.BASELINE = new Sk.builtin.int_( 0); + mod.BASELINE = new Sk.builtin.int_(0); // Align text to the top - mod.TOP = new Sk.builtin.int_( 101); + mod.TOP = new Sk.builtin.int_(101); // Align text from the bottom, using the baseline - mod.BOTTOM = new Sk.builtin.int_( 102); - + mod.BOTTOM = new Sk.builtin.int_(102); + // UV Texture coordinate modes - mod.NORMAL = new Sk.builtin.int_( 1); - mod.NORMALIZED = new Sk.builtin.int_( 1); - mod.IMAGE = new Sk.builtin.int_( 2); - + mod.NORMAL = new Sk.builtin.int_(1); + mod.NORMALIZED = new Sk.builtin.int_(1); + mod.IMAGE = new Sk.builtin.int_(2); + // Text placement modes - mod.MODEL = new Sk.builtin.int_( 4); - mod.SHAPE = new Sk.builtin.int_( 5); - + mod.MODEL = new Sk.builtin.int_(4); + mod.SHAPE = new Sk.builtin.int_(5); + // Lighting modes - mod.AMBIENT = new Sk.builtin.int_( 0); - mod.DIRECTIONAL = new Sk.builtin.int_( 1); + mod.AMBIENT = new Sk.builtin.int_(0); + mod.DIRECTIONAL = new Sk.builtin.int_(1); //POINT: 2, Shared with Shape constant - mod.SPOT = new Sk.builtin.int_( 3); + mod.SPOT = new Sk.builtin.int_(3); // Color modes mod.RGB = new Sk.builtin.int_(1); @@ -133,7 +133,7 @@ var $builtinmodule = function (name) { mod.HSB = new Sk.builtin.int_(3); mod.ALPHA = new Sk.builtin.int_(4); mod.CMYK = new Sk.builtin.int_(5); - + // Image file types mod.TIFF = new Sk.builtin.int_(0); mod.TARGA = new Sk.builtin.int_(1); @@ -154,21 +154,21 @@ var $builtinmodule = function (name) { mod.P3D = new Sk.builtin.int_(2); mod.OPENGL = new Sk.builtin.int_(2); mod.PDF = new Sk.builtin.int_(0); - mod.DXF = new Sk.builtin.int_(0); + mod.DXF = new Sk.builtin.int_(0); // Platform IDs - mod.OTHER = new Sk.builtin.int_( 0); - mod.WINDOWS = new Sk.builtin.int_( 1); - mod.MAXOSX = new Sk.builtin.int_( 2); - mod.LINUX = new Sk.builtin.int_( 3); - - mod.EPSILON = new Sk.builtin.float_( 0.0001); - - mod.MAX_FLOAT = new Sk.builtin.float_( 3.4028235e+38); - mod.MIN_FLOAT = new Sk.builtin.float_( -3.4028235e+38); - mod.MAX_INT = new Sk.builtin.int_( 2147483647); - mod.MIN_INT = new Sk.builtin.int_( -2147483648); - + mod.OTHER = new Sk.builtin.int_(0); + mod.WINDOWS = new Sk.builtin.int_(1); + mod.MAXOSX = new Sk.builtin.int_(2); + mod.LINUX = new Sk.builtin.int_(3); + + mod.EPSILON = new Sk.builtin.float_(0.0001); + + mod.MAX_FLOAT = new Sk.builtin.float_(3.4028235e+38); + mod.MIN_FLOAT = new Sk.builtin.float_(-3.4028235e+38); + mod.MAX_INT = new Sk.builtin.int_(2147483647); + mod.MIN_INT = new Sk.builtin.int_(-2147483648); + // Constants mod.HALF_PI = new Sk.builtin.float_(Math.PI / 2.0); mod.THIRD_PI = new Sk.builtin.float_(Math.PI / 3.0); @@ -177,10 +177,10 @@ var $builtinmodule = function (name) { mod.TAU = new Sk.builtin.float_(Math.PI * 2.0); mod.QUARTER_PI = new Sk.builtin.float_(Math.PI / 4.0); - mod.DEG_TO_RAD = new Sk.builtin.float_( Math.PI / 180); - mod.RAD_TO_DEG = new Sk.builtin.float_( 180 / Math.PI); + mod.DEG_TO_RAD = new Sk.builtin.float_(Math.PI / 180); + mod.RAD_TO_DEG = new Sk.builtin.float_(180 / Math.PI); - mod.WHITESPACE = Sk.builtin.str(" \t\n\r\f\u00A0"); + mod.WHITESPACE = new Sk.builtin.str(" \t\n\r\f\u00A0"); // Shape modes mod.POINT = new Sk.builtin.int_(2); mod.POINTS = new Sk.builtin.int_(2); @@ -202,46 +202,46 @@ var $builtinmodule = function (name) { mod.SPHERE = new Sk.builtin.int_(40); mod.BOX = new Sk.builtin.int_(41); - mod.GROUP = new Sk.builtin.int_( 0); - mod.PRIMITIVE = new Sk.builtin.int_( 1); + mod.GROUP = new Sk.builtin.int_(0); + mod.PRIMITIVE = new Sk.builtin.int_(1); //PATH: 21, // shared with Shape PATH - mod.GEOMETRY = new Sk.builtin.int_( 3); - + mod.GEOMETRY = new Sk.builtin.int_(3); + // Shape Vertex - mod.VERTEX = new Sk.builtin.int_( 0); - mod.BEZIER_VERTEX = new Sk.builtin.int_( 1); - mod.CURVE_VERTEX = new Sk.builtin.int_( 2); - mod.BREAK = new Sk.builtin.int_( 3); - mod.CLOSESHAPE = new Sk.builtin.int_( 4); - + mod.VERTEX = new Sk.builtin.int_(0); + mod.BEZIER_VERTEX = new Sk.builtin.int_(1); + mod.CURVE_VERTEX = new Sk.builtin.int_(2); + mod.BREAK = new Sk.builtin.int_(3); + mod.CLOSESHAPE = new Sk.builtin.int_(4); + // Blend modes - mod.REPLACE = new Sk.builtin.int_(0); - mod.BLEND = new Sk.builtin.int_(1 << 0); - mod.ADD = new Sk.builtin.int_(1 << 1); - mod.SUBTRACT = new Sk.builtin.int_(1 << 2); - mod.LIGHTEST = new Sk.builtin.int_(1 << 3); - mod.DARKEST = new Sk.builtin.int_(1 << 4); + mod.REPLACE = new Sk.builtin.int_(0); + mod.BLEND = new Sk.builtin.int_(1 << 0); + mod.ADD = new Sk.builtin.int_(1 << 1); + mod.SUBTRACT = new Sk.builtin.int_(1 << 2); + mod.LIGHTEST = new Sk.builtin.int_(1 << 3); + mod.DARKEST = new Sk.builtin.int_(1 << 4); mod.DIFFERENCE = new Sk.builtin.int_(1 << 5); - mod.EXCLUSION = new Sk.builtin.int_(1 << 6); - mod.MULTIPLY = new Sk.builtin.int_(1 << 7); - mod.SCREEN = new Sk.builtin.int_(1 << 8); - mod.OVERLAY = new Sk.builtin.int_(1 << 9); + mod.EXCLUSION = new Sk.builtin.int_(1 << 6); + mod.MULTIPLY = new Sk.builtin.int_(1 << 7); + mod.SCREEN = new Sk.builtin.int_(1 << 8); + mod.OVERLAY = new Sk.builtin.int_(1 << 9); mod.HARD_LIGHT = new Sk.builtin.int_(1 << 10); mod.SOFT_LIGHT = new Sk.builtin.int_(1 << 11); - mod.DODGE = new Sk.builtin.int_(1 << 12); - mod.BURN = new Sk.builtin.int_(1 << 13); + mod.DODGE = new Sk.builtin.int_(1 << 12); + mod.BURN = new Sk.builtin.int_(1 << 13); // Color component bit masks - mod.ALPHA_MASK = new Sk.builtin.int_( 0xff000000); - mod.RED_MASK = new Sk.builtin.int_( 0x00ff0000); - mod.GREEN_MASK = new Sk.builtin.int_( 0x0000ff00); - mod.BLUE_MASK = new Sk.builtin.int_( 0x000000ff); - + mod.ALPHA_MASK = new Sk.builtin.int_(0xff000000); + mod.RED_MASK = new Sk.builtin.int_(0x00ff0000); + mod.GREEN_MASK = new Sk.builtin.int_(0x0000ff00); + mod.BLUE_MASK = new Sk.builtin.int_(0x000000ff); + // Projection matrices - mod.CUSTOM = new Sk.builtin.int_( 0); - mod.ORTHOGRAPHIC = new Sk.builtin.int_( 2); - mod.PERSPECTIVE = new Sk.builtin.int_( 3); - + mod.CUSTOM = new Sk.builtin.int_(0); + mod.ORTHOGRAPHIC = new Sk.builtin.int_(2); + mod.PERSPECTIVE = new Sk.builtin.int_(3); + // Cursors mod.ARROW = new Sk.builtin.str("default"); mod.CROSS = new Sk.builtin.str("crosshair"); @@ -249,7 +249,7 @@ var $builtinmodule = function (name) { mod.MOVE = new Sk.builtin.str("move"); mod.TEXT = new Sk.builtin.str("text"); mod.WAIT = new Sk.builtin.str("wait"); - mod.NOCURSOR = Sk.builtin.assk$("url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto", Sk.builtin.int_.str); + mod.NOCURSOR = Sk.builtin.assk$("url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto"); // Hints mod.DISABLE_OPENGL_2X_SMOOTH = new Sk.builtin.int_(1); @@ -267,7 +267,7 @@ var $builtinmodule = function (name) { mod.HINT_COUNT = new Sk.builtin.int_(10); // Shape closing modes - mod.OPEN = new Sk.builtin.int_(1); + mod.OPEN = new Sk.builtin.int_(1); mod.CLOSE = new Sk.builtin.int_(2); // Filter/convert types @@ -281,7 +281,7 @@ var $builtinmodule = function (name) { mod.DILATE = new Sk.builtin.int_(18); // Both key and keyCode will be equal to these values - mod.BACKSPACE = new Sk.builtin.int_( 8); + mod.BACKSPACE = new Sk.builtin.int_(8); mod.TAB = new Sk.builtin.int_(9); mod.ENTER = new Sk.builtin.int_(10); mod.RETURN = new Sk.builtin.int_(13); @@ -358,7 +358,7 @@ var $builtinmodule = function (name) { mod.rect = new Sk.builtin.func(function (x, y, width, height, radius) { var rad; - if (typeof(radius) === "undefined") { + if (typeof (radius) === "undefined") { mod.processing.rect(x.v, y.v, width.v, height.v); } else { mod.processing.rect(x.v, y.v, width.v, height.v, radius.v); @@ -370,13 +370,13 @@ var $builtinmodule = function (name) { }); mod.bezier = new Sk.builtin.func(function (x1, y1, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) { - if (typeof(a7) === "undefined") { - // bezier(x1, y1, cx1, cy1, cx2, cy2, x2, y2); + if (typeof (a7) === "undefined") { + // bezier(x1, y1, cx1, cy1, cx2, cy2, x2, y2); mod.processing.bezier(x1.v, y1.v, a1.v, a2.v, a3.v, a4.v, a5.v, a6.v); - } else { - // bezier(x1, y1, z1, cx1, cy1, cz1, cx2, cy2, cz2, x2, y2, z2); + } else { + // bezier(x1, y1, z1, cx1, cy1, cz1, cx2, cy2, cz2, x2, y2, z2); mod.processing.bezier(x1.v, y1.v, a1.v, a2.v, a3.v, a4.v, a5.v, a6.v, a7.v, a8.v, a9.v, a10.v); - } + } }); mod.alpha = new Sk.builtin.func(function (r, g, b) { @@ -385,830 +385,829 @@ var $builtinmodule = function (name) { // a color object // g, and b may be undefined. If they hold values it will // be assumed that we have an r,g,b color tuple - if (typeof(g) === "undefined") { + if (typeof (g) === "undefined") { return new Sk.builtin.float_(mod.processing.alpha(r.v)); - } else if (typeof(b) === "undefined") { + } else if (typeof (b) === "undefined") { return new Sk.builtin.float_(mod.processing.alpha(r.v, g.v)); } else { return new Sk.builtin.float_(mod.processing.alpha(r.v, g.v, b.v)); - } + } }); mod.ambient = new Sk.builtin.func(function (r, g, b) { - // ambient(gray) - // ambient(red, green blue) + // ambient(gray) + // ambient(red, green blue) // r will be either: // a number in which case the fill will be grayscale // a color object // g, and b may be undefined. If they hold values it will // be assumed that we have an r,g,b color tuple - if (typeof(g) === "undefined") { + if (typeof (g) === "undefined") { mod.processing.ambient(r.v); - } else if (typeof(b) === "undefined") { + } else if (typeof (b) === "undefined") { mod.processing.ambient(r.v, g.v); } else { mod.processing.ambient(r.v, g.v, b.v); - } + } }); mod.ambientLight = new Sk.builtin.func(function (v1, v2, v3, x, y, z) { - // ambientLight(v1,v2,v3) - // ambientLight(v1,v2,v3,x,y,z) - if (typeof(x) === "undefined") { + // ambientLight(v1,v2,v3) + // ambientLight(v1,v2,v3,x,y,z) + if (typeof (x) === "undefined") { mod.processing.ambientLight(v1.v, v2.v, v3.v); - } else if (typeof(y) === "undefined") { + } else if (typeof (y) === "undefined") { mod.processing.ambientLight(v1.v, v2.v, v3.v, x.v); - } else if (typeof(z) === "undefined") { + } else if (typeof (z) === "undefined") { mod.processing.ambientLight(v1.v, v2.v, v3.v, x.v, y.v); } else { mod.processing.ambientLight(v1.v, v2.v, v3.v, x.v, y.v, z.v); - } + } }); mod.beginCamera = new Sk.builtin.func(function () { - mod.processing.beginCamera(); + mod.processing.beginCamera(); }); mod.beginShape = new Sk.builtin.func(function (mode) { - if (typeof(mode) === "undefined") { + if (typeof (mode) === "undefined") { mode = mod.POLYGON; } mod.processing.beginShape(mode.v); }); mod.bezierDetail = new Sk.builtin.func(function (resolution) { - // Sets the resolution at which Beziers display. The default - // value is 20. This function is only useful when using the - // P3D or OPENGL renderer as the default (JAVA2D) renderer - // does not use this information. - if (typeof(resolution) !== "undefined") { + // Sets the resolution at which Beziers display. The default + // value is 20. This function is only useful when using the + // P3D or OPENGL renderer as the default (JAVA2D) renderer + // does not use this information. + if (typeof (resolution) !== "undefined") { resolution = resolution.v; } else { resolution = 20; - } + } mod.processing.bezierDetail(resolution); }); - mod.bezierPoint = new Sk.builtin.func(function (a,b,c,d,t) { - mod.processing.bezierPoint(a.v,b.v,c.v,d.v,t.v); + mod.bezierPoint = new Sk.builtin.func(function (a, b, c, d, t) { + mod.processing.bezierPoint(a.v, b.v, c.v, d.v, t.v); }); - mod.bezierTangent = new Sk.builtin.func(function (a,b,c,d,t) { - mod.processing.bezierTangent(a.v,b.v,c.v,d.v,t.v); + mod.bezierTangent = new Sk.builtin.func(function (a, b, c, d, t) { + mod.processing.bezierTangent(a.v, b.v, c.v, d.v, t.v); }); mod.bezierVertex = new Sk.builtin.func(function (v1, v2, v3, v4, v5, v6, - v7, v8, v9) { - // bezierVertex(cx1, cy1, cx2, cy2, x, y) - // bezierVertex(cx1, cy1, cz1, cx2, cy2, cz2, x, y, z) - if (typeof(v7) === "undefined") { - mod.processing.bezierVertex(v1.v, v2.v, v3.v, v4.v, v5.v, v6.v); - } else if (typeof(v8) === "undefined") { - mod.processing.bezierVertex(v1.v, v2.v, v3.v, v4.v, v5.v, v6.v, - v7.v); - } else if (typeof(v9) === "undefined") { - mod.processing.bezierVertex(v1.v, v2.v, v3.v, v4.v, v5.v, v6.v, - v7.v, v8.v); - } else { - mod.processing.bezierVertex(v1.v, v2.v, v3.v, v4.v, v5.v, v6.v, - v7.v, v8.v, v9.v); - } + v7, v8, v9) { + // bezierVertex(cx1, cy1, cx2, cy2, x, y) + // bezierVertex(cx1, cy1, cz1, cx2, cy2, cz2, x, y, z) + if (typeof (v7) === "undefined") { + mod.processing.bezierVertex(v1.v, v2.v, v3.v, v4.v, v5.v, v6.v); + } else if (typeof (v8) === "undefined") { + mod.processing.bezierVertex(v1.v, v2.v, v3.v, v4.v, v5.v, v6.v, + v7.v); + } else if (typeof (v9) === "undefined") { + mod.processing.bezierVertex(v1.v, v2.v, v3.v, v4.v, v5.v, v6.v, + v7.v, v8.v); + } else { + mod.processing.bezierVertex(v1.v, v2.v, v3.v, v4.v, v5.v, v6.v, + v7.v, v8.v, v9.v); + } }); mod.blend = new Sk.builtin.func(function (v1, v2, v3, v4, v5, - v6, v7, v8, v9, v10) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.float_) { - // blend(x, y,width,height,dx, dy,dwidth,dheight,MODE) - mod.processing.blend(v1.v, v2.v, v3.v, v4.v, v5.v, - v6.v, v7.v, v8.v, v9.v); - } else { - // blend(srcImg,x,y, width, height,dx,dy, dwidth, dheight,MODE) - mod.processing.blend(v1.v, v2.v, v3.v, v4.v, v5.v, - v6.v, v7.v, v8.v, v9.v, v10.v); - } + v6, v7, v8, v9, v10) { + if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.float_) { + // blend(x, y,width,height,dx, dy,dwidth,dheight,MODE) + mod.processing.blend(v1.v, v2.v, v3.v, v4.v, v5.v, + v6.v, v7.v, v8.v, v9.v); + } else { + // blend(srcImg,x,y, width, height,dx,dy, dwidth, dheight,MODE) + mod.processing.blend(v1.v, v2.v, v3.v, v4.v, v5.v, + v6.v, v7.v, v8.v, v9.v, v10.v); + } }); mod.blendColor = new Sk.builtin.func(function (c1, c2, mode) { - // blendColor(c1,c2,MODE) + // blendColor(c1,c2,MODE) var c = Sk.misceval.callsimArray(mod.color, [ - new Sk.builtin.int_(0), - new Sk.builtin.int_(0), - new Sk.builtin.int_(0)]); - c.v = mod.processing.blendColor(c1.v, c2.v, mode.v); - return c; + new Sk.builtin.int_(0), + new Sk.builtin.int_(0), + new Sk.builtin.int_(0)]); + c.v = mod.processing.blendColor(c1.v, c2.v, mode.v); + return c; }); mod.brightness = new Sk.builtin.func(function (r, g, b) { - if (typeof(g) === "undefined") { - return new Sk.builtin.float_(mod.processing.brightness(r.v)); - } else if (typeof(b) === "undefined") { - return new Sk.builtin.float_(mod.processing.brightness(r.v, g.v)); + if (typeof (g) === "undefined") { + return new Sk.builtin.float_(mod.processing.brightness(r.v)); + } else if (typeof (b) === "undefined") { + return new Sk.builtin.float_(mod.processing.brightness(r.v, g.v)); } else { - return new Sk.builtin.float_(mod.processing.brightness(r.v, g.v, b.v)); - } + return new Sk.builtin.float_(mod.processing.brightness(r.v, g.v, b.v)); + } }); mod.camera = new Sk.builtin.func(function (eyeX, eyeY, eyeZ, - centerX, centerY, centerZ, - upX, upY, upZ) { - // camera() - // camera(eyeX, eyeY, eyeZ,centerX, centerY, centerZ,upX, upY, upZ) - if (typeof(eyeX) === "undefined") { - mod.processing.camera(); - } else { - mod.processing.camera(eyeX.v, eyeY.v, eyeZ.v, - centerX.v, centerY.v, centerZ.v, - upX.v, upY.v, upZ.v); - } + centerX, centerY, centerZ, + upX, upY, upZ) { + // camera() + // camera(eyeX, eyeY, eyeZ,centerX, centerY, centerZ,upX, upY, upZ) + if (typeof (eyeX) === "undefined") { + mod.processing.camera(); + } else { + mod.processing.camera(eyeX.v, eyeY.v, eyeZ.v, + centerX.v, centerY.v, centerZ.v, + upX.v, upY.v, upZ.v); + } }); mod.constrain = new Sk.builtin.func(function (value, min, max) { - return new Sk.builtin.float_(mod.processing.constrain(value.v, min.v, max.v)); + return new Sk.builtin.float_(mod.processing.constrain(value.v, min.v, max.v)); }); mod.copy = new Sk.builtin.func(function (v1, v2, v3, v4, v5, - v6, v7, v8, v9) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.float_) { - // copy(x, y,width,height,dx, dy,dwidth,dheight) - mod.processing.copy(v1.v, v2.v, v3.v, v4.v, v5.v, - v6.v, v7.v, v8.v); - } else { - // copy(srcImg,x,y, width, height,dx,dy, dwidth, dheight) - mod.processing.copy(v1.v, v2.v, v3.v, v4.v, v5.v, - v6.v, v7.v, v8.v, v9.v); - } + v6, v7, v8, v9) { + if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.float_) { + // copy(x, y,width,height,dx, dy,dwidth,dheight) + mod.processing.copy(v1.v, v2.v, v3.v, v4.v, v5.v, + v6.v, v7.v, v8.v); + } else { + // copy(srcImg,x,y, width, height,dx,dy, dwidth, dheight) + mod.processing.copy(v1.v, v2.v, v3.v, v4.v, v5.v, + v6.v, v7.v, v8.v, v9.v); + } }); mod.createFont = new Sk.builtin.func(function (name, size, smooth, charset) { - // createFont(name, size) - // createFont(name, size, smooth) - // createFont(name, size, smooth, charset) - var font = Sk.misceval.callsimArray(mod.PFont); - if (typeof(smooth) === "undefined") { - font.v = mod.processing.createFont(name.v, size.v); - } else if (typeof(charset) === "undefined") { - font.v = mod.processing.createFont(name.v, size.v, smooth.v); - } else { - font.v = mod.processing.createFont(name.v, size.v, smooth.v, charset.v); - } - return font; + // createFont(name, size) + // createFont(name, size, smooth) + // createFont(name, size, smooth, charset) + var font = Sk.misceval.callsimArray(mod.PFont); + if (typeof (smooth) === "undefined") { + font.v = mod.processing.createFont(name.v, size.v); + } else if (typeof (charset) === "undefined") { + font.v = mod.processing.createFont(name.v, size.v, smooth.v); + } else { + font.v = mod.processing.createFont(name.v, size.v, smooth.v, charset.v); + } + return font; }); mod.createGraphics = new Sk.builtin.func(function (width, height, renderer, filename) { - // createGraphics(width, height, renderer) - // createGraphics(width, height, renderer, filename) - var graphics = Sk.misceval.callsimArray(mod.PGraphics); - if (typeof(filename) === "undefined") { - graphics.v = mod.processing.createGraphics(width.v, height.v, renderer.v); - } else { - graphics.v = mod.processing.createGraphics(width.v, height.v, renderer.v, filename.v); - } - return graphics; + // createGraphics(width, height, renderer) + // createGraphics(width, height, renderer, filename) + var graphics = Sk.misceval.callsimArray(mod.PGraphics); + if (typeof (filename) === "undefined") { + graphics.v = mod.processing.createGraphics(width.v, height.v, renderer.v); + } else { + graphics.v = mod.processing.createGraphics(width.v, height.v, renderer.v, filename.v); + } + return graphics; }); mod.createImage = new Sk.builtin.func(function (width, height, format) { - var image = Sk.misceval.callsimArray(mod.PImage); - image.v = mod.processing.createImage(width.v, height.v, format.v); - return image; + var image = Sk.misceval.callsimArray(mod.PImage); + image.v = mod.processing.createImage(width.v, height.v, format.v); + return image; }); mod.cursor = new Sk.builtin.func(function (v, x, y) { - // cursor() - // cursor(MODE) - // cursor(image,x,y) - if (typeof(v) === "undefined") { - mod.processing.cursor(); - } else if (typeof(x) === "undefined") { - mod.processing.cursor(v.v); - } else if (typeof(y) === "undefined") { - mod.processing.cursor(v.v, x.v); - } else { - mod.processing.cursor(v.v, x.v, y.v); - } + // cursor() + // cursor(MODE) + // cursor(image,x,y) + if (typeof (v) === "undefined") { + mod.processing.cursor(); + } else if (typeof (x) === "undefined") { + mod.processing.cursor(v.v); + } else if (typeof (y) === "undefined") { + mod.processing.cursor(v.v, x.v); + } else { + mod.processing.cursor(v.v, x.v, y.v); + } }); mod.curve = new Sk.builtin.func(function (v1, v2, v3, v4, - v5, v6, v7, v8, - v9, v10, v11, v12) { - // curve(x1, y1, x2, y2, x3, y3, x4, y4); - // curve(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); - if (typeof(v9) === "undefined") { - mod.processing.curve(v1.v, v2.v, v3.v, v4.v, - v5.v, v6.v, v7.v, v8.v); - } else if (typeof(v10) === "undefined") { - mod.processing.curve(v1.v, v2.v, v3.v, v4.v, - v5.v, v6.v, v7.v, v8.v, - v9.v); - } else if (typeof(v11) === "undefined") { - mod.processing.curve(v1.v, v2.v, v3.v, v4.v, - v5.v, v6.v, v7.v, v8.v, - v9.v, v10.v); - } else if (typeof(v12) === "undefined") { - mod.processing.curve(v1.v, v2.v, v3.v, v4.v, - v5.v, v6.v, v7.v, v8.v, - v9.v, v10.v, v11.v); - } else { - mod.processing.curve(v1.v, v2.v, v3.v, v4.v, - v5.v, v6.v, v7.v, v8.v, - v9.v, v10.v, v11.v, v12.v); - } + v5, v6, v7, v8, + v9, v10, v11, v12) { + // curve(x1, y1, x2, y2, x3, y3, x4, y4); + // curve(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4); + if (typeof (v9) === "undefined") { + mod.processing.curve(v1.v, v2.v, v3.v, v4.v, + v5.v, v6.v, v7.v, v8.v); + } else if (typeof (v10) === "undefined") { + mod.processing.curve(v1.v, v2.v, v3.v, v4.v, + v5.v, v6.v, v7.v, v8.v, + v9.v); + } else if (typeof (v11) === "undefined") { + mod.processing.curve(v1.v, v2.v, v3.v, v4.v, + v5.v, v6.v, v7.v, v8.v, + v9.v, v10.v); + } else if (typeof (v12) === "undefined") { + mod.processing.curve(v1.v, v2.v, v3.v, v4.v, + v5.v, v6.v, v7.v, v8.v, + v9.v, v10.v, v11.v); + } else { + mod.processing.curve(v1.v, v2.v, v3.v, v4.v, + v5.v, v6.v, v7.v, v8.v, + v9.v, v10.v, v11.v, v12.v); + } }); mod.curveDetail = new Sk.builtin.func(function (detail) { - // curveDetail(detail) - mod.processing.curveDetail(detail.v); + // curveDetail(detail) + mod.processing.curveDetail(detail.v); }); - mod.curvePoint = new Sk.builtin.func(function (a,b,c,d,t) { - // curvePoint(a,b,c,d,t) - mod.processing.curvePoint(a.v,b.v,c.v,d.v,t.v); + mod.curvePoint = new Sk.builtin.func(function (a, b, c, d, t) { + // curvePoint(a,b,c,d,t) + mod.processing.curvePoint(a.v, b.v, c.v, d.v, t.v); }); - mod.curveTangent = new Sk.builtin.func(function (a,b,c,d,t) { - // curveTangent(a,b,c,d,t) - mod.processing.curveTangent(a.v,b.v,c.v,d.v,t.v); + mod.curveTangent = new Sk.builtin.func(function (a, b, c, d, t) { + // curveTangent(a,b,c,d,t) + mod.processing.curveTangent(a.v, b.v, c.v, d.v, t.v); }); mod.curveTightness = new Sk.builtin.func(function (squishy) { - // curveTightness(squishy) - mod.processing.curveTightness(squishy.v); + // curveTightness(squishy) + mod.processing.curveTightness(squishy.v); }); mod.curveVertex = new Sk.builtin.func(function (x, y, z) { - // curveVertex(x, y) - // curveVertex(x, y, z) - if (typeof(z) === "undefined") { - mod.processing.curveVertex(x.v, y.v); - } else { - mod.processing.curveVertex(x.v, y.v, z.v); - } + // curveVertex(x, y) + // curveVertex(x, y, z) + if (typeof (z) === "undefined") { + mod.processing.curveVertex(x.v, y.v); + } else { + mod.processing.curveVertex(x.v, y.v, z.v); + } }); mod.day = new Sk.builtin.func(function () { - return new Sk.builtin.int_(mod.processing.day()); + return new Sk.builtin.int_(mod.processing.day()); }); mod.degrees = new Sk.builtin.func(function (angle) { - // degrees(angle) - return new Sk.builtin.float_(mod.processing.degrees(angle.v)); + // degrees(angle) + return new Sk.builtin.float_(mod.processing.degrees(angle.v)); }); - mod.directionalLight = new Sk.builtin.func(function (v1,v2,v3,nx,ny,nz) { - // directionalLight(v1,v2,v3,nx,ny,nz) - mod.processing.directionalLight(v1.v,v2.v,v3.v,nx.v,ny.v,nz.v); + mod.directionalLight = new Sk.builtin.func(function (v1, v2, v3, nx, ny, nz) { + // directionalLight(v1,v2,v3,nx,ny,nz) + mod.processing.directionalLight(v1.v, v2.v, v3.v, nx.v, ny.v, nz.v); }); mod.dist = new Sk.builtin.func(function (x1, y1, z1, x2, y2, z2) { - // dist(x1, y1, x2, y2) - // dist(x1, y1, z1, x2, y2, z2) - if (typeof(y2) === "undefined") { - return new Sk.builtin.float_(mod.processing.dist(x1.v, y1.v, z1.v, x2.v)); - } else if (typeof(z2) === "undefined") { - return new Sk.builtin.float_(mod.processing.dist(x1.v, y1.v, z1.v, x2.v, y2.v)); - } else { - return new Sk.builtin.float_(mod.processing.dist(x1.v, y1.v, z1.v, x2.v, y2.v, z2.v)); - } + // dist(x1, y1, x2, y2) + // dist(x1, y1, z1, x2, y2, z2) + if (typeof (y2) === "undefined") { + return new Sk.builtin.float_(mod.processing.dist(x1.v, y1.v, z1.v, x2.v)); + } else if (typeof (z2) === "undefined") { + return new Sk.builtin.float_(mod.processing.dist(x1.v, y1.v, z1.v, x2.v, y2.v)); + } else { + return new Sk.builtin.float_(mod.processing.dist(x1.v, y1.v, z1.v, x2.v, y2.v, z2.v)); + } }); mod.emissive = new Sk.builtin.func(function (v1, v2, v3) { - // emissive(gray) - // emissive(color) - // emissive(v1,v2,v3) - if (typeof(v2) === "undefined") { - mod.processing.emissive(v1.v); - } else if (typeof(v3) === "undefined") { - mod.processing.emissive(v1.v, v2.v); - } else { - mod.processing.emissive(v1.v, v2.v, v3.v); - } + // emissive(gray) + // emissive(color) + // emissive(v1,v2,v3) + if (typeof (v2) === "undefined") { + mod.processing.emissive(v1.v); + } else if (typeof (v3) === "undefined") { + mod.processing.emissive(v1.v, v2.v); + } else { + mod.processing.emissive(v1.v, v2.v, v3.v); + } }); mod.endCamera = new Sk.builtin.func(function () { - // endCamera() - mod.processing.endCamera(); + // endCamera() + mod.processing.endCamera(); }); mod.endShape = new Sk.builtin.func(function (mode) { - // endShape() - // endShape(MODE) - if (typeof(mode) === "undefined") { - mod.processing.endShape(); - } else { - mod.processing.endShape(mode.v); - } + // endShape() + // endShape(MODE) + if (typeof (mode) === "undefined") { + mod.processing.endShape(); + } else { + mod.processing.endShape(mode.v); + } }); mod.filter = new Sk.builtin.func(function (mode, srcImg) { - // filter(MODE) - // filter(MODE, srcImg) - if (typeof(srcImg) === "undefined") { - mod.processing.filter(mode.v); - } else { - mod.processing.filter(mode.v, srcImg.v); - } + // filter(MODE) + // filter(MODE, srcImg) + if (typeof (srcImg) === "undefined") { + mod.processing.filter(mode.v); + } else { + mod.processing.filter(mode.v, srcImg.v); + } }); mod.frustum = new Sk.builtin.func(function (left, right, bottom, top, near, far) { - // frustum(left, right, bottom,top, near, far) - mod.processing.frustum(left, right, bottom, top, near, far); + // frustum(left, right, bottom,top, near, far) + mod.processing.frustum(left, right, bottom, top, near, far); }); mod.hint = new Sk.builtin.func(function (item) { - // hint(item) - mod.processing.hint(item); + // hint(item) + mod.processing.hint(item); }); mod.hour = new Sk.builtin.func(function () { - return new Sk.builtin.int_(mod.processing.hour()); + return new Sk.builtin.int_(mod.processing.hour()); }); mod.hue = new Sk.builtin.func(function (color) { - // hue(color) - return new Sk.builtin.float_(mod.processing.hue(color.v)); + // hue(color) + return new Sk.builtin.float_(mod.processing.hue(color.v)); }); mod.imageMode = new Sk.builtin.func(function (mode) { - mod.processing.imageMode(mode.v); + mod.processing.imageMode(mode.v); }); mod.lerp = new Sk.builtin.func(function (value1, value2, amt) { - // lerp(value1, value2, amt) - // returns float - return new Sk.builtin.float_(mod.processing.lerp(value1.v, value2.v, amt.v)); + // lerp(value1, value2, amt) + // returns float + return new Sk.builtin.float_(mod.processing.lerp(value1.v, value2.v, amt.v)); }); mod.lerpColor = new Sk.builtin.func(function (c1, c2, amt) { - // lerpColor(c1, c2, amt) - // returns color + // lerpColor(c1, c2, amt) + // returns color var c = Sk.misceval.callsimArray(mod.color, [ - new Sk.builtin.int_(0), - new Sk.builtin.int_(0), - new Sk.builtin.int_(0)]); - c.v = mod.processing.lerpColor(c1.v, c2.v, amt.v); - return c; + new Sk.builtin.int_(0), + new Sk.builtin.int_(0), + new Sk.builtin.int_(0)]); + c.v = mod.processing.lerpColor(c1.v, c2.v, amt.v); + return c; }); mod.lightFalloff = new Sk.builtin.func(function (constant, linear, quadratic) { - // lightFalloff(constant,linear,quadratic) - mod.processing.lightFalloff(constant.v, linear.v, quadratic.v); + // lightFalloff(constant,linear,quadratic) + mod.processing.lightFalloff(constant.v, linear.v, quadratic.v); }); mod.lights = new Sk.builtin.func(function () { - mod.processing.lights(); + mod.processing.lights(); }); mod.lightSpecular = new Sk.builtin.func(function (v1, v2, v3) { - // lightSpecular(v1,v2,v3) - mod.processing.lightSpecular(v1.v, v2.v, v3.v); + // lightSpecular(v1,v2,v3) + mod.processing.lightSpecular(v1.v, v2.v, v3.v); }); mod.loadBytes = new Sk.builtin.func(function (filename) { - // loadBytes(filename) - // returns byte[] - return new Sk.builtin.list(mod.processing.loadBytes(filename.v)); + // loadBytes(filename) + // returns byte[] + return new Sk.builtin.list(mod.processing.loadBytes(filename.v)); }); mod.loadFont = new Sk.builtin.func(function (fontname) { - // loadFont(fontname) - // returns font - var font = Sk.misceval.callsimArray(mod.PFont); - font.v = mod.processing.loadFont(fontname.v); - return font; + // loadFont(fontname) + // returns font + var font = Sk.misceval.callsimArray(mod.PFont); + font.v = mod.processing.loadFont(fontname.v); + return font; }); mod.loadShape = new Sk.builtin.func(function (filename) { - // loadShape(filename) - // returns shape - var shape = Sk.misceval.callsimArray(mod.PShapeSVG, [ - new Sk.builtin.str("string"), - filename]); - return shape; + // loadShape(filename) + // returns shape + var shape = Sk.misceval.callsimArray(mod.PShapeSVG, [ + new Sk.builtin.str("string"), + filename]); + return shape; }); mod.loadStrings = new Sk.builtin.func(function (filename) { - // loadStrings(filename) - // returns string [] - return new Sk.builtin.list(mod.processing.loadStrings(filename.v)); + // loadStrings(filename) + // returns string [] + return new Sk.builtin.list(mod.processing.loadStrings(filename.v)); }); mod.mag = new Sk.builtin.func(function (a, b, c) { - // mag(a,b) - // mag(a,b,c) - // returns magnitude as float - if (typeof(c) === "undefined") { - return new Sk.builtin.float_(mod.processing.mag(a.v, b.v)); + // mag(a,b) + // mag(a,b,c) + // returns magnitude as float + if (typeof (c) === "undefined") { + return new Sk.builtin.float_(mod.processing.mag(a.v, b.v)); } else { - return new Sk.builtin.float_(mod.processing.mag(a.v, b.v, c.v)); - } + return new Sk.builtin.float_(mod.processing.mag(a.v, b.v, c.v)); + } }); - mod.map = new Sk.builtin.func(function (value,low1,high1,low2,high2) { - // map(value,low1,high1,low2,high2) - // returns float - return new Sk.builtin.float_(mod.processing.map(value.v,low1.v,high1.v, - low2.v,high2.v)); + mod.map = new Sk.builtin.func(function (value, low1, high1, low2, high2) { + // map(value,low1,high1,low2,high2) + // returns float + return new Sk.builtin.float_(mod.processing.map(value.v, low1.v, high1.v, + low2.v, high2.v)); }); mod.millis = new Sk.builtin.func(function () { - return new Sk.builtin.int_(mod.processing.millis()); + return new Sk.builtin.int_(mod.processing.millis()); }); mod.minute = new Sk.builtin.func(function () { - return new Sk.builtin.int_(mod.processing.minute()); + return new Sk.builtin.int_(mod.processing.minute()); }); mod.modelX = new Sk.builtin.func(function (x, y, z) { - // modelX(x,y,z) - // returns float - return new Sk.builtin.float_(mod.processing.modelX(x.v, y.v, z.v)); + // modelX(x,y,z) + // returns float + return new Sk.builtin.float_(mod.processing.modelX(x.v, y.v, z.v)); }); mod.modelY = new Sk.builtin.func(function (x, y, z) { - // modelY(x,y,z) - // returns float - return new Sk.builtin.float_(mod.processing.modelY(x.v, y.v, z.v)); + // modelY(x,y,z) + // returns float + return new Sk.builtin.float_(mod.processing.modelY(x.v, y.v, z.v)); }); mod.modelZ = new Sk.builtin.func(function (x, y, z) { - // modelZ(x,y,z) - // returns float - return new Sk.builtin.float_(mod.processing.modelZ(x.v, y.v, z.v)); + // modelZ(x,y,z) + // returns float + return new Sk.builtin.float_(mod.processing.modelZ(x.v, y.v, z.v)); }); mod.month = new Sk.builtin.func(function () { - return new Sk.builtin.int_(mod.processing.month()); + return new Sk.builtin.int_(mod.processing.month()); }); mod.noCursor = new Sk.builtin.func(function () { - mod.processing.noCursor(); + mod.processing.noCursor(); }); mod.noise = new Sk.builtin.func(function (x, y, z) { - // noise(x) - // noise(x, y) - // noise(x, y, z) - // returns float - if (typeof(y) === "undefined") { - return new Sk.builtin.float_(mod.processing.noise(x.v)); - } else if (typeof(z) === "undefined") { - return new Sk.builtin.float_(mod.processing.noise(x.v, y.v)); + // noise(x) + // noise(x, y) + // noise(x, y, z) + // returns float + if (typeof (y) === "undefined") { + return new Sk.builtin.float_(mod.processing.noise(x.v)); + } else if (typeof (z) === "undefined") { + return new Sk.builtin.float_(mod.processing.noise(x.v, y.v)); } else { - return new Sk.builtin.float_(mod.processing.noise(x.v, y.v, z.v)); - } + return new Sk.builtin.float_(mod.processing.noise(x.v, y.v, z.v)); + } }); mod.noiseDetail = new Sk.builtin.func(function (octaves, falloff) { - // noiseDetail(octaves); - // noiseDetail(octaves,falloff); - mod.processing.noiseDetail(octaves.v, falloff.v); + // noiseDetail(octaves); + // noiseDetail(octaves,falloff); + mod.processing.noiseDetail(octaves.v, falloff.v); }); mod.noiseSeed = new Sk.builtin.func(function (value) { - // noiseSeed(value); int - // returns float - return new Sk.builtin.float_(mod.processing.noiseSeed(value.v)); + // noiseSeed(value); int + // returns float + return new Sk.builtin.float_(mod.processing.noiseSeed(value.v)); }); mod.noLights = new Sk.builtin.func(function () { - mod.processing.noLights(); + mod.processing.noLights(); }); mod.norm = new Sk.builtin.func(function (value, low, high) { - // norm(value, low, high) - // returns float - return new Sk.builtin.float_(mod.processing.norm(value.v, low.v, high.v)); + // norm(value, low, high) + // returns float + return new Sk.builtin.float_(mod.processing.norm(value.v, low.v, high.v)); }); mod.normal = new Sk.builtin.func(function (nx, ny, nz) { - // normal(nx,ny,nz) - // returns None - mod.processing.normal(nx.v, ny.v, nz.v); + // normal(nx,ny,nz) + // returns None + mod.processing.normal(nx.v, ny.v, nz.v); }); mod.noTint = new Sk.builtin.func(function () { - mod.processing.noTint(); + mod.processing.noTint(); }); mod.ortho = new Sk.builtin.func(function (left, right, bottom, top, near, far) { - // ortho(left, right, bottom,top, near,far) - // returns None - mod.processing.ortho(left.v, right.v, bottom.v, top.v, near.v, far.v); + // ortho(left, right, bottom,top, near,far) + // returns None + mod.processing.ortho(left.v, right.v, bottom.v, top.v, near.v, far.v); }); mod.perspective = new Sk.builtin.func(function (fov, aspect, zNear, zFar) { - // perspective() - // perspective(fov, aspect, zNear, zFar) - // returns None - if (typeof(fov) === "undefined") { - mod.processing.perspective(); - } else if (typeof(aspect) === "undefined") { - mod.processing.perspective(fov.v); - } else if (typeof(zNear) === "undefined") { - mod.processing.perspective(fov.v, aspect.v); - } else if (typeof(zFar) === "undefined") { - mod.processing.perspective(fov.v, aspect.v, zNear.v); + // perspective() + // perspective(fov, aspect, zNear, zFar) + // returns None + if (typeof (fov) === "undefined") { + mod.processing.perspective(); + } else if (typeof (aspect) === "undefined") { + mod.processing.perspective(fov.v); + } else if (typeof (zNear) === "undefined") { + mod.processing.perspective(fov.v, aspect.v); + } else if (typeof (zFar) === "undefined") { + mod.processing.perspective(fov.v, aspect.v, zNear.v); } else { - mod.processing.perspective(fov.v, aspect.v, zNear.v, zFar.v); - } + mod.processing.perspective(fov.v, aspect.v, zNear.v, zFar.v); + } }); - mod.pointLight = new Sk.builtin.func(function (v1,v2,v3,nx,ny,nz) { - // pointLight(v1,v2,v3,nx,ny,nz) - // returns None - mod.processing.pointLight(v1.v,v2.v,v3.v,nx.v,ny.v,nz.v); + mod.pointLight = new Sk.builtin.func(function (v1, v2, v3, nx, ny, nz) { + // pointLight(v1,v2,v3,nx,ny,nz) + // returns None + mod.processing.pointLight(v1.v, v2.v, v3.v, nx.v, ny.v, nz.v); }); mod.printCamera = new Sk.builtin.func(function () { - // printCamera() - // returns None - mod.processing.printCamera(); + // printCamera() + // returns None + mod.processing.printCamera(); }); mod.println = new Sk.builtin.func(function (data) { - // println(data) - mod.processing.println(data.v); + // println(data) + mod.processing.println(data.v); }); mod.printProjection = new Sk.builtin.func(function () { - // printProjection() - // returns None - mod.processing.printProjection(); + // printProjection() + // returns None + mod.processing.printProjection(); }); mod.radians = new Sk.builtin.func(function (angle) { - // radians(angle) - // returns int or float - return new Sk.builtin.float_(mod.processing.radians(angle.v)); + // radians(angle) + // returns int or float + return new Sk.builtin.float_(mod.processing.radians(angle.v)); }); mod.randomSeed = new Sk.builtin.func(function (value) { - // noiseSeed(value); - // returns float - return new Sk.builtin.float_(mod.processing.randomSeed(value.v)); + // noiseSeed(value); + // returns float + return new Sk.builtin.float_(mod.processing.randomSeed(value.v)); }); mod.random = new Sk.builtin.func(function (v1, v2) { - // random(); - // random(high); - // random(low, high); - // returns float - if (typeof(v1) === "undefined") { - return new Sk.builtin.float_(mod.processing.random()); - } else if (typeof(v2) === "undefined") { - return new Sk.builtin.float_(mod.processing.random(v1.v)); + // random(); + // random(high); + // random(low, high); + // returns float + if (typeof (v1) === "undefined") { + return new Sk.builtin.float_(mod.processing.random()); + } else if (typeof (v2) === "undefined") { + return new Sk.builtin.float_(mod.processing.random(v1.v)); } else { - return new Sk.builtin.float_(mod.processing.random(v1.v, v2.v)); - } + return new Sk.builtin.float_(mod.processing.random(v1.v, v2.v)); + } }); mod.requestImage = new Sk.builtin.func(function (filename, extension) { - // requestImage(filename) - // requestImage(filename, extension) - var image = Sk.misceval.callsimArray(mod.PImage); - if (typeof(extension) === "undefined") { - image.v = mod.processing.requestImage(filename.v); + // requestImage(filename) + // requestImage(filename, extension) + var image = Sk.misceval.callsimArray(mod.PImage); + if (typeof (extension) === "undefined") { + image.v = mod.processing.requestImage(filename.v); } else { - image.v = mod.processing.requestImage(filename.v, extension.v); - } - return image; + image.v = mod.processing.requestImage(filename.v, extension.v); + } + return image; }); mod.saturation = new Sk.builtin.func(function (color) { - // saturation(color) - // returns float - return new Sk.builtin.float_(mod.processing.saturation(color.v)); + // saturation(color) + // returns float + return new Sk.builtin.float_(mod.processing.saturation(color.v)); }); mod.save = new Sk.builtin.func(function (filename) { - // save(filename) - // returns None - mod.processing.save(filename.v); + // save(filename) + // returns None + mod.processing.save(filename.v); }); mod.saveFrame = new Sk.builtin.func(function (filename) { - // saveFrame() - // saveFrame(filename-####.ext) - // returns None - if (typeof(filename) === "undefined") { - mod.processing.saveFrame(); + // saveFrame() + // saveFrame(filename-####.ext) + // returns None + if (typeof (filename) === "undefined") { + mod.processing.saveFrame(); } else { - mod.processing.saveFrame(filename.v); - } + mod.processing.saveFrame(filename.v); + } }); mod.saveStrings = new Sk.builtin.func(function (filename, strings) { - // saveStrings(filename,strings) - mod.processing.saveStrings(filename.v, strings.v); + // saveStrings(filename,strings) + mod.processing.saveStrings(filename.v, strings.v); }); mod.screenX = new Sk.builtin.func(function (x, y, z) { - // screenX(x,y,z) - // returns float - return new Sk.builtin.float_(mod.processing.screenX(x.v, y.v, z.v)); + // screenX(x,y,z) + // returns float + return new Sk.builtin.float_(mod.processing.screenX(x.v, y.v, z.v)); }); mod.screenY = new Sk.builtin.func(function (x, y, z) { - // screenY(x,y,z) - // returns float - return new Sk.builtin.float_(mod.processing.screenY(x.v, y.v, z.v)); + // screenY(x,y,z) + // returns float + return new Sk.builtin.float_(mod.processing.screenY(x.v, y.v, z.v)); }); mod.screenZ = new Sk.builtin.func(function (x, y, z) { - // screenZ(x,y,z) - // returns float - return new Sk.builtin.float_(mod.processing.screenZ(x.v, y.v, z.v)); + // screenZ(x,y,z) + // returns float + return new Sk.builtin.float_(mod.processing.screenZ(x.v, y.v, z.v)); }); mod.second = new Sk.builtin.func(function () { - return new Sk.builtin.int_(mod.processing.second()); + return new Sk.builtin.int_(mod.processing.second()); }); mod.shape = new Sk.builtin.func(function (sh, x, y, width, height) { - // shape(sh) - // shape(sh,x,y) - // shape(sh,x,y,width,height) - // returns? - if (typeof(x) === "undefined") { - mod.processing.shape(sh.v); - } else if (typeof(y) === "undefined") { - mod.processing.shape(sh.v,x.v); - } else if (typeof(width) === "undefined") { - mod.processing.shape(sh.v,x.v,y.v); - } else if (typeof(height) === "undefined") { - mod.processing.shape(sh.v,x.v,y.v,width.v); + // shape(sh) + // shape(sh,x,y) + // shape(sh,x,y,width,height) + // returns? + if (typeof (x) === "undefined") { + mod.processing.shape(sh.v); + } else if (typeof (y) === "undefined") { + mod.processing.shape(sh.v, x.v); + } else if (typeof (width) === "undefined") { + mod.processing.shape(sh.v, x.v, y.v); + } else if (typeof (height) === "undefined") { + mod.processing.shape(sh.v, x.v, y.v, width.v); } else { - mod.processing.shape(sh.v,x.v,y.v,width.v,height.v); - } + mod.processing.shape(sh.v, x.v, y.v, width.v, height.v); + } }); mod.shapeMode = new Sk.builtin.func(function (mode) { - // shapeMode(MODE) - mod.processing.shapeMode(mode.v); + // shapeMode(MODE) + mod.processing.shapeMode(mode.v); }); mod.shininess = new Sk.builtin.func(function (shine) { - // shininess(shine) - // returns None - mod.processing.shininess(shine.v); - }); - - mod.specular = new Sk.builtin.func(function (v1,v2,v3) { - // specular(gray) - // specular(color) - // specular(v1,v2,v3) - if (typeof(v2) === "undefined") { - mod.processing.specular(v1.v); - } else if (typeof(v3) === "undefined") { - mod.processing.specular(v1.v,v2.v); + // shininess(shine) + // returns None + mod.processing.shininess(shine.v); + }); + + mod.specular = new Sk.builtin.func(function (v1, v2, v3) { + // specular(gray) + // specular(color) + // specular(v1,v2,v3) + if (typeof (v2) === "undefined") { + mod.processing.specular(v1.v); + } else if (typeof (v3) === "undefined") { + mod.processing.specular(v1.v, v2.v); } else { - mod.processing.specular(v1.v,v2.v,v3.v); - } + mod.processing.specular(v1.v, v2.v, v3.v); + } }); - mod.spotLight = new Sk.builtin.func(function (v1,v2,v3,nx,ny,nz,angle,concentration) { - // spotLight(v1,v2,v3,nx,ny,nz,angle,concentration) - // returns None - mod.processing.spotLight(v1.v,v2.v,v3.v,nx.v,ny.v,nz.v,angle.v,concentration.v); + mod.spotLight = new Sk.builtin.func(function (v1, v2, v3, nx, ny, nz, angle, concentration) { + // spotLight(v1,v2,v3,nx,ny,nz,angle,concentration) + // returns None + mod.processing.spotLight(v1.v, v2.v, v3.v, nx.v, ny.v, nz.v, angle.v, concentration.v); }); mod.sq = new Sk.builtin.func(function (value) { - // sq(value) - // returns squared number - return new Sk.builtin.float_(mod.processing.sq(value)); + // sq(value) + // returns squared number + return new Sk.builtin.float_(mod.processing.sq(value)); }); mod.status = new Sk.builtin.func(function (text) { - // status(text) - mod.processing.status(text.v); + // status(text) + mod.processing.status(text.v); }); mod.textAlign = new Sk.builtin.func(function (align, yalign) { - // textAlign(ALIGN) - // textAlign(ALIGN, YALIGN) - // returns None - if (typeof(yalign) === "undefined") { - mod.processing.textAlign(align.v); + // textAlign(ALIGN) + // textAlign(ALIGN, YALIGN) + // returns None + if (typeof (yalign) === "undefined") { + mod.processing.textAlign(align.v); } else { - mod.processing.textAlign(align.v, yalign.v); - } + mod.processing.textAlign(align.v, yalign.v); + } }); mod.textAscent = new Sk.builtin.func(function () { - // returns float - return new Sk.builtin.float_(mod.processing.textAscent()); + // returns float + return new Sk.builtin.float_(mod.processing.textAscent()); }); mod.textDescent = new Sk.builtin.func(function () { - // returns float - return new Sk.builtin.float_(mod.processing.textDescent()); + // returns float + return new Sk.builtin.float_(mod.processing.textDescent()); }); mod.textFont = new Sk.builtin.func(function (font, size) { - // textFont(font) - // textFont(font, size) - if (typeof(size) === "undefined") { - mod.processing.textFont(font.v); + // textFont(font) + // textFont(font, size) + if (typeof (size) === "undefined") { + mod.processing.textFont(font.v); } else { - mod.processing.textFont(font.v, size.v); - } + mod.processing.textFont(font.v, size.v); + } }); mod.textLeading = new Sk.builtin.func(function (dist) { - // textLeading(dist) - // returns None - mod.processing.textLeading(dist.v); + // textLeading(dist) + // returns None + mod.processing.textLeading(dist.v); }); mod.textMode = new Sk.builtin.func(function (mode) { - // textMode(MODE) - // returns None - mod.processing.textMode(mode.v); + // textMode(MODE) + // returns None + mod.processing.textMode(mode.v); }); mod.textSize = new Sk.builtin.func(function (size) { - // textSize(size) - // returns None - mod.processing.textSize(size.v); + // textSize(size) + // returns None + mod.processing.textSize(size.v); }); mod.texture = new Sk.builtin.func(function (img) { - // texture(img) - // returns None - mod.processing.texture(img.v); + // texture(img) + // returns None + mod.processing.texture(img.v); }); mod.textureMode = new Sk.builtin.func(function (mode) { - // textureMode(MODE) - // returns None - mod.processing.textureMode(mode.v); + // textureMode(MODE) + // returns None + mod.processing.textureMode(mode.v); }); mod.textWidth = new Sk.builtin.func(function (data) { - // textWidth(data) - // returns float - return new Sk.builtin.float_(mod.processing.textWidth(data.v)); + // textWidth(data) + // returns float + return new Sk.builtin.float_(mod.processing.textWidth(data.v)); }); mod.tint = new Sk.builtin.func(function (v1, v2, v3, v4) { - // tint(gray) - // tint(gray, alpha) - // tint(value1, value2, value3) - // tint(value1, value2, value3, alpha) - // tint(color) - // tint(color, alpha) - // tint(hex) - // tint(hex, alpha) - if (typeof(v2) === "undefined") { - mod.processing.tint(v1.v); - } else if (typeof(v3) === "undefined") { - mod.processing.tint(v1.v, v2.v); - } else if (typeof(v4) === "undefined") { - mod.processing.tint(v1.v, v2.v, v3.v); + // tint(gray) + // tint(gray, alpha) + // tint(value1, value2, value3) + // tint(value1, value2, value3, alpha) + // tint(color) + // tint(color, alpha) + // tint(hex) + // tint(hex, alpha) + if (typeof (v2) === "undefined") { + mod.processing.tint(v1.v); + } else if (typeof (v3) === "undefined") { + mod.processing.tint(v1.v, v2.v); + } else if (typeof (v4) === "undefined") { + mod.processing.tint(v1.v, v2.v, v3.v); } else { - mod.processing.tint(v1.v, v2.v, v3.v, v4.v); - } + mod.processing.tint(v1.v, v2.v, v3.v, v4.v); + } }); mod.updatePixels = new Sk.builtin.func(function () { - // updatePixels() - mod.processing.updatePixels(); + // updatePixels() + mod.processing.updatePixels(); }); mod.vertex = new Sk.builtin.func(function (x, y, z, u, v) { - // vertex(x, y); - // vertex(x, y, z); - // vertex(x, y, u, v); - // vertex(x, y, z, u, v); - if (typeof(z) === "undefined") { - mod.processing.vertex(x.v, y.v); - } else if (typeof(u) === "undefined") { - mod.processing.vertex(x.v, y.v, z.v); - } else if (typeof(v) === "undefined") { - mod.processing.vertex(x.v, y.v, z.v, u.v); + // vertex(x, y); + // vertex(x, y, z); + // vertex(x, y, u, v); + // vertex(x, y, z, u, v); + if (typeof (z) === "undefined") { + mod.processing.vertex(x.v, y.v); + } else if (typeof (u) === "undefined") { + mod.processing.vertex(x.v, y.v, z.v); + } else if (typeof (v) === "undefined") { + mod.processing.vertex(x.v, y.v, z.v, u.v); } else { - mod.processing.vertex(x.v, y.v, z.v, u.v, v.v); - } + mod.processing.vertex(x.v, y.v, z.v, u.v, v.v); + } }); mod.year = new Sk.builtin.func(function () { - return new Sk.builtin.int_(mod.processing.year()); + return new Sk.builtin.int_(mod.processing.year()); }); // 3D Primitives - mod.box = new Sk.builtin.func(function(size) { + mod.box = new Sk.builtin.func(function (size) { mod.processing.box(size.v); }); - mod.sphere = new Sk.builtin.func(function(radius) { + mod.sphere = new Sk.builtin.func(function (radius) { mod.processing.sphere(radius.v); }); - mod.sphereDetail = new Sk.builtin.func(function(res,vres) { - if (typeof(vres) === "undefined") { + mod.sphereDetail = new Sk.builtin.func(function (res, vres) { + if (typeof (vres) === "undefined") { mod.processing.sphereDetail(res.v); - } - else { + } else { mod.processing.sphereDetail(res.v, vres.v); } }); @@ -1216,10 +1215,10 @@ var $builtinmodule = function (name) { // Color mod.background = new Sk.builtin.func(function (r, g, b) { - if (typeof(g) !== "undefined") { + if (typeof (g) !== "undefined") { g = g.v; } - if (typeof(b) !== "undefined") { + if (typeof (b) !== "undefined") { b = b.v; } @@ -1234,13 +1233,13 @@ var $builtinmodule = function (name) { // g, and b may be undefined. If they hold values it will // be assumed that we have an r,g,b color tuple // alpha may also be undefined - if defined, it is the opacity of the fill - if (typeof(g) !== "undefined") { + if (typeof (g) !== "undefined") { g = g.v; } - if (typeof(b) !== "undefined") { + if (typeof (b) !== "undefined") { b = b.v; } - if (typeof(alpha) !== "undefined") { + if (typeof (alpha) !== "undefined") { alpha = alpha.v; } @@ -1251,13 +1250,13 @@ var $builtinmodule = function (name) { mod.stroke = new Sk.builtin.func(function (r, g, b, alpha) { - if (typeof(g) !== "undefined") { + if (typeof (g) !== "undefined") { g = g.v; } - if (typeof(b) !== "undefined") { + if (typeof (b) !== "undefined") { b = b.v; } - if (typeof(alpha) !== "undefined") { + if (typeof (alpha) !== "undefined") { alpha = alpha.v; } @@ -1274,19 +1273,18 @@ var $builtinmodule = function (name) { // mode is one of RGB or HSB // maxV is either the max value for all color elements // or the range for Red/Hue (depending on mode) if maxG and maxB are defined - if (typeof(maxV) === "undefined") { + if (typeof (maxV) === "undefined") { maxV = 255; - } - else { + } else { maxV = maxV.v; } - if (typeof(maxG) !== "undefined") { + if (typeof (maxG) !== "undefined") { maxG = maxG.v; } - if (typeof(maxB) !== "undefined") { + if (typeof (maxB) !== "undefined") { maxB = maxB.v; } - if (typeof(maxAlpha) !== "undefined") { + if (typeof (maxAlpha) !== "undefined") { maxAlpha = maxAlpha.v; } @@ -1334,7 +1332,7 @@ var $builtinmodule = function (name) { // NOTE: difference with ProcessingJS // Use environment.frameCount - + // NOTE: difference with ProcessingJS // Use environment.online @@ -1346,7 +1344,7 @@ var $builtinmodule = function (name) { mod.renderMode = mod.P2D; mod.size = new Sk.builtin.func(function (w, h, mode) { - if (typeof(mode) === "undefined") { + if (typeof (mode) === "undefined") { mode = mod.P2D; } mod.processing.size(w.v, h.v, mode.v); @@ -1420,25 +1418,25 @@ var $builtinmodule = function (name) { mod.processing.rotate(rads.v); }); - mod.rotateX = new Sk.builtin.func(function(rads) { + mod.rotateX = new Sk.builtin.func(function (rads) { mod.processing.rotateX(rads.v); }); - mod.rotateY = new Sk.builtin.func(function(rads) { + mod.rotateY = new Sk.builtin.func(function (rads) { mod.processing.rotateY(rads.v); }); - mod.rotateZ = new Sk.builtin.func(function(rads) { + mod.rotateZ = new Sk.builtin.func(function (rads) { mod.processing.rotateZ(rads.v); }); mod.scale = new Sk.builtin.func(function (sx, sy, sz) { - if (typeof(sy) === "undefined") { + if (typeof (sy) === "undefined") { sy = 1.0; } else { sy = sy.v; } - if (typeof(sz) === "undefined") { + if (typeof (sz) === "undefined") { sz = 1.0; } else { sz = sz.v; @@ -1447,12 +1445,12 @@ var $builtinmodule = function (name) { }); mod.translate = new Sk.builtin.func(function (sx, sy, sz) { - if (typeof(sy) === "undefined") { + if (typeof (sy) === "undefined") { sy = 1.0; } else { sy = sy.v; } - if (typeof(sz) === "undefined") { + if (typeof (sz) === "undefined") { sz = 1.0; } else { sz = sz.v; @@ -1460,30 +1458,30 @@ var $builtinmodule = function (name) { mod.processing.translate(sx.v, sy, sz); }); - mod.popMatrix = new Sk.builtin.func(function() { + mod.popMatrix = new Sk.builtin.func(function () { mod.processing.popMatrix(); }); - mod.pushMatrix = new Sk.builtin.func(function() { + mod.pushMatrix = new Sk.builtin.func(function () { mod.processing.pushMatrix(); }); - mod.applyMatrix = new Sk.builtin.func(function() { + mod.applyMatrix = new Sk.builtin.func(function () { var args = Array.prototype.slice.call(arguments, 0, 16), i; for (i = 0; i < args.length; i++) { - args[i] = typeof(args[i]) === "undefined" ? 0.0 : args[i].v; + args[i] = typeof (args[i]) === "undefined" ? 0.0 : args[i].v; } mod.processing.applyMatrix.apply(mod.processing, args); }); - mod.resetMatrix = new Sk.builtin.func(function() { + mod.resetMatrix = new Sk.builtin.func(function () { mod.processing.resetMatrix(); }); - mod.printMatrix = new Sk.builtin.func(function() { + mod.printMatrix = new Sk.builtin.func(function () { return Sk.ffi.remapToPy(mod.processing.printMatrix()); }); @@ -1495,7 +1493,7 @@ var $builtinmodule = function (name) { // // ////////////////////////////////////////////////////////////////////// mod.run = new Sk.builtin.func(function () { - function sketchProc (processing) { + function sketchProc(processing) { mod.processing = processing; // processing.setup = function() { @@ -1518,8 +1516,7 @@ var $builtinmodule = function (name) { if (wait === true) { if (looping === true) { return; - } - else { + } else { processing.loop(); return; } @@ -1532,13 +1529,12 @@ var $builtinmodule = function (name) { mod.frameCount = processing.frameCount; if (Sk.globals["draw"]) { - try { - Sk.misceval.callsimArray(Sk.globals["draw"]); - } - catch(e) { + try { + Sk.misceval.callsimArray(Sk.globals["draw"]); + } catch (e) { Sk.uncaughtException(e); } - } + } }; var callBacks = ["setup", "mouseMoved", "mouseClicked", "mouseDragged", "mouseMoved", "mouseOut", @@ -1553,11 +1549,13 @@ var $builtinmodule = function (name) { var canvas = document.getElementById(Sk.canvas); if (!canvas) { - throw new Error("Processing module: Canvas element not specified") + throw new Error("Processing module: Canvas element not specified"); } - window.Processing.logger = { log : function(message) { - Sk.misceval.print_(message); - }}; + window.Processing.logger = { + log: function (message) { + Sk.misceval.print_(message); + } + }; // if a Processing instance already exists it's likely still running, stop it by exiting instance = window.Processing.getInstanceById(Sk.canvas); if (instance) { @@ -1575,23 +1573,18 @@ var $builtinmodule = function (name) { mouseClass = function ($gbl, $loc) { $loc.__getattr__ = new Sk.builtin.func(function (self, key) { - key = Sk.ffi.remapToJs(key); + key = Sk.ffi.remapToJs(key); if (key === "x") { return Sk.builtin.assk$(mod.processing.mouseX); - } - else if (key === "y") { + } else if (key === "y") { return Sk.builtin.assk$(mod.processing.mouseY); - } - else if (key === "px") { + } else if (key === "px") { return Sk.builtin.assk$(mod.processing.pmouseX); - } - else if (key === "py") { + } else if (key === "py") { return Sk.builtin.assk$(mod.processing.pmouseY); - } - else if (key === "pressed") { - return new Sk.builtin.bool(mod.processing.__mousePressed); - } - else if (key === "button") { + } else if (key === "pressed") { + return new Sk.builtin.bool(mod.processing.__mousePressed); + } else if (key === "button") { return Sk.builtin.assk$(mod.processing.mouseButton); } }); @@ -1606,14 +1599,12 @@ var $builtinmodule = function (name) { keyboardClass = function ($gbl, $loc) { $loc.__getattr__ = new Sk.builtin.func(function (self, key) { - key = Sk.ffi.remapToJs(key); + key = Sk.ffi.remapToJs(key); if (key === "key") { return new Sk.builtin.str(mod.processing.key.toString()); - } - else if (key === "keyCode") { + } else if (key === "keyCode") { return Sk.builtin.assk$(mod.processing.keyCode); - } - else if (key === "keyPressed") { + } else if (key === "keyPressed") { return new Sk.builtin.str(mod.processing.keyPressed); } // todo bool }); @@ -1628,23 +1619,18 @@ var $builtinmodule = function (name) { environmentClass = function ($gbl, $loc) { $loc.__getattr__ = new Sk.builtin.func(function (self, key) { - key = Sk.ffi.remapToJs(key); + key = Sk.ffi.remapToJs(key); if (key === "frameCount") { return Sk.builtin.assk$(mod.processing.frameCount); - } - else if (key === "frameRate") { + } else if (key === "frameRate") { return Sk.builtin.assk$(mod.processing.frameRate); - } - else if (key === "height") { + } else if (key === "height") { return Sk.builtin.assk$(mod.processing.height); - } - else if (key === "width") { + } else if (key === "width") { return Sk.builtin.assk$(mod.processing.width); - } - else if (key === "online") { + } else if (key === "online") { return new Sk.builtin.bool(mod.processing.online); - } - else if (key === "focused") { + } else if (key === "focused") { return new Sk.builtin.bool(mod.processing.focused); } }); @@ -1662,14 +1648,12 @@ var $builtinmodule = function (name) { }); $loc.__getattr__ = new Sk.builtin.func(function (self, key) { - key = Sk.ffi.remapToJs(key); + key = Sk.ffi.remapToJs(key); if (key === "height") { return Sk.builtin.assk$(mod.processing.height); - } - else if (key === "width") { + } else if (key === "width") { return Sk.builtin.assk$(mod.processing.width); - } - else if (key === "pixels") { + } else if (key === "pixels") { if (self.pixels == null) { self.pixels = new Sk.builtin.list(mod.processing.pixels.toArray()); } @@ -1691,13 +1675,13 @@ var $builtinmodule = function (name) { colorClass = function ($gbl, $loc) { /* images are loaded async.. so its best to preload them */ $loc.__init__ = new Sk.builtin.func(function (self, val1, val2, val3, alpha) { - if (typeof(val2) !== "undefined") { + if (typeof (val2) !== "undefined") { val2 = val2.v; } - if (typeof(val3) !== "undefined") { + if (typeof (val3) !== "undefined") { val3 = val3.v; } - if (typeof(alpha) !== "undefined") { + if (typeof (alpha) !== "undefined") { alpha = alpha.v; } self.v = mod.processing.color(val1.v, val2, val3, alpha); @@ -1724,23 +1708,23 @@ var $builtinmodule = function (name) { imageClass = function ($gbl, $loc) { /* images are loaded async.. so its best to preload them */ $loc.__init__ = new Sk.builtin.func(function (self, arg1, arg2, arg3) { - // PImage() - // PImage(img) - // PImage(width,height) - // PImage(width,height,format) - if (typeof(arg1) === "undefined") { - self.v = new mod.processing.PImage(); - } else if (typeof(arg2) === "undefined") { - self.v = new mod.processing.PImage(arg1.v); - } else if (typeof(arg3) === "undefined") { - self.v = new mod.processing.PImage(arg1.v, arg2.v); - } else { - self.v = new mod.processing.PImage(arg1.v, arg2.v, arg3.v); - } + // PImage() + // PImage(img) + // PImage(width,height) + // PImage(width,height,format) + if (typeof (arg1) === "undefined") { + self.v = new mod.processing.PImage(); + } else if (typeof (arg2) === "undefined") { + self.v = new mod.processing.PImage(arg1.v); + } else if (typeof (arg3) === "undefined") { + self.v = new mod.processing.PImage(arg1.v, arg2.v); + } else { + self.v = new mod.processing.PImage(arg1.v, arg2.v, arg3.v); + } }); $loc.__getattr__ = new Sk.builtin.func(function (self, key) { - key = Sk.ffi.remapToJs(key); + key = Sk.ffi.remapToJs(key); if (key === "width") { return Sk.builtin.assk$(self.v.width); } @@ -1754,17 +1738,17 @@ var $builtinmodule = function (name) { mod.loadImage = new Sk.builtin.func(function (imfile) { var i = mod.processing.loadImage(imfile.v); imList.push(i); - var image = Sk.misceval.callsimArray(mod.PImage); - image.v = i; + var image = Sk.misceval.callsimArray(mod.PImage); + image.v = i; return image; }); mod.image = new Sk.builtin.func(function (im, x, y, w, h) { - // image(img, x, y) - // image(img, x, y, width, height) - if (typeof(w) === "undefined") { + // image(img, x, y) + // image(img, x, y, width, height) + if (typeof (w) === "undefined") { mod.processing.image(im.v, x.v, y.v); - } else { + } else { mod.processing.image(im.v, x.v, y.v, w.v, h.v); } }); @@ -1785,266 +1769,266 @@ var $builtinmodule = function (name) { vectorClass = function ($gbl, $loc) { $loc.__init__ = new Sk.builtin.func(function (self, x, y, z) { - // PVector() - // PVector(x,y) - // PVector(x,y,z) - if (typeof(x) === "undefined") { - self.v = new mod.processing.PVector(); - } else if (typeof(z) === "undefined") { - self.v = new mod.processing.PVector(x.v, y.v); + // PVector() + // PVector(x,y) + // PVector(x,y,z) + if (typeof (x) === "undefined") { + self.v = new mod.processing.PVector(); + } else if (typeof (z) === "undefined") { + self.v = new mod.processing.PVector(x.v, y.v); } else { - self.v = new mod.processing.PVector(x.v, y.v, z.v); - } + self.v = new mod.processing.PVector(x.v, y.v, z.v); + } }); $loc.__getattr__ = new Sk.builtin.func(function (self, key) { - key = Sk.ffi.remapToJs(key); + key = Sk.ffi.remapToJs(key); if (key === "x") { return Sk.builtin.assk$(self.v.x); } else if (key === "y") { return Sk.builtin.assk$(self.v.y); } else if (key === "z") { return Sk.builtin.assk$(self.v.z); - } - }); - + } + }); + $loc.get = new Sk.builtin.func(function (self) { - // get() Gets a copy of the vector + // get() Gets a copy of the vector var new_vec = Sk.misceval.callsimArray(mod.PVector); - new_vec.v = self.v.get(); - return new_vec; - }); - - $loc.set = new Sk.builtin.func(function (self, x, y, x) { - // set() Sets the x, y, z component of the vector - if (typeof(z) === "undefined") { - self.v.set(x.v, y.v); - } else { - self.v.set(x.v, y.v, z.v); - } - }); - - $loc.mag = new Sk.builtin.func(function (self) { - // mag() Calculates the magnitude (length) of the vector - // and returns the result as a float - return Sk.builtin.assk$(self.v.mag()); - }); - - $loc.add = new Sk.builtin.func(function (self, vec) { - // add() Adds one vector to another + new_vec.v = self.v.get(); + return new_vec; + }); + + $loc.set = new Sk.builtin.func(function (self, x, y, x) { + // set() Sets the x, y, z component of the vector + if (typeof (z) === "undefined") { + self.v.set(x.v, y.v); + } else { + self.v.set(x.v, y.v, z.v); + } + }); + + $loc.mag = new Sk.builtin.func(function (self) { + // mag() Calculates the magnitude (length) of the vector + // and returns the result as a float + return Sk.builtin.assk$(self.v.mag()); + }); + + $loc.add = new Sk.builtin.func(function (self, vec) { + // add() Adds one vector to another var new_vec = Sk.misceval.callsimArray(mod.PVector); - new_vec.v = self.v.add(vec.v); - return new_vec; - }); + new_vec.v = self.v.add(vec.v); + return new_vec; + }); - $loc.sub = new Sk.builtin.func(function (self, vec) { - // sub() Subtracts one vector from another + $loc.sub = new Sk.builtin.func(function (self, vec) { + // sub() Subtracts one vector from another var new_vec = Sk.misceval.callsimArray(mod.PVector); - new_vec.v = self.v.sub(vec.v); - return new_vec; - }); + new_vec.v = self.v.sub(vec.v); + return new_vec; + }); - $loc.mult = new Sk.builtin.func(function (self, vec) { - // mult() Multiplies the vector by a scalar + $loc.mult = new Sk.builtin.func(function (self, vec) { + // mult() Multiplies the vector by a scalar var new_vec = Sk.misceval.callsimArray(mod.PVector); - new_vec.v = self.v.mult(vec.v); - return new_vec; - }); + new_vec.v = self.v.mult(vec.v); + return new_vec; + }); - $loc.div = new Sk.builtin.func(function (self, vec) { - // div() Divides the vector by a scalar + $loc.div = new Sk.builtin.func(function (self, vec) { + // div() Divides the vector by a scalar var new_vec = Sk.misceval.callsimArray(mod.PVector); - new_vec.v = self.v.dic(vec.v); - return new_vec; - }); - - $loc.dist = new Sk.builtin.func(function (self, vec) { - // dist() Calculate the Euclidean distance between two points - return Sk.builtin.assk$(self.v.dist(vec.v)); - }); - - $loc.dot = new Sk.builtin.func(function (self, v1, v2, v3) { - // dot() Calculates the dot product - // returns float - // vec.dot(x,y,z) - // vec.dot(v) - if (typeof(v2) === 'undefined') { - return Sk.builtin.assk$(self.v.dot(v1.v)); - } else { - return Sk.builtin.assk$(self.v.dot(v1.v, v2.v, v3.v)); - } - }); - - $loc.cross = new Sk.builtin.func(function (self, vec) { - // cross() Calculates the cross product + new_vec.v = self.v.dic(vec.v); + return new_vec; + }); + + $loc.dist = new Sk.builtin.func(function (self, vec) { + // dist() Calculate the Euclidean distance between two points + return Sk.builtin.assk$(self.v.dist(vec.v)); + }); + + $loc.dot = new Sk.builtin.func(function (self, v1, v2, v3) { + // dot() Calculates the dot product + // returns float + // vec.dot(x,y,z) + // vec.dot(v) + if (typeof (v2) === "undefined") { + return Sk.builtin.assk$(self.v.dot(v1.v)); + } else { + return Sk.builtin.assk$(self.v.dot(v1.v, v2.v, v3.v)); + } + }); + + $loc.cross = new Sk.builtin.func(function (self, vec) { + // cross() Calculates the cross product var new_vec = Sk.misceval.callsimArray(mod.PVector); - new_vec.v = self.v.cross(vec.v); - return new_vec; - }); - - $loc.normalize = new Sk.builtin.func(function (self) { - // normalize() Normalizes the vector - self.v.normalize(); - }); - - $loc.limit = new Sk.builtin.func(function (self, value) { - // limit() Limits the magnitude of the vector - self.v.limit(value.v); - }); - - $loc.angleBetween = new Sk.builtin.func(function (self, vec) { - // angleBetween() Calculates the angle between two vectors - return Sk.builtin.assk$(self.v.angleBetween(vec.v)); - }); - - $loc.array = new Sk.builtin.func(function (self) { - // array() - return new Sk.builtin.list(self.v.array()); - }); + new_vec.v = self.v.cross(vec.v); + return new_vec; + }); + + $loc.normalize = new Sk.builtin.func(function (self) { + // normalize() Normalizes the vector + self.v.normalize(); + }); + + $loc.limit = new Sk.builtin.func(function (self, value) { + // limit() Limits the magnitude of the vector + self.v.limit(value.v); + }); + + $loc.angleBetween = new Sk.builtin.func(function (self, vec) { + // angleBetween() Calculates the angle between two vectors + return Sk.builtin.assk$(self.v.angleBetween(vec.v)); + }); + + $loc.array = new Sk.builtin.func(function (self) { + // array() + return new Sk.builtin.list(self.v.array()); + }); }; fontClass = function ($gbl, $loc) { $loc.__init__ = new Sk.builtin.func(function (self, input) { - // PFont() - // PFont(input) - if (typeof(input) === "undefined") { - self.v = new mod.processing.PFont(); + // PFont() + // PFont(input) + if (typeof (input) === "undefined") { + self.v = new mod.processing.PFont(); } else { - self.v = new mod.processing.PVector(input.v); - } + self.v = new mod.processing.PVector(input.v); + } }); $loc.list = new Sk.builtin.func(function (self) { - // font.list() - return new Sk.builtin.list(self.v.list()); - }); + // font.list() + return new Sk.builtin.list(self.v.list()); + }); }; graphicsClass = function ($gbl, $loc) { $loc.__init__ = new Sk.builtin.func(function (self, x, y, z) { - // PGraphics() - // PGraphics(width,height) - // PGraphics(width,height,applet) - if (typeof(x) === "undefined") { - self.v = new mod.processing.PVector(); - } else if (typeof(z) === "undefined") { - self.v = new mod.processing.PVector(x.v, y.v); + // PGraphics() + // PGraphics(width,height) + // PGraphics(width,height,applet) + if (typeof (x) === "undefined") { + self.v = new mod.processing.PVector(); + } else if (typeof (z) === "undefined") { + self.v = new mod.processing.PVector(x.v, y.v); } else { - self.v = new mod.processing.PVector(x.v, y.v, z.v); - } + self.v = new mod.processing.PVector(x.v, y.v, z.v); + } }); $loc.beginDraw = new Sk.builtin.func(function (self) { - self.v.beginDraw(); - }); + self.v.beginDraw(); + }); $loc.endDraw = new Sk.builtin.func(function (self) { - self.v.endDraw(); - }); + self.v.endDraw(); + }); }; - + shapeClass = function ($gbl, $loc) { $loc.__init__ = new Sk.builtin.func(function (self, arg1, arg2, arg3) { - if (typeof(arg1) === "undefined") { - // special version for Skulpt - self.v = null; - // Will fill in manually in getChild() - } else if (typeof(arg2) === "undefined") { - self.v = new mod.processing.PShapeSVG(arg1.v); - } else if (typeof(arg3) === "undefined") { - self.v = new mod.processing.PShapeSVG(arg1.v, arg2.v); - } else { - self.v = new mod.processing.PShapeSVG(arg1.v, arg2.v, arg3.v); - } + if (typeof (arg1) === "undefined") { + // special version for Skulpt + self.v = null; + // Will fill in manually in getChild() + } else if (typeof (arg2) === "undefined") { + self.v = new mod.processing.PShapeSVG(arg1.v); + } else if (typeof (arg3) === "undefined") { + self.v = new mod.processing.PShapeSVG(arg1.v, arg2.v); + } else { + self.v = new mod.processing.PShapeSVG(arg1.v, arg2.v, arg3.v); + } }); $loc.__getattr__ = new Sk.builtin.func(function (self, key) { - key = Sk.ffi.remapToJs(key); + key = Sk.ffi.remapToJs(key); if (key === "width") { return Sk.builtin.assk$(self.v.width); } else if (key === "height") { return Sk.builtin.assk$(self.v.height); - } - }); + } + }); $loc.isVisible = new Sk.builtin.func(function (self) { - // isVisible() Returns a boolean value "true" if the image is set to be visible, "false" if not - return new Sk.builtin.bool(self.v.isVisible()); - }); + // isVisible() Returns a boolean value "true" if the image is set to be visible, "false" if not + return new Sk.builtin.bool(self.v.isVisible()); + }); $loc.setVisible = new Sk.builtin.func(function (self, value) { - // setVisible() Sets the shape to be visible or invisible - self.v.setVisible(value.v); - }); + // setVisible() Sets the shape to be visible or invisible + self.v.setVisible(value.v); + }); $loc.disableStyle = new Sk.builtin.func(function (self) { - // disableStyle() Disables the shape's style data and uses Processing styles - self.v.disableStyle(); - }); + // disableStyle() Disables the shape's style data and uses Processing styles + self.v.disableStyle(); + }); $loc.enableStyle = new Sk.builtin.func(function (self) { - // enableStyle() Enables the shape's style data and ignores the Processing styles - self.v.enableStyle(); - }); + // enableStyle() Enables the shape's style data and ignores the Processing styles + self.v.enableStyle(); + }); $loc.getChild = new Sk.builtin.func(function (self, shape) { - // getChild() Returns a child element of a shape as a PShapeSVG object - var child = self.v.getChild(shape.v); - if (child != null) { - // special method for Skulpt: - var new_shape = Sk.misceval.callsimArray(mod.PShapeSVG); - // Now fill in value: - new_shape.v = child; - return new_shape; - } else { - return null; - } - }); + // getChild() Returns a child element of a shape as a PShapeSVG object + var child = self.v.getChild(shape.v); + if (child != null) { + // special method for Skulpt: + var new_shape = Sk.misceval.callsimArray(mod.PShapeSVG); + // Now fill in value: + new_shape.v = child; + return new_shape; + } else { + return null; + } + }); $loc.translate = new Sk.builtin.func(function (self, x, y, z) { - // translate() Displaces the shape - // sh.translate(x,y) - // sh.translate(x,y,z) - if (typeof(z) === "undefined") { - self.v.translate(x.v, y.v); - } else { - self.v.translate(x.v, y.v, z.v); - } - }); + // translate() Displaces the shape + // sh.translate(x,y) + // sh.translate(x,y,z) + if (typeof (z) === "undefined") { + self.v.translate(x.v, y.v); + } else { + self.v.translate(x.v, y.v, z.v); + } + }); $loc.rotate = new Sk.builtin.func(function (self, angle) { - // rotate() Rotates the shape - self.v.rotate(angle.v); - }); + // rotate() Rotates the shape + self.v.rotate(angle.v); + }); $loc.rotateX = new Sk.builtin.func(function (self, angle) { - // rotateX() Rotates the shape around the x-axis - self.v.rotateX(angle.v); - }); + // rotateX() Rotates the shape around the x-axis + self.v.rotateX(angle.v); + }); $loc.rotateY = new Sk.builtin.func(function (self) { - // rotateY() Rotates the shape around the y-axis - self.v.rotateY(angle.v); - }); + // rotateY() Rotates the shape around the y-axis + self.v.rotateY(angle.v); + }); $loc.rotateZ = new Sk.builtin.func(function (self) { - // rotateZ() Rotates the shape around the z-axis - self.v.rotateZ(angle.v); - }); + // rotateZ() Rotates the shape around the z-axis + self.v.rotateZ(angle.v); + }); $loc.scale = new Sk.builtin.func(function (self, x, y, z) { - // scale() Increases and decreases the size of a shape - // sh.scale(size) - // sh.scale(x,y) - // sh.scale(x,y,z) - if (typeof(y) === "undefined") { - self.v.scale(x.v); - } else if (typeof(z) === "undefined") { - self.v.scale(x.v, y.v); - } else { - self.v.scale(x.v, y.v, z.v); - } - }); + // scale() Increases and decreases the size of a shape + // sh.scale(size) + // sh.scale(x,y) + // sh.scale(x,y,z) + if (typeof (y) === "undefined") { + self.v.scale(x.v); + } else if (typeof (z) === "undefined") { + self.v.scale(x.v, y.v); + } else { + self.v.scale(x.v, y.v, z.v); + } + }); }; mod.PFont = Sk.misceval.buildClass(mod, fontClass, "PFont", []); diff --git a/src/lib/pythonds/basic/__init__.py b/src/lib/pythonds/basic/__init__.py index befc5d852a..00f98acc17 100644 --- a/src/lib/pythonds/basic/__init__.py +++ b/src/lib/pythonds/basic/__init__.py @@ -1,9 +1,5 @@ - -#__all__ = ["stack"] - - -#from .stack import Stack -#from .queue import Queue - +# __all__ = ["stack"] +# from .stack import Stack +# from .queue import Queue diff --git a/src/lib/pythonds/basic/deque.py b/src/lib/pythonds/basic/deque.py index 6f81f1a4a8..cbc7aa74e7 100644 --- a/src/lib/pythonds/basic/deque.py +++ b/src/lib/pythonds/basic/deque.py @@ -2,27 +2,27 @@ # Introduction to Data Structures and Algorithms in Python # Copyright 2005 # -#deque.py - - -class Deque: - def __init__(self): - self.items = [] - - def isEmpty(self): - return self.items == [] - - def addFront(self, item): - self.items.append(item) - - def addRear(self, item): - self.items.insert(0,item) - - def removeFront(self): - return self.items.pop() - - def removeRear(self): - return self.items.pop(0) - - def size(self): - return len(self.items) +# deque.py + + +class Deque: + def __init__(self): + self.items = [] + + def isEmpty(self): + return self.items == [] + + def addFront(self, item): + self.items.append(item) + + def addRear(self, item): + self.items.insert(0, item) + + def removeFront(self): + return self.items.pop() + + def removeRear(self): + return self.items.pop(0) + + def size(self): + return len(self.items) diff --git a/src/lib/pythonds/basic/queue.py b/src/lib/pythonds/basic/queue.py index e4b9ae776c..4cdc6ede7f 100644 --- a/src/lib/pythonds/basic/queue.py +++ b/src/lib/pythonds/basic/queue.py @@ -2,20 +2,20 @@ # Introduction to Data Structures and Algorithms in Python # Copyright 2005 # -#queue.py - -class Queue: - def __init__(self): - self.items = [] - - def isEmpty(self): - return self.items == [] - - def enqueue(self, item): - self.items.insert(0,item) - - def dequeue(self): - return self.items.pop() - - def size(self): - return len(self.items) +# queue.py + +class Queue: + def __init__(self): + self.items = [] + + def isEmpty(self): + return self.items == [] + + def enqueue(self, item): + self.items.insert(0, item) + + def dequeue(self): + return self.items.pop() + + def size(self): + return len(self.items) diff --git a/src/lib/pythonds/basic/stack.py b/src/lib/pythonds/basic/stack.py index d8f1cd8e63..e14668c4fb 100644 --- a/src/lib/pythonds/basic/stack.py +++ b/src/lib/pythonds/basic/stack.py @@ -2,24 +2,23 @@ # Introduction to Data Structures and Algorithms in Python # Copyright 2005 # -#stack.py - -class Stack: - def __init__(self): - self.items = [] - - def isEmpty(self): - return self.items == [] - - def push(self, item): - self.items.append(item) - - def pop(self): - return self.items.pop() - - def peek(self): - return self.items[len(self.items)-1] - - def size(self): - return len(self.items) - +# stack.py + +class Stack: + def __init__(self): + self.items = [] + + def isEmpty(self): + return self.items == [] + + def push(self, item): + self.items.append(item) + + def pop(self): + return self.items.pop() + + def peek(self): + return self.items[len(self.items) - 1] + + def size(self): + return len(self.items) diff --git a/src/lib/pythonds/graphs/__init__.py b/src/lib/pythonds/graphs/__init__.py index c1640185b7..8cc014b294 100644 --- a/src/lib/pythonds/graphs/__init__.py +++ b/src/lib/pythonds/graphs/__init__.py @@ -1,5 +1,3 @@ - - from .adjGraph import Graph from .adjGraph import Vertex from .priorityQueue import PriorityQueue diff --git a/src/lib/pythonds/graphs/adjGraph.py b/src/lib/pythonds/graphs/adjGraph.py index 3a14397f28..66afda9d81 100644 --- a/src/lib/pythonds/graphs/adjGraph.py +++ b/src/lib/pythonds/graphs/adjGraph.py @@ -9,41 +9,43 @@ import os import unittest + class Graph: def __init__(self): self.vertices = {} self.numVertices = 0 - - def addVertex(self,key): + + def addVertex(self, key): self.numVertices = self.numVertices + 1 newVertex = Vertex(key) self.vertices[key] = newVertex return newVertex - - def getVertex(self,n): + + def getVertex(self, n): if n in self.vertices: return self.vertices[n] else: return None - def __contains__(self,n): + def __contains__(self, n): return n in self.vertices - - def addEdge(self,f,t,cost=0): - if f not in self.vertices: - nv = self.addVertex(f) - if t not in self.vertices: - nv = self.addVertex(t) - self.vertices[f].addNeighbor(self.vertices[t],cost) - + + def addEdge(self, f, t, cost=0): + if f not in self.vertices: + nv = self.addVertex(f) + if t not in self.vertices: + nv = self.addVertex(t) + self.vertices[f].addNeighbor(self.vertices[t], cost) + def getVertices(self): return list(self.vertices.keys()) - + def __iter__(self): return iter(self.vertices.values()) - + + class Vertex: - def __init__(self,num): + def __init__(self, num): self.id = num self.connectedTo = {} self.color = 'white' @@ -54,69 +56,70 @@ def __init__(self,num): # def __lt__(self,o): # return self.id < o.id - - def addNeighbor(self,nbr,weight=0): + + def addNeighbor(self, nbr, weight=0): self.connectedTo[nbr] = weight - - def setColor(self,color): + + def setColor(self, color): self.color = color - - def setDistance(self,d): + + def setDistance(self, d): self.dist = d - def setPred(self,p): + def setPred(self, p): self.pred = p - def setDiscovery(self,dtime): + def setDiscovery(self, dtime): self.disc = dtime - - def setFinish(self,ftime): + + def setFinish(self, ftime): self.fin = ftime - + def getFinish(self): return self.fin - + def getDiscovery(self): return self.disc - + def getPred(self): return self.pred - + def getDistance(self): return self.dist - + def getColor(self): return self.color - + def getConnections(self): return self.connectedTo.keys() - - def getWeight(self,nbr): + + def getWeight(self, nbr): return self.connectedTo[nbr] - + def __str__(self): - return str(self.id) + ":color " + self.color + ":disc " + str(self.disc) + ":fin " + str(self.fin) + ":dist " + str(self.dist) + ":pred \n\t[" + str(self.pred)+ "]\n" - + return str(self.id) + ":color " + self.color + ":disc " + str(self.disc) + ":fin " + str( + self.fin) + ":dist " + str(self.dist) + ":pred \n\t[" + str(self.pred) + "]\n" + def getId(self): return self.id + class adjGraphTests(unittest.TestCase): def setUp(self): self.tGraph = Graph() - + def testMakeGraph(self): gFile = open("test.dat") for line in gFile: fVertex, tVertex = line.split('|') fVertex = int(fVertex) tVertex = int(tVertex) - self.tGraph.addEdge(fVertex,tVertex) + self.tGraph.addEdge(fVertex, tVertex) for i in self.tGraph: adj = i.getAdj() for k in adj: print(i, k) - + if __name__ == '__main__': unittest.main() - diff --git a/src/lib/pythonds/graphs/priorityQueue.py b/src/lib/pythonds/graphs/priorityQueue.py index b7d873bf58..4507102faa 100644 --- a/src/lib/pythonds/graphs/priorityQueue.py +++ b/src/lib/pythonds/graphs/priorityQueue.py @@ -4,25 +4,26 @@ # import unittest + # this implementation of binary heap takes key value pairs, # we will assume that the keys are all comparable class PriorityQueue: def __init__(self): - self.heapArray = [(0,0)] + self.heapArray = [(0, 0)] self.currentSize = 0 - def buildHeap(self,alist): + def buildHeap(self, alist): self.currentSize = len(alist) - self.heapArray = [(0,0)] + self.heapArray = [(0, 0)] for i in alist: self.heapArray.append(i) - i = len(alist) // 2 + i = len(alist) // 2 while (i > 0): self.percDown(i) i = i - 1 - - def percDown(self,i): + + def percDown(self, i): while (i * 2) <= self.currentSize: mc = self.minChild(i) if self.heapArray[i][0] > self.heapArray[mc][0]: @@ -30,28 +31,28 @@ def percDown(self,i): self.heapArray[i] = self.heapArray[mc] self.heapArray[mc] = tmp i = mc - - def minChild(self,i): - if i*2 > self.currentSize: + + def minChild(self, i): + if i * 2 > self.currentSize: return -1 else: - if i*2 + 1 > self.currentSize: - return i*2 + if i * 2 + 1 > self.currentSize: + return i * 2 else: - if self.heapArray[i*2][0] < self.heapArray[i*2+1][0]: - return i*2 + if self.heapArray[i * 2][0] < self.heapArray[i * 2 + 1][0]: + return i * 2 else: - return i*2+1 + return i * 2 + 1 - def percUp(self,i): + def percUp(self, i): while i // 2 > 0: - if self.heapArray[i][0] < self.heapArray[i//2][0]: - tmp = self.heapArray[i//2] - self.heapArray[i//2] = self.heapArray[i] - self.heapArray[i] = tmp - i = i//2 - - def add(self,k): + if self.heapArray[i][0] < self.heapArray[i // 2][0]: + tmp = self.heapArray[i // 2] + self.heapArray[i // 2] = self.heapArray[i] + self.heapArray[i] = tmp + i = i // 2 + + def add(self, k): self.heapArray.append(k) self.currentSize = self.currentSize + 1 self.percUp(self.currentSize) @@ -63,14 +64,14 @@ def delMin(self): self.heapArray.pop() self.percDown(1) return retval - + def isEmpty(self): if self.currentSize == 0: return True else: return False - def decreaseKey(self,val,amt): + def decreaseKey(self, val, amt): # this is a little wierd, but we need to find the heap thing to decrease by # looking at its value done = False @@ -83,24 +84,24 @@ def decreaseKey(self,val,amt): else: i = i + 1 if myKey > 0: - self.heapArray[myKey] = (amt,self.heapArray[myKey][1]) + self.heapArray[myKey] = (amt, self.heapArray[myKey][1]) self.percUp(myKey) - - def __contains__(self,vtx): + + def __contains__(self, vtx): for pair in self.heapArray: if pair[1] == vtx: return True return False - + + class TestBinHeap(unittest.TestCase): def setUp(self): self.theHeap = PriorityQueue() - self.theHeap.add((2,'x')) - self.theHeap.add((3,'y')) - self.theHeap.add((5,'z')) - self.theHeap.add((6,'a')) - self.theHeap.add((4,'d')) - + self.theHeap.add((2, 'x')) + self.theHeap.add((3, 'y')) + self.theHeap.add((5, 'z')) + self.theHeap.add((6, 'a')) + self.theHeap.add((4, 'd')) def testInsert(self): assert self.theHeap.currentSize == 5 @@ -108,10 +109,11 @@ def testInsert(self): def testDelmin(self): assert self.theHeap.delMin() == 'x' assert self.theHeap.delMin() == 'y' - + def testDecKey(self): - self.theHeap.decreaseKey('d',1) + self.theHeap.decreaseKey('d', 1) assert self.theHeap.delMin() == 'd' - + + if __name__ == '__main__': unittest.main() diff --git a/src/lib/pythonds/trees/__init__.py b/src/lib/pythonds/trees/__init__.py index 614ccf1b67..a23426731e 100644 --- a/src/lib/pythonds/trees/__init__.py +++ b/src/lib/pythonds/trees/__init__.py @@ -1,7 +1,4 @@ - # from .binaryTree import BinaryTree # from .balance import AVLTree # from .bst import BinarySearchTree # from .binheap import BinHeap - - diff --git a/src/lib/pythonds/trees/balance.py b/src/lib/pythonds/trees/balance.py index 08fb515106..8fa46f7bef 100644 --- a/src/lib/pythonds/trees/balance.py +++ b/src/lib/pythonds/trees/balance.py @@ -6,6 +6,7 @@ from .bst import BinarySearchTree, TreeNode + class AVLTree(BinarySearchTree): ''' Author: Brad Miller @@ -26,22 +27,21 @@ class AVLTree(BinarySearchTree): put(k,v) ''' - - def _put(self,key,val,currentNode): + def _put(self, key, val, currentNode): if key < currentNode.key: if currentNode.hasLeftChild(): - self._put(key,val,currentNode.leftChild) + self._put(key, val, currentNode.leftChild) else: - currentNode.leftChild = TreeNode(key,val,parent=currentNode) + currentNode.leftChild = TreeNode(key, val, parent=currentNode) self.updateBalance(currentNode.leftChild) else: if currentNode.hasRightChild(): - self._put(key,val,currentNode.rightChild) + self._put(key, val, currentNode.rightChild) else: - currentNode.rightChild = TreeNode(key,val,parent=currentNode) - self.updateBalance(currentNode.rightChild) + currentNode.rightChild = TreeNode(key, val, parent=currentNode) + self.updateBalance(currentNode.rightChild) - def updateBalance(self,node): + def updateBalance(self, node): if node.balanceFactor > 1 or node.balanceFactor < -1: self.rebalance(node) return @@ -54,7 +54,7 @@ def updateBalance(self,node): if node.parent.balanceFactor != 0: self.updateBalance(node.parent) - def rebalance(self,node): + def rebalance(self, node): if node.balanceFactor < 0: if node.rightChild.balanceFactor > 0: # Do an LR Rotation @@ -72,7 +72,7 @@ def rebalance(self,node): # single right self.rotateRight(node) - def rotateLeft(self,rotRoot): + def rotateLeft(self, rotRoot): newRoot = rotRoot.rightChild rotRoot.rightChild = newRoot.leftChild if newRoot.leftChild != None: @@ -90,8 +90,7 @@ def rotateLeft(self,rotRoot): rotRoot.balanceFactor = rotRoot.balanceFactor + 1 - min(newRoot.balanceFactor, 0) newRoot.balanceFactor = newRoot.balanceFactor + 1 + max(rotRoot.balanceFactor, 0) - - def rotateRight(self,rotRoot): + def rotateRight(self, rotRoot): newRoot = rotRoot.leftChild rotRoot.leftChild = newRoot.rightChild if newRoot.rightChild != None: @@ -108,4 +107,3 @@ def rotateRight(self,rotRoot): rotRoot.parent = newRoot rotRoot.balanceFactor = rotRoot.balanceFactor - 1 - max(newRoot.balanceFactor, 0) newRoot.balanceFactor = newRoot.balanceFactor - 1 + min(rotRoot.balanceFactor, 0) - diff --git a/src/lib/pythonds/trees/binaryTree.py b/src/lib/pythonds/trees/binaryTree.py index 53253ac75b..0ceaf27b1b 100644 --- a/src/lib/pythonds/trees/binaryTree.py +++ b/src/lib/pythonds/trees/binaryTree.py @@ -7,21 +7,22 @@ class BinaryTree: """ A recursive implementation of Binary Tree Using links and Nodes approach. - """ - def __init__(self,rootObj): + """ + + def __init__(self, rootObj): self.key = rootObj self.leftChild = None self.rightChild = None - def insertLeft(self,newNode): + def insertLeft(self, newNode): if self.leftChild == None: self.leftChild = BinaryTree(newNode) else: t = BinaryTree(newNode) t.left = self.leftChild self.leftChild = t - - def insertRight(self,newNode): + + def insertRight(self, newNode): if self.rightChild == None: self.rightChild = BinaryTree(newNode) else: @@ -38,10 +39,10 @@ def getRightChild(self): def getLeftChild(self): return self.leftChild - def setRootVal(self,obj): + def setRootVal(self, obj): self.key = obj - def getRootVal(self,): + def getRootVal(self, ): return self.key def inorder(self): @@ -58,7 +59,6 @@ def postorder(self): self.rightChild.postorder() print(self.key) - def preorder(self): print(self.key) if self.leftChild: @@ -76,24 +76,26 @@ def printexp(self): print(')') def postordereval(self): - opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv} + opers = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv} res1 = None res2 = None if self.leftChild: - res1 = self.leftChild.postordereval() #// \label{peleft} + res1 = self.leftChild.postordereval() # // \label{peleft} if self.rightChild: - res2 = self.rightChild.postordereval() #// \label{peright} + res2 = self.rightChild.postordereval() # // \label{peright} if res1 and res2: - return opers[self.key](res1,res2) #// \label{peeval} + return opers[self.key](res1, res2) # // \label{peeval} else: return self.key + def inorder(tree): if tree != None: inorder(tree.getLeftChild()) print(tree.getRootVal()) inorder(tree.getRightChild()) + def printexp(tree): if tree.leftChild: print('(') @@ -101,7 +103,8 @@ def printexp(tree): print(tree.getRootVal()) if tree.rightChild: printexp(tree.getRightChild()) - print(')') + print(')') + def printexp(tree): sVal = "" @@ -111,23 +114,25 @@ def printexp(tree): sVal = sVal + printexp(tree.getRightChild()) + ')' return sVal + def postordereval(tree): - opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv} + opers = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv} res1 = None res2 = None if tree: - res1 = postordereval(tree.getLeftChild()) #// \label{peleft} - res2 = postordereval(tree.getRightChild()) #// \label{peright} + res1 = postordereval(tree.getLeftChild()) # // \label{peleft} + res2 = postordereval(tree.getRightChild()) # // \label{peright} if res1 and res2: - return opers[tree.getRootVal()](res1,res2) #// \label{peeval} + return opers[tree.getRootVal()](res1, res2) # // \label{peeval} else: return tree.getRootVal() + def height(tree): if tree == None: return -1 else: - return 1 + max(height(tree.leftChild),height(tree.rightChild)) + return 1 + max(height(tree.leftChild), height(tree.rightChild)) # t = BinaryTree(7) # t.insertLeft(3) diff --git a/src/lib/pythonds/trees/binheap.py b/src/lib/pythonds/trees/binheap.py index c9b0b92edd..fb72e4ac2b 100644 --- a/src/lib/pythonds/trees/binheap.py +++ b/src/lib/pythonds/trees/binheap.py @@ -9,8 +9,7 @@ def __init__(self): self.heapList = [0] self.currentSize = 0 - - def buildHeap(self,alist): + def buildHeap(self, alist): i = len(alist) // 2 self.currentSize = len(alist) self.heapList = [0] + alist[:] @@ -19,9 +18,9 @@ def buildHeap(self,alist): print(self.heapList, i) self.percDown(i) i = i - 1 - print(self.heapList,i) - - def percDown(self,i): + print(self.heapList, i) + + def percDown(self, i): while (i * 2) <= self.currentSize: mc = self.minChild(i) if self.heapList[i] > self.heapList[mc]: @@ -29,8 +28,8 @@ def percDown(self,i): self.heapList[i] = self.heapList[mc] self.heapList[mc] = tmp i = mc - - def minChild(self,i): + + def minChild(self, i): if i * 2 + 1 > self.currentSize: return i * 2 else: @@ -39,15 +38,15 @@ def minChild(self,i): else: return i * 2 + 1 - def percUp(self,i): + def percUp(self, i): while i // 2 > 0: - if self.heapList[i] < self.heapList[i//2]: - tmp = self.heapList[i // 2] - self.heapList[i // 2] = self.heapList[i] - self.heapList[i] = tmp + if self.heapList[i] < self.heapList[i // 2]: + tmp = self.heapList[i // 2] + self.heapList[i // 2] = self.heapList[i] + self.heapList[i] = tmp i = i // 2 - - def insert(self,k): + + def insert(self, k): self.heapList.append(k) self.currentSize = self.currentSize + 1 self.percUp(self.currentSize) @@ -59,7 +58,7 @@ def delMin(self): self.heapList.pop() self.percDown(1) return retval - + def isEmpty(self): if currentSize == 0: return True diff --git a/src/lib/pythonds/trees/bst.py b/src/lib/pythonds/trees/bst.py index 75d1d408c0..0f5f15d5bf 100644 --- a/src/lib/pythonds/trees/bst.py +++ b/src/lib/pythonds/trees/bst.py @@ -28,64 +28,62 @@ class BinarySearchTree: def __init__(self): self.root = None self.size = 0 - - def put(self,key,val): + + def put(self, key, val): if self.root: - self._put(key,val,self.root) + self._put(key, val, self.root) else: - self.root = TreeNode(key,val) + self.root = TreeNode(key, val) self.size = self.size + 1 - def _put(self,key,val,currentNode): + def _put(self, key, val, currentNode): if key < currentNode.key: if currentNode.hasLeftChild(): - self._put(key,val,currentNode.leftChild) + self._put(key, val, currentNode.leftChild) else: - currentNode.leftChild = TreeNode(key,val,parent=currentNode) + currentNode.leftChild = TreeNode(key, val, parent=currentNode) else: if currentNode.hasRightChild(): - self._put(key,val,currentNode.rightChild) + self._put(key, val, currentNode.rightChild) else: - currentNode.rightChild = TreeNode(key,val,parent=currentNode) - - def __setitem__(self,k,v): - self.put(k,v) + currentNode.rightChild = TreeNode(key, val, parent=currentNode) + + def __setitem__(self, k, v): + self.put(k, v) - def get(self,key): + def get(self, key): if self.root: - res = self._get(key,self.root) + res = self._get(key, self.root) if res: return res.payload else: return None else: return None - - def _get(self,key,currentNode): + + def _get(self, key, currentNode): if not currentNode: return None elif currentNode.key == key: return currentNode elif key < currentNode.key: - return self._get(key,currentNode.leftChild) + return self._get(key, currentNode.leftChild) else: - return self._get(key,currentNode.rightChild) - - - def __getitem__(self,key): + return self._get(key, currentNode.rightChild) + + def __getitem__(self, key): res = self.get(key) if res: return res else: raise KeyError('Error, key not in tree') - - def __contains__(self,key): - if self._get(key,self.root): + def __contains__(self, key): + if self._get(key, self.root): return True else: return False - + def length(self): return self.size @@ -94,13 +92,13 @@ def __len__(self): def __iter__(self): return self.root.__iter__() - - def delete(self,key): + + def delete(self, key): if self.size > 1: - nodeToRemove = self._get(key,self.root) + nodeToRemove = self._get(key, self.root) if nodeToRemove: self.remove(nodeToRemove) - self.size = self.size-1 + self.size = self.size - 1 else: raise KeyError('Error, key not in tree') elif self.size == 1 and self.root.key == key: @@ -109,21 +107,21 @@ def delete(self,key): else: raise KeyError('Error, key not in tree') - def __delitem__(self,key): + def __delitem__(self, key): self.delete(key) - - def remove(self,currentNode): - if currentNode.isLeaf(): #leaf + + def remove(self, currentNode): + if currentNode.isLeaf(): # leaf if currentNode == currentNode.parent.leftChild: currentNode.parent.leftChild = None else: currentNode.parent.rightChild = None - elif currentNode.hasBothChildren(): #interior + elif currentNode.hasBothChildren(): # interior succ = currentNode.findSuccessor() succ.spliceOut() currentNode.key = succ.key currentNode.payload = succ.payload - else: # this node has one child + else: # this node has one child if currentNode.hasLeftChild(): if currentNode.isLeftChild(): currentNode.leftChild.parent = currentNode.parent @@ -133,9 +131,9 @@ def remove(self,currentNode): currentNode.parent.rightChild = currentNode.leftChild else: currentNode.replaceNodeData(currentNode.leftChild.key, - currentNode.leftChild.payload, - currentNode.leftChild.leftChild, - currentNode.leftChild.rightChild) + currentNode.leftChild.payload, + currentNode.leftChild.leftChild, + currentNode.leftChild.rightChild) else: if currentNode.isLeftChild(): currentNode.rightChild.parent = currentNode.parent @@ -145,14 +143,14 @@ def remove(self,currentNode): currentNode.parent.rightChild = currentNode.rightChild else: currentNode.replaceNodeData(currentNode.rightChild.key, - currentNode.rightChild.payload, - currentNode.rightChild.leftChild, - currentNode.rightChild.rightChild) + currentNode.rightChild.payload, + currentNode.rightChild.leftChild, + currentNode.rightChild.rightChild) def inorder(self): self._inorder(self.root) - def _inorder(self,tree): + def _inorder(self, tree): if tree != None: self._inorder(tree.leftChild) print(tree.key) @@ -165,33 +163,33 @@ def _postorder(self, tree): if tree: self._postorder(tree.rightChild) self._postorder(tree.leftChild) - print(tree.key) + print(tree.key) def preorder(self): - self._preorder(self,self.root) + self._preorder(self, self.root) - def _preorder(self,tree): + def _preorder(self, tree): if tree: - print(tree.key) + print(tree.key) self._preorder(tree.leftChild) self._preorder(tree.rightChild) - + class TreeNode: - def __init__(self,key,val,left=None,right=None,parent=None): + def __init__(self, key, val, left=None, right=None, parent=None): self.key = key self.payload = val self.leftChild = left self.rightChild = right self.parent = parent self.balanceFactor = 0 - + def hasLeftChild(self): return self.leftChild def hasRightChild(self): return self.rightChild - + def isLeftChild(self): return self.parent and self.parent.leftChild == self @@ -209,8 +207,8 @@ def hasAnyChildren(self): def hasBothChildren(self): return self.rightChild and self.leftChild - - def replaceNodeData(self,key,value,lc,rc): + + def replaceNodeData(self, key, value, lc, rc): self.key = key self.payload = value self.leftChild = lc @@ -219,7 +217,7 @@ def replaceNodeData(self,key,value,lc,rc): self.leftChild.parent = self if self.hasRightChild(): self.rightChild.parent = self - + def findSuccessor(self): succ = None if self.hasRightChild(): @@ -234,7 +232,6 @@ def findSuccessor(self): self.parent.rightChild = self return succ - def spliceOut(self): if self.isLeaf(): if self.isLeftChild(): @@ -271,5 +268,3 @@ def __iter__(self): if self.hasRightChild(): for elem in self.rightChild: yield elem - - diff --git a/src/lib/random.js b/src/lib/random.js index 977a780d63..72fddb7745 100644 --- a/src/lib/random.js +++ b/src/lib/random.js @@ -84,7 +84,7 @@ var MersenneTwister = function (seed) { /* mti==N+1 means mt[N] is not initialized */ this.init_genrand(seed); -} +}; /* initializes mt[N] with a seed */ MersenneTwister.prototype.init_genrand = function (s) { @@ -100,7 +100,7 @@ MersenneTwister.prototype.init_genrand = function (s) { this.mt[this.mti] >>>= 0; /* for >32 bit machines */ } -} +}; /* initialize by an array with array-length */ /* init_key is the array for initializing keys */ @@ -113,7 +113,7 @@ MersenneTwister.prototype.init_by_array = function (init_key, key_length) { j = 0; k = (this.N > key_length ? this.N : key_length); for (; k; k--) { - var s = this.mt[i - 1] ^ (this.mt[i - 1] >>> 30) + var s = this.mt[i - 1] ^ (this.mt[i - 1] >>> 30); this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1664525) << 16) + ((s & 0x0000ffff) * 1664525))) + init_key[j] + j; /* non linear */ @@ -145,7 +145,7 @@ MersenneTwister.prototype.init_by_array = function (init_key, key_length) { this.mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */ -} +}; /* generates a random number on [0,0xffffffff]-interval */ MersenneTwister.prototype.genrand_int32 = function () { @@ -156,8 +156,7 @@ MersenneTwister.prototype.genrand_int32 = function () { if (this.mti >= this.N) { /* generate N words at one time */ var kk; - if (this.mti == this.N + 1) /* if init_genrand() has not been called, */ - { + if (this.mti == this.N + 1) { /* if init_genrand() has not been called, */ this.init_genrand(5489); } /* a default initial seed is used */ @@ -185,36 +184,36 @@ MersenneTwister.prototype.genrand_int32 = function () { y ^= (y >>> 18); return y >>> 0; -} +}; /* generates a random number on [0,0x7fffffff]-interval */ MersenneTwister.prototype.genrand_int31 = function () { return (this.genrand_int32() >>> 1); -} +}; /* generates a random number on [0,1]-real-interval */ MersenneTwister.prototype.genrand_real1 = function () { return this.genrand_int32() * (1.0 / 4294967295.0); /* divided by 2^32-1 */ -} +}; /* generates a random number on [0,1)-real-interval */ MersenneTwister.prototype.random = function () { return this.genrand_int32() * (1.0 / 4294967296.0); /* divided by 2^32 */ -} +}; /* generates a random number on (0,1)-real-interval */ MersenneTwister.prototype.genrand_real3 = function () { return (this.genrand_int32() + 0.5) * (1.0 / 4294967296.0); /* divided by 2^32 */ -} +}; /* generates a random number on [0,1) with 53-bit resolution*/ MersenneTwister.prototype.genrand_res53 = function () { var a = this.genrand_int32() >>> 5, b = this.genrand_int32() >>> 6; - return(a * 67108864.0 + b) * (1.0 / 9007199254740992.0); -} + return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0); +}; /* These real versions are due to Isaku Wada, 2002/01/09 added */ @@ -232,8 +231,7 @@ var $builtinmodule = function (name) { if (arguments.length > 0) { myGenerator = new MersenneTwister(x); - } - else { + } else { myGenerator = new MersenneTwister(); } @@ -330,14 +328,14 @@ var $builtinmodule = function (name) { step = Sk.builtin.asnum$(step); return randrange(start, stop, step); }); - + mod.uniform = new Sk.builtin.func(function (a, b) { Sk.builtin.pyCheckArgsLen("uniform", arguments.length, 2, 2); a = Sk.builtin.asnum$(a); b = Sk.builtin.asnum$(b); var rnd = myGenerator.genrand_res53(); - c = a + rnd * (b - a) + c = a + rnd * (b - a); return new Sk.builtin.float_(c); }); @@ -356,7 +354,7 @@ var $builtinmodule = function (name) { high = swap; } if ((mode === undefined) || (mode instanceof Sk.builtin.none)) { - mode = (high - low)/2.0; + mode = (high - low) / 2.0; } else { Sk.builtin.pyCheckType("mode", "number", Sk.builtin.checkNumber(mode)); mode = Sk.builtin.asnum$(mode); @@ -364,7 +362,7 @@ var $builtinmodule = function (name) { // https://en.wikipedia.org/wiki/Triangular_distribution rnd = myGenerator.genrand_res53(); - if (rnd < (mode - low)/(high - low)) { + if (rnd < (mode - low) / (high - low)) { sample = low + Math.sqrt(rnd * (high - low) * (mode - low)); } else { sample = high - Math.sqrt((1 - rnd) * (high - low) * (high - mode)); @@ -373,7 +371,7 @@ var $builtinmodule = function (name) { return new Sk.builtin.float_(sample); }); - var normalSample = function(mu, sigma) { + var normalSample = function (mu, sigma) { var r1, r2, u, v, s; // Box-Muller transform @@ -388,15 +386,15 @@ var $builtinmodule = function (name) { } else { r1 = myGenerator.genrand_res53(); r2 = myGenerator.genrand_res53(); - u = Math.sqrt(-2*Math.log(r1)); - v = 2*Math.PI*r2; + u = Math.sqrt(-2 * Math.log(r1)); + v = 2 * Math.PI * r2; s = u * Math.cos(v); nextNormalSample = u * Math.sin(v); } - return mu + sigma*s; + return mu + sigma * s; }; - + mod.gauss = new Sk.builtin.func(function (mu, sigma) { Sk.builtin.pyCheckArgsLen("gauss", arguments.length, 2, 2); Sk.builtin.pyCheckType("mu", "number", Sk.builtin.checkNumber(mu)); @@ -431,7 +429,7 @@ var $builtinmodule = function (name) { lambd = Sk.builtin.asnum$(lambd); var rnd = myGenerator.genrand_res53(); - return new Sk.builtin.float_(-Math.log(rnd)/lambd); + return new Sk.builtin.float_(-Math.log(rnd) / lambd); }); mod.choice = new Sk.builtin.func(function (seq) { @@ -439,7 +437,7 @@ var $builtinmodule = function (name) { Sk.builtin.pyCheckType("seq", "sequence", Sk.builtin.checkSequence(seq)); if (seq.sq$length !== undefined) { - var r = toInt(myGenerator.genrand_res53() * seq.sq$length()); + var r = new Sk.builtin.int_(toInt(myGenerator.genrand_res53() * seq.sq$length())); return seq.mp$subscript(r); } else { throw new Sk.builtin.TypeError("object has no length"); @@ -449,11 +447,20 @@ var $builtinmodule = function (name) { mod.shuffle = new Sk.builtin.func(function (x) { Sk.builtin.pyCheckArgsLen("shuffle", arguments.length, 1, 1); Sk.builtin.pyCheckType("x", "sequence", Sk.builtin.checkSequence(x)); - - if (x.sq$length !== undefined) { + // make this faster we kow that it's a list so just use the array + if (x.constructor === Sk.builtin.list) { + const L = x.v; + for (var i = L.length - 1; i > 0; i -= 1) { + var r = toInt(myGenerator.genrand_res53() * (i + 1)); + var tmp = L[r]; + L[r] = L[i]; + L[i] = tmp; + } + } else if (x.sq$length !== undefined) { if (x.mp$ass_subscript !== undefined) { for (var i = x.sq$length() - 1; i > 0; i -= 1) { - var r = toInt(myGenerator.genrand_res53() * (i + 1)); + var r = new Sk.builtin.int_(toInt(myGenerator.genrand_res53() * (i + 1))); + i = new Sk.builtin.int_(i); var tmp = x.mp$subscript(r); x.mp$ass_subscript(r, x.mp$subscript(i)); x.mp$ass_subscript(i, tmp); @@ -478,7 +485,7 @@ var $builtinmodule = function (name) { Sk.builtin.pyCheckType("population", "iterable", Sk.builtin.checkIterable(population)); Sk.builtin.pyCheckType("k", "integer", Sk.builtin.checkInt(k)); k = Sk.builtin.asnum$(k); - + // "Algorithm R" in // https://en.wikipedia.org/wiki/Reservoir_sampling // @@ -494,8 +501,8 @@ var $builtinmodule = function (name) { reservoir = []; iter = Sk.abstr.iter(population); for (i = 0, elem = iter.tp$iternext(); - elem !== undefined; - i++, elem = iter.tp$iternext()) { + elem !== undefined; + i++, elem = iter.tp$iternext()) { j = Math.floor(myGenerator.genrand_res53() * (i + 1)); if (i < k) { // Fill the reservoir @@ -513,13 +520,13 @@ var $builtinmodule = function (name) { } } } - + if (i < k) { throw new Sk.builtin.ValueError("sample larger than population"); } - return Sk.builtin.list(reservoir); + return new Sk.builtin.list(reservoir); }); return mod; -} +}; diff --git a/src/lib/re.js b/src/lib/re.js index f587c7a4da..69b6e92796 100644 --- a/src/lib/re.js +++ b/src/lib/re.js @@ -121,7 +121,7 @@ var $builtinmodule = function (name) { }; _split.co_varnames = ["pattern", "string", "maxsplit", "flags"]; - _split.$defaults = [ new Sk.builtin.int_(0), new Sk.builtin.int_(0) ]; + _split.$defaults = [new Sk.builtin.int_(0), new Sk.builtin.int_(0)]; mod.split = new Sk.builtin.func(_split); @@ -188,7 +188,7 @@ var $builtinmodule = function (name) { }; _findall.co_varnames = ["pattern", "string", "flags"]; - _findall.$defaults = [ new Sk.builtin.int_(0) ]; + _findall.$defaults = [new Sk.builtin.int_(0)]; mod.findall = new Sk.builtin.func(_findall); @@ -198,6 +198,7 @@ var $builtinmodule = function (name) { self.thematch = thematch; self.re = pattern; self.string = string; + return Sk.builtin.none.none$; }); $loc.groups = new Sk.builtin.func(function (self) { @@ -276,7 +277,7 @@ var $builtinmodule = function (name) { }; _search.co_varnames = ["pattern", "string", "flags"]; - _search.$defaults = [ new Sk.builtin.int_(0) ]; + _search.$defaults = [new Sk.builtin.int_(0)]; mod.search = new Sk.builtin.func(_search); @@ -306,7 +307,7 @@ var $builtinmodule = function (name) { }; _match.co_varnames = ["pattern", "string", "flags"]; - _match.$defaults = [ new Sk.builtin.int_(0) ]; + _match.$defaults = [new Sk.builtin.int_(0)]; mod.match = new Sk.builtin.func(_match); @@ -320,11 +321,12 @@ var $builtinmodule = function (name) { } else { self.flags = flags; } + return Sk.builtin.none.none$; }); - _repr = new Sk.builtin.func( function (self) { + _repr = new Sk.builtin.func(function (self) { var ret = "re.compile('" + Sk.ffi.remapToPy(self.re) + "')"; - return Sk.ffi.remapToPy(ret.substring(0,212)); + return Sk.ffi.remapToPy(ret.substring(0, 212)); }); $loc.__str__ = _repr; @@ -332,7 +334,7 @@ var $builtinmodule = function (name) { $loc.__repr__ = _repr; // Given a string, start, and end position, return sliced string - _slice = function(string, pos, endpos) { + _slice = function (string, pos, endpos) { // Per docs, ^ should match index after newlines. // this matches the first var str = Sk.ffi.remapToJs(string); @@ -358,7 +360,7 @@ var $builtinmodule = function (name) { }; _re_search.co_varnames = ["self", "string", "pos", "endpos"]; - _re_search.$defaults = [ new Sk.builtin.int_(0), Sk.builtin.none.none$ ]; + _re_search.$defaults = [new Sk.builtin.int_(0), Sk.builtin.none.none$]; $loc.search = new Sk.builtin.func(_re_search); @@ -372,7 +374,7 @@ var $builtinmodule = function (name) { }; _re_match.co_varnames = ["self", "string", "pos", "endpos"]; - _re_match.$defaults = [ new Sk.builtin.int_(0), Sk.builtin.none.none$ ]; + _re_match.$defaults = [new Sk.builtin.int_(0), Sk.builtin.none.none$]; $loc.match = new Sk.builtin.func(_re_match); @@ -390,7 +392,7 @@ var $builtinmodule = function (name) { }; _re_split.co_varnames = ["self", "string", "maxsplit"]; - _re_split.$defaults = [ new Sk.builtin.int_(0) ]; + _re_split.$defaults = [new Sk.builtin.int_(0)]; $loc.split = new Sk.builtin.func(_re_split); @@ -403,7 +405,7 @@ var $builtinmodule = function (name) { }; _re_findall.co_varnames = ["self", "string", "pos", "endpos"]; - _re_findall.$defaults = [ new Sk.builtin.int_(0), Sk.builtin.none.none$ ]; + _re_findall.$defaults = [new Sk.builtin.int_(0), Sk.builtin.none.none$]; $loc.findall = new Sk.builtin.func(_re_findall); @@ -427,7 +429,8 @@ var $builtinmodule = function (name) { }); // No need to purge since we don't cache - mod.purge = new Sk.builtin.func(function () {}); + mod.purge = new Sk.builtin.func(function () { + }); return mod; }; diff --git a/src/lib/reprlib.py b/src/lib/reprlib.py index 827e256129..43cf4413c8 100644 --- a/src/lib/reprlib.py +++ b/src/lib/reprlib.py @@ -5,6 +5,7 @@ from itertools import islice from _thread import get_ident + def recursive_repr(fillvalue='...'): 'Decorator to make a repr function return fillvalue for a recursive call' @@ -32,6 +33,7 @@ def wrapper(self): return decorating_function + class Repr: def __init__(self): @@ -119,18 +121,18 @@ def repr_dict(self, x, level): def repr_str(self, x, level): s = original_repr(x[:self.maxstring]) if len(s) > self.maxstring: - i = max(0, (self.maxstring-3)//2) - j = max(0, self.maxstring-3-i) - s = original_repr(x[:i] + x[len(x)-j:]) - s = s[:i] + '...' + s[len(s)-j:] + i = max(0, (self.maxstring - 3) // 2) + j = max(0, self.maxstring - 3 - i) + s = original_repr(x[:i] + x[len(x) - j:]) + s = s[:i] + '...' + s[len(s) - j:] return s def repr_int(self, x, level): - s = original_repr(x) # XXX Hope this isn't too slow... + s = original_repr(x) # XXX Hope this isn't too slow... if len(s) > self.maxlong: - i = max(0, (self.maxlong-3)//2) - j = max(0, self.maxlong-3-i) - s = s[:i] + '...' + s[len(s)-j:] + i = max(0, (self.maxlong - 3) // 2) + j = max(0, self.maxlong - 3 - i) + s = s[:i] + '...' + s[len(s) - j:] return s def repr_instance(self, x, level): @@ -141,9 +143,9 @@ def repr_instance(self, x, level): except Exception: return '<%s instance at %#x>' % (x.__class__.__name__, id(x)) if len(s) > self.maxother: - i = max(0, (self.maxother-3)//2) - j = max(0, self.maxother-3-i) - s = s[:i] + '...' + s[len(s)-j:] + i = max(0, (self.maxother - 3) // 2) + j = max(0, self.maxother - 3 - i) + s = s[:i] + '...' + s[len(s) - j:] return s @@ -156,6 +158,7 @@ def _possibly_sorted(x): except Exception: return list(x) + original_repr = repr aRepr = Repr() repr = aRepr.repr diff --git a/src/lib/requests/__init__.js b/src/lib/requests/__init__.js index 6ec6431f04..02de4855b5 100644 --- a/src/lib/requests/__init__.js +++ b/src/lib/requests/__init__.js @@ -1,5 +1,5 @@ var $builtinmodule = function (name) { - var request = {__name__: Sk.builtin.str("requests")}; + var request = {__name__: new Sk.builtin.str("requests")}; //~ Classes ................................................................. @@ -23,7 +23,7 @@ var $builtinmodule = function (name) { } self.currentLine = 0; self.pos$ = 0; - Sk.abstr.sattr(self, Sk.builtin.str("text"), Sk.ffi.remapToPy(self.data$), true); + Sk.abstr.sattr(self, new Sk.builtin.str("text"), Sk.ffi.remapToPy(self.data$), true); }); @@ -31,7 +31,7 @@ var $builtinmodule = function (name) { $loc.__str__ = new Sk.builtin.func(function (self) { return Sk.ffi.remapToPy(""); }); - + $loc.__repr__ = $loc.__str__; // ------------------------------------------------------------ @@ -44,7 +44,7 @@ var $builtinmodule = function (name) { } return new Sk.builtin.str(this.$lines[this.$index++]); }, { - $obj : self, + $obj: self, $index: 0, $lines: allLines }); @@ -88,7 +88,7 @@ var $builtinmodule = function (name) { } return new Sk.builtin.list(arr); }); - + // ------------------------------------------------------------ $loc.json = new Sk.builtin.func(function (self) { return Sk.ffi.remapToPy(JSON.parse(self.data$)); @@ -116,18 +116,18 @@ var $builtinmodule = function (name) { if (Sk.requestsGet) { return Sk.misceval.callsim(request.Response, Sk.requestsGet(Sk.ffi.remapToJs(url), data, timeout)); } - var prom = new Promise(function(resolve, reject) { + var prom = new Promise(function (resolve, reject) { if (Sk.requestsGet) { - Sk.requestsGet(Sk.ffi.remapToJs(url), data, timeout).then(function(result) { + Sk.requestsGet(Sk.ffi.remapToJs(url), data, timeout).then(function (result) { resolve(Sk.misceval.callsim(request.Response, result)); - }, function(err) { + }, function (err) { console.log("Err1"); reject(err); //resolve(Sk.misceval.callsim(request.Response, err)); }); } else { var xmlhttp = new XMLHttpRequest(); - + xmlhttp.addEventListener("loadend", function (e) { resolve(Sk.misceval.callsim(request.Response, xmlhttp.responseText)); }); @@ -146,7 +146,7 @@ var $builtinmodule = function (name) { var susp = new Sk.misceval.Suspension(); - susp.resume = function() { + susp.resume = function () { console.log("err2", susp); if (susp.data["error"]) { //throw new Sk.builtin.IOError(susp.data["error"].message); @@ -155,13 +155,13 @@ var $builtinmodule = function (name) { return resolution; } }; - + susp.data = { type: "Sk.promise", - promise: prom.then(function(value) { + promise: prom.then(function (value) { resolution = value; return value; - }, function(err) { + }, function (err) { console.log("err3", err); resolution = ""; //throw err; diff --git a/src/lib/signal.js b/src/lib/signal.js index dd45ebc570..e5aa8dbaf4 100644 --- a/src/lib/signal.js +++ b/src/lib/signal.js @@ -50,9 +50,9 @@ var $builtinmodule = function (name) { /** * Hold the execution of skulpt until an external signal has been * triggered. - * + * * @returns - */ + */ mod.pause = new Sk.builtin.func(function () { Sk.builtin.pyCheckArgsLen("pause", arguments.length, 0, 0); var susp = new Sk.misceval.Suspension(); @@ -64,14 +64,15 @@ var $builtinmodule = function (name) { promise: new Promise(function (resolve, reject) { if (Sk.signals != null && Sk.signals.addEventListener) { // Define handler here, in order to remove it later - function handleSignal (signal) { + function handleSignal(signal) { Sk.signals.removeEventListener(handleSignal); resolve(); } + Sk.signals.addEventListener(handleSignal); } else { - console.warn('signal.pause() not supported'); - Sk.misceval.print_('signal.pause() not supported') + console.warn("signal.pause() not supported"); + Sk.misceval.print_("signal.pause() not supported"); // if signal has not been configured, just resume immediatelly resolve(); } @@ -81,7 +82,7 @@ var $builtinmodule = function (name) { }); mod.signal = new Sk.builtin.func(function () { - throw new Sk.builtin.NotImplementedError('signal.signal is not supported.'); + throw new Sk.builtin.NotImplementedError("signal.signal is not supported."); }); return mod; diff --git a/src/lib/sound/__init__.js b/src/lib/sound/__init__.js index d0dbafc09a..33067823f7 100644 --- a/src/lib/sound/__init__.js +++ b/src/lib/sound/__init__.js @@ -1,3 +1,3 @@ -var $builtinmodule = function() { - return {}; +var $builtinmodule = function () { + return {}; }; diff --git a/src/lib/sound/sample.js b/src/lib/sound/sample.js index 4464a2cf36..38a0acc965 100644 --- a/src/lib/sound/sample.js +++ b/src/lib/sound/sample.js @@ -1,20 +1,20 @@ -var $builtinmodule = function() { +var $builtinmodule = function () { var mod, sampleWrapper; mod = {}; sampleWrapper = { - getSound : new Sk.builtin.func(function (sample) { + getSound: new Sk.builtin.func(function (sample) { Sk.builtin.pyCheckArgs("getSound", arguments, 1); return sample._sound; }), - getSampleValue : new Sk.builtin.func(function (sample) { + getSampleValue: new Sk.builtin.func(function (sample) { Sk.builtin.pyCheckArgs("getSampleValue", arguments, 1); return new Sk.builtin.float_(sample._internalSound.getLeftSample(sample._index)); }), - setSampleValue : new Sk.builtin.func(function (sample, value) { + setSampleValue: new Sk.builtin.func(function (sample, value) { Sk.builtin.pyCheckArgs("setSampleValue", arguments, 2); sample._internalSound.setLeftSample(sample._index, Sk.ffi.unwrapo(value)); }), @@ -31,13 +31,13 @@ var $builtinmodule = function() { $loc.__str__ = new Sk.builtin.func(function (self) { Sk.builtin.pyCheckArgs("__str__", arguments, 1); return new Sk.builtin.str("Sample at " + self._index + " with value " + - self._internalSound.getLeftSample(self._index)); + self._internalSound.getLeftSample(self._index)); }); $loc.__repr__ = new Sk.builtin.func(function (self) { Sk.builtin.pyCheckArgs("__repr__", arguments, 1); return new Sk.builtin.str("Sample at " + self._index + " with value " + - self._internalSound.getLeftSample(self._index)); + self._internalSound.getLeftSample(self._index)); }); goog.object.extend($loc, sampleWrapper); diff --git a/src/lib/sound/sound.js b/src/lib/sound/sound.js index c025a13903..153ed1d452 100644 --- a/src/lib/sound/sound.js +++ b/src/lib/sound/sound.js @@ -1,5 +1,5 @@ // Do not include this module directly as it has dependencies -var $builtinmodule = function() { +var $builtinmodule = function () { var soundWrapper, mod, Sample; mod = {}; @@ -13,172 +13,184 @@ var $builtinmodule = function() { sound._sound.stop(); }), - play : new Sk.builtin.func(function(sound) { + play: new Sk.builtin.func(function (sound) { Sk.builtin.pyCheckArgs("play", arguments, 1); sound._sound.play(); }), - blockingPlay : new Sk.builtin.func(function(sound) { + blockingPlay: new Sk.builtin.func(function (sound) { Sk.builtin.pyCheckArgs("blockingPlay", arguments, 1); Sk.future(function (continueWith) { sound._sound.play(continueWith); }); }), - getDuration : new Sk.builtin.func(function(sound) { + getDuration: new Sk.builtin.func(function (sound) { Sk.builtin.pyCheckArgs("getDuration", arguments, 1); return new Sk.builtin.float_(sound._sound.getDuration()); }), - getNumSamples : new Sk.builtin.func(function(sound) { + getNumSamples: new Sk.builtin.func(function (sound) { Sk.builtin.pyCheckArgs("getNumSamples", arguments, 1); return new Sk.builtin.int_(sound._sound.getLength()); }), - getLength : new Sk.builtin.func(function(sound) { + getLength: new Sk.builtin.func(function (sound) { Sk.builtin.pyCheckArgs("getLength", arguments, 1); return new Sk.builtin.int_(sound._sound.getLength()); }), - getSamplingRate : new Sk.builtin.func(function(sound) { + getSamplingRate: new Sk.builtin.func(function (sound) { Sk.builtin.pyCheckArgs("getSamplingRate", arguments, 1); return new Sk.builtin.int_(sound._sound.getSamplingRate()); }), - setSampleValueAt : new Sk.builtin.func(function(sound, index, value) { + setSampleValueAt: new Sk.builtin.func(function (sound, index, value) { var length; Sk.builtin.pyCheckArgs("setSampleValueAt", arguments, 3); length = sound._sound.getLength(); - if(index < 0 || index >= length) { + if (index < 0 || index >= length) { throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); } - if(!(value instanceof Sk.builtin.int_)) { + if (!(value instanceof Sk.builtin.int_)) { throw new Sk.builtin.TypeError("Value must be an integer"); } value = Sk.ffi.unwrapo(value); - if(value < -32768) { value = -32768; } - if(value > 32767) { value = 32767; } + if (value < -32768) { + value = -32768; + } + if (value > 32767) { + value = 32767; + } sound._sound.setLeftSample(Sk.ffi.unwrapo(index), pythy.Sound.map16BitIntToFloat(value)); }), - setLeftSample : new Sk.builtin.func(function(sound, index, value) { + setLeftSample: new Sk.builtin.func(function (sound, index, value) { var length; Sk.builtin.pyCheckArgs("setLeftSample", arguments, 3); length = sound._sound.getLength(); - if(index < 0 || index >= length) { + if (index < 0 || index >= length) { throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); } - if(!(value instanceof Sk.builtin.int_)) { + if (!(value instanceof Sk.builtin.int_)) { throw new Sk.builtin.TypeError("Value must be an integer"); } value = Sk.ffi.unwrapo(value); - if(value < -32768) { value = -32768; } - if(value > 32767) { value = 32767; } + if (value < -32768) { + value = -32768; + } + if (value > 32767) { + value = 32767; + } sound._sound.setLeftSample(Sk.ffi.unwrapo(index), pythy.Sound.map16BitIntToFloat(value)); }), - setRightSample : new Sk.builtin.func(function(sound, index, value) { + setRightSample: new Sk.builtin.func(function (sound, index, value) { var length; Sk.builtin.pyCheckArgs("setRightSample", arguments, 3); length = sound._sound.getLength(); - if(index < 0 || index >= length) { + if (index < 0 || index >= length) { throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); } - if(!(value instanceof Sk.builtin.int_)) { + if (!(value instanceof Sk.builtin.int_)) { throw new Sk.builtin.TypeError("Value must be an integer"); } value = Sk.ffi.unwrapo(value); - if(value < -32768) { value = -32768; } - if(value > 32767) { value = 32767; } + if (value < -32768) { + value = -32768; + } + if (value > 32767) { + value = 32767; + } sound._sound.setRightSample(Sk.ffi.unwrapo(index), pythy.Sound.map16BitIntToFloat(value)); }), - getSampleValueAt : new Sk.builtin.func(function(sound, index) { + getSampleValueAt: new Sk.builtin.func(function (sound, index) { var length; Sk.builtin.pyCheckArgs("getSampleValueAt", arguments, 2); length = sound._sound.getLength(); - if(index < 0 || index >= length) { + if (index < 0 || index >= length) { throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); } return new Sk.builtin.int_(pythy.Sound.mapFloatTo16BitInt(sound._sound.getLeftSample(Sk.ffi.unwrapo(index)))); }), - getLeftSample : new Sk.builtin.func(function(sound, index) { + getLeftSample: new Sk.builtin.func(function (sound, index) { var length; Sk.builtin.pyCheckArgs("getLeftSample", arguments, 2); length = sound._sound.getLength(); - if(index < 0 || index >= length) { + if (index < 0 || index >= length) { throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); } return new Sk.builtin.int_(pythy.Sound.mapFloatTo16BitInt(sound._sound.getLeftSample(Sk.ffi.unwrapo(index)))); }), - getRightSample : new Sk.builtin.func(function(sound, index) { + getRightSample: new Sk.builtin.func(function (sound, index) { var length; Sk.builtin.pyCheckArgs("getRightSample", arguments, 2); length = sound._sound.getLength(); - if(index < 0 || index >= length) { + if (index < 0 || index >= length) { throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); } return new Sk.builtin.int_(pythy.Sound.mapFloatTo16BitInt(sound._sound.getRightSample(Sk.ffi.unwrapo(index)))); }), - getSampleObjectAt : new Sk.builtin.func(function (sound, index) { + getSampleObjectAt: new Sk.builtin.func(function (sound, index) { var length; Sk.builtin.pyCheckArgs("getSampleObjectAt", arguments, 2); length = sound._sound.getLength(); - if(index < 0 || index >= length) { + if (index < 0 || index >= length) { throw new Sk.builtin.ValueError("Index must have a value between 0 and " + length); } return Sk.misceval.callsim(Sample, sound, index); }), - getSamples : new Sk.builtin.func(function (sound) { + getSamples: new Sk.builtin.func(function (sound) { var samples, len; - + Sk.builtin.pyCheckArgs("getSamples", arguments, 1); samples = []; len = sound._sound.getLength(); - for(var i = 0; i < len; i++) { + for (var i = 0; i < len; i++) { samples.push(Sk.misceval.callsim(Sample, sound, Sk.builtin.int_(i))); } @@ -191,7 +203,7 @@ var $builtinmodule = function() { onError = function (continueWith) { return function (errorMsg) { - if(errorMsg.indexOf("File") !== -1) { + if (errorMsg.indexOf("File") !== -1) { continueWith(new Sk.builtin.ValueError(errorMsg + ". Is the URL incorrect?")); } else { continueWith(new Sk.builtin.ValueError(errorMsg)); @@ -206,12 +218,12 @@ var $builtinmodule = function() { arg0 = arguments[1]; - if(arg0 instanceof Sk.builtin.str) { + if (arg0 instanceof Sk.builtin.str) { arg0 = Sk.ffi.unwrapo(arg0); //url res = Sk.future(function (continueWith) { new window.pythy.Sound(continueWith, onError(continueWith), arg0); - }); - } else if(arg0.tp$name === "Sound") { + }); + } else if (arg0.tp$name === "Sound") { res = Sk.future(function (continueWith) { new window.pythy.Sound(continueWith, onError(continueWith), arg0._sound); }); @@ -223,49 +235,49 @@ var $builtinmodule = function() { }); } - if(res instanceof window.pythy.Sound) { + if (res instanceof window.pythy.Sound) { sound._sound = res; - } else if(res) { + } else if (res) { throw res; } }); - $loc.__str__ = new Sk.builtin.func(function(sound) { + $loc.__str__ = new Sk.builtin.func(function (sound) { var str; Sk.builtin.pyCheckArgs("__str__", arguments, 1); str = "Sound, "; - if(sound._sound.url) { + if (sound._sound.url) { str += "File: " + sound._sound.url + ", "; } return new Sk.builtin.str(str + "Number of samples: " + sound._sound.getLength()); }); - $loc.__repr__ = new Sk.builtin.func(function(sound) { + $loc.__repr__ = new Sk.builtin.func(function (sound) { var str; Sk.builtin.pyCheckArgs("__repr__", arguments, 1); str = "Sound, "; - if(sound._sound.url) { + if (sound._sound.url) { str += "File: " + sound._sound.url + ", "; } return new Sk.builtin.str(str + "Number of samples: " + sound._sound.getLength()); }); - $loc.writeToFile = new Sk.builtin.func(function(sound, path) { + $loc.writeToFile = new Sk.builtin.func(function (sound, path) { Sk.builtin.pyCheckArgs("writeToFile", arguments, 2); sound._sound.save(Sk.ffi.unwrapo(path)); }); $loc.duplicate = new Sk.builtin.func(function (sound) { Sk.builtin.pyCheckArgs("duplicate", arguments, 1); - return Sk.misceval.callsim(mod.Sound, sound); + return Sk.misceval.callsim(mod.Sound, sound); }); goog.object.extend($loc, soundWrapper); @@ -277,7 +289,7 @@ var $builtinmodule = function() { goog.object.extend(mod, { duplicateSound: new Sk.builtin.func(function (sound) { Sk.builtin.pyCheckArgs("duplicateSound", arguments, 1); - return Sk.misceval.callsim(mod.Sound, sound); + return Sk.misceval.callsim(mod.Sound, sound); }), makeSound: new Sk.builtin.func(function (url) { @@ -295,7 +307,7 @@ var $builtinmodule = function() { Sk.builtin.pyCheckArgs("makeEmptySoundBySeconds", arguments, [1, 2]); - if(Sk.ffi.unwrapo(seconds) < 0) { + if (Sk.ffi.unwrapo(seconds) < 0) { throw new Sk.builtin.ValueError("Duration can not be negative"); } numSamples = Sk.ffi.unwrapo(seconds) * (Sk.ffi.unwrapo(samplingRate) || window.pythy.Sound.SAMPLE_RATE); @@ -307,7 +319,7 @@ var $builtinmodule = function() { window.pythy.soundTool.start(sound._sound); }), - writeSoundTo : new Sk.builtin.func(function(sound, path) { + writeSoundTo: new Sk.builtin.func(function (sound, path) { Sk.builtin.pyCheckArgs("writeSoundTo", arguments, 2); sound._sound.save(Sk.ffi.unwrapo(path)); }) diff --git a/src/lib/stat.py b/src/lib/stat.py index 5ea14c1362..adb768fe88 100644 --- a/src/lib/stat.py +++ b/src/lib/stat.py @@ -5,17 +5,18 @@ # Indices for stat struct members in the tuple returned by os.stat() -ST_MODE = 0 -ST_INO = 1 -ST_DEV = 2 +ST_MODE = 0 +ST_INO = 1 +ST_DEV = 2 ST_NLINK = 3 -ST_UID = 4 -ST_GID = 5 -ST_SIZE = 6 +ST_UID = 4 +ST_GID = 5 +ST_SIZE = 6 ST_ATIME = 7 ST_MTIME = 8 ST_CTIME = 9 + # Extract bits from the mode def S_IMODE(mode): @@ -24,61 +25,71 @@ def S_IMODE(mode): """ return mode & 0o7777 + def S_IFMT(mode): """Return the portion of the file's mode that describes the file type. """ return mode & 0o170000 + # Constants used as S_IFMT() for various file types # (not all are implemented on all systems) -S_IFDIR = 0o040000 # directory -S_IFCHR = 0o020000 # character device -S_IFBLK = 0o060000 # block device -S_IFREG = 0o100000 # regular file -S_IFIFO = 0o010000 # fifo (named pipe) -S_IFLNK = 0o120000 # symbolic link +S_IFDIR = 0o040000 # directory +S_IFCHR = 0o020000 # character device +S_IFBLK = 0o060000 # block device +S_IFREG = 0o100000 # regular file +S_IFIFO = 0o010000 # fifo (named pipe) +S_IFLNK = 0o120000 # symbolic link S_IFSOCK = 0o140000 # socket file + # Functions to test for each file type def S_ISDIR(mode): """Return True if mode is from a directory.""" return S_IFMT(mode) == S_IFDIR + def S_ISCHR(mode): """Return True if mode is from a character special device file.""" return S_IFMT(mode) == S_IFCHR + def S_ISBLK(mode): """Return True if mode is from a block special device file.""" return S_IFMT(mode) == S_IFBLK + def S_ISREG(mode): """Return True if mode is from a regular file.""" return S_IFMT(mode) == S_IFREG + def S_ISFIFO(mode): """Return True if mode is from a FIFO (named pipe).""" return S_IFMT(mode) == S_IFIFO + def S_ISLNK(mode): """Return True if mode is from a symbolic link.""" return S_IFMT(mode) == S_IFLNK + def S_ISSOCK(mode): """Return True if mode is from a socket.""" return S_IFMT(mode) == S_IFSOCK + # Names for permission bits S_ISUID = 0o4000 # set UID bit S_ISGID = 0o2000 # set GID bit -S_ENFMT = S_ISGID # file locking enforcement +S_ENFMT = S_ISGID # file locking enforcement S_ISVTX = 0o1000 # sticky bit S_IREAD = 0o0400 # Unix V7 synonym for S_IRUSR -S_IWRITE = 0o0200 # Unix V7 synonym for S_IWUSR +S_IWRITE = 0o0200 # Unix V7 synonym for S_IWUSR S_IEXEC = 0o0100 # Unix V7 synonym for S_IXUSR S_IRWXU = 0o0700 # mask for owner permissions S_IRUSR = 0o0400 # read by owner @@ -95,48 +106,48 @@ def S_ISSOCK(mode): # Names for file flags -UF_NODUMP = 0x00000001 # do not dump file +UF_NODUMP = 0x00000001 # do not dump file UF_IMMUTABLE = 0x00000002 # file may not be changed -UF_APPEND = 0x00000004 # file may only be appended to -UF_OPAQUE = 0x00000008 # directory is opaque when viewed through a union stack -UF_NOUNLINK = 0x00000010 # file may not be renamed or deleted -UF_COMPRESSED = 0x00000020 # OS X: file is hfs-compressed -UF_HIDDEN = 0x00008000 # OS X: file should not be displayed -SF_ARCHIVED = 0x00010000 # file may be archived +UF_APPEND = 0x00000004 # file may only be appended to +UF_OPAQUE = 0x00000008 # directory is opaque when viewed through a union stack +UF_NOUNLINK = 0x00000010 # file may not be renamed or deleted +UF_COMPRESSED = 0x00000020 # OS X: file is hfs-compressed +UF_HIDDEN = 0x00008000 # OS X: file should not be displayed +SF_ARCHIVED = 0x00010000 # file may be archived SF_IMMUTABLE = 0x00020000 # file may not be changed -SF_APPEND = 0x00040000 # file may only be appended to -SF_NOUNLINK = 0x00100000 # file may not be renamed or deleted -SF_SNAPSHOT = 0x00200000 # file is a snapshot file - +SF_APPEND = 0x00040000 # file may only be appended to +SF_NOUNLINK = 0x00100000 # file may not be renamed or deleted +SF_SNAPSHOT = 0x00200000 # file is a snapshot file _filemode_table = ( - ((S_IFLNK, "l"), - (S_IFSOCK, "s"), # Must appear before IFREG and IFDIR as IFSOCK == IFREG | IFDIR - (S_IFREG, "-"), - (S_IFBLK, "b"), - (S_IFDIR, "d"), - (S_IFCHR, "c"), - (S_IFIFO, "p")), - - ((S_IRUSR, "r"),), - ((S_IWUSR, "w"),), - ((S_IXUSR|S_ISUID, "s"), - (S_ISUID, "S"), - (S_IXUSR, "x")), - - ((S_IRGRP, "r"),), - ((S_IWGRP, "w"),), - ((S_IXGRP|S_ISGID, "s"), - (S_ISGID, "S"), - (S_IXGRP, "x")), - - ((S_IROTH, "r"),), - ((S_IWOTH, "w"),), - ((S_IXOTH|S_ISVTX, "t"), - (S_ISVTX, "T"), - (S_IXOTH, "x")) + ((S_IFLNK, "l"), + (S_IFSOCK, "s"), # Must appear before IFREG and IFDIR as IFSOCK == IFREG | IFDIR + (S_IFREG, "-"), + (S_IFBLK, "b"), + (S_IFDIR, "d"), + (S_IFCHR, "c"), + (S_IFIFO, "p")), + + ((S_IRUSR, "r"),), + ((S_IWUSR, "w"),), + ((S_IXUSR | S_ISUID, "s"), + (S_ISUID, "S"), + (S_IXUSR, "x")), + + ((S_IRGRP, "r"),), + ((S_IWGRP, "w"),), + ((S_IXGRP | S_ISGID, "s"), + (S_ISGID, "S"), + (S_IXGRP, "x")), + + ((S_IROTH, "r"),), + ((S_IWOTH, "w"),), + ((S_IXOTH | S_ISVTX, "t"), + (S_ISVTX, "T"), + (S_IXOTH, "x")) ) + def filemode(mode): """Convert a file's mode to a string of the form '-rwxrwxrwx'.""" perm = [] @@ -171,9 +182,8 @@ def filemode(mode): FILE_ATTRIBUTE_TEMPORARY = 256 FILE_ATTRIBUTE_VIRTUAL = 65536 - # If available, use C implementation try: from _stat import * except ImportError: - pass \ No newline at end of file + pass diff --git a/src/lib/string.js b/src/lib/string.js index d96e8eda0c..9d2d6d5dcd 100644 --- a/src/lib/string.js +++ b/src/lib/string.js @@ -8,45 +8,45 @@ var $builtinmodule = function (name) { var mod = {}; - mod.ascii_lowercase = Sk.builtin.str('abcdefghijklmnopqrstuvwxyz'); - mod.ascii_uppercase = Sk.builtin.str('ABCDEFGHIJKLMNOPQRSTUVWXYZ'); - mod.ascii_letters = Sk.builtin.str(mod.ascii_lowercase.v + mod.ascii_uppercase.v); + mod.ascii_lowercase = new Sk.builtin.str("abcdefghijklmnopqrstuvwxyz"); + mod.ascii_uppercase = new Sk.builtin.str("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + mod.ascii_letters = new Sk.builtin.str(mod.ascii_lowercase.v + mod.ascii_uppercase.v); - mod.lowercase = Sk.builtin.str('abcdefghijklmnopqrstuvwxyz'); - mod.uppercase = Sk.builtin.str('ABCDEFGHIJKLMNOPQRSTUVWXYZ'); - mod.letters = Sk.builtin.str(mod.lowercase.v + mod.uppercase.v); + mod.lowercase = new Sk.builtin.str("abcdefghijklmnopqrstuvwxyz"); + mod.uppercase = new Sk.builtin.str("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + mod.letters = new Sk.builtin.str(mod.lowercase.v + mod.uppercase.v); - mod.digits = Sk.builtin.str('0123456789'); - mod.hexdigits = Sk.builtin.str('0123456789abcdefABCDEF'); - mod.octdigits = Sk.builtin.str('01234567'); + mod.digits = new Sk.builtin.str("0123456789"); + mod.hexdigits = new Sk.builtin.str("0123456789abcdefABCDEF"); + mod.octdigits = new Sk.builtin.str("01234567"); - mod.punctuation = Sk.builtin.str('!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'); - mod.whitespace = Sk.builtin.str('\t\n\x0b\x0c\r '); + mod.punctuation = new Sk.builtin.str("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"); + mod.whitespace = new Sk.builtin.str("\t\n\x0b\x0c\r "); /* Note: The docs for string.printable say that it's the concatenation of string.digits, * string.letters, string.punctuation, and string.whitespace. The CPython interpreter * outputs the whitespace characters in one order when string.whitespace is used, and a * slightly different order when string.printable is used. I've elected to follow the * behavior of CPython here rather than the spec. */ - mod.printable = Sk.builtin.str(mod.digits.v + mod.letters.v + mod.punctuation.v + " \t\n\r\x0b\x0c"); + mod.printable = new Sk.builtin.str(mod.digits.v + mod.letters.v + mod.punctuation.v + " \t\n\r\x0b\x0c"); mod.split = new Sk.builtin.func(function (s, sep, maxsplit) { - return Sk.misceval.callsimArray(Sk.builtin.str.prototype['split'], [s, sep, maxsplit]); + return Sk.misceval.callsimArray(Sk.builtin.str.prototype["split"], [s, sep, maxsplit]); }); /* Return a copy of word with only its first character capitalized. */ mod.capitalize = new Sk.builtin.func(function (word) { - return Sk.misceval.callsimArray(Sk.builtin.str.prototype['capitalize'], [word]); + return Sk.misceval.callsimArray(Sk.builtin.str.prototype["capitalize"], [word]); }); /* Concatenate a list or tuple of words with intervening occurrences * of sep. The default value for sep is a single space character. */ mod.join = new Sk.builtin.func(function (words, sep) { if (sep === undefined) { - sep = Sk.builtin.str(' '); + sep = new Sk.builtin.str(" "); } - return Sk.misceval.callsimArray(Sk.builtin.str.prototype['join'], [sep, words]); + return Sk.misceval.callsimArray(Sk.builtin.str.prototype["join"], [sep, words]); }); @@ -55,12 +55,12 @@ var $builtinmodule = function (name) { * Note that this replaces runs of whitespace characters by a single * space, and removes leading and trailing whitespace. */ mod.capwords = new Sk.builtin.func(function (s, sep) { - Sk.builtin.pyCheckArgsLen('capwords', arguments.length, 1, 2); + Sk.builtin.pyCheckArgsLen("capwords", arguments.length, 1, 2); if (!Sk.builtin.checkString(s)) { throw new Sk.builtin.TypeError("s must be a string"); } if (sep === undefined) { - sep = Sk.builtin.str(' '); + sep = new Sk.builtin.str(" "); } if (!Sk.builtin.checkString(sep)) { throw new Sk.builtin.TypeError("sep must be a string"); @@ -69,7 +69,7 @@ var $builtinmodule = function (name) { var words = Sk.misceval.callsimArray(mod.split, [s, sep]); var capWords = []; for (var i = 0; i < words.v.length; i++) { - var word = Sk.builtin.list.prototype['list_subscript_'].call(words, i); + var word = Sk.abstr.sequenceGetItem(words, i); var cap = Sk.misceval.callsimArray(mod.capitalize, [word]); capWords.push(cap); } diff --git a/src/lib/test/__init__.py b/src/lib/test/__init__.py index e50469626a..e80ec55ef2 100644 --- a/src/lib/test/__init__.py +++ b/src/lib/test/__init__.py @@ -1,12 +1,13 @@ __author__ = 'bmiller' + def testEqual(actual, expected): if type(expected) == type(1): if actual == expected: print('Pass') return True elif type(expected) == type(1.11): - if abs(actual-expected) < 0.00001: + if abs(actual - expected) < 0.00001: print('Pass') return True else: @@ -16,6 +17,6 @@ def testEqual(actual, expected): print('Test Failed: expected ' + str(expected) + ' but got ' + str(actual)) return False + def testNotEqual(actual, expected): pass - diff --git a/src/lib/textwrap.py b/src/lib/textwrap.py index 9ed3eb3253..9010e21c94 100644 --- a/src/lib/textwrap.py +++ b/src/lib/textwrap.py @@ -1 +1,464 @@ -raise NotImplementedError("textwrap is not yet implemented in Skulpt") +"""Text wrapping and filling. +""" + +# Copyright (C) 1999-2001 Gregory P. Ward. +# Copyright (C) 2002, 2003 Python Software Foundation. +# Written by Greg Ward + +import re, string + +__all__ = ['TextWrapper', 'wrap', 'fill', 'dedent', 'indent', 'shorten'] + +# Hardcode the recognized whitespace characters to the US-ASCII +# whitespace characters. The main reason for doing this is that +# some Unicode spaces (like \u00a0) are non-breaking whitespaces. +_whitespace = '\t\n\x0b\x0c\r ' + + +class TextWrapper: + """ + Object for wrapping/filling text. The public interface consists of + the wrap() and fill() methods; the other methods are just there for + subclasses to override in order to tweak the default behaviour. + If you want to completely replace the main wrapping algorithm, + you'll probably have to override _wrap_chunks(). + Several instance attributes control various aspects of wrapping: + width (default: 70) + the maximum width of wrapped lines (unless break_long_words + is false) + initial_indent (default: "") + string that will be prepended to the first line of wrapped + output. Counts towards the line's width. + subsequent_indent (default: "") + string that will be prepended to all lines save the first + of wrapped output; also counts towards each line's width. + expand_tabs (default: true) + Expand tabs in input text to spaces before further processing. + Each tab will become 0 .. 'tabsize' spaces, depending on its position + in its line. If false, each tab is treated as a single character. + tabsize (default: 8) + Expand tabs in input text to 0 .. 'tabsize' spaces, unless + 'expand_tabs' is false. + replace_whitespace (default: true) + Replace all whitespace characters in the input text by spaces + after tab expansion. Note that if expand_tabs is false and + replace_whitespace is true, every tab will be converted to a + single space! + fix_sentence_endings (default: false) + Ensure that sentence-ending punctuation is always followed + by two spaces. Off by default because the algorithm is + (unavoidably) imperfect. + break_long_words (default: true) + Break words longer than 'width'. If false, those words will not + be broken, and some lines might be longer than 'width'. + break_on_hyphens (default: true) + Allow breaking hyphenated words. If true, wrapping will occur + preferably on whitespaces and right after hyphens part of + compound words. + drop_whitespace (default: true) + Drop leading and trailing whitespace from lines. + max_lines (default: None) + Truncate wrapped lines. + placeholder (default: ' [...]') + Append to the last line of truncated text. + """ + + unicode_whitespace_trans = {} + # uspace = ord(' ') + uspace = ' ' + for x in _whitespace: + # unicode_whitespace_trans[ord(x)] = uspace + unicode_whitespace_trans[x] = uspace + + # This funky little regex is just the trick for splitting + # text up into word-wrappable chunks. E.g. + # "Hello there -- you goof-ball, use the -b option!" + # splits into + # Hello/ /there/ /--/ /you/ /goof-/ball,/ /use/ /the/ /-b/ /option! + # (after stripping out empty strings). + wordsep_re = re.compile( + r'(\s+|' # any whitespace + r'[^\s\w]*\w+[^0-9\W]-(?=\w+[^0-9\W]))') # hyphenated words + em_dash = re.compile(r'(\s+|' # any whitespace + r'[^\s\w]*\w+[^0-9\W]-(?=\w+[^0-9\W])|' # hyphenated words + r'(?!^)-{2,}(?=\w))') # em-dash + + # This less funky little regex just split on recognized spaces. E.g. + # "Hello there -- you goof-ball, use the -b option!" + # splits into + # Hello/ /there/ /--/ /you/ /goof-ball,/ /use/ /the/ /-b/ /option!/ + wordsep_simple_re = re.compile(r'(\s+)') + + # XXX this is not locale- or charset-aware -- string.lowercase + # is US-ASCII only (and therefore English-only) + sentence_end_re = re.compile(r'[a-z]' # lowercase letter + r'[\.\!\?]' # sentence-ending punct. + r'[\"\']?' # optional end-of-quote + r'\Z') # end of chunk + sentence_end_re = r'[a-z][\.\!\?][\"\']?' + + def __init__(self, + width=70, + initial_indent="", + subsequent_indent="", + expand_tabs=True, + replace_whitespace=True, + fix_sentence_endings=False, + break_long_words=True, + drop_whitespace=True, + break_on_hyphens=True, + tabsize=8, + max_lines=None, + placeholder=' [...]'): + self.width = width + self.initial_indent = initial_indent + self.subsequent_indent = subsequent_indent + self.expand_tabs = expand_tabs + self.replace_whitespace = replace_whitespace + self.fix_sentence_endings = fix_sentence_endings + self.break_long_words = break_long_words + self.drop_whitespace = drop_whitespace + self.break_on_hyphens = break_on_hyphens + self.tabsize = tabsize + self.max_lines = max_lines + self.placeholder = placeholder + + # -- Private methods ----------------------------------------------- + # (possibly useful for subclasses to override) + + def _munge_whitespace(self, text): + """_munge_whitespace(text : string) -> string + Munge whitespace in text: expand tabs and convert all other + whitespace characters to spaces. Eg. " foo\\tbar\\n\\nbaz" + becomes " foo bar baz". + """ + if self.expand_tabs: + text = text.expandtabs(self.tabsize) + if self.replace_whitespace: + for key, val in self.unicode_whitespace_trans.items(): + text = text.replace(key, val) + return text + + def _split(self, text): + """_split(text : string) -> [string] + Split the text to wrap into indivisible chunks. Chunks are + not quite the same as words; see _wrap_chunks() for full + details. As an example, the text + Look, goof-ball -- use the -b option! + breaks into the following chunks: + 'Look,', ' ', 'goof-', 'ball', ' ', '--', ' ', + 'use', ' ', 'the', ' ', '-b', ' ', 'option!' + if break_on_hyphens is True, or in: + 'Look,', ' ', 'goof-ball', ' ', '--', ' ', + 'use', ' ', 'the', ' ', '-b', ' ', option!' + otherwise. + """ + if self.break_on_hyphens is True: + chunks = self.wordsep_re.split(text) + if "--" in text: + chunks = [item + for sublist in [self.em_dash.split(chunk) for chunk in chunks] + for item in sublist] + else: + chunks = self.wordsep_simple_re.split(text) + chunks = [c for c in chunks if c] + return chunks + + def _fix_sentence_endings(self, chunks): + """_fix_sentence_endings(chunks : [string]) + Correct for sentence endings buried in 'chunks'. Eg. when the + original text contains "... foo.\\nBar ...", munge_whitespace() + and split() will convert that to [..., "foo.", " ", "Bar", ...] + which has one too few spaces; this method simply changes the one + space to two. + """ + i = 0 + # patsearch = self.sentence_end_re.search + while i < len(chunks) - 1: + if chunks[i + 1] == " " and re.search(self.sentence_end_re, chunks[i]) and chunks[i][-1] in ".!?\"\'": + chunks[i + 1] = " " + i += 2 + else: + i += 1 + + def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): + """_handle_long_word(chunks : [string], + cur_line : [string], + cur_len : int, width : int) + Handle a chunk of text (most likely a word, not whitespace) that + is too long to fit in any line. + """ + # Figure out when indent is larger than the specified width, and make + # sure at least one character is stripped off on every pass + if width < 1: + space_left = 1 + else: + space_left = width - cur_len + + # If we're allowed to break long words, then do so: put as much + # of the next chunk onto the current line as will fit. + if self.break_long_words: + cur_line.append(reversed_chunks[-1][:space_left]) + reversed_chunks[-1] = reversed_chunks[-1][space_left:] + + # Otherwise, we have to preserve the long word intact. Only add + # it to the current line if there's nothing already there -- + # that minimizes how much we violate the width constraint. + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + # If we're not allowed to break long words, and there's already + # text on the current line, do nothing. Next time through the + # main loop of _wrap_chunks(), we'll wind up here again, but + # cur_len will be zero, so the next line will be entirely + # devoted to the long word that we can't handle right now. + + def _wrap_chunks(self, chunks): + """_wrap_chunks(chunks : [string]) -> [string] + Wrap a sequence of text chunks and return a list of lines of + length 'self.width' or less. (If 'break_long_words' is false, + some lines may be longer than this.) Chunks correspond roughly + to words and the whitespace between them: each chunk is + indivisible (modulo 'break_long_words'), but a line break can + come between any two chunks. Chunks should not have internal + whitespace; ie. a chunk is either all whitespace or a "word". + Whitespace chunks will be removed from the beginning and end of + lines, but apart from that whitespace is preserved. + """ + lines = [] + if self.width <= 0: + raise ValueError("invalid width %r (must be > 0)" % self.width) + if self.max_lines is not None: + if self.max_lines > 1: + indent = self.subsequent_indent + else: + indent = self.initial_indent + if len(indent) + len(self.placeholder.lstrip()) > self.width: + raise ValueError("placeholder too large for max width") + + # Arrange in reverse order so items can be efficiently popped + # from a stack of chucks. + chunks.reverse() + + while chunks: + + # Start the list of chunks that will make up the current line. + # cur_len is just the length of all the chunks in cur_line. + cur_line = [] + cur_len = 0 + + # Figure out which static string will prefix this line. + if lines: + indent = self.subsequent_indent + else: + indent = self.initial_indent + + # Maximum width for this line. + width = self.width - len(indent) + + # First chunk on line is whitespace -- drop it, unless this + # is the very beginning of the text (ie. no lines started yet). + if self.drop_whitespace and chunks[-1].strip() == '' and lines: + del chunks[-1] + + while chunks: + l = len(chunks[-1]) + + # Can at least squeeze this chunk onto the current line. + if cur_len + l <= width: + cur_line.append(chunks.pop()) + cur_len += l + + # Nope, this line is full. + else: + break + + # The current line is full, and the next chunk is too big to + # fit on *any* line (not just this one). + if chunks and len(chunks[-1]) > width: + self._handle_long_word(chunks, cur_line, cur_len, width) + cur_len = sum(map(len, cur_line)) + + # If the last chunk on this line is all whitespace, drop it. + if self.drop_whitespace and cur_line and cur_line[-1].strip() == '': + cur_len -= len(cur_line[-1]) + del cur_line[-1] + + if cur_line: + if (self.max_lines is None or + len(lines) + 1 < self.max_lines or + (not chunks or + self.drop_whitespace and + len(chunks) == 1 and + not chunks[0].strip()) and cur_len <= width): + # Convert current line back to a string and store it in + # list of all lines (return value). + lines.append(indent + ''.join(cur_line)) + else: + while cur_line: + if (cur_line[-1].strip() and + cur_len + len(self.placeholder) <= width): + cur_line.append(self.placeholder) + lines.append(indent + ''.join(cur_line)) + break + cur_len -= len(cur_line[-1]) + del cur_line[-1] + else: + if lines: + prev_line = lines[-1].rstrip() + if (len(prev_line) + len(self.placeholder) <= + self.width): + lines[-1] = prev_line + self.placeholder + break + lines.append(indent + self.placeholder.lstrip()) + break + + return lines + + def _split_chunks(self, text): + text = self._munge_whitespace(text) + return self._split(text) + + # -- Public interface ---------------------------------------------- + + def wrap(self, text): + """wrap(text : string) -> [string] + Reformat the single paragraph in 'text' so it fits in lines of + no more than 'self.width' columns, and return a list of wrapped + lines. Tabs in 'text' are expanded with string.expandtabs(), + and all other whitespace characters (including newline) are + converted to space. + """ + chunks = self._split_chunks(text) + if self.fix_sentence_endings: + self._fix_sentence_endings(chunks) + return self._wrap_chunks(chunks) + + def fill(self, text): + """fill(text : string) -> string + Reformat the single paragraph in 'text' to fit in lines of no + more than 'self.width' columns, and return a new string + containing the entire wrapped paragraph. + """ + return "\n".join(self.wrap(text)) + + +# -- Convenience interface --------------------------------------------- + +def wrap(text, width=70, **kwargs): + """Wrap a single paragraph of text, returning a list of wrapped lines. + Reformat the single paragraph in 'text' so it fits in lines of no + more than 'width' columns, and return a list of wrapped lines. By + default, tabs in 'text' are expanded with string.expandtabs(), and + all other whitespace characters (including newline) are converted to + space. See TextWrapper class for available keyword args to customize + wrapping behaviour. + """ + w = TextWrapper(width=width, **kwargs) + return w.wrap(text) + + +def fill(text, width=70, **kwargs): + """Fill a single paragraph of text, returning a new string. + Reformat the single paragraph in 'text' to fit in lines of no more + than 'width' columns, and return a new string containing the entire + wrapped paragraph. As with wrap(), tabs are expanded and other + whitespace characters converted to space. See TextWrapper class for + available keyword args to customize wrapping behaviour. + """ + w = TextWrapper(width=width, **kwargs) + return w.fill(text) + + +def shorten(text, width, **kwargs): + """Collapse and truncate the given text to fit in the given width. + The text first has its whitespace collapsed. If it then fits in + the *width*, it is returned as is. Otherwise, as many words + as possible are joined and then the placeholder is appended:: + >>> textwrap.shorten("Hello world!", width=12) + 'Hello world!' + >>> textwrap.shorten("Hello world!", width=11) + 'Hello [...]' + """ + w = TextWrapper(width=width, max_lines=1, **kwargs) + return w.fill(' '.join(text.strip().split())) + + +# -- Loosely related functionality ------------------------------------- + +# _whitespace_only_re = re.compile('^[ \t]+$', re.MULTILINE) +# _leading_whitespace_re = re.compile('(^[ \t]*)(?:[^ \t\n])', re.MULTILINE) + +def dedent(text): + """Remove any common leading whitespace from every line in `text`. + This can be used to make triple-quoted strings line up with the left + edge of the display, while still presenting them in the source code + in indented form. + Note that tabs and spaces are both treated as whitespace, but they + are not equal: the lines " hello" and "\\thello" are + considered to have no common leading whitespace. + Entirely blank lines are normalized to a newline character. + """ + # Look for the longest leading string of spaces and tabs common to + # all lines. + margin = None + + indents = re.findall(r'(^[ \t]*)(?:[^ \t\n])', text, re.MULTILINE) + for indent in indents: + if margin is None: + margin = indent + + # Current line more deeply indented than previous winner: + # no change (previous winner is still on top). + elif indent.startswith(margin): + pass + + # Current line consistent with and no deeper than previous winner: + # it's the new winner. + elif margin.startswith(indent): + margin = indent + + # Find the largest common whitespace between current line and previous + # winner. + else: + for i, (x, y) in enumerate(zip(margin, indent)): + if x != y: + margin = margin[:i] + break + # sanity check (testing/debugging only) + if 0 and margin: + for line in text.split("\n"): + assert not line or line.startswith(margin), \ + "line = %r, margin = %r" % (line, margin) + + if margin: + lines = [line[len(margin):] + if line.strip() + else line.strip() + for line in text.split("\n")] + text = "\n".join(lines) + return text + + +def indent(text, prefix, predicate=None): + """Adds 'prefix' to the beginning of selected lines in 'text'. + If 'predicate' is provided, 'prefix' will only be added to the lines + where 'predicate(line)' is True. If 'predicate' is not provided, + it will default to adding 'prefix' to all non-empty lines that do not + consist solely of whitespace characters. + """ + if predicate is None: + def predicate(line): + return line.strip() + + def prefixed_lines(): + for line in text.splitlines(True): + yield (prefix + line if predicate(line) else line) + + return ''.join(prefixed_lines()) + + +if __name__ == "__main__": + # print dedent("\tfoo\n\tbar") + # print dedent(" \thello there\n \t how are you?") + print(dedent("Hello there.\n This is indented.")) diff --git a/src/lib/time.js b/src/lib/time.js index 2ede7cc575..cca8e2a663 100644 --- a/src/lib/time.js +++ b/src/lib/time.js @@ -10,9 +10,8 @@ var $builtinmodule = function (name) { var mod = {}; - mod.__file__ = "/src/lib/time/__init__.js"; - mod.__package__ = Sk.builtin.none.none$; + mod.__package__ = new Sk.builtin.str(""); var struct_time_fields = { "tm_year": "year, for example, 1993", @@ -26,7 +25,7 @@ var $builtinmodule = function (name) { "tm_isdst": "1 if summer time is in effect, 0 if not, and -1 if unknown" }; - var struct_time_f = Sk.builtin.make_structseq('time', 'struct_time', struct_time_fields); + var struct_time_f = Sk.builtin.make_structseq("time", "struct_time", struct_time_fields); mod.struct_time = struct_time_f; @@ -48,22 +47,21 @@ var $builtinmodule = function (name) { mod.time = new Sk.builtin.func(function () { Sk.builtin.pyCheckArgsLen("time", arguments.length, 0, 0); var res = Date.now(); - if (this.performance && this.performance.now) - { + if (this.performance && this.performance.now) { res = res + performance.now() % 1; } return Sk.builtin.assk$(res / 1000, undefined); }); // This is an experimental implementation of time.sleep(), using suspensions - mod.sleep = new Sk.builtin.func(function(delay) { + mod.sleep = new Sk.builtin.func(function (delay) { Sk.builtin.pyCheckArgsLen("sleep", arguments.length, 1, 1); Sk.builtin.pyCheckType("delay", "float", Sk.builtin.checkNumber(delay)); - return new Sk.misceval.promiseToSuspension(new Promise(function(resolve) { - Sk.setTimeout(function() { + return new Sk.misceval.promiseToSuspension(new Promise(function (resolve) { + Sk.setTimeout(function () { resolve(Sk.builtin.none.none$); - }, Sk.ffi.remapToJs(delay)*1000); + }, Sk.ffi.remapToJs(delay) * 1000); })); }); @@ -73,17 +71,21 @@ var $builtinmodule = function (name) { } function isLeapYear(year) { - if((year & 3) != 0) return false; + if ((year & 3) != 0) { + return false; + } return ((year % 100) != 0 || (year % 400) == 0); } - function getDayOfYear(date,utc) { + function getDayOfYear(date, utc) { utc = utc || false; var dayCount = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; var mn = utc ? date.getUTCMonth() : date.getMonth(); var dn = utc ? date.getUTCDate() : date.getDate(); var dayOfYear = dayCount[mn] + dn; - if(mn > 1 && isLeapYear(utc ? date.getUTCFullYear() : date.getFullYear())) dayOfYear++; + if (mn > 1 && isLeapYear(utc ? date.getUTCFullYear() : date.getFullYear())) { + dayOfYear++; + } return dayOfYear; } @@ -127,7 +129,7 @@ var $builtinmodule = function (name) { // Try 2nd way, using the locale string, this does not work in Safari (26.07.2016) try { - var localeString = date.toLocaleString(language, { timeZoneName: "short" }); + var localeString = date.toLocaleString(language, {timeZoneName: "short"}); result = localeString.split(" "); return result[result.length - 1]; } catch (e) { @@ -140,9 +142,9 @@ var $builtinmodule = function (name) { var jan = new Date(2002, 0, 1); var jul = new Date(2002, 6, 1); if (dst(jan)) { - return [Sk.builtin.str(timeZoneName(jul)), Sk.builtin.str(timeZoneName(jan))]; + return [new Sk.builtin.str(timeZoneName(jul)), new Sk.builtin.str(timeZoneName(jan))]; } else { - return [Sk.builtin.str(timeZoneName(jan)), Sk.builtin.str(timeZoneName(jul))]; + return [new Sk.builtin.str(timeZoneName(jan)), new Sk.builtin.str(timeZoneName(jul))]; } } @@ -177,7 +179,7 @@ var $builtinmodule = function (name) { mod.localtime = new Sk.builtin.func(localtime_f); - mod.gmtime = new Sk.builtin.func(function(secs) { + mod.gmtime = new Sk.builtin.func(function (secs) { Sk.builtin.pyCheckArgsLen("localtime", arguments.length, 0, 1); var d = new Date(); if (secs) { @@ -192,45 +194,42 @@ var $builtinmodule = function (name) { var daynames = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; function asctime_f(time) { - if (!time || Sk.builtin.checkNone(time)) - { + if (!time || Sk.builtin.checkNone(time)) { time = localtime_f(); } else if (!(time instanceof struct_time_f)) { time = new struct_time_f(time); } - if (time instanceof Sk.builtin.tuple && time.v.length == 9) - { + if (time instanceof Sk.builtin.tuple && time.v.length == 9) { // todo: test validity?? var parts = []; parts.push(daynames[Sk.builtin.asnum$(time.v[6])]); - parts.push(monthnames[Sk.builtin.asnum$(time.v[1])-1]); - parts.push(padLeft(Sk.builtin.asnum$(time.v[2]).toString(), 2, '0')); + parts.push(monthnames[Sk.builtin.asnum$(time.v[1]) - 1]); + parts.push(padLeft(Sk.builtin.asnum$(time.v[2]).toString(), 2, "0")); parts.push( - padLeft(Sk.builtin.asnum$(time.v[3]).toString(), 2, '0') + ":" + - padLeft(Sk.builtin.asnum$(time.v[4]).toString(), 2, '0') + ":" + - padLeft(Sk.builtin.asnum$(time.v[5]).toString(), 2, '0') + padLeft(Sk.builtin.asnum$(time.v[3]).toString(), 2, "0") + ":" + + padLeft(Sk.builtin.asnum$(time.v[4]).toString(), 2, "0") + ":" + + padLeft(Sk.builtin.asnum$(time.v[5]).toString(), 2, "0") ); - parts.push(padLeft(Sk.builtin.asnum$(time.v[0]).toString(), 4, '0')); + parts.push(padLeft(Sk.builtin.asnum$(time.v[0]).toString(), 4, "0")); - return Sk.builtin.str(parts.join(" ")); + return new Sk.builtin.str(parts.join(" ")); } } mod.asctime = new Sk.builtin.func(asctime_f); - mod.ctime = new Sk.builtin.func(function(secs) { + mod.ctime = new Sk.builtin.func(function (secs) { return asctime_f(localtime_f(secs)); }); function mktime_f(time) { - if (time instanceof Sk.builtin.tuple && time.v.length == 9) - { + if (time instanceof Sk.builtin.tuple && time.v.length == 9) { var d = new Date(Sk.builtin.asnum$(time.v[0]), - Sk.builtin.asnum$(time.v[1])-1, - Sk.builtin.asnum$(time.v[2]), - Sk.builtin.asnum$(time.v[3]), - Sk.builtin.asnum$(time.v[4]), - Sk.builtin.asnum$(time.v[5])); + Sk.builtin.asnum$(time.v[1]) - 1, + Sk.builtin.asnum$(time.v[2]), + Sk.builtin.asnum$(time.v[3]), + Sk.builtin.asnum$(time.v[4]), + Sk.builtin.asnum$(time.v[5])); return Sk.builtin.assk$(d.getTime() / 1000, undefined); } else { throw new Sk.builtin.TypeError("mktime() requires a struct_time or 9-tuple"); @@ -260,14 +259,13 @@ var $builtinmodule = function (name) { A tuple of two strings: the first is the name of the local non-DST timezone, the second is the name of the local DST timezone. If no DST timezone is defined, the second string should not be used. */ - mod.tzname = Sk.builtin.tuple(timeZoneNames()); + mod.tzname = new Sk.builtin.tuple(timeZoneNames()); - mod.accept2dyear = Sk.builtin.assk$(1, Sk.builtin.int_); + mod.accept2dyear = Sk.builtin.assk$(1); - mod.clock = new Sk.builtin.func(function() { + mod.clock = new Sk.builtin.func(function () { var res = 0.0; - if (this.performance && this.performance.now) - { + if (this.performance && this.performance.now) { res = performance.now() / 1000; } else { res = new Date().getTime() / 1000; @@ -282,8 +280,7 @@ var $builtinmodule = function (name) { if (!Sk.builtin.checkString(format)) { throw new Sk.builtin.TypeError("format must be a string"); } - if (!t) - { + if (!t) { t = localtime_f(); } else if (!(t instanceof struct_time_f)) { t = new struct_time_f(t); @@ -293,21 +290,19 @@ var $builtinmodule = function (name) { jsFormat = Sk.ffi.remapToJs(format); - return Sk.ffi.remapToPy(strftime(jsFormat, new Date(mktime_f(t).v*1000))); + return Sk.ffi.remapToPy(strftime(jsFormat, new Date(mktime_f(t).v * 1000))); } mod.strftime = new Sk.builtin.func(strftime_f); - function tzset_f() - { + function tzset_f() { throw new Sk.builtin.NotImplementedError("time.tzset() is not yet implemented"); Sk.builtin.pyCheckArgsLen("tzset", arguments.length, 0, 0); } mod.tzset = new Sk.builtin.func(tzset_f); - function strptime_f(s, format) - { + function strptime_f(s, format) { Sk.builtin.pyCheckArgsLen("strptime", arguments.length, 1, 2); Sk.builtin.pyCheckType("string", "string", Sk.builtin.checkString(s)); if (format !== undefined) { diff --git a/src/lib/token.js b/src/lib/token.js new file mode 100644 index 0000000000..7d18ec8189 --- /dev/null +++ b/src/lib/token.js @@ -0,0 +1,36 @@ +/* Implementation of the Python token module */ + +var $builtinmodule = function (name) { + var mod = {}; + + mod.__file__ = "/src/lib/token.py"; + + const tok_name_values = []; + for (token in Sk.token.tok_name) { + const token_name = Sk.token.tok_name[token].slice(2); + const token_num = parseInt(token, 10); + + tok_name_values.push(Sk.ffi.remapToPy(token_num)); + tok_name_values.push(Sk.ffi.remapToPy(token_name)); + + mod[token_name] = Sk.ffi.remapToPy(token_num); + } + mod.tok_name = new Sk.builtin.dict(tok_name_values); + + mod.ISTERMINAL = new Sk.builtin.func(function (token) { + Sk.builtin.pyCheckArgsLen("ISTERMINAL", arguments.length, 1, 1); + return Sk.token.ISTERMINAL(Sk.ffi.remapToJs(token)); + }); + + mod.ISNONTERMINAL = new Sk.builtin.func(function (token) { + Sk.builtin.pyCheckArgsLen("ISNONTERMINAL", arguments.length, 1, 1); + return Sk.token.ISNONTERMINAL(Sk.ffi.remapToJs(token)); + }); + + mod.ISEOF = new Sk.builtin.func(function (token) { + Sk.builtin.pyCheckArgsLen("ISEOF", arguments.length, 1, 1); + return Sk.token.ISEOF(Sk.ffi.remapToJs(token)); + }); + + return mod; +}; diff --git a/src/lib/token.py b/src/lib/token.py deleted file mode 100644 index c3246046bc..0000000000 --- a/src/lib/token.py +++ /dev/null @@ -1 +0,0 @@ -raise NotImplementedError("token is not yet implemented in Skulpt") diff --git a/src/lib/tokenize.js b/src/lib/tokenize.js new file mode 100644 index 0000000000..ee1cb11078 --- /dev/null +++ b/src/lib/tokenize.js @@ -0,0 +1,49 @@ +/* Implementation of the python tokenize module */ + +var $builtinmodule = function (name) { + var mod = {}; + + mod.tokenize = new Sk.builtin.func(function (readline) { + Sk.builtin.pyCheckArgsLen("tokenize", 1, 1); + Sk.builtin.checkFunction(readline); + + // We construct a list of all tokens, since we can't yield from + // within the tokenizer function as it currently exists. This may + // be inefficient for tokenizing large files + const tokens = []; + + function receiveToken(token) { + tokens.push( + new Sk.builtin.tuple([ + Sk.ffi.remapToPy(token.type), + Sk.ffi.remapToPy(token.string), + new Sk.builtin.tuple([Sk.ffi.remapToPy(token.start[0]), Sk.ffi.remapToPy(token.start[1])]), + new Sk.builtin.tuple([Sk.ffi.remapToPy(token.end[0]), Sk.ffi.remapToPy(token.end[1])]), + Sk.ffi.remapToPy(token.line) + ]) + ); + } + + function jsReadline() { + const line = Sk.misceval.callsimArray(readline); + return Sk.ffi.remapToJs(line); + } + + Sk._tokenize("", jsReadline, "UTF-8", receiveToken); + + return new Sk.builtin.list(tokens); + }); + + + /** + * @constructor + * @extends Sk.builtin.Exception + * @param {*=} args Typically called with a single string argument + */ + mod.TokenError = function (...args) { + Sk.builtin.Exception.apply(this, args); + }; + Sk.abstr.setUpInheritance("TokenError", Sk.builtin.TokenError, Sk.builtin.Exception); + + return mod; +}; diff --git a/src/lib/tokenize.py b/src/lib/tokenize.py deleted file mode 100644 index 85c083a0bf..0000000000 --- a/src/lib/tokenize.py +++ /dev/null @@ -1 +0,0 @@ -raise NotImplementedError("tokenize is not yet implemented in Skulpt") diff --git a/src/lib/traceback.py b/src/lib/traceback.py index 756d8f17d1..6a358a4f5b 100644 --- a/src/lib/traceback.py +++ b/src/lib/traceback.py @@ -2,17 +2,21 @@ import collections + class linecache: @staticmethod def getline(filename, lineno, module_globals=None): return "Apples and bananas" + @staticmethod def lazycache(filename, globals): return "Frogs and ketchup" + @staticmethod def checkcache(filename): return "Weird ocean city" + import sys from itertools import islice @@ -23,6 +27,7 @@ def checkcache(filename): 'FrameSummary', 'StackSummary', 'TracebackException', 'walk_stack', 'walk_tb'] + # # Formatting and printing lists of traceback lines. # @@ -35,6 +40,7 @@ def print_list(extracted_list, file=None): for item in StackSummary.from_list(extracted_list).format(): print(item, file=file, end="") + def format_list(extracted_list): """Format a list of tuples or FrameSummary objects for printing. @@ -49,6 +55,7 @@ def format_list(extracted_list): """ return StackSummary.from_list(extracted_list).format() + # # Printing and Extracting Tracebacks. # @@ -63,10 +70,12 @@ def print_tb(tb, limit=None, file=None): """ print_list(extract_tb(tb, limit=limit), file=file) + def format_tb(tb, limit=None): """A shorthand for 'format_list(extract_tb(tb, limit))'.""" return extract_tb(tb, limit=limit).format() + def extract_tb(tb, limit=None): """ Return a StackSummary object representing a list of @@ -82,6 +91,7 @@ def extract_tb(tb, limit=None): """ return StackSummary.extract(walk_tb(tb), limit=limit) + # # Exception formatting and output. # @@ -161,22 +171,26 @@ def _format_final_exc_line(etype, value): line = "%s: %s\n" % (etype, valuestr) return line + def _some_str(value): try: return str(value) except: return '' % type(value).__name__ + # -- def print_exc(limit=None, file=None, chain=True): """Shorthand for 'print_exception(*sys.exc_info(), limit, file)'.""" print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain) + def format_exc(limit=None, chain=True): """Like print_exc() but return a string.""" return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain)) + def print_last(limit=None, file=None, chain=True): """This is a shorthand for 'print_exception(sys.last_type, sys.last_value, sys.last_traceback, limit, file)'.""" @@ -185,6 +199,7 @@ def print_last(limit=None, file=None, chain=True): print_exception(sys.last_type, sys.last_value, sys.last_traceback, limit, file, chain) + # # Printing and Extracting Stacks. # @@ -252,7 +267,7 @@ class FrameSummary: __slots__ = ('filename', 'lineno', 'name', 'line', 'locals') def __init__(self, filename, lineno, name, *, lookup_line=True, - locals=None, line=None): + locals=None, line=None): """Construct a FrameSummary. :param lookup_line: If True, `linecache` is consulted for the source @@ -266,7 +281,8 @@ def __init__(self, filename, lineno, name, *, lookup_line=True, self.lineno = lineno self.name = name self._line = line - #if lookup_line: + self.line = line + # if lookup_line: # self.line self.locals = {k: repr(v) for k, v in locals.items()} if locals else None @@ -293,8 +309,8 @@ def __repr__(self): def __len__(self): return 4 - #@property - #def line(self): + # @property + # def line(self): # if self._line is None: # self._line = linecache.getline(self.filename, self.lineno).strip() # return self._line @@ -324,7 +340,7 @@ def walk_tb(tb): tb = tb.tb_next -_RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. +_RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c. class StackSummary(list): @@ -416,14 +432,14 @@ def format(self): count = 0 for frame in self: if (last_file is None or last_file != frame.filename or - last_line is None or last_line != frame.lineno or - last_name is None or last_name != frame.name): + last_line is None or last_line != frame.lineno or + last_name is None or last_name != frame.name): if count > _RECURSIVE_CUTOFF: count -= _RECURSIVE_CUTOFF result.append(( - ' [Previous line repeated {count} more ' - 'time{s_count}]\n' - ).format(count=count, s_count="s" if count > 1 else "")) + ' [Previous line repeated {count} more ' + 'time{s_count}]\n' + ).format(count=count, s_count="s" if count > 1 else "")) last_file = frame.filename last_line = frame.lineno last_name = frame.name @@ -443,9 +459,9 @@ def format(self): if count > _RECURSIVE_CUTOFF: count -= _RECURSIVE_CUTOFF result.append(( - ' [Previous line repeated {count} more ' - 'time{s_count}]\n' - ).format(count=count, s_count='s' if count > 1 else '')) + ' [Previous line repeated {count} more ' + 'time{s_count}]\n' + ).format(count=count, s_count='s' if count > 1 else '')) return result @@ -478,7 +494,7 @@ class TracebackException: """ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, - lookup_lines=True, capture_locals=False, _seen=None): + lookup_lines=True, capture_locals=False, _seen=None): # NB: we need to accept exc_traceback, exc_value, exc_traceback to # permit backwards compat with the existing API, otherwise we # need stub thunk objects just to glue it together. @@ -489,7 +505,7 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, # Gracefully handle (the way Python 2.4 and earlier did) the case of # being called with no type or value (None, None, None). if (exc_value and exc_value.__cause__ is not None - and id(exc_value.__cause__) not in _seen): + and id(exc_value.__cause__) not in _seen): cause = TracebackException( type(exc_value.__cause__), exc_value.__cause__, @@ -501,7 +517,7 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, else: cause = None if (exc_value and exc_value.__context__ is not None - and id(exc_value.__context__) not in _seen): + and id(exc_value.__context__) not in _seen): context = TracebackException( type(exc_value.__context__), exc_value.__context__, @@ -532,7 +548,7 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, self.text = exc_value.text self.offset = exc_value.offset self.msg = exc_value.msg - #if lookup_lines: + # if lookup_lines: # self._load_lines() @classmethod @@ -540,7 +556,7 @@ def from_exception(cls, exc, *args, **kwargs): """Create a TracebackException from an exception.""" return cls(type(exc), exc, exc.__traceback__, *args, **kwargs) - #def _load_lines(self): + # def _load_lines(self): # """Private API. force all lines in the stack to be loaded.""" # for frame in self.stack: # frame.line @@ -617,21 +633,21 @@ def format(self, chain=True): """ if chain: if self.__cause__ is not None: - #yield from self.__cause__.format(chain=chain) + # yield from self.__cause__.format(chain=chain) for g in self.__cause__.format(chain=chain): yield g yield _cause_message elif (self.__context__ is not None and - not self.__suppress_context__): - #yield from self.__context__.format(chain=chain) + not self.__suppress_context__): + # yield from self.__context__.format(chain=chain) for g in self.__cause__.format(chain=chain): yield g yield _context_message if self.exc_traceback is not None: yield 'Traceback (most recent call last):\n' - #yield from self.stack.format() + # yield from self.stack.format() for g in self.stack.format(): yield g - #yield from self.format_exception_only() + # yield from self.format_exception_only() for g in self.format_exception_only(): yield g diff --git a/src/lib/turtle.js b/src/lib/turtle.js index 3378c5f824..3a8cb70c48 100644 --- a/src/lib/turtle.js +++ b/src/lib/turtle.js @@ -19,23 +19,23 @@ var $builtinmodule = function (name) { } function generateTurtleModule(_target) { - var _module = {__name__: Sk.builtin.str("turtle")}, + var _module = {__name__: new Sk.builtin.str("turtle")}, _durationSinceRedraw = 0, - _focus = true, - OPTIMAL_FRAME_RATE = 1000/30, - SHAPES = {}, - TURTLE_COUNT = 0, - Types = {}, - _defaultSetup = { - target : "turtle", // DOM element or id of parent container - width : 400, // if set to 0 it will use the target width - height : 400, // if set to 0 it will use the target height - worldWidth : 0, // if set to 0 it will use config.width + _focus = true, + OPTIMAL_FRAME_RATE = 1000 / 30, + SHAPES = {}, + TURTLE_COUNT = 0, + Types = {}, + _defaultSetup = { + target: "turtle", // DOM element or id of parent container + width: 400, // if set to 0 it will use the target width + height: 400, // if set to 0 it will use the target height + worldWidth: 0, // if set to 0 it will use config.width worldHeight: 0, // if set to 0 it will use config.height - animate : true, // enabled/disable all animated rendering - bufferSize : 0, // default turtle buffer size - allowUndo : true, // enable ability to use the undo buffer - assets : {} + animate: true, // enabled/disable all animated rendering + bufferSize: 0, // default turtle buffer size + allowUndo: true, // enable ability to use the undo buffer + assets: {} }, _frameRequest, _frameRequestTimeout, @@ -51,14 +51,14 @@ var $builtinmodule = function (name) { _target.setAttribute("tabindex", 0); } - Types.FLOAT = function(value) { + Types.FLOAT = function (value) { return Sk.builtin.float_(value); }; - Types.COLOR = function(value) { + Types.COLOR = function (value) { if (typeof value === "string") { return new Sk.builtin.str(value); } else { - for(var i = 0; i < 3; i++) { + for (var i = 0; i < 3; i++) { value[i] = Sk.builtin.assk$(value[i]); } if (value.length === 4) { @@ -67,7 +67,7 @@ var $builtinmodule = function (name) { return new Sk.builtin.tuple(value); } }; - Types.TURTLE_LIST = function(value) { + Types.TURTLE_LIST = function (value) { var skValues = []; for (var i = 0; i < value.length; i++) { skValues.push(value[i].skInstance); @@ -75,31 +75,31 @@ var $builtinmodule = function (name) { return new Sk.builtin.tuple(skValues); }; - SHAPES.arrow = [[-10,0],[10,0],[0,10]]; - SHAPES.square = [[ 10,-10],[10,10],[-10,10],[-10, -10]]; - SHAPES.triangle = [[10,-5.77],[0,11.55],[-10,-5.77]]; - SHAPES.classic = [[0,0],[-5,-9],[0,-7],[5,-9]]; - SHAPES.turtle = [ - [0,16],[-2,14],[-1,10],[-4,7],[-7,9],[-9,8],[-6,5],[-7,1],[-5,-3],[-8,-6], - [-6,-8],[-4,-5],[0,-7],[4,-5],[6,-8],[8,-6],[5,-3],[7,1],[6,5],[9,8],[7,9], - [4,7],[1,10],[2,14] + SHAPES.arrow = [[-10, 0], [10, 0], [0, 10]]; + SHAPES.square = [[10, -10], [10, 10], [-10, 10], [-10, -10]]; + SHAPES.triangle = [[10, -5.77], [0, 11.55], [-10, -5.77]]; + SHAPES.classic = [[0, 0], [-5, -9], [0, -7], [5, -9]]; + SHAPES.turtle = [ + [0, 16], [-2, 14], [-1, 10], [-4, 7], [-7, 9], [-9, 8], [-6, 5], [-7, 1], [-5, -3], [-8, -6], + [-6, -8], [-4, -5], [0, -7], [4, -5], [6, -8], [8, -6], [5, -3], [7, 1], [6, 5], [9, 8], [7, 9], + [4, 7], [1, 10], [2, 14] ]; SHAPES.circle = [ - [10,0],[9.51,3.09],[8.09,5.88],[5.88,8.09],[3.09,9.51],[0,10],[-3.09,9.51], - [-5.88,8.09],[-8.09,5.88],[-9.51,3.09],[-10,0],[-9.51,-3.09],[-8.09,-5.88], - [-5.88,-8.09],[-3.09,-9.51],[-0,-10],[3.09,-9.51],[5.88,-8.09],[8.09,-5.88], - [9.51,-3.09] + [10, 0], [9.51, 3.09], [8.09, 5.88], [5.88, 8.09], [3.09, 9.51], [0, 10], [-3.09, 9.51], + [-5.88, 8.09], [-8.09, 5.88], [-9.51, 3.09], [-10, 0], [-9.51, -3.09], [-8.09, -5.88], + [-5.88, -8.09], [-3.09, -9.51], [-0, -10], [3.09, -9.51], [5.88, -8.09], [8.09, -5.88], + [9.51, -3.09] ]; - _config = (function() { + _config = (function () { var key; if (!Sk.TurtleGraphics) { Sk.TurtleGraphics = {}; } - for(key in _defaultSetup) { + for (key in _defaultSetup) { if (!Sk.TurtleGraphics.hasOwnProperty(key)) { Sk.TurtleGraphics[key] = _defaultSetup[key]; } @@ -110,16 +110,16 @@ var $builtinmodule = function (name) { function getAsset(name) { var assets = _config.assets, - asset = (typeof assets === "function") ? assets(name) : assets[name]; + asset = (typeof assets === "function") ? assets(name) : assets[name]; if (typeof asset === "string") { - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { var img = new Image(); - img.onload = function() { + img.onload = function () { _config.assets[name] = this; resolve(img); }; - img.onerror = function() { + img.onerror = function () { reject(new Error("Missing asset: " + asset)); }; img.src = asset; @@ -141,30 +141,30 @@ var $builtinmodule = function (name) { // checking FrameManager.willRenderNext() function InstantPromise(err, result) { this.lastResult = result; - this.lastError = err; + this.lastError = err; } - InstantPromise.prototype.then = function(cb) { + InstantPromise.prototype.then = function (cb) { if (this.lastError) { return this; } try { this.lastResult = cb(this.lastResult); - } catch(e) { + } catch (e) { this.lastResult = undefined; - this.lastError = e; + this.lastError = e; } return this.lastResult instanceof Promise ? this.lastResult : this; }; - InstantPromise.prototype.catch = function(cb) { + InstantPromise.prototype.catch = function (cb) { if (this.lastError) { try { this.lastResult = cb(this.lastError); - this.lastError = undefined; - } catch(e) { + this.lastError = undefined; + } catch (e) { this.lastResult = undefined; this.lastError = e; } @@ -178,6 +178,7 @@ var $builtinmodule = function (name) { } var _frameManager; + function getFrameManager() { if (!_frameManager) { _frameManager = new FrameManager(); @@ -185,11 +186,11 @@ var $builtinmodule = function (name) { return _frameManager; } - (function(proto) { + (function (proto) { var browserFrame; - (function(frame) { + (function (frame) { if (frame) { - browserFrame = function(method) { + browserFrame = function (method) { return (_frameRequest = frame(method)); }; } @@ -197,7 +198,7 @@ var $builtinmodule = function (name) { function animationFrame(delay) { if (!_config.animate) { - return function(method) { + return function (method) { method(); }; } @@ -206,7 +207,7 @@ var $builtinmodule = function (name) { return browserFrame; } - return function(method) { + return function (method) { _frameRequestTimeout = window.setTimeout( method, delay || OPTIMAL_FRAME_RATE @@ -215,33 +216,33 @@ var $builtinmodule = function (name) { }; } - proto.willRenderNext = function() { - return !!(this._buffer && this._frameCount+1 === this.frameBuffer()); + proto.willRenderNext = function () { + return !!(this._buffer && this._frameCount + 1 === this.frameBuffer()); }; - proto.turtles = function() { + proto.turtles = function () { return this._turtles; }; - proto.addTurtle = function(turtle) { + proto.addTurtle = function (turtle) { this._turtles.push(turtle); }; - proto.reset = function() { + proto.reset = function () { if (this._turtles) { - for(var i = this._turtles.length; --i >= 0;) { + for (var i = this._turtles.length; --i >= 0;) { this._turtles[i].reset(); } } - this._turtles = []; - this._frames = []; - this._frameCount = 0; - this._buffer = 1; - this._rate = 0; + this._turtles = []; + this._frames = []; + this._frameCount = 0; + this._buffer = 1; + this._rate = 0; this._animationFrame = animationFrame(); }; - proto.addFrame = function(method, countAsFrame) { + proto.addFrame = function (method, countAsFrame) { var instant = false; if (countAsFrame) { @@ -252,17 +253,17 @@ var $builtinmodule = function (name) { instant = ( !_config.animate || - (this._buffer && this._frameCount === this.frameBuffer()) + (this._buffer && this._frameCount === this.frameBuffer()) ); return instant ? this.update() : new InstantPromise(); }; - proto.frames = function() { + proto.frames = function () { return this._frames; }; - proto.frameBuffer = function(buffer) { + proto.frameBuffer = function (buffer) { if (typeof buffer === "number") { this._buffer = buffer | 0; if (buffer && buffer <= this._frameCount) { @@ -272,7 +273,7 @@ var $builtinmodule = function (name) { return this._buffer; }; - proto.refreshInterval = function(rate) { + proto.refreshInterval = function (rate) { if (typeof rate === "number") { this._rate = rate | 0; this._animationFrame = animationFrame(rate); @@ -280,23 +281,23 @@ var $builtinmodule = function (name) { return this._rate; }; - proto.update = function() { + proto.update = function () { return (this._frames && this._frames.length) ? this.requestAnimationFrame() : new InstantPromise(); }; - proto.requestAnimationFrame = function() { - var frames = this._frames, + proto.requestAnimationFrame = function () { + var frames = this._frames, animationFrame = this._animationFrame, - turtles = this._turtles, - sprites = getScreen().spriteLayer(), + turtles = this._turtles, + sprites = getScreen().spriteLayer(), turtle, i; - this._frames = []; + this._frames = []; this._frameCount = 0; - return new Promise(function(resolve) { + return new Promise(function (resolve) { animationFrame(function paint() { for (i = 0; i < frames.length; i++) { if (frames[i]) { @@ -319,16 +320,16 @@ var $builtinmodule = function (name) { function MouseHandler() { var self = this; - this._target = getTarget(); + this._target = getTarget(); this._managers = {}; this._handlers = { - mousedown : function(e) { + mousedown: function (e) { self.onEvent("mousedown", e); }, - mouseup : function(e) { + mouseup: function (e) { self.onEvent("mouseup", e); }, - mousemove : function(e) { + mousemove: function (e) { self.onEvent("mousemove", e); } }; @@ -337,21 +338,23 @@ var $builtinmodule = function (name) { } } - (function(proto) { - proto.onEvent = function(type, e) { - var managers = this._managers[type], + (function (proto) { + proto.onEvent = function (type, e) { + var managers = this._managers[type], moveManagers = this._managers["mousemove"], - computed = false, + computed = false, x, y, localX, localY, i; function computeCoordinates() { - if (computed) {return;} + if (computed) { + return; + } var world = getScreen(); - var rect = world.spriteLayer().canvas.getBoundingClientRect(); - x = e.clientX - rect.left | 0; - y = e.clientY - rect.top | 0; - localX = x * world.xScale + world.llx; - localY = y * world.yScale + world.ury; + var rect = world.spriteLayer().canvas.getBoundingClientRect(); + x = e.clientX - rect.left | 0; + y = e.clientY - rect.top | 0; + localX = x * world.xScale + world.llx; + localY = y * world.yScale + world.ury; computed = true; } @@ -369,18 +372,18 @@ var $builtinmodule = function (name) { for (i = managers.length; --i >= 0;) { if (type === "mousemove" && managers[i].canMove() && managers[i].test(x, y, localX, localY)) { managers[i].trigger([localX, localY]); - }else if(type==="mousedown" && managers[i].test(x, y, localX, localY)){//For onclick event + } else if (type === "mousedown" && managers[i].test(x, y, localX, localY)) {//For onclick event managers[i].trigger([localX, localY]); } } } }; - proto.reset = function() { + proto.reset = function () { this._managers = {}; }; - proto.addManager = function(type, manager) { + proto.addManager = function (type, manager) { if (!this._managers[type]) { this._managers[type] = []; } @@ -391,19 +394,21 @@ var $builtinmodule = function (name) { })(MouseHandler.prototype); function EventManager(type, target) { - this._type = type; - this._target = target; + this._type = type; + this._target = target; this._handlers = undefined; getMouseHandler().addManager(type, this); } - (function(proto) { - proto.reset = function() { + (function (proto) { + proto.reset = function () { this._handlers = undefined; }; - proto.canMove = function(value) { - if (!this._target || !this._target.hitTest) {return false;} + proto.canMove = function (value) { + if (!this._target || !this._target.hitTest) { + return false; + } if (value !== undefined) { this._target.hitTest.hit = value; @@ -412,13 +417,13 @@ var $builtinmodule = function (name) { return this._target.hitTest.hit; }; - proto.test = function(x, y, localX, localY) { + proto.test = function (x, y, localX, localY) { return this._target && this._target.hitTest ? this._target.hitTest(x, y, localX, localY) : !!this._target; }; - proto.trigger = function(args) { + proto.trigger = function (args) { var handlers = this._handlers, i; @@ -429,12 +434,13 @@ var $builtinmodule = function (name) { } }; - proto.addHandler = function(handler, add) { + proto.addHandler = function (handler, add) { var handlers = this._handlers; if (!add && handlers && handlers.length) { - // remove all existing handlers - while (handlers.shift()) {/* noop */} + // remove all existing handlers + while (handlers.shift()) {/* noop */ + } } if (typeof handler !== "function") { @@ -461,52 +467,52 @@ var $builtinmodule = function (name) { Turtle.RADIANS = 2 * Math.PI; - (function(proto) { - proto.hitTest = function(mouseX, mouseY, localX, localY) { + (function (proto) { + proto.hitTest = function (mouseX, mouseY, localX, localY) { var context = getScreen().hitTestLayer(); clearLayer(context); drawTurtle(this.getState(), context); - var pixel = context.getImageData(mouseX,mouseY,1,1).data; + var pixel = context.getImageData(mouseX, mouseY, 1, 1).data; // check alpha first since it is most likely to have a value - return pixel[3] ||pixel[0] || pixel[1] || pixel[2]; + return pixel[3] || pixel[0] || pixel[1] || pixel[2]; }; - proto.addUpdate = function(method, countAsFrame, stateChanges) { - var self = this, + proto.addUpdate = function (method, countAsFrame, stateChanges) { + var self = this, state = this.getState(), - args = Array.prototype.slice.call(arguments, stateChanges ? 2 : 3); + args = Array.prototype.slice.call(arguments, stateChanges ? 2 : 3); - return getFrameManager().addFrame(function() { + return getFrameManager().addFrame(function () { if (method) { method.apply(state, args); } if (stateChanges) { - for(var key in stateChanges) { + for (var key in stateChanges) { state[key] = stateChanges[key]; } } }, countAsFrame); }; - proto.getState = function() { + proto.getState = function () { var self = this; if (!this._state) { this._state = { - x : this._x, - y : this._y, - angle : this._angle, - radians : this._radians, - shape : this._shape, - color : this._color, - fill : this._fill, - filling : this._filling, - size : this._size, - speed : this._computed_speed, - down : this._down, - shown : this._shown, - colorMode : this._colorMode, - context : function() { + x: this._x, + y: this._y, + angle: this._angle, + radians: this._radians, + shape: this._shape, + color: this._color, + fill: this._fill, + filling: this._filling, + size: this._size, + speed: this._computed_speed, + down: this._down, + shown: this._shown, + colorMode: this._colorMode, + context: function () { return self.getPaper(); } }; @@ -514,32 +520,32 @@ var $builtinmodule = function (name) { return this._state; }; - proto.translate = function(startX, startY, dx, dy, beginPath, isCircle) { + proto.translate = function (startX, startY, dx, dy, beginPath, isCircle) { var self = this; return translate(this, startX, startY, dx, dy, beginPath, isCircle) - .then(function(coords) { + .then(function (coords) { self._x = coords[0]; self._y = coords[1]; }); }; - proto.rotate = function(startAngle, delta, isCircle) { + proto.rotate = function (startAngle, delta, isCircle) { var self = this; return rotate(this, startAngle, delta, isCircle) - .then(function(heading) { - self._angle = heading.angle; + .then(function (heading) { + self._angle = heading.angle; self._radians = heading.radians; }); }; - proto.queueMoveBy = function(startX, startY, theta, distance) { + proto.queueMoveBy = function (startX, startY, theta, distance) { var dx = Math.cos(theta) * distance, dy = Math.sin(theta) * distance; return this.translate(startX, startY, dx, dy, true); }; - proto.queueTurnTo = function(startAngle, endAngle) { + proto.queueTurnTo = function (startAngle, endAngle) { endAngle = endAngle % this._fullCircle; if (endAngle < 0) { endAngle += this._fullCircle; @@ -547,40 +553,40 @@ var $builtinmodule = function (name) { return this.rotate(startAngle, endAngle - startAngle); }; - proto.getManager = function(type) { + proto.getManager = function (type) { if (!this._managers[type]) { this._managers[type] = new EventManager(type, this); } return this._managers[type]; }; - proto.getPaper = function() { + proto.getPaper = function () { return this._paper || (this._paper = createLayer(2)); }; - proto.reset = function() { - this._x = 0; - this._y = 0; - this._radians = 0; - this._angle = 0; - this._shown = true; - this._down = true; - this._color = "black"; - this._fill = "black"; - this._shape = "classic"; - this._size = 1; - this._filling = false; + proto.reset = function () { + this._x = 0; + this._y = 0; + this._radians = 0; + this._angle = 0; + this._shown = true; + this._down = true; + this._color = "black"; + this._fill = "black"; + this._shape = "classic"; + this._size = 1; + this._filling = false; this._undoBuffer = []; - this._speed = 3; + this._speed = 3; this._computed_speed = 5; - this._colorMode = 1.0; - this._state = undefined; + this._colorMode = 1.0; + this._state = undefined; - for(var key in this._managers) { + for (var key in this._managers) { this._managers[key].reset(); } - this._isRadians = false; + this._isRadians = false; this._fullCircle = 360; this._bufferSize = typeof _config.bufferSize === "number" ? _config.bufferSize : @@ -590,12 +596,12 @@ var $builtinmodule = function (name) { this._paper = undefined; }; - proto.$degrees = function(fullCircle) { + proto.$degrees = function (fullCircle) { fullCircle = (typeof fullCircle === "number") ? Math.abs(fullCircle) : 360; - this._isRadians = false; + this._isRadians = false; if (!fullCircle || !this._fullCircle) { this._angle = this._radians = 0; } else { @@ -605,16 +611,16 @@ var $builtinmodule = function (name) { return this.addUpdate( undefined, false, - {angle:this._angle, radians: this._radians} + {angle: this._angle, radians: this._radians} ); }; - proto.$degrees.minArgs = 0; + proto.$degrees.minArgs = 0; proto.$degrees.co_varnames = ["fullcircle"]; - proto.$degrees.returnType = Types.FLOAT; + proto.$degrees.returnType = Types.FLOAT; - proto.$radians = function() { + proto.$radians = function () { if (!this._isRadians) { - this._isRadians = true; + this._isRadians = true; this._angle = this._radians; this._fullCircle = Turtle.RADIANS; } @@ -623,82 +629,82 @@ var $builtinmodule = function (name) { }; proto.$radians.returnType = Types.FLOAT; - proto.$position = proto.$pos = function() { + proto.$position = proto.$pos = function () { return [this.$xcor(), this.$ycor()]; }; - proto.$position.returnType = function(value) { + proto.$position.returnType = function (value) { return new Sk.builtin.tuple([ Sk.builtin.float_(value[0]), Sk.builtin.float_(value[1]) ]); }; - proto.$towards = function(x,y) { - var coords = getCoordinates(x,y), + proto.$towards = function (x, y) { + var coords = getCoordinates(x, y), radians = Math.PI + Math.atan2(this._y - coords.y, this._x - coords.x), - angle = radians * (this._fullCircle / Turtle.RADIANS); + angle = radians * (this._fullCircle / Turtle.RADIANS); return angle; }; proto.$towards.co_varnames = ["x", "y"]; - proto.$towards.minArgs = 1; - proto.$towards.returnType = Types.FLOAT; + proto.$towards.minArgs = 1; + proto.$towards.returnType = Types.FLOAT; - proto.$distance = function(x,y) { - var coords = getCoordinates(x,y), - dx = coords.x - this._x, - dy = coords.y - this._y; + proto.$distance = function (x, y) { + var coords = getCoordinates(x, y), + dx = coords.x - this._x, + dy = coords.y - this._y; return Math.sqrt(dx * dx + dy * dy); }; proto.$distance.co_varnames = ["x", "y"]; - proto.$distance.minArgs = 1; - proto.$distance.returnType = Types.FLOAT; + proto.$distance.minArgs = 1; + proto.$distance.returnType = Types.FLOAT; - proto.$heading = function() { + proto.$heading = function () { return Math.abs(this._angle) < 1e-13 ? 0 : this._angle; }; proto.$heading.returnType = Types.FLOAT; - proto.$xcor = function() { + proto.$xcor = function () { return Math.abs(this._x) < 1e-13 ? 0 : this._x; }; proto.$xcor.returnType = Types.FLOAT; - proto.$ycor = function() { + proto.$ycor = function () { return Math.abs(this._y) < 1e-13 ? 0 : this._y; }; proto.$ycor.returnType = Types.FLOAT; - proto.$forward = proto.$fd = function(distance) { + proto.$forward = proto.$fd = function (distance) { pushUndo(this); return this.queueMoveBy(this._x, this._y, this._radians, distance); }; proto.$forward.co_varnames = proto.$fd.co_varnames = ["distance"]; - proto.$undo = function() { + proto.$undo = function () { popUndo(this); }; - proto.$undobufferentries = function() { + proto.$undobufferentries = function () { return this._undoBuffer.length; }; - proto.$setundobuffer = function(size) { + proto.$setundobuffer = function (size) { this._bufferSize = typeof size === "number" ? Math.min(Math.abs(size), 1000) : 0; }; proto.$setundobuffer.co_varnames = ["size"]; - proto.$backward = proto.$back = proto.$bk = function(distance) { + proto.$backward = proto.$back = proto.$bk = function (distance) { pushUndo(this); return this.queueMoveBy(this._x, this._y, this._radians, -distance); }; proto.$backward.co_varnames = proto.$back.co_varnames = proto.$bk.co_varnames = ["distance"]; - proto.$goto_$rw$ = proto.$setpos = proto.$setposition = function(x,y) { - var coords = getCoordinates(x,y); + proto.$goto_$rw$ = proto.$setpos = proto.$setposition = function (x, y) { + var coords = getCoordinates(x, y); pushUndo(this); @@ -711,72 +717,72 @@ var $builtinmodule = function (name) { proto.$goto_$rw$.co_varnames = proto.$setpos.co_varnames = proto.$setposition.co_varnames = ["x", "y"]; proto.$goto_$rw$.minArgs = proto.$setpos.minArgs = proto.$setposition.minArgs = 1; - proto.$setx = function(x) { + proto.$setx = function (x) { return this.translate(this._x, this._y, x - this._x, 0, true); }; proto.$setx.co_varnames = ["x"]; - proto.$sety = function(y) { + proto.$sety = function (y) { return this.translate(this._x, this._y, 0, y - this._y, true); }; proto.$sety.co_varnames = ["y"]; - proto.$home = function() { - var self = this, + proto.$home = function () { + var self = this, angle = this._angle; pushUndo(this); return self.translate(this._x, this._y, -this._x, -this._y, true) - .then(function(position) { + .then(function (position) { return self.queueTurnTo(angle, 0); }) - .then(function(heading) { + .then(function (heading) { return undefined; }); }; - proto.$right = proto.$rt = function(angle) { + proto.$right = proto.$rt = function (angle) { pushUndo(this); return this.rotate(this._angle, -angle); }; proto.$right.co_varnames = proto.$rt.co_varnames = ["angle"]; - proto.$left = proto.$lt = function(angle) { + proto.$left = proto.$lt = function (angle) { pushUndo(this); return this.rotate(this._angle, angle); }; proto.$left.co_varnames = proto.$lt.co_varnames = ["angle"]; - proto.$setheading = proto.$seth = function(angle) { + proto.$setheading = proto.$seth = function (angle) { pushUndo(this); return this.queueTurnTo(this._angle, angle); }; proto.$setheading.co_varnames = proto.$seth.co_varnames = ["angle"]; function circleRotate(turtle, angle, radians) { - return function() { + return function () { return turtle.addUpdate( undefined, - false,{angle:angle, radians:radians} + false, {angle: angle, radians: radians} ); }; } function circleSegment(turtle, x, y, dx, dy, beginPath) { - return function() { + return function () { return turtle.translate(x, y, dx, dy, beginPath, true); }; } - proto.$circle = function(radius, extent, steps) { - var self = this, - x = this._x, - y = this._y, - angle = this._angle, - heading = {}, - states = [], - scale = 1/getScreen().lineScale, + proto.$circle = function (radius, extent, steps) { + var self = this, + x = this._x, + y = this._y, + angle = this._angle, + heading = {}, + states = [], + scale = 1 / getScreen().lineScale, beginPath = true, endAngle, frac, w, w2, l, i, dx, dy, promise; @@ -787,12 +793,12 @@ var $builtinmodule = function (name) { } if (steps === undefined) { - frac = Math.abs(extent)/self._fullCircle; - steps = 1 + ((Math.min(11+Math.abs(radius*scale)/6, 59)*frac) | 0); + frac = Math.abs(extent) / self._fullCircle; + steps = 1 + ((Math.min(11 + Math.abs(radius * scale) / 6, 59) * frac) | 0); } - w = extent / steps; + w = extent / steps; w2 = 0.5 * w; - l = 2 * radius * Math.sin(w*Math.PI/self._fullCircle); + l = 2 * radius * Math.sin(w * Math.PI / self._fullCircle); if (radius < 0) { l = -l; @@ -807,7 +813,7 @@ var $builtinmodule = function (name) { angle += w2; - for(i = 0; i < steps; i++) { + for (i = 0; i < steps; i++) { calculateHeading(self, angle + w * i, heading); dx = Math.cos(heading.radians) * l; dy = Math.sin(heading.radians) * l; @@ -819,9 +825,9 @@ var $builtinmodule = function (name) { beginPath = false; } - promise = promise.then(function() { + promise = promise.then(function () { calculateHeading(self, endAngle, heading); - self._angle = heading.angle; + self._angle = heading.angle; self._radians = heading.radians; return self.addUpdate(undefined, true, heading); }); @@ -829,27 +835,27 @@ var $builtinmodule = function (name) { return promise; }; proto.$circle.co_varnames = ["radius", "extent", "steps"]; - proto.$circle.minArgs = 1; + proto.$circle.minArgs = 1; - proto.$penup = proto.$up = proto.$pu = function() { + proto.$penup = proto.$up = proto.$pu = function () { this._down = false; - return this.addUpdate(undefined, false, {down:false}); + return this.addUpdate(undefined, false, {down: false}); }; - proto.$pendown = proto.$down = proto.$pd = function() { + proto.$pendown = proto.$down = proto.$pd = function () { this._down = true; - return this.addUpdate(undefined, false, {down:true}); + return this.addUpdate(undefined, false, {down: true}); }; - proto.$isdown = function() { + proto.$isdown = function () { return this._down; }; - proto.$speed = function(speed) { + proto.$speed = function (speed) { if (speed !== undefined) { - this._speed = Math.max(0, Math.min(1000, speed)); + this._speed = Math.max(0, Math.min(1000, speed)); this._computed_speed = Math.max(0, speed * 2 - 1); - return this.addUpdate(undefined, false, {speed:this._computed_speed}); + return this.addUpdate(undefined, false, {speed: this._computed_speed}); } return this._speed; @@ -857,10 +863,10 @@ var $builtinmodule = function (name) { proto.$speed.minArgs = 0; proto.$speed.co_varnames = ["speed"]; - proto.$pencolor = function(r,g,b,a) { + proto.$pencolor = function (r, g, b, a) { if (r !== undefined) { - this._color = createColor(this._colorMode,r,g,b,a); - return this.addUpdate(undefined, this._shown, {color : this._color}); + this._color = createColor(this._colorMode, r, g, b, a); + return this.addUpdate(undefined, this._shown, {color: this._color}); } return hexToRGB(this._color); @@ -869,10 +875,10 @@ var $builtinmodule = function (name) { proto.$pencolor.minArgs = 0; proto.$pencolor.returnType = Types.COLOR; - proto.$fillcolor = function(r,g,b,a) { + proto.$fillcolor = function (r, g, b, a) { if (r !== undefined) { - this._fill = createColor(this._colorMode,r,g,b,a); - return this.addUpdate(undefined, this._shown, {fill : this._fill}); + this._fill = createColor(this._colorMode, r, g, b, a); + return this.addUpdate(undefined, this._shown, {fill: this._fill}); } return hexToRGB(this._fill); @@ -881,55 +887,57 @@ var $builtinmodule = function (name) { proto.$fillcolor.minArgs = 0; proto.$fillcolor.returnType = Types.COLOR; - proto.$color = function(color, fill, b, a) { + proto.$color = function (color, fill, b, a) { if (color !== undefined) { if (fill === undefined || b !== undefined) { this._color = createColor(this._colorMode, color, fill, b, a); - this._fill = this._color; + this._fill = this._color; } else { this._color = createColor(this._colorMode, color); - this._fill = createColor(this._colorMode, fill); + this._fill = createColor(this._colorMode, fill); } return this.addUpdate(undefined, this._shown, { - color : this._color, - fill : this._fill + color: this._color, + fill: this._fill }); } return [this.$pencolor(), this.$fillcolor()]; }; proto.$color.minArgs = 0; proto.$color.co_varnames = ["color", "fill", "b", "a"]; - proto.$color.returnType = function(value) { + proto.$color.returnType = function (value) { return new Sk.builtin.tuple([ Types.COLOR(value[0]), Types.COLOR(value[1]) ]); }; - proto.$fill = function(flag) { + proto.$fill = function (flag) { var self = this; if (flag !== undefined) { flag = !!flag; - if (flag === this._filling) {return;} + if (flag === this._filling) { + return; + } this._filling = flag; if (flag) { pushUndo(this); return this.addUpdate(undefined, false, { - filling : true, - fillBuffer : [{x : this._x, y : this._y}] + filling: true, + fillBuffer: [{x: this._x, y: this._y}] }); } else { pushUndo(this); return this.addUpdate( - function() { + function () { this.fillBuffer.push(this); drawFill.call(this); }, true, { - filling : false, - fillBuffer : undefined + filling: false, + fillBuffer: undefined } ); } @@ -940,22 +948,22 @@ var $builtinmodule = function (name) { proto.$fill.co_varnames = ["flag"]; proto.$fill.minArgs = 0; - proto.$begin_fill = function() { + proto.$begin_fill = function () { return this.$fill(true); }; - proto.$end_fill = function() { + proto.$end_fill = function () { return this.$fill(false); }; - proto.$stamp = function() { + proto.$stamp = function () { pushUndo(this); - return this.addUpdate(function() { + return this.addUpdate(function () { drawTurtle(this, this.context()); }, true); }; - proto.$dot = function(size, color, g, b, a) { + proto.$dot = function (size, color, g, b, a) { pushUndo(this); size = Sk.builtin.asnum$(size); size = (typeof size === "number") ? @@ -970,7 +978,7 @@ var $builtinmodule = function (name) { }; proto.$dot.co_varnames = ["size", "color", "g", "b", "a"]; - proto.$write = function(message, move, align, font) { + proto.$write = function (message, move, align, font) { var self = this, promise, face, size, type, width; @@ -1000,9 +1008,9 @@ var $builtinmodule = function (name) { if (move && (align === "left" || align === "center")) { width = measureText(message, font); if (align === "center") { - width = width/2; + width = width / 2; } - promise = promise.then(function() { + promise = promise.then(function () { var state = self.getState(); return self.translate(state.x, state.y, width, 0, true); }); @@ -1011,12 +1019,12 @@ var $builtinmodule = function (name) { return promise; }; proto.$write.co_varnames = ["message", "move", "align", "font"]; - proto.$write.minArgs = 1; + proto.$write.minArgs = 1; - proto.$pensize = proto.$width = function(size) { + proto.$pensize = proto.$width = function (size) { if (size !== undefined) { this._size = size; - return this.addUpdate(undefined, this._shown, {size : size}); + return this.addUpdate(undefined, this._shown, {size: size}); } return this._size; @@ -1024,113 +1032,113 @@ var $builtinmodule = function (name) { proto.$pensize.minArgs = proto.$width.minArgs = 0; proto.$pensize.co_varnames = proto.$width.co_varnames = ["width"]; - proto.$showturtle = proto.$st = function() { + proto.$showturtle = proto.$st = function () { this._shown = true; - return this.addUpdate(undefined, true, {shown : true}); + return this.addUpdate(undefined, true, {shown: true}); }; - proto.$hideturtle = proto.$ht = function() { + proto.$hideturtle = proto.$ht = function () { this._shown = false; - return this.addUpdate(undefined, true, {shown : false}); + return this.addUpdate(undefined, true, {shown: false}); }; - proto.$isvisible = function() { + proto.$isvisible = function () { return this._shown; }; - proto.$shape = function(shape) { + proto.$shape = function (shape) { if (shape && SHAPES[shape]) { this._shape = shape; - return this.addUpdate(undefined, this._shown, {shape : shape}); + return this.addUpdate(undefined, this._shown, {shape: shape}); } return this._shape; }; - proto.$shape.minArgs = 0; + proto.$shape.minArgs = 0; proto.$shape.co_varnames = ["name"]; //colormode supported - proto.$colormode = function(cmode){ - if(cmode !== undefined){ - if(cmode === 255) { + proto.$colormode = function (cmode) { + if (cmode !== undefined) { + if (cmode === 255) { this._colorMode = 255; } else { this._colorMode = 1.0; - } - return this.addUpdate(undefined, this._shown, {colorMode : this._colorMode}); + } + return this.addUpdate(undefined, this._shown, {colorMode: this._colorMode}); } return this._colorMode; }; - proto.$colormode.minArgs = 0; + proto.$colormode.minArgs = 0; proto.$colormode.co_varnames = ["cmode"]; - proto.$colormode.returnType = function(value) { + proto.$colormode.returnType = function (value) { return value === 255 ? Sk.builtin.int_(255) : Sk.builtin.float_(1.0); }; - proto.$window_width = function() { + proto.$window_width = function () { return this._screen.$window_width(); }; - proto.$window_height = function() { + proto.$window_height = function () { return this._screen.$window_height(); }; - proto.$tracer = function(n, delay) { + proto.$tracer = function (n, delay) { return this._screen.$tracer(n, delay); }; - proto.$tracer.minArgs = 0; + proto.$tracer.minArgs = 0; proto.$tracer.co_varnames = ["n", "delay"]; - proto.$update = function() { + proto.$update = function () { return this._screen.$update(); }; - proto.$delay = function(delay) { + proto.$delay = function (delay) { return this._screen.$delay(delay); }; - proto.$delay.minArgs = 0; + proto.$delay.minArgs = 0; proto.$delay.co_varnames = ["delay"]; - proto.$reset = function() { + proto.$reset = function () { this.reset(); return this.$clear(); }; - proto.$mainloop = proto.$done = function() { + proto.$mainloop = proto.$done = function () { return this._screen.$mainloop(); }; - proto.$clear = function() { - return this.addUpdate(function() { + proto.$clear = function () { + return this.addUpdate(function () { clearLayer(this.context()); }, true); }; proto.$dot.minArgs = 0; - proto.$onclick = function(method, btn, add) { + proto.$onclick = function (method, btn, add) { this.getManager("mousedown").addHandler(method, add); }; proto.$onclick.minArgs = 1; proto.$onclick.co_varnames = ["method", "btn", "add"]; - proto.$onrelease = function(method, btn, add) { + proto.$onrelease = function (method, btn, add) { this.getManager("mouseup").addHandler(method, add); }; proto.$onrelease.minArgs = 1; proto.$onrelease.co_varnames = ["method", "btn", "add"]; - proto.$ondrag = function(method, btn, add) { + proto.$ondrag = function (method, btn, add) { this.getManager("mousemove").addHandler(method, add); }; proto.$ondrag.minArgs = 1; proto.$ondrag.co_varnames = ["method", "btn", "add"]; - proto.$getscreen = function() { + proto.$getscreen = function () { return Sk.misceval.callsimArray(_module.Screen); }; proto.$getscreen.isSk = true; - proto.$clone = function() { + proto.$clone = function () { var newTurtleInstance = Sk.misceval.callsimOrSuspendArray(_module.Turtle); @@ -1160,53 +1168,53 @@ var $builtinmodule = function (name) { return newTurtleInstance; }; - proto.$clone.returnType = function(value) { - // When I return the instance here, I'm not sure if it ends up with the right "Turtle" python type. + proto.$clone.returnType = function (value) { + // When I return the instance here, I'm not sure if it ends up with the right "Turtle" python type. return value; }; - proto.$getturtle = proto.$getpen = function() { + proto.$getturtle = proto.$getpen = function () { return this.skInstance; }; proto.$getturtle.isSk = true; })(Turtle.prototype); function Screen() { - var w,h; - this._frames = 1; - this._delay = undefined; - this._bgcolor = "none"; - this._mode = "standard"; - this._managers = {}; + var w, h; + this._frames = 1; + this._delay = undefined; + this._bgcolor = "none"; + this._mode = "standard"; + this._managers = {}; this._keyLogger = {}; w = (_config.worldWidth || _config.width || getWidth()) / 2; h = (_config.worldHeight || _config.height || getHeight()) / 2; - this.setUpWorld(-w,-h,w,h); + this.setUpWorld(-w, -h, w, h); } - (function(proto) { - proto.spriteLayer = function() { + (function (proto) { + proto.spriteLayer = function () { return this._sprites || (this._sprites = createLayer(3)); }; - proto.bgLayer = function() { + proto.bgLayer = function () { return this._background || (this._background = createLayer(1)); }; - proto.hitTestLayer = function() { - return this._hitTest || (this._hitTest = createLayer(0,true)); + proto.hitTestLayer = function () { + return this._hitTest || (this._hitTest = createLayer(0, true)); }; - proto.getManager = function(type) { + proto.getManager = function (type) { if (!this._managers[type]) { this._managers[type] = new EventManager(type, this); } return this._managers[type]; }; - proto.reset = function() { + proto.reset = function () { var key; this._keyListeners = undefined; @@ -1232,7 +1240,7 @@ var $builtinmodule = function (name) { this._timer = undefined; } - for(key in this._managers) { + for (key in this._managers) { this._managers[key].reset(); } @@ -1243,19 +1251,19 @@ var $builtinmodule = function (name) { this._background = undefined; }; - proto.setUpWorld = function(llx, lly, urx, ury) { + proto.setUpWorld = function (llx, lly, urx, ury) { var world = this; - world.llx = llx; - world.lly = lly; - world.urx = urx; - world.ury = ury; - world.xScale = (urx - llx) / getWidth(); - world.yScale = -1 * (ury - lly) / getHeight(); + world.llx = llx; + world.lly = lly; + world.urx = urx; + world.ury = ury; + world.xScale = (urx - llx) / getWidth(); + world.yScale = -1 * (ury - lly) / getHeight(); world.lineScale = Math.min(Math.abs(world.xScale), Math.abs(world.yScale)); }; - proto.$setup = function(width, height, startX, startY) { + proto.$setup = function (width, height, startX, startY) { if (isNaN(parseFloat(width))) { width = getWidth(); } @@ -1270,7 +1278,7 @@ var $builtinmodule = function (name) { height = getHeight() * height; } - this._width = width; + this._width = width; this._height = height; this._xOffset = (startX !== undefined && !isNaN(parseInt(startX))) ? @@ -1285,14 +1293,14 @@ var $builtinmodule = function (name) { return this._setworldcoordinates(this.llx, this.lly, this.urx, this.ury); } - return this._setworldcoordinates(-width/2, -height/2, width/2, height/2); + return this._setworldcoordinates(-width / 2, -height / 2, width / 2, height / 2); }; - proto.$setup.minArgs = 0; + proto.$setup.minArgs = 0; proto.$setup.co_varnames = ["width", "height", "startx", "starty"]; - proto.$register_shape = proto.$addshape = function(name, points) { + proto.$register_shape = proto.$addshape = function (name, points) { if (!points) { - return getAsset(name).then(function(asset) { + return getAsset(name).then(function (asset) { SHAPES[name] = asset; }); } else { @@ -1301,11 +1309,11 @@ var $builtinmodule = function (name) { }; proto.$register_shape.minArgs = 1; - proto.$getshapes = function() { + proto.$getshapes = function () { return Object.keys(SHAPES); }; - proto.$tracer = function(frames, delay) { + proto.$tracer = function (frames, delay) { if (frames !== undefined || delay !== undefined) { if (typeof delay === "number") { this._delay = delay; @@ -1324,7 +1332,7 @@ var $builtinmodule = function (name) { proto.$tracer.co_varnames = ["frames", "delay"]; proto.$tracer.minArgs = 0; - proto.$delay = function(delay) { + proto.$delay = function (delay) { if (delay !== undefined) { return this.$tracer(undefined, delay); } @@ -1333,8 +1341,8 @@ var $builtinmodule = function (name) { }; proto.$delay.co_varnames = ["delay"]; - proto._setworldcoordinates = function(llx, lly, urx, ury) { - var world = this, + proto._setworldcoordinates = function (llx, lly, urx, ury) { + var world = this, turtles = getFrameManager().turtles(); this.setUpWorld(llx, lly, urx, ury); @@ -1350,55 +1358,55 @@ var $builtinmodule = function (name) { return this.$clear(); }; - proto.$setworldcoordinates = function(llx, lly, urx, ury) { + proto.$setworldcoordinates = function (llx, lly, urx, ury) { this._mode = "world"; return this._setworldcoordinates(llx, lly, urx, ury); }; proto.$setworldcoordinates.co_varnames = ["llx", "lly", "urx", "ury"]; proto.minArgs = 4; - proto.$clear = proto.$clearscreen = function() { + proto.$clear = proto.$clearscreen = function () { this.reset(); return this.$reset(); }; - proto.$update = function() { + proto.$update = function () { return getFrameManager().update(); }; - proto.$reset = proto.$resetscreen = function() { + proto.$reset = proto.$resetscreen = function () { var self = this, turtles = getFrameManager().turtles(); - return getFrameManager().addFrame(function() { + return getFrameManager().addFrame(function () { applyWorld(self, self._sprites); applyWorld(self, self._background); - for(var i = 0; i < turtles.length; i++) { + for (var i = 0; i < turtles.length; i++) { turtles[i].reset(); applyWorld(self, turtles[i]._paper); } }, true); }; - proto.$window_width = function() { + proto.$window_width = function () { return getWidth(); }; - proto.$window_height = function() { + proto.$window_height = function () { return getHeight(); }; proto.$delay.minArgs = 0; - proto.$turtles = function() { + proto.$turtles = function () { return getFrameManager().turtles(); }; proto.$turtles.returnType = Types.TURTLE_LIST; - proto.$bgpic = function(name) { + proto.$bgpic = function (name) { var self; if (name) { self = this; - return getAsset(name).then(function(asset) { + return getAsset(name).then(function (asset) { clearLayer(self.bgLayer(), undefined, asset); }); } @@ -1408,7 +1416,7 @@ var $builtinmodule = function (name) { proto.$bgpic.minArgs = 0; proto.$bgpic.co_varnames = ["name"]; - proto.$bgcolor = function(color, g, b, a) { + proto.$bgcolor = function (color, g, b, a) { if (color !== undefined) { this._bgcolor = createColor(this._colorMode, color, g, b, a); clearLayer(this.bgLayer(), this._bgcolor); @@ -1422,82 +1430,90 @@ var $builtinmodule = function (name) { proto.$bgcolor.returnType = Types.COLOR; // no-op - just defined for consistency with python version - proto.$mainloop = proto.$done = function() { + proto.$mainloop = proto.$done = function () { return undefined; }; - proto.$bye = function() { + proto.$bye = function () { return Sk.TurtleGraphics.reset(); }; - proto.$exitonclick = function() { + proto.$exitonclick = function () { this._exitOnClick = true; - return this.getManager("mousedown").addHandler(function() { + return this.getManager("mousedown").addHandler(function () { resetTurtle(); }, false); }; - proto.$onclick = function(method, btn, add) { - if (this._exitOnClick) {return;} + proto.$onclick = function (method, btn, add) { + if (this._exitOnClick) { + return; + } this.getManager("mousedown").addHandler(method, add); }; proto.$onclick.minArgs = 1; proto.$onclick.co_varnames = ["method", "btn", "add"]; var KEY_MAP = { - "8" : /^back(space)?$/i, - "9" : /^tab$/i, - "13" : /^(enter|return)$/i, - "16" : /^shift$/i, - "17" : /^(ctrl|control)$/i, - "18" : /^alt$/i, - "27" : /^esc(ape)?$/i, - "32" : /^space$/i, - "33" : /^page[\s\-]?up$/i, - "34" : /^page[\s\-]?down$/i, - "35" : /^end$/i, - "36" : /^home$/i, - "37" : /^left([\s\-]?arrow)?$/i, - "38" : /^up([\s\-]?arrow)?$/i, - "39" : /^right([\s\-]?arrow)?$/i, - "40" : /^down([\s\-]?arrow)?$/i, - "45" : /^insert$/i, - "46" : /^del(ete)?$/i - }; - - proto._createKeyRepeater = function(key, code) { + "8": /^back(space)?$/i, + "9": /^tab$/i, + "13": /^(enter|return)$/i, + "16": /^shift$/i, + "17": /^(ctrl|control)$/i, + "18": /^alt$/i, + "27": /^esc(ape)?$/i, + "32": /^space$/i, + "33": /^page[\s\-]?up$/i, + "34": /^page[\s\-]?down$/i, + "35": /^end$/i, + "36": /^home$/i, + "37": /^left([\s\-]?arrow)?$/i, + "38": /^up([\s\-]?arrow)?$/i, + "39": /^right([\s\-]?arrow)?$/i, + "40": /^down([\s\-]?arrow)?$/i, + "45": /^insert$/i, + "46": /^del(ete)?$/i + }; + + proto._createKeyRepeater = function (key, code) { var self = this; // set a timeout for 333ms and if key has not yet been // released, fire another event and continue firing // at a rate of ~20 times per second until key is released - self._keyLogger[code] = window.setTimeout(function() { - // trigger the first repeat after the longer delay + self._keyLogger[code] = window.setTimeout(function () { + // trigger the first repeat after the longer delay self._keyListeners[key](); // set up the repeat interval with the quick delay - self._keyLogger[code] = window.setInterval(function() { + self._keyLogger[code] = window.setInterval(function () { self._keyListeners[key](); }, 50); }, 333); }; - proto._createKeyDownListener = function() { + proto._createKeyDownListener = function () { var self = this; - if (this._keyDownListener) {return;} + if (this._keyDownListener) { + return; + } - this._keyDownListener = function(e) { - if (!focusTurtle()) {return;} + this._keyDownListener = function (e) { + if (!focusTurtle()) { + return; + } - var code = e.charCode || e.keyCode, + var code = e.charCode || e.keyCode, pressed = String.fromCharCode(code).toLowerCase(), key, inKeyMap; - if (self._keyLogger[code]) {return;} + if (self._keyLogger[code]) { + return; + } for (key in self._keyListeners) { inKeyMap = (key.length > 1 && KEY_MAP[code] && KEY_MAP[code].test(key)); if (key === pressed || inKeyMap) { - // trigger the intial keydown handler + // trigger the intial keydown handler self._keyListeners[key](); self._createKeyRepeater(key, code); e.preventDefault(); @@ -1509,40 +1525,44 @@ var $builtinmodule = function (name) { getTarget().addEventListener("keydown", this._keyDownListener); }; - proto._createKeyUpListener = function() { + proto._createKeyUpListener = function () { var self = this; - if (this._keyUpListener) {return;} + if (this._keyUpListener) { + return; + } - this._keyUpListener = function(e) { + this._keyUpListener = function (e) { var interval = self._keyLogger[e.charCode || e.keyCode]; if (interval !== undefined) { e.preventDefault(); window.clearInterval(interval); window.clearTimeout(interval); - delete(self._keyLogger[e.charCode || e.keyCode]); + delete (self._keyLogger[e.charCode || e.keyCode]); } }; getTarget().addEventListener("keyup", this._keyUpListener); }; - proto.$listen = function() { + proto.$listen = function () { this._createKeyUpListener(); this._createKeyDownListener(); }; - proto.$onkey = function(method, keyValue) { + proto.$onkey = function (method, keyValue) { if (typeof keyValue === "function") { var temp = method; - method = keyValue; + method = keyValue; keyValue = temp; } keyValue = String(keyValue).toLowerCase(); if (method && typeof method === "function") { - if (!this._keyListeners) {this._keyListeners = {};} + if (!this._keyListeners) { + this._keyListeners = {}; + } this._keyListeners[keyValue] = method; } else { delete this._keyListeners[keyValue]; @@ -1551,20 +1571,20 @@ var $builtinmodule = function (name) { proto.$onkey.minArgs = 2; proto.$onkey.co_varnames = ["method", "keyValue"]; - proto.$onscreenclick = function(method, btn, add) { + proto.$onscreenclick = function (method, btn, add) { this.getManager("mousedown").addHandler(method, add); }; proto.$onscreenclick.minArgs = 1; proto.$onscreenclick.co_varnames = ["method", "btn", "add"]; - proto.$ontimer = function(method, interval) { + proto.$ontimer = function (method, interval) { if (this._timer) { window.clearTimeout(this._timer); this._timer = undefined; } if (method && typeof interval === "number") { - this._timer = window.setTimeout(method, Math.max(0, interval|0)); + this._timer = window.setTimeout(method, Math.max(0, interval | 0)); } }; proto.$ontimer.minArgs = 0; @@ -1600,33 +1620,33 @@ var $builtinmodule = function (name) { function getWidth() { return ( (_screenInstance && _screenInstance._width) || - _config.width || - getTarget().clientWidth || - _defaultSetup.width + _config.width || + getTarget().clientWidth || + _defaultSetup.width ) | 0; } function getHeight() { return ( (_screenInstance && _screenInstance._height) || - _config.height || - getTarget().clientHeight || - _defaultSetup.height + _config.height || + getTarget().clientHeight || + _defaultSetup.height ) | 0; } function createLayer(zIndex, isHidden) { var canvas = document.createElement("canvas"), - width = getWidth(), + width = getWidth(), height = getHeight(), offset = getTarget().firstChild ? (-height) + "px" : "0", context; - canvas.width = width; - canvas.height = height; + canvas.width = width; + canvas.height = height; canvas.style.position = "relative"; - canvas.style.display = "block"; - canvas.style.setProperty("margin-top",offset); + canvas.style.display = "block"; + canvas.style.setProperty("margin-top", offset); canvas.style.setProperty("z-index", zIndex); if (isHidden) { canvas.style.display = "none"; @@ -1655,14 +1675,16 @@ var $builtinmodule = function (name) { } function applyWorld(world, context) { - var llx = world.llx, - lly = world.lly, - urx = world.urx, - ury = world.ury, + var llx = world.llx, + lly = world.lly, + urx = world.urx, + ury = world.ury, xScale = world.xScale, yScale = world.yScale; - if (!context) {return;} + if (!context) { + return; + } clearLayer(context); @@ -1689,19 +1711,19 @@ var $builtinmodule = function (name) { turtle._undoBuffer = []; } - while(turtle._undoBuffer.length > turtle._bufferSize) { + while (turtle._undoBuffer.length > turtle._bufferSize) { turtle._undoBuffer.shift(); } - undoState = {}; + undoState = {}; properties = "x y angle radians color fill down filling shown shape size".split(" "); - for(i = 0; i < properties.length; i++) { + for (i = 0; i < properties.length; i++) { undoState[properties[i]] = turtle["_" + properties[i]]; } turtle._undoBuffer.push(undoState); - return turtle.addUpdate(function() { + return turtle.addUpdate(function () { undoState.fillBuffer = this.fillBuffer ? this.fillBuffer.slice() : undefined; if (turtle._paper && turtle._paper.canvas) { undoState.image = turtle._paper.canvas.toDataURL(); @@ -1710,6 +1732,7 @@ var $builtinmodule = function (name) { } var undoImage = new Image(); + function popUndo(turtle) { var undoState; @@ -1723,12 +1746,14 @@ var $builtinmodule = function (name) { return; } - for(var key in undoState) { - if (key === "image" || key === "fillBuffer") {continue;} + for (var key in undoState) { + if (key === "image" || key === "fillBuffer") { + continue; + } turtle["_" + key] = undoState[key]; } - return turtle.addUpdate(function() { + return turtle.addUpdate(function () { var img; if (undoState.image) { undoImage.src = undoState.image; @@ -1747,10 +1772,12 @@ var $builtinmodule = function (name) { } function clearLayer(context, color, image) { - if (!context) {return;} + if (!context) { + return; + } context.save(); - context.setTransform(1,0,0,1,0,0); + context.setTransform(1, 0, 0, 1, 0, 0); if (color) { context.fillStyle = color; context.fillRect(0, 0, context.canvas.width, context.canvas.height); @@ -1766,37 +1793,39 @@ var $builtinmodule = function (name) { } function drawTurtle(state, context) { - var shape = SHAPES[state.shape], - world = getScreen(), - width = getWidth(), + var shape = SHAPES[state.shape], + world = getScreen(), + width = getWidth(), height = getHeight(), xScale = world.xScale, yScale = world.yScale, x, y, bearing; - if (!context) {return;} + if (!context) { + return; + } - x = Math.cos(state.radians) / xScale; - y = Math.sin(state.radians) / yScale; - bearing = Math.atan2(y, x) - Math.PI/2; + x = Math.cos(state.radians) / xScale; + y = Math.sin(state.radians) / yScale; + bearing = Math.atan2(y, x) - Math.PI / 2; context.save(); context.translate(state.x, state.y); - context.scale(xScale,yScale); + context.scale(xScale, yScale); if (shape.nodeName) { context.rotate(bearing + Math.PI); var iw = shape.naturalWidth; var ih = shape.naturalHeight; - context.drawImage(shape, 0, 0, iw, ih, -iw/2, -ih/2, iw, ih); + context.drawImage(shape, 0, 0, iw, ih, -iw / 2, -ih / 2, iw, ih); } else { context.rotate(bearing); context.beginPath(); - context.lineWidth = 1; + context.lineWidth = 1; context.strokeStyle = state.color; - context.fillStyle = state.fill; + context.fillStyle = state.fill; context.moveTo(shape[0][0], shape[0][1]); - for(var i = 1; i < shape.length; i++) { + for (var i = 1; i < shape.length; i++) { context.lineTo(shape[i][0], shape[i][1]); } context.closePath(); @@ -1809,21 +1838,24 @@ var $builtinmodule = function (name) { function drawDot(size, color) { var context = this.context(), - screen = getScreen(), - xScale = screen.xScale, - yScale = screen.yScale; + screen = getScreen(), + xScale = screen.xScale, + yScale = screen.yScale; - if (!context) {return;} + if (!context) { + return; + } context.beginPath(); context.moveTo(this.x, this.y); - size = size * Math.min(Math.abs(xScale),Math.abs(yScale)); - context.arc(this.x, this.y, size/2, 0, Turtle.RADIANS); + size = size * Math.min(Math.abs(xScale), Math.abs(yScale)); + context.arc(this.x, this.y, size / 2, 0, Turtle.RADIANS); context.closePath(); context.fillStyle = color || this.color; context.fill(); } var textMeasuringContext = document.createElement("canvas").getContext("2d"); + function measureText(message, font) { if (font) { textMeasuringContext.font = font; @@ -1834,7 +1866,9 @@ var $builtinmodule = function (name) { function drawText(message, align, font) { var context = this.context(); - if (!context) {return;} + if (!context) { + return; + } context.save(); if (font) { @@ -1844,26 +1878,28 @@ var $builtinmodule = function (name) { context.textAlign = align; } - context.scale(1,-1); + context.scale(1, -1); context.fillStyle = this.fill; context.fillText(message, this.x, -this.y); context.restore(); } function drawLine(loc, beginPath, endPath) { - // TODO: make steps in path use square ends of lines - // and open and close path at the right times. - // See if we can minimize calls to stroke + // TODO: make steps in path use square ends of lines + // and open and close path at the right times. + // See if we can minimize calls to stroke var context = this.context(); - if (!context) {return;} + if (!context) { + return; + } if (beginPath) { context.beginPath(); context.moveTo(this.x, this.y); } - context.lineWidth = this.size * getScreen().lineScale; + context.lineWidth = this.size * getScreen().lineScale; context.strokeStyle = this.color; context.lineTo(loc.x, loc.y); context.stroke(); @@ -1871,28 +1907,30 @@ var $builtinmodule = function (name) { function drawFill() { var context = this.context(), - path = this.fillBuffer, + path = this.fillBuffer, i; - if (!context || !path || !path.length) {return;} + if (!context || !path || !path.length) { + return; + } context.save(); context.beginPath(); - context.moveTo(path[0].x,path[0].y); - for(i = 1; i < path.length; i++) { + context.moveTo(path[0].x, path[0].y); + for (i = 1; i < path.length; i++) { context.lineTo(path[i].x, path[i].y); } context.closePath(); context.fillStyle = this.fill; context.fill(); - for(i = 1; i < path.length; i++) { + for (i = 1; i < path.length; i++) { if (!path[i].stroke) { continue; } context.beginPath(); - context.moveTo(path[i-1].x, path[i-1].y); - context.lineWidth = path[i].size * getScreen().lineScale; + context.moveTo(path[i - 1].x, path[i - 1].y); + context.lineWidth = path[i].size * getScreen().lineScale; context.strokeStyle = path[i].color; context.lineTo(path[i].x, path[i].y); context.stroke(); @@ -1901,79 +1939,79 @@ var $builtinmodule = function (name) { } function partialTranslate(turtle, x, y, beginPath, countAsFrame) { - return function() { + return function () { return turtle.addUpdate( - function(loc) { + function (loc) { if (this.down) { drawLine.call(this, loc, beginPath); } }, countAsFrame, - {x : x, y : y}, + {x: x, y: y}, beginPath ); }; } function translate(turtle, startX, startY, dx, dy, beginPath, isCircle) { - // speed is in pixels per ms - var speed = turtle._computed_speed, - screen = getScreen(), - xScale = Math.abs(screen.xScale), - yScale = Math.abs(screen.yScale), - x = startX, - y = startY, - pixels = Math.sqrt(dx * dx * xScale + dy * dy * yScale), + // speed is in pixels per ms + var speed = turtle._computed_speed, + screen = getScreen(), + xScale = Math.abs(screen.xScale), + yScale = Math.abs(screen.yScale), + x = startX, + y = startY, + pixels = Math.sqrt(dx * dx * xScale + dy * dy * yScale), // TODO: allow fractional frame updates? - frames = speed ? Math.round(Math.max(1, pixels / speed)) : 1, - xStep = dx / frames, - yStep = dy / frames, + frames = speed ? Math.round(Math.max(1, pixels / speed)) : 1, + xStep = dx / frames, + yStep = dy / frames, promise = getFrameManager().willRenderNext() ? Promise.resolve() : new InstantPromise(), countAsFrame = (!speed && isCircle) ? false : true, i; - turtle.addUpdate(function() { + turtle.addUpdate(function () { if (this.filling) { this.fillBuffer.push({ - x : this.x, - y : this.y, - stroke : this.down, - color : this.color, - size : this.size + x: this.x, + y: this.y, + stroke: this.down, + color: this.color, + size: this.size }); } }, false); - for(i = 0; i < frames; i++) { - x = startX + xStep * (i+1); - y = startY + yStep * (i+1); + for (i = 0; i < frames; i++) { + x = startX + xStep * (i + 1); + y = startY + yStep * (i + 1); promise = promise.then( partialTranslate(turtle, x, y, beginPath, countAsFrame) ); beginPath = false; } - return promise.then(function() { + return promise.then(function () { return [startX + dx, startY + dy]; }); } function partialRotate(turtle, angle, radians, countAsFrame) { - return function() { - return turtle.addUpdate(undefined, countAsFrame, {angle:angle, radians:radians}); + return function () { + return turtle.addUpdate(undefined, countAsFrame, {angle: angle, radians: radians}); }; } function rotate(turtle, startAngle, delta, isCircle) { - var speed = turtle._computed_speed, - degrees = delta / turtle._fullCircle * 360, - frames = speed ? Math.round(Math.max(1, Math.abs(degrees) / speed)) : 1, - dAngle = delta / frames, - heading = {}, + var speed = turtle._computed_speed, + degrees = delta / turtle._fullCircle * 360, + frames = speed ? Math.round(Math.max(1, Math.abs(degrees) / speed)) : 1, + dAngle = delta / frames, + heading = {}, countAsFrame = (!speed && isCircle) ? false : true, - promise = getFrameManager().willRenderNext() ? + promise = getFrameManager().willRenderNext() ? Promise.resolve() : new InstantPromise(), i; @@ -1981,14 +2019,14 @@ var $builtinmodule = function (name) { // TODO: request how many frames are remaining and only queue up // a single rotation per screen update - for(i = 0; i < frames; i++) { - calculateHeading(turtle, startAngle + dAngle * (i+1), heading); + for (i = 0; i < frames; i++) { + calculateHeading(turtle, startAngle + dAngle * (i + 1), heading); promise = promise.then( partialRotate(turtle, heading.angle, heading.radians, countAsFrame) ); } - return promise.then(function() { + return promise.then(function () { return calculateHeading(turtle, startAngle + delta); }); } @@ -1998,7 +2036,7 @@ var $builtinmodule = function (name) { y = (x && (x.y || x._y || x[1])) || 0; x = (x && (x.x || x._x || x[0])) || 0; } - return {x:x, y:y}; + return {x: x, y: y}; } // Modified solution of Tim Down's version from stackoverflow @@ -2017,8 +2055,8 @@ var $builtinmodule = function (name) { } } else if (/^#?[a-f\d]{3}|[a-f\d]{6}$/i.exec(hex)) { if (hex.length === 4) { - // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") - hex = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, function(m, r, g, b) { + // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") + hex = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, function (m, r, g, b) { return r + r + g + g + b + b; }); } @@ -2036,7 +2074,7 @@ var $builtinmodule = function (name) { return result; } - function createColor(turtleColorMode,color, g, b, a) { + function createColor(turtleColorMode, color, g, b, a) { var i; if (g !== undefined) { @@ -2044,21 +2082,21 @@ var $builtinmodule = function (name) { } if (color.constructor === Array && color.length) { - if(turtleColorMode === 255){//mode is 255 - for(i = 0; i < 3; i++) { - if(typeof color[i] === "number") { + if (turtleColorMode === 255) {//mode is 255 + for (i = 0; i < 3; i++) { + if (typeof color[i] === "number") { color[i] = Math.max(0, Math.min(255, parseInt(color[i]))); - } else { + } else { throw new Sk.builtin.ValueError("bad color sequence"); } } } else {//In python,if the colormode not equals 255,it should be 1.0 - for(i = 0; i < 3; i++) { - if(typeof color[i] === "number") { - if(color[i] <= 1){ + for (i = 0; i < 3; i++) { + if (typeof color[i] === "number") { + if (color[i] <= 1) { color[i] = Math.max(0, Math.min(255, parseInt(255 * color[i]))); } else { - //Raise TurtleGraphicsError,Here use ValueError instead + //Raise TurtleGraphicsError,Here use ValueError instead throw new Sk.builtin.ValueError("bad color sequence"); } } else { @@ -2070,7 +2108,7 @@ var $builtinmodule = function (name) { color[3] = Math.max(0, Math.min(1, color[i])); color = "rgba(" + color.join(",") + ")"; } else { - color = "rgb(" + color.slice(0,3).join(",") + ")"; + color = "rgb(" + color.slice(0, 3).join(",") + ")"; } } else if (typeof color === "string" && !color.match(/\s*url\s*\(/i)) { color = color.replace(/\s+/g, ""); @@ -2082,7 +2120,7 @@ var $builtinmodule = function (name) { } function calculateHeading(turtle, value, heading) { - var angle = turtle._angle || 0, + var angle = turtle._angle || 0, radians = turtle._radians || 0; heading || (heading = {}); @@ -2091,32 +2129,34 @@ var $builtinmodule = function (name) { if (turtle._isRadians) { angle = radians = value % Turtle.RADIANS; } else if (turtle._fullCircle) { - angle = (value % turtle._fullCircle); + angle = (value % turtle._fullCircle); radians = angle / turtle._fullCircle * Turtle.RADIANS; } else { angle = radians = 0; } if (angle < 0) { - angle += turtle._fullCircle; + angle += turtle._fullCircle; radians += Turtle.RADIANS; } } - heading.angle = angle; + heading.angle = angle; heading.radians = radians; return heading; } function pythonToJavascriptFunction(pyValue, scope) { - return function() { + return function () { var argsJs = Array.prototype.slice.call(arguments), argsPy = argsJs.map( - function(argJs) {return Sk.ffi.remapToPy(argJs);} + function (argJs) { + return Sk.ffi.remapToPy(argJs); + } ); - if (typeof(scope) !== "undefined") { + if (typeof (scope) !== "undefined") { argsPy.unshift(scope); } @@ -2128,20 +2168,20 @@ var $builtinmodule = function (name) { function addModuleMethod(klass, module, method, scopeGenerator) { var publicMethodName = method.replace(/^\$/, ""), - displayName = publicMethodName.replace(/_\$[a-z]+\$$/i, ""), - maxArgs = klass.prototype[method].length, - minArgs = klass.prototype[method].minArgs, - co_varnames = klass.prototype[method].co_varnames || [], - returnType = klass.prototype[method].returnType, - isSk = klass.prototype[method].isSk, + displayName = publicMethodName.replace(/_\$[a-z]+\$$/i, ""), + maxArgs = klass.prototype[method].length, + minArgs = klass.prototype[method].minArgs, + co_varnames = klass.prototype[method].co_varnames || [], + returnType = klass.prototype[method].returnType, + isSk = klass.prototype[method].isSk, wrapperFn; if (minArgs === undefined) { minArgs = maxArgs; } - wrapperFn = function() { - var args = Array.prototype.slice.call(arguments, 0), + wrapperFn = function () { + var args = Array.prototype.slice.call(arguments, 0), instance = scopeGenerator ? scopeGenerator() : args.shift().instance, i, result, susp, resolution, lengthError; @@ -2181,7 +2221,7 @@ var $builtinmodule = function (name) { try { result = instance[method].apply(instance, args); - } catch(e) { + } catch (e) { if (window && window.console) { window.console.log("wrapped method failed"); window.console.log(e.stack); @@ -2194,7 +2234,7 @@ var $builtinmodule = function (name) { } if (result instanceof Promise) { - result = result.catch(function(e) { + result = result.catch(function (e) { if (window && window.console) { window.console.log("promise failed"); window.console.log(e.stack); @@ -2204,7 +2244,7 @@ var $builtinmodule = function (name) { susp = new Sk.misceval.Suspension(); - susp.resume = function() { + susp.resume = function () { return (resolution === undefined) ? Sk.builtin.none.none$ : Sk.ffi.remapToPy(resolution); @@ -2212,7 +2252,7 @@ var $builtinmodule = function (name) { susp.data = { type: "Sk.promise", - promise: result.then(function(value) { + promise: result.then(function (value) { resolution = value; return value; }) @@ -2220,8 +2260,12 @@ var $builtinmodule = function (name) { return susp; } else { - if (result === undefined) {return Sk.builtin.none.none$;} - if (isSk) {return result;} + if (result === undefined) { + return Sk.builtin.none.none$; + } + if (isSk) { + return result; + } if (typeof returnType === "function") { return returnType(result); } @@ -2238,7 +2282,7 @@ var $builtinmodule = function (name) { } if (!scopeGenerator) { - // make room for the "self" argument + // make room for the "self" argument wrapperFn.co_varnames.unshift("self"); } @@ -2251,7 +2295,7 @@ var $builtinmodule = function (name) { self.instance.skInstance = self; }); - for(var key in Turtle.prototype) { + for (var key in Turtle.prototype) { if (/^\$[a-z_]+/.test(key)) { addModuleMethod(Turtle, $loc, key); } @@ -2263,14 +2307,14 @@ var $builtinmodule = function (name) { self.instance = getScreen(); }); - for(var key in Screen.prototype) { + for (var key in Screen.prototype) { if (/^\$[a-z_]+/.test(key)) { addModuleMethod(Screen, $loc, key); } } } - for(var key in Turtle.prototype) { + for (var key in Turtle.prototype) { if (/^\$[a-z_]+/.test(key)) { addModuleMethod(Turtle, _module, key, ensureAnonymous); } @@ -2324,10 +2368,10 @@ var $builtinmodule = function (name) { } _durationSinceRedraw = 0; - _screenInstance = undefined; - _anonymousTurtle = undefined; - _mouseHandler = undefined; - TURTLE_COUNT = 0; + _screenInstance = undefined; + _anonymousTurtle = undefined; + _mouseHandler = undefined; + TURTLE_COUNT = 0; } function stopTurtle() { @@ -2338,19 +2382,19 @@ var $builtinmodule = function (name) { } _durationSinceRedraw = 0; - _screenInstance = undefined; - _anonymousTurtle = undefined; - _mouseHandler = undefined; - TURTLE_COUNT = 0; + _screenInstance = undefined; + _anonymousTurtle = undefined; + _mouseHandler = undefined; + TURTLE_COUNT = 0; } return { - skModule : _module, - reset : resetTurtle, - stop : stopTurtle, - focus : focusTurtle, - Turtle : Turtle, - Screen : Screen + skModule: _module, + reset: resetTurtle, + stop: stopTurtle, + focus: focusTurtle, + Turtle: Turtle, + Screen: Screen }; } @@ -2365,12 +2409,12 @@ var $builtinmodule = function (name) { } Sk.TurtleGraphics.module = currentTarget.turtleInstance.skModule; - Sk.TurtleGraphics.reset = currentTarget.turtleInstance.reset; - Sk.TurtleGraphics.stop = currentTarget.turtleInstance.stop; - Sk.TurtleGraphics.focus = currentTarget.turtleInstance.focus; + Sk.TurtleGraphics.reset = currentTarget.turtleInstance.reset; + Sk.TurtleGraphics.stop = currentTarget.turtleInstance.stop; + Sk.TurtleGraphics.focus = currentTarget.turtleInstance.focus; Sk.TurtleGraphics.raw = { - Turtle : currentTarget.turtleInstance.Turtle, - Screen : currentTarget.turtleInstance.Screen + Turtle: currentTarget.turtleInstance.Turtle, + Screen: currentTarget.turtleInstance.Screen }; return currentTarget.turtleInstance.skModule; diff --git a/src/lib/types.py b/src/lib/types.py index 3f0e2bdc34..16cf0df7b3 100644 --- a/src/lib/types.py +++ b/src/lib/types.py @@ -17,7 +17,7 @@ TypeType = type ObjectType = object IntType = int -LongType = long +# LongType = long FloatType = float BooleanType = bool try: @@ -41,28 +41,41 @@ ListType = list DictType = DictionaryType = dict + def _f(): pass + + FunctionType = type(_f) -LambdaType = type(lambda: None) # Same as FunctionType -#CodeType = type(_f.func_code) +LambdaType = type(lambda: None) # Same as FunctionType + + +# CodeType = type(_f.func_code) def _g(): yield 1 + + GeneratorType = type(_g()) + class _C: def _m(self): pass + + ClassType = type(_C) -UnboundMethodType = type(_C._m) # Same as MethodType +UnboundMethodType = type(_C._m) # Same as MethodType _x = _C() InstanceType = type(_x) MethodType = type(_x._m) BuiltinFunctionType = type(len) -BuiltinMethodType = type([].append) # Same as BuiltinFunctionType +BuiltinMethodType = type([].append) # Same as BuiltinFunctionType ModuleType = type(sys) FileType = file -XRangeType = xrange +try: + XRangeType = xrange +except NameError: + pass # try: # raise TypeError @@ -82,5 +95,5 @@ def _m(self): pass # GetSetDescriptorType = type(FunctionType.func_code) # MemberDescriptorType = type(FunctionType.func_globals) -del sys, _f, _g, _C, _x # Not for export +del sys, _f, _g, _C, _x # Not for export __all__ = list(n for n in globals() if n[:1] != '_') diff --git a/src/lib/unittest/__init__.py b/src/lib/unittest/__init__.py index a9b7580c68..e35a69f0c8 100644 --- a/src/lib/unittest/__init__.py +++ b/src/lib/unittest/__init__.py @@ -5,7 +5,68 @@ ''' -class TestCase: + +class _AssertRaisesContext(object): + """A context manager used to implement TestCase.assertRaises* methods.""" + + def __init__(self, expected, test_case): + self.test_case = test_case + self.expected = expected + + def _is_subtype(self, expected, basetype): + if isinstance(expected, tuple): + return all(_is_subtype(e, basetype) for e in expected) + return isinstance(expected, type) and issubclass(expected, basetype) + + def handle(self, args, kwargs): + """ + If args is empty, assertRaises is being used as a + context manager, so return self. + If args is not empty, call a callable passing positional and keyword + arguments. + """ + try: + if not self._is_subtype(self.expected, BaseException): + raise TypeError('assertRaises() arg 1 must be an exception type or tuple of exception types') + if not args: + return self + + callable_obj = args[0] + args = args[1:] + with self: + callable_obj(*args, **kwargs) + + finally: + # bpo-23890: manually break a reference cycle + self = None + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + res = True + feedback = "" + try: + act_exc = exc_type.__name__ + except AttributeError: + act_exc = str(exc_type) + try: + exp_exc = self.expected.__name__ + except AttributeError: + exp_exc = str(self.expected) + + if exc_type is None: + res = False + feedback = "{} not raised".format(exp_exc) + elif not issubclass(exc_type, self.expected): + res = False + feedback = "Expected {} but got {}".format(exp_exc, act_exc) + + self.test_case.appendResult(res, act_exc, exp_exc, feedback) + return True + + +class TestCase(object): def __init__(self): self.numPassed = 0 self.numFailed = 0 @@ -16,21 +77,17 @@ def __init__(self): testNames = {} for name in dir(self): if name[:4] == 'test' and name not in testNames: - self.tlist.append(getattr(self,name)) - testNames[name]=True + self.tlist.append(getattr(self, name)) + testNames[name] = True def setUp(self): pass def tearDown(self): pass - - def cleanName(self,funcName): - # work around skulpts lack of an __name__ - funcName = str(funcName) - funcName = funcName[13:] - funcName = funcName[:funcName.find('<')-3] - return funcName + + def cleanName(self, funcName): + return funcName.__func__.__name__ def main(self): @@ -55,48 +112,48 @@ def main(self): self.showSummary() def assertEqual(self, actual, expected, feedback=""): - res = actual==expected + res = actual == expected if not res and feedback == "": - feedback = "Expected %s to equal %s" % (str(actual),str(expected)) - self.appendResult(res, actual ,expected, feedback) + feedback = "Expected %s to equal %s" % (str(actual), str(expected)) + self.appendResult(res, actual, expected, feedback) def assertNotEqual(self, actual, expected, feedback=""): res = actual != expected if not res and feedback == "": - feedback = "Expected %s to not equal %s" % (str(actual),str(expected)) + feedback = "Expected %s to not equal %s" % (str(actual), str(expected)) self.appendResult(res, actual, expected, feedback) - def assertTrue(self,x, feedback=""): + def assertTrue(self, x, feedback=""): res = bool(x) is True if not res and feedback == "": feedback = "Expected %s to be True" % (str(x)) self.appendResult(res, x, True, feedback) - def assertFalse(self,x, feedback=""): + def assertFalse(self, x, feedback=""): res = not bool(x) if not res and feedback == "": feedback = "Expected %s to be False" % (str(x)) self.appendResult(res, x, False, feedback) - def assertIs(self,a,b, feedback=""): + def assertIs(self, a, b, feedback=""): res = a is b if not res and feedback == "": - feedback = "Expected %s to be the same object as %s" % (str(a),str(b)) + feedback = "Expected %s to be the same object as %s" % (str(a), str(b)) self.appendResult(res, a, b, feedback) - def assertIsNot(self,a,b, feedback=""): + def assertIsNot(self, a, b, feedback=""): res = a is not b if not res and feedback == "": - feedback = "Expected %s to not be the same object as %s" % (str(a),str(b)) + feedback = "Expected %s to not be the same object as %s" % (str(a), str(b)) self.appendResult(res, a, b, feedback) - def assertIsNone(self,x, feedback=""): + def assertIsNone(self, x, feedback=""): res = x is None if not res and feedback == "": feedback = "Expected %s to be None" % (str(x)) self.appendResult(res, x, None, feedback) - def assertIsNotNone(self,x, feedback=""): + def assertIsNotNone(self, x, feedback=""): res = x is not None if not res and feedback == "": feedback = "Expected %s to not be None" % (str(x)) @@ -105,38 +162,38 @@ def assertIsNotNone(self,x, feedback=""): def assertIn(self, a, b, feedback=""): res = a in b if not res and feedback == "": - feedback = "Expected %s to be in %s" % (str(a),str(b)) + feedback = "Expected %s to be in %s" % (str(a), str(b)) self.appendResult(res, a, b, feedback) def assertNotIn(self, a, b, feedback=""): res = a not in b if not res and feedback == "": - feedback = "Expected %s to not be in %s" % (str(a),str(b)) + feedback = "Expected %s to not be in %s" % (str(a), str(b)) self.appendResult(res, a, b, feedback) - def assertIsInstance(self,a,b, feedback=""): - res = isinstance(a,b) + def assertIsInstance(self, a, b, feedback=""): + res = isinstance(a, b) if not res and feedback == "": feedback = "Expected %s to be an instance of %s" % (str(a), str(b)) self.appendResult(res, a, b, feedback) - def assertNotIsInstance(self,a,b, feedback=""): - res = not isinstance(a,b) + def assertNotIsInstance(self, a, b, feedback=""): + res = not isinstance(a, b) if not res and feedback == "": - feedback = "Expected %s to not be an instance of %s" % (str(a),str(b)) + feedback = "Expected %s to not be an instance of %s" % (str(a), str(b)) self.appendResult(res, a, b, feedback) def assertAlmostEqual(self, a, b, places=7, feedback="", delta=None): if delta is not None: - res = abs(a-b) <= delta + res = abs(a - b) <= delta else: if places is None: places = 7 - res = round(a-b, places) == 0 - + res = round(a - b, places) == 0 + if not res and feedback == "": - feedback = "Expected %s to equal %s" % (str(a),str(b)) + feedback = "Expected %s to equal %s" % (str(a), str(b)) self.appendResult(res, a, b, feedback) def assertNotAlmostEqual(self, a, b, places=7, feedback="", delta=None): @@ -147,65 +204,52 @@ def assertNotAlmostEqual(self, a, b, places=7, feedback="", delta=None): if places is None: places = 7 - res = round(a-b, places) != 0 + res = round(a - b, places) != 0 if not res and feedback == "": - feedback = "Expected %s to not equal %s" % (str(a),str(b)) + feedback = "Expected %s to not equal %s" % (str(a), str(b)) self.appendResult(res, a, b, feedback) - def assertGreater(self,a,b, feedback=""): + def assertGreater(self, a, b, feedback=""): res = a > b if not res and feedback == "": - feedback = "Expected %s to be greater than %s" % (str(a),str(b)) + feedback = "Expected %s to be greater than %s" % (str(a), str(b)) self.appendResult(res, a, b, feedback) - def assertGreaterEqual(self,a,b, feedback=""): + def assertGreaterEqual(self, a, b, feedback=""): res = a >= b if not res and feedback == "": - feedback = "Expected %s to be >= %s" % (str(a),str(b)) + feedback = "Expected %s to be >= %s" % (str(a), str(b)) self.appendResult(res, a, b, feedback) def assertLess(self, a, b, feedback=""): res = a < b if not res and feedback == "": - feedback = "Expected %s to be less than %s" % (str(a),str(b)) + feedback = "Expected %s to be less than %s" % (str(a), str(b)) self.appendResult(res, a, b, feedback) - def assertLessEqual(self,a,b, feedback=""): + def assertLessEqual(self, a, b, feedback=""): res = a <= b if not res and feedback == "": - feedback = "Expected %s to be <= %s" % (str(a),str(b)) + feedback = "Expected %s to be <= %s" % (str(a), str(b)) self.appendResult(res, a, b, feedback) - def appendResult(self,res,actual,expected,feedback): + def appendResult(self, res, actual, expected, feedback): if res: msg = 'Pass' self.assertPassed += 1 else: - msg = 'Fail: ' + feedback + msg = 'Fail: ' + feedback print(msg) self.assertFailed += 1 - def assertRaises(self, exception, callable=None, *args, **kwds): - # with is currently not supported hence we just try and catch - if callable is None: - raise NotImplementedError("assertRaises does currently not support assert contexts") - if kwds: - raise NotImplementedError("assertRaises does currently not support **kwds") - - res = False - actualerror = str(exception()) + def assertRaises(self, expected_exception, *args, **kwargs): + context = _AssertRaisesContext(expected_exception, self) try: - callable(*args) - except exception as ex: - res = True - except Exception as inst: - actualerror = str(inst) - print("ACT = ", actualerror, str(exception())) - else: - actualerror = "No Error" - - self.appendResult(res, str(exception()), actualerror, "") + return context.handle(args, kwargs) + finally: + # bpo-23890: manually break a reference cycle + context = None def fail(self, msg=None): if msg is None: @@ -216,14 +260,13 @@ def fail(self, msg=None): self.assertFailed += 1 def showSummary(self): - pct = self.numPassed / (self.numPassed+self.numFailed) * 100 - print("Ran %d tests, passed: %d failed: %d\n" % (self.numPassed+self.numFailed, - self.numPassed, self.numFailed)) - + pct = self.numPassed / (self.numPassed + self.numFailed) * 100 + print("Ran %d tests, passed: %d failed: %d\n" % (self.numPassed + self.numFailed, + self.numPassed, self.numFailed)) def main(verbosity=1): - glob = globals() # globals() still needs work + glob = globals() # globals() still needs work for name in glob: if type(glob[name]) == type and issubclass(glob[name], TestCase): try: diff --git a/src/lib/unittest/gui.py b/src/lib/unittest/gui.py index e01b3258e4..f029d96773 100644 --- a/src/lib/unittest/gui.py +++ b/src/lib/unittest/gui.py @@ -1,123 +1,121 @@ import document from unittest import TestCase -class TestCaseGui(TestCase): - def __init__(self): - TestCase.__init__(self) - self.divid = document.currentDiv() - self.mydiv = document.getElementById(self.divid) - res = document.getElementById(self.divid+'_unit_results') - if res: - self.resdiv = res - res.innerHTML = '' - else: - self.resdiv = document.createElement('div') - self.resdiv.setAttribute('id',self.divid+'_unit_results') - self.resdiv.setAttribute('class','unittest-results') - self.mydiv.appendChild(self.resdiv) - - def main(self): - t = document.createElement('table') - self.resTable = t - self.resdiv.appendChild(self.resTable) - - headers = ['Result','Actual Value','Expected Value','Notes'] - row = document.createElement('tr') - for item in headers: - head = document.createElement('th') - head.setAttribute('class','ac-feedback') - head.innerHTML = item - head.setCSS('text-align','center') - row.appendChild(head) - self.resTable.appendChild(row) +class TestCaseGui(TestCase): + def __init__(self): + TestCase.__init__(self) + self.divid = document.currentDiv() + self.mydiv = document.getElementById(self.divid) + res = document.getElementById(self.divid + '_unit_results') + if res: + self.resdiv = res + res.innerHTML = '' + else: + self.resdiv = document.createElement('div') + self.resdiv.setAttribute('id', self.divid + '_unit_results') + self.resdiv.setAttribute('class', 'unittest-results') + self.mydiv.appendChild(self.resdiv) - for func in self.tlist: - try: - self.setUp() - func() - self.tearDown() - except Exception as e: - self.appendResult('Error', None, None, e) - self.numFailed += 1 - self.showSummary() + def main(self): + t = document.createElement('table') + self.resTable = t + self.resdiv.appendChild(self.resTable) - def appendResult(self,res,actual,expected,param): - trimActual = False - if len(str(actual)) > 15: - trimActual = True - actualType = type(actual) - trimExpected = False - if len(str(expected)) > 15: - trimExpected = True - expectedType = type(expected) - row = document.createElement('tr') - err = False - if res == 'Error': - err = True - msg = 'Error: %s' % param - errorData = document.createElement('td') - errorData.setAttribute('class','ac-feedback') - errorData.innerHTML = 'ERROR' - errorData.setCSS('background-color','#de8e96') - errorData.setCSS('text-align','center') - row.appendChild(errorData) - elif res: - passed = document.createElement('td') - passed.setAttribute('class','ac-feedback') - passed.innerHTML = 'Pass' - passed.setCSS('background-color','#83d382') - passed.setCSS('text-align','center') - row.appendChild(passed) - self.numPassed += 1 - else: - fail = document.createElement('td') - fail.setAttribute('class','ac-feedback') - fail.innerHTML = 'Fail' - fail.setCSS('background-color','#de8e96') - fail.setCSS('text-align','center') - row.appendChild(fail) - self.numFailed += 1 + headers = ['Result', 'Actual Value', 'Expected Value', 'Notes'] + row = document.createElement('tr') + for item in headers: + head = document.createElement('th') + head.setAttribute('class', 'ac-feedback') + head.innerHTML = item + head.setCSS('text-align', 'center') + row.appendChild(head) + self.resTable.appendChild(row) + for func in self.tlist: + try: + self.setUp() + func() + self.tearDown() + except Exception as e: + self.appendResult('Error', None, None, e) + self.numFailed += 1 + self.showSummary() - act = document.createElement('td') - act.setAttribute('class','ac-feedback') - if trimActual: - actHTML = str(actual)[:5] + "..." + str(actual)[-5:] - if actualType == str: - actHTML = repr(actHTML) - act.innerHTML = actHTML - else: - act.innerHTML = repr(actual) - act.setCSS('text-align','center') - row.appendChild(act) + def appendResult(self, res, actual, expected, param): + trimActual = False + if len(str(actual)) > 15: + trimActual = True + actualType = type(actual) + trimExpected = False + if len(str(expected)) > 15: + trimExpected = True + expectedType = type(expected) + row = document.createElement('tr') + err = False + if res == 'Error': + err = True + msg = 'Error: %s' % param + errorData = document.createElement('td') + errorData.setAttribute('class', 'ac-feedback') + errorData.innerHTML = 'ERROR' + errorData.setCSS('background-color', '#de8e96') + errorData.setCSS('text-align', 'center') + row.appendChild(errorData) + elif res: + passed = document.createElement('td') + passed.setAttribute('class', 'ac-feedback') + passed.innerHTML = 'Pass' + passed.setCSS('background-color', '#83d382') + passed.setCSS('text-align', 'center') + row.appendChild(passed) + self.numPassed += 1 + else: + fail = document.createElement('td') + fail.setAttribute('class', 'ac-feedback') + fail.innerHTML = 'Fail' + fail.setCSS('background-color', '#de8e96') + fail.setCSS('text-align', 'center') + row.appendChild(fail) + self.numFailed += 1 - expect = document.createElement('td') - expect.setAttribute('class','ac-feedback') + act = document.createElement('td') + act.setAttribute('class', 'ac-feedback') + if trimActual: + actHTML = str(actual)[:5] + "..." + str(actual)[-5:] + if actualType == str: + actHTML = repr(actHTML) + act.innerHTML = actHTML + else: + act.innerHTML = repr(actual) + act.setCSS('text-align', 'center') + row.appendChild(act) - if trimExpected: - expectedHTML = str(expected)[:5] + "..." + str(expected)[-5:] - if expectedType == str: - expectedHTML = repr(expectedHTML) - expect.innerHTML = expectedHTML - else: - expect.innerHTML = repr(expected) - expect.setCSS('text-align','center') - row.appendChild(expect) - inp = document.createElement('td') - inp.setAttribute('class','ac-feedback') + expect = document.createElement('td') + expect.setAttribute('class', 'ac-feedback') - if err: - inp.innerHTML = msg - else: - inp.innerHTML = param - inp.setCSS('text-align','center') - row.appendChild(inp) - self.resTable.appendChild(row) + if trimExpected: + expectedHTML = str(expected)[:5] + "..." + str(expected)[-5:] + if expectedType == str: + expectedHTML = repr(expectedHTML) + expect.innerHTML = expectedHTML + else: + expect.innerHTML = repr(expected) + expect.setCSS('text-align', 'center') + row.appendChild(expect) + inp = document.createElement('td') + inp.setAttribute('class', 'ac-feedback') + if err: + inp.innerHTML = msg + else: + inp.innerHTML = param + inp.setCSS('text-align', 'center') + row.appendChild(inp) + self.resTable.appendChild(row) - def showSummary(self): - pct = self.numPassed / (self.numPassed+self.numFailed) * 100 - pTag = document.createElement('p') - pTag.innerHTML = "You passed: " + str(pct) + "% of the tests" - self.resdiv.appendChild(pTag) + def showSummary(self): + pct = self.numPassed / (self.numPassed + self.numFailed) * 100 + pTag = document.createElement('p') + pTag.innerHTML = "You passed: " + str(pct) + "% of the tests" + self.resdiv.appendChild(pTag) diff --git a/src/lib/unittest/mock.py b/src/lib/unittest/mock.py index 9d0462265d..5d9873f9a9 100644 --- a/src/lib/unittest/mock.py +++ b/src/lib/unittest/mock.py @@ -72,4 +72,4 @@ def pass_through(target, new=None, return_value=None): patch = pass_through -patch.dict = pass_through \ No newline at end of file +patch.dict = pass_through diff --git a/src/lib/urllib/request/__init__.js b/src/lib/urllib/request/__init__.js index a70d9795bc..9e3fcf1def 100644 --- a/src/lib/urllib/request/__init__.js +++ b/src/lib/urllib/request/__init__.js @@ -19,7 +19,7 @@ var $builtinmodule = function (name) { self.lineList = self.data$.split("\n"); self.lineList = self.lineList.slice(0, -1); for (var i = 0; i < self.lineList.length; i++) { - self.lineList[i] = self.lineList[i] + '\n'; + self.lineList[i] = self.lineList[i] + "\n"; } self.currentLine = 0; self.pos$ = 0; @@ -28,7 +28,7 @@ var $builtinmodule = function (name) { // ------------------------------------------------------------ $loc.__str__ = new Sk.builtin.func(function (self) { - return Sk.ffi.remapToPy(''); + return Sk.ffi.remapToPy(""); }); @@ -42,7 +42,7 @@ var $builtinmodule = function (name) { } return new Sk.builtin.str(this.$lines[this.$index++]); }, { - $obj : self, + $obj: self, $index: 0, $lines: allLines }); @@ -90,7 +90,7 @@ var $builtinmodule = function (name) { }; request.Response = - Sk.misceval.buildClass(request, response, 'Response', []); + Sk.misceval.buildClass(request, response, "Response", []); //~ Module functions ........................................................ @@ -106,7 +106,7 @@ var $builtinmodule = function (name) { * constructs a Response. */ request.urlopen = new Sk.builtin.func(function (url, data, timeout) { - var prom = new Promise(function(resolve, reject) { + var prom = new Promise(function (resolve, reject) { var xmlhttp = new XMLHttpRequest(); xmlhttp.addEventListener("loadend", function (e) { @@ -126,16 +126,16 @@ var $builtinmodule = function (name) { var susp = new Sk.misceval.Suspension(); - susp.resume = function() { + susp.resume = function () { return resolution; }; susp.data = { type: "Sk.promise", - promise: prom.then(function(value) { + promise: prom.then(function (value) { resolution = value; return value; - }, function(err) { + }, function (err) { resolution = ""; return err; }) diff --git a/src/lib/webgl/__init__.js b/src/lib/webgl/__init__.js index 2fd04fd804..5df850d076 100644 --- a/src/lib/webgl/__init__.js +++ b/src/lib/webgl/__init__.js @@ -1,358 +1,352 @@ -var $builtinmodule = function(name) -{ - var mod = {}; - - var makeFailHTML = function(msg) { - return '' + - '
    ArgumentsExpectedActual
    ArgumentsReturnedExpected
    ' + - '
    ' + - '
    ' + - '
    ' + msg + '
    ' + - '
    ' + - '
    '; - }; - - var GET_A_WEBGL_BROWSER = '' + - 'This page requires a browser that supports WebGL.
    ' + - 'Click here to upgrade your browser.'; - - var NEED_HARDWARE = '' + - "It doesn't appear your computer can support WebGL.
    " + - 'Click here for more information.'; - - var create3DContext = function(canvas) { - var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; - var gl = null; - for (var ii = 0; ii < names.length; ++ii) { - try { - gl = canvas.getContext(names[ii]); - } - catch(e) { - } - if (gl) { - break; - } - } - if (gl) { - // Disallow selection by default. This keeps the cursor from changing to an - // I-beam when the user clicks and drags. It's easier on the eyes. - function returnFalse() { - return false; - } - - canvas.onselectstart = returnFalse; - canvas.onmousedown = returnFalse; - } - return gl; - }; - - var setupWebGL = function(canvasContainerId, opt_canvas) { - var container = document.getElementById(canvasContainerId); - var context; - if (!opt_canvas) { - opt_canvas = container.getElementsByTagName("canvas")[0]; - } - if (!opt_canvas) { - // this browser doesn't support the canvas tag at all. Not even 2d. - container.innerHTML = makeFailHTML(GET_A_WEBGL_BROWSER); - return; - } - - var gl = create3DContext(opt_canvas); - if (!gl) { - // TODO(gman): fix to official way to detect that it's the user's machine, not the browser. - var browserStrings = navigator.userAgent.match(/(\w+\/.*? )/g); - var browsers = {}; - try { - for (var b = 0; b < browserStrings.length; ++b) { - var parts = browserStrings[b].match(/(\w+)/g); - var bb = []; - for (var ii = 1; ii < parts.length; ++ii) { - bb.push(parseInt(parts[ii])); - } - browsers[parts[0]] = bb; +var $builtinmodule = function (name) { + var mod = {}; + + var makeFailHTML = function (msg) { + return "" + + "" + + "
    " + + "
    " + + "
    " + msg + "
    " + + "
    " + + "
    "; + }; + + var GET_A_WEBGL_BROWSER = "" + + "This page requires a browser that supports WebGL.
    " + + "Click here to upgrade your browser."; + + var NEED_HARDWARE = "" + + "It doesn't appear your computer can support WebGL.
    " + + "Click here for more information."; + + var create3DContext = function (canvas) { + var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; + var gl = null; + for (var ii = 0; ii < names.length; ++ii) { + try { + gl = canvas.getContext(names[ii]); + } catch (e) { + } + if (gl) { + break; + } } - } - catch (e) { - } - if (browsers.Chrome && - (browsers.Chrome[0] > 7 || - (browsers.Chrome[0] == 7 && browsers.Chrome[1] > 0) || - (browsers.Chrome[0] == 7 && browsers.Chrome[1] == 0 && browsers.Chrome[2] >= 521))) { - container.innerHTML = makeFailHTML(NEED_HARDWARE); - } - else { - container.innerHTML = makeFailHTML(GET_A_WEBGL_BROWSER); - } - } - return gl; - }; - - /** - * The Context encapsulates the underlying WebGL native JavaScript API. - */ - mod.Context = Sk.misceval.buildClass(mod, function($gbl, $loc) { - $loc.__init__ = new Sk.builtin.func( - function(self, canvasid) { - var canvas = document.getElementById(canvasid.v); - var gl = setupWebGL(canvasid.v, canvas) - if (!gl) { - throw new Error("Your browser does not appear to support WebGL."); + if (gl) { + // Disallow selection by default. This keeps the cursor from changing to an + // I-beam when the user clicks and drags. It's easier on the eyes. + function returnFalse() { + return false; + } + + canvas.onselectstart = returnFalse; + canvas.onmousedown = returnFalse; + } + return gl; + }; + + var setupWebGL = function (canvasContainerId, opt_canvas) { + var container = document.getElementById(canvasContainerId); + var context; + if (!opt_canvas) { + opt_canvas = container.getElementsByTagName("canvas")[0]; + } + if (!opt_canvas) { + // this browser doesn't support the canvas tag at all. Not even 2d. + container.innerHTML = makeFailHTML(GET_A_WEBGL_BROWSER); + return; } - self.gl = gl; - - // Copy symbolic constants and functions from native WebGL, encapsulating where necessary. - for (var k in gl.__proto__) { - if (typeof gl.__proto__[k] === 'number') { - Sk.abstr.objectSetItem(self['$d'], new Sk.builtin.str(k), gl.__proto__[k]); - } - else if (typeof gl.__proto__[k] === "function") { - switch(k) { - case 'bufferData': { - } - break; - case 'clearColor': { - } - break; - case 'drawArrays': { - } - break; - case 'getAttribLocation': { - } - break; - case 'getUniformLocation': { - } - break; - case 'shaderSource': { - } - break; - case 'uniformMatrix4fv': { - } - break; - case 'vertexAttribPointer': { - } - break; - case 'viewport': { - } - break; - default: { - (function(key) { - Sk.abstr.objectSetItem(self['$d'], new Sk.builtin.str(k), new Sk.builtin.func(function() { - var f = gl.__proto__[key]; - return f.apply(gl, arguments); - })); - }(k)); - } + var gl = create3DContext(opt_canvas); + if (!gl) { + // TODO(gman): fix to official way to detect that it's the user's machine, not the browser. + var browserStrings = navigator.userAgent.match(/(\w+\/.*? )/g); + var browsers = {}; + try { + for (var b = 0; b < browserStrings.length; ++b) { + var parts = browserStrings[b].match(/(\w+)/g); + var bb = []; + for (var ii = 1; ii < parts.length; ++ii) { + bb.push(parseInt(parts[ii])); + } + browsers[parts[0]] = bb; + } + } catch (e) { + } + if (browsers.Chrome && + (browsers.Chrome[0] > 7 || + (browsers.Chrome[0] == 7 && browsers.Chrome[1] > 0) || + (browsers.Chrome[0] == 7 && browsers.Chrome[1] == 0 && browsers.Chrome[2] >= 521))) { + container.innerHTML = makeFailHTML(NEED_HARDWARE); + } else { + container.innerHTML = makeFailHTML(GET_A_WEBGL_BROWSER); } - } } + return gl; + }; + + /** + * The Context encapsulates the underlying WebGL native JavaScript API. + */ + mod.Context = Sk.misceval.buildClass(mod, function ($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func( + function (self, canvasid) { + var canvas = document.getElementById(canvasid.v); + var gl = setupWebGL(canvasid.v, canvas); + if (!gl) { + throw new Error("Your browser does not appear to support WebGL."); + } + + self.gl = gl; + + // Copy symbolic constants and functions from native WebGL, encapsulating where necessary. + for (var k in gl.__proto__) { + if (typeof gl.__proto__[k] === "number") { + Sk.abstr.objectSetItem(self["$d"], new Sk.builtin.str(k), gl.__proto__[k]); + } else if (typeof gl.__proto__[k] === "function") { + switch (k) { + case "bufferData": { + } + break; + case "clearColor": { + } + break; + case "drawArrays": { + } + break; + case "getAttribLocation": { + } + break; + case "getUniformLocation": { + } + break; + case "shaderSource": { + } + break; + case "uniformMatrix4fv": { + } + break; + case "vertexAttribPointer": { + } + break; + case "viewport": { + } + break; + default: { + (function (key) { + Sk.abstr.objectSetItem(self["$d"], new Sk.builtin.str(k), new Sk.builtin.func(function () { + var f = gl.__proto__[key]; + return f.apply(gl, arguments); + })); + }(k)); + } + } + } + } + + gl.clearColor(100.0 / 255.0, 149.0 / 255.0, 237.0 / 255.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + ); + + $loc.tp$getattr = Sk.generic.getAttr; - gl.clearColor(100.0/255.0, 149.0/255.0, 237.0/255.0, 1.0); - gl.clear(gl.COLOR_BUFFER_BIT); - } - ); - - $loc.tp$getattr = Sk.builtin.object.prototype.GenericGetAttr; - - $loc.bufferData = new Sk.builtin.func( - function(self, target, data, usage) { - self.gl.bufferData(target, data.v, usage); - } - ); - - $loc.clearColor = new Sk.builtin.func( - function(self, red, green, blue, alpha) { - self.gl.clearColor(Sk.builtin.asnum$(red), Sk.builtin.asnum$(green), Sk.builtin.asnum$(blue), Sk.builtin.asnum$(alpha)); - } - ); - - $loc.getAttribLocation = new Sk.builtin.func( - function(self, program, name) { - return self.gl.getAttribLocation(program, name.v); - } - ); - - $loc.getUniformLocation = new Sk.builtin.func( - function(self, program, name) { - return self.gl.getUniformLocation(program, name.v); - } - ); - - $loc.shaderSource = new Sk.builtin.func( - function(self, shader, src) { - self.gl.shaderSource(shader, src.v); - } - ); - - $loc.drawArrays = new Sk.builtin.func( - function(self, mode, first, count) { - self.gl.drawArrays(Sk.builtin.asnum$(mode), Sk.builtin.asnum$(first), Sk.builtin.asnum$(count)); - } - ); - - $loc.vertexAttribPointer = new Sk.builtin.func( - function(self, index, size, type, normalized, stride, dunno) { - self.gl.vertexAttribPointer(index, Sk.builtin.asnum$(size), Sk.builtin.asnum$(type), normalized, Sk.builtin.asnum$(stride), Sk.builtin.asnum$(dunno)); - } - ); - - $loc.viewport = new Sk.builtin.func( - function(self, x, y, width, height) { - self.gl.viewport(Sk.builtin.asnum$(x), Sk.builtin.asnum$(y), Sk.builtin.asnum$(width), Sk.builtin.asnum$(height)); - } - ); - - $loc.uniformMatrix4fv = new Sk.builtin.func( - function(self, location, transpose, values) { + $loc.bufferData = new Sk.builtin.func( + function (self, target, data, usage) { + self.gl.bufferData(target, data.v, usage); + } + ); + + $loc.clearColor = new Sk.builtin.func( + function (self, red, green, blue, alpha) { + self.gl.clearColor(Sk.builtin.asnum$(red), Sk.builtin.asnum$(green), Sk.builtin.asnum$(blue), Sk.builtin.asnum$(alpha)); + } + ); + + $loc.getAttribLocation = new Sk.builtin.func( + function (self, program, name) { + return self.gl.getAttribLocation(program, name.v); + } + ); + + $loc.getUniformLocation = new Sk.builtin.func( + function (self, program, name) { + return self.gl.getUniformLocation(program, name.v); + } + ); + + $loc.shaderSource = new Sk.builtin.func( + function (self, shader, src) { + self.gl.shaderSource(shader, src.v); + } + ); + + $loc.drawArrays = new Sk.builtin.func( + function (self, mode, first, count) { + self.gl.drawArrays(Sk.builtin.asnum$(mode), Sk.builtin.asnum$(first), Sk.builtin.asnum$(count)); + } + ); + + $loc.vertexAttribPointer = new Sk.builtin.func( + function (self, index, size, type, normalized, stride, dunno) { + self.gl.vertexAttribPointer(index, Sk.builtin.asnum$(size), Sk.builtin.asnum$(type), normalized, Sk.builtin.asnum$(stride), Sk.builtin.asnum$(dunno)); + } + ); + + $loc.viewport = new Sk.builtin.func( + function (self, x, y, width, height) { + self.gl.viewport(Sk.builtin.asnum$(x), Sk.builtin.asnum$(y), Sk.builtin.asnum$(width), Sk.builtin.asnum$(height)); + } + ); + + $loc.uniformMatrix4fv = new Sk.builtin.func( + function (self, location, transpose, values) { // console.log("location " + (typeof location)); // console.log("transpose " + (typeof transpose)); // console.log("values.v " + (typeof values.v)); - self.gl.uniformMatrix4fv(Sk.builtin.asnum$(location), transpose, values.v); - } - ); - - $loc.setDrawFunc = new Sk.builtin.func(function(self, func) { - var startTime = (new Date()).getTime(); - var intervalId = setInterval( - function() { - Sk.misceval.callsimArray(func, [self, (new Date()).getTime() - startTime]); - }, 1000.0 / 60.0); // 60 fps - }); - - }, 'Context', []); - - mod.Float32Array = Sk.misceval.buildClass(mod, function($gbl, $loc) { - $loc.__init__ = new Sk.builtin.func(function(self, data) { - if (typeof data === "number") { - self.v = new Float32Array(data); - } - else { - self.v = new Float32Array(Sk.ffi.remapToJs(data)); - } - }); - - $loc.__repr__ = new Sk.builtin.func(function(self) { - var copy = []; - for (var i = 0; i < self.v.length; ++i) { - copy.push(self.v[i]); - } - return new Sk.builtin.str("[" + copy.join(', ') + "]"); - }); - }, 'Float32Array', []); - - /** - * A 4x4 (mutable) matrix suitable for OpenGL. - * - * Mutability is chosen for performance. - * The inderlying implementation is Float32Array. - * The indexing of the elements is - * 0 4 8 12 - * 1 5 9 13 - * 2 6 10 14 - * 3 7 11 15 - */ - mod.Matrix4x4 = Sk.misceval.buildClass(mod, function($gbl, $loc) { - $loc.__init__ = new Sk.builtin.func(function(self, data) { - self.v = new Float32Array(Sk.ffi.remapToJs(data)); - }); - - $loc.identity = new Sk.builtin.func( - function(self) { - - var m = self.v; - - m[0] = 1; - m[1] = 0; - m[2] = 0; - m[3] = 0; - - m[4] = 0; - m[5] = 1; - m[6] = 0; - m[7] = 0; - - m[8] = 0; - m[9] = 0; - m[10] = 1; - m[11] = 0; - - m[12] = 0; - m[13] = 0; - m[14] = 0; - m[15] = 1; - } - ); - - $loc.perspective = new Sk.builtin.func( - function(self, fov, aspect, near, far) { - - var t = Math.tan(Math.PI * 0.5 - 0.5 * (Sk.builtin.asnum$(fov) * Math.PI / 180)); - var a = Sk.builtin.asnum$(aspect) - var n = Sk.builtin.asnum$(near) - var f = Sk.builtin.asnum$(far) - var k = 1.0 / (n - f); - - var m = self.v; - - m[0] = t / a; - m[1] = 0; - m[2] = 0; - m[3] = 0; - - m[4] = 0; - m[5] = t; - m[6] = 0; - m[7] = 0; - - m[8] = 0; - m[9] = 0; - m[10] = (n + f) * k; - m[11] = -1; - - m[12] = 0; - m[13] = 0; - m[14] = n * f * k * 2; - m[15] = 0; - } - ); - - $loc.translate = new Sk.builtin.func( - function(self, translation) { - - var m = self.v; - var t = Sk.ffi.remapToJs(translation); - - m[0] = 1; - m[1] = 0; - m[2] = 0; - m[3] = 0; - - m[4] = 0; - m[5] = 1; - m[6] = 0; - m[7] = 0; - - m[8] = 0; - m[9] = 0; - m[10] = 1; - m[11] = 0; - - m[12] = t[0]; - m[13] = t[1]; - m[14] = t[2]; - m[15] = 1; - } - ); - - $loc.__repr__ = new Sk.builtin.func(function(self) { - var copy = []; - for (var i = 0; i < self.v.length; ++i) { - copy.push(self.v[i]); - } - return new Sk.builtin.str("[" + copy.join(', ') + "]"); - }); - }, 'Matrix4x4', []); - - return mod; + self.gl.uniformMatrix4fv(Sk.builtin.asnum$(location), transpose, values.v); + } + ); + + $loc.setDrawFunc = new Sk.builtin.func(function (self, func) { + var startTime = (new Date()).getTime(); + var intervalId = setInterval( + function () { + Sk.misceval.callsimArray(func, [self, (new Date()).getTime() - startTime]); + }, 1000.0 / 60.0); // 60 fps + }); + + }, "Context", []); + + mod.Float32Array = Sk.misceval.buildClass(mod, function ($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self, data) { + if (typeof data === "number") { + self.v = new Float32Array(data); + } else { + self.v = new Float32Array(Sk.ffi.remapToJs(data)); + } + }); + + $loc.__repr__ = new Sk.builtin.func(function (self) { + var copy = []; + for (var i = 0; i < self.v.length; ++i) { + copy.push(self.v[i]); + } + return new Sk.builtin.str("[" + copy.join(", ") + "]"); + }); + }, "Float32Array", []); + + /** + * A 4x4 (mutable) matrix suitable for OpenGL. + * + * Mutability is chosen for performance. + * The inderlying implementation is Float32Array. + * The indexing of the elements is + * 0 4 8 12 + * 1 5 9 13 + * 2 6 10 14 + * 3 7 11 15 + */ + mod.Matrix4x4 = Sk.misceval.buildClass(mod, function ($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self, data) { + self.v = new Float32Array(Sk.ffi.remapToJs(data)); + }); + + $loc.identity = new Sk.builtin.func( + function (self) { + + var m = self.v; + + m[0] = 1; + m[1] = 0; + m[2] = 0; + m[3] = 0; + + m[4] = 0; + m[5] = 1; + m[6] = 0; + m[7] = 0; + + m[8] = 0; + m[9] = 0; + m[10] = 1; + m[11] = 0; + + m[12] = 0; + m[13] = 0; + m[14] = 0; + m[15] = 1; + } + ); + + $loc.perspective = new Sk.builtin.func( + function (self, fov, aspect, near, far) { + + var t = Math.tan(Math.PI * 0.5 - 0.5 * (Sk.builtin.asnum$(fov) * Math.PI / 180)); + var a = Sk.builtin.asnum$(aspect); + var n = Sk.builtin.asnum$(near); + var f = Sk.builtin.asnum$(far); + var k = 1.0 / (n - f); + + var m = self.v; + + m[0] = t / a; + m[1] = 0; + m[2] = 0; + m[3] = 0; + + m[4] = 0; + m[5] = t; + m[6] = 0; + m[7] = 0; + + m[8] = 0; + m[9] = 0; + m[10] = (n + f) * k; + m[11] = -1; + + m[12] = 0; + m[13] = 0; + m[14] = n * f * k * 2; + m[15] = 0; + } + ); + + $loc.translate = new Sk.builtin.func( + function (self, translation) { + + var m = self.v; + var t = Sk.ffi.remapToJs(translation); + + m[0] = 1; + m[1] = 0; + m[2] = 0; + m[3] = 0; + + m[4] = 0; + m[5] = 1; + m[6] = 0; + m[7] = 0; + + m[8] = 0; + m[9] = 0; + m[10] = 1; + m[11] = 0; + + m[12] = t[0]; + m[13] = t[1]; + m[14] = t[2]; + m[15] = 1; + } + ); + + $loc.__repr__ = new Sk.builtin.func(function (self) { + var copy = []; + for (var i = 0; i < self.v.length; ++i) { + copy.push(self.v[i]); + } + return new Sk.builtin.str("[" + copy.join(", ") + "]"); + }); + }, "Matrix4x4", []); + + return mod; }; diff --git a/src/lib/webgl/math.js b/src/lib/webgl/math.js index f2b2804b78..3e691e8bb6 100644 --- a/src/lib/webgl/math.js +++ b/src/lib/webgl/math.js @@ -1,288 +1,266 @@ -var $builtinmodule = function(name) -{ +var $builtinmodule = function (name) { var mod = {}; // todo; should probably put this in a math package - mod.Mat44 = Sk.misceval.buildClass(mod, function($gbl, $loc) - { - $loc.__init__ = new Sk.builtin.func(function(self) - { - Sk.misceval.callsimArray($loc.loadIdentity, [self]); - self.stack = []; - }); - - $loc.push = new Sk.builtin.func(function(self) - { - self.stack.push(self.elements.slice(0)); - }); - - $loc.pop = new Sk.builtin.func(function(self) - { - self.elements = self.stack.pop(); - }); - - $loc.loadIdentity = new Sk.builtin.func(function(self) - { - self.elements = [1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0]; - }); - - $loc.transform3 = new Sk.builtin.func(function(self, v) - { - var e = self.elements; - return Sk.misceval.callsimArray(mod.Vec3, [ - e[0] * v.x + e[4] * v.y + e[8] * v.z, - e[1] * v.x + e[5] * v.y + e[9] * v.z, - e[2] * v.x + e[6] * v.y + e[10] * v.z]); - }); - - $loc.scale = new Sk.builtin.func(function(self, sx, sy, sz) - { - self.elements[0*4+0] *= sx; - self.elements[0*4+1] *= sx; - self.elements[0*4+2] *= sx; - self.elements[0*4+3] *= sx; - - self.elements[1*4+0] *= sy; - self.elements[1*4+1] *= sy; - self.elements[1*4+2] *= sy; - self.elements[1*4+3] *= sy; - - self.elements[2*4+0] *= sz; - self.elements[2*4+1] *= sz; - self.elements[2*4+2] *= sz; - self.elements[2*4+3] *= sz; - - return self; - }); - - $loc.translate = new Sk.builtin.func(function(self, tx, ty, tz) - { - self.elements[3*4+0] += self.elements[0*4+0] * tx + self.elements[1*4+0] * ty + self.elements[2*4+0] * tz; - self.elements[3*4+1] += self.elements[0*4+1] * tx + self.elements[1*4+1] * ty + self.elements[2*4+1] * tz; - self.elements[3*4+2] += self.elements[0*4+2] * tx + self.elements[1*4+2] * ty + self.elements[2*4+2] * tz; - self.elements[3*4+3] += self.elements[0*4+3] * tx + self.elements[1*4+3] * ty + self.elements[2*4+3] * tz; - return self; - }); - - $loc.rotate = new Sk.builtin.func(function(self, angle, x, y, z) - { - var mag = Math.sqrt(x*x + y*y + z*z); - var sinAngle = Math.sin(angle * Math.PI / 180.0); - var cosAngle = Math.cos(angle * Math.PI / 180.0); - - if (mag > 0) - { - var xx, yy, zz, xy, yz, zx, xs, ys, zs; - var oneMinusCos; - var rotMat; - - x /= mag; - y /= mag; - z /= mag; - - xx = x * x; - yy = y * y; - zz = z * z; - xy = x * y; - yz = y * z; - zx = z * x; - xs = x * sinAngle; - ys = y * sinAngle; - zs = z * sinAngle; - oneMinusCos = 1.0 - cosAngle; - - rotMat = Sk.misceval.callsimArray(mod.Mat44); - - rotMat.elements[0*4+0] = (oneMinusCos * xx) + cosAngle; - rotMat.elements[0*4+1] = (oneMinusCos * xy) - zs; - rotMat.elements[0*4+2] = (oneMinusCos * zx) + ys; - rotMat.elements[0*4+3] = 0.0; - - rotMat.elements[1*4+0] = (oneMinusCos * xy) + zs; - rotMat.elements[1*4+1] = (oneMinusCos * yy) + cosAngle; - rotMat.elements[1*4+2] = (oneMinusCos * yz) - xs; - rotMat.elements[1*4+3] = 0.0; - - rotMat.elements[2*4+0] = (oneMinusCos * zx) - ys; - rotMat.elements[2*4+1] = (oneMinusCos * yz) + xs; - rotMat.elements[2*4+2] = (oneMinusCos * zz) + cosAngle; - rotMat.elements[2*4+3] = 0.0; - - rotMat.elements[3*4+0] = 0.0; - rotMat.elements[3*4+1] = 0.0; - rotMat.elements[3*4+2] = 0.0; - rotMat.elements[3*4+3] = 1.0; - - rotMat = rotMat.multiply(self); - self.elements = rotMat.elements; - } - return self; - }); - - $loc.multiply = new Sk.builtin.func(function(self, right) - { - var tmp = Sk.misceval.callsimArray(mod.Mat44); - - for (var i = 0; i < 4; i++) - { - tmp.elements[i*4+0] = - (self.elements[i*4+0] * right.elements[0*4+0]) + - (self.elements[i*4+1] * right.elements[1*4+0]) + - (self.elements[i*4+2] * right.elements[2*4+0]) + - (self.elements[i*4+3] * right.elements[3*4+0]) ; - - tmp.elements[i*4+1] = - (self.elements[i*4+0] * right.elements[0*4+1]) + - (self.elements[i*4+1] * right.elements[1*4+1]) + - (self.elements[i*4+2] * right.elements[2*4+1]) + - (self.elements[i*4+3] * right.elements[3*4+1]) ; - - tmp.elements[i*4+2] = - (self.elements[i*4+0] * right.elements[0*4+2]) + - (self.elements[i*4+1] * right.elements[1*4+2]) + - (self.elements[i*4+2] * right.elements[2*4+2]) + - (self.elements[i*4+3] * right.elements[3*4+2]) ; - - tmp.elements[i*4+3] = - (self.elements[i*4+0] * right.elements[0*4+3]) + - (self.elements[i*4+1] * right.elements[1*4+3]) + - (self.elements[i*4+2] * right.elements[2*4+3]) + - (self.elements[i*4+3] * right.elements[3*4+3]) ; - } - - self.elements = tmp.elements; - return self; - }); - - /* Following gluLookAt implementation is adapted from - * the Mesa 3D Graphics library. http://www.mesa3d.org - */ - // todo; rewrite this with proper vec/mat ops - $loc.lookAt = new Sk.builtin.func(function(self, eyeX, eyeY, eyeZ, - centerX, centerY, centerZ, - upX, upY, upZ) - { - /* Z vector */ - var z = [ - eyeX - centerX, - eyeY - centerY, - eyeZ - centerZ - ]; - var mag = Math.sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]); - if (mag) - { - z[0] /= mag; - z[1] /= mag; - z[2] /= mag; - } - - /* Y vector */ - var y = [ upX, upY, upZ ]; - - /* X vector = Y cross Z */ - var x = []; - x[0] = y[1] * z[2] - y[2] * z[1]; - x[1] = -y[0] * z[2] + y[2] * z[0]; - x[2] = y[0] * z[1] - y[1] * z[0]; - - /* Recompute Y = Z cross X */ - y[0] = z[1] * x[2] - z[2] * x[1]; - y[1] = -z[0] * x[2] + z[2] * x[0]; - y[2] = z[0] * x[1] - z[1] * x[0]; - - /* mpichler, 19950515 */ - /* cross product gives area of parallelogram, which is < 1.0 for - * non-perpendicular unit-length vectors; so normalize x, y here - */ - - mag = Math.sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]); - if (mag) { - x[0] /= mag; - x[1] /= mag; - x[2] /= mag; - } - - mag = Math.sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]); - if (mag) { - y[0] /= mag; - y[1] /= mag; - y[2] /= mag; - } - - var lookAt = Sk.misceval.callsimArray(mod.Mat44); - lookAt.elements[0 * 4 + 0] = x[0]; - lookAt.elements[1 * 4 + 0] = x[1]; - lookAt.elements[2 * 4 + 0] = x[2]; - lookAt.elements[3 * 4 + 0] = 0.; - lookAt.elements[0 * 4 + 1] = y[0]; - lookAt.elements[1 * 4 + 1] = y[1]; - lookAt.elements[2 * 4 + 1] = y[2]; - lookAt.elements[3 * 4 + 1] = 0.; - lookAt.elements[0 * 4 + 2] = z[0]; - lookAt.elements[1 * 4 + 2] = z[1]; - lookAt.elements[2 * 4 + 2] = z[2]; - lookAt.elements[3 * 4 + 2] = 0.; - lookAt.elements[0 * 4 + 3] = 0.; - lookAt.elements[1 * 4 + 3] = 0.; - lookAt.elements[2 * 4 + 3] = 0.; - lookAt.elements[3 * 4 + 3] = 1.; - - // log(lookAt.elements); - - lookAt = lookAt.multiply(self); - self.elements = lookAt.elements; - self.translate(-eyeX, -eyeY, -eyeZ); - - // log(this.elements); - - return self; - }); - }, - 'Mat44', []); + mod.Mat44 = Sk.misceval.buildClass(mod, function ($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self) { + Sk.misceval.callsimArray($loc.loadIdentity, [self]); + self.stack = []; + }); - // todo; should probably put this in a math package - mod.Mat33 = Sk.misceval.buildClass(mod, function($gbl, $loc) - { - $loc.__init__ = new Sk.builtin.func(function(self) - { - Sk.misceval.callsimArray($loc.loadIdentity, [self]); - }); - - $loc.loadIdentity = new Sk.builtin.func(function(self) - { - self.elements = [1.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - 0.0, 0.0, 1.0]; - }); - }, - 'Mat33', []); - - mod.Vec3 = Sk.misceval.buildClass(mod, function($gbl, $loc) - { - $loc.__init__ = new Sk.builtin.func(function(self, x, y, z) - { - self.x = x; - self.y = y; - self.z = z; - }); - $loc.__sub__ = new Sk.builtin.func(function(self, other) - { - return Sk.misceval.callsimArray(mod.Vec3, [self.x - other.x, self.y - other.y, self.z - other.z]); - }); - }, - 'Vec3', []); - - mod.cross = new Sk.builtin.func(function(v1, v2) - { - Sk.asserts.assert(v1 instanceof mod.Vec3 && v2 instanceof mod.Vec3); + $loc.push = new Sk.builtin.func(function (self) { + self.stack.push(self.elements.slice(0)); + }); + + $loc.pop = new Sk.builtin.func(function (self) { + self.elements = self.stack.pop(); + }); + + $loc.loadIdentity = new Sk.builtin.func(function (self) { + self.elements = [1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0]; + }); + + $loc.transform3 = new Sk.builtin.func(function (self, v) { + var e = self.elements; return Sk.misceval.callsimArray(mod.Vec3, [ - v1.y * v2.z - v1.z * v2.y, - v1.z * v2.x - v1.x * v2.z, - v1.x * v2.y - v1.y * v2.x]); + e[0] * v.x + e[4] * v.y + e[8] * v.z, + e[1] * v.x + e[5] * v.y + e[9] * v.z, + e[2] * v.x + e[6] * v.y + e[10] * v.z]); + }); + + $loc.scale = new Sk.builtin.func(function (self, sx, sy, sz) { + self.elements[0 * 4 + 0] *= sx; + self.elements[0 * 4 + 1] *= sx; + self.elements[0 * 4 + 2] *= sx; + self.elements[0 * 4 + 3] *= sx; + + self.elements[1 * 4 + 0] *= sy; + self.elements[1 * 4 + 1] *= sy; + self.elements[1 * 4 + 2] *= sy; + self.elements[1 * 4 + 3] *= sy; + + self.elements[2 * 4 + 0] *= sz; + self.elements[2 * 4 + 1] *= sz; + self.elements[2 * 4 + 2] *= sz; + self.elements[2 * 4 + 3] *= sz; + + return self; + }); + + $loc.translate = new Sk.builtin.func(function (self, tx, ty, tz) { + self.elements[3 * 4 + 0] += self.elements[0 * 4 + 0] * tx + self.elements[1 * 4 + 0] * ty + self.elements[2 * 4 + 0] * tz; + self.elements[3 * 4 + 1] += self.elements[0 * 4 + 1] * tx + self.elements[1 * 4 + 1] * ty + self.elements[2 * 4 + 1] * tz; + self.elements[3 * 4 + 2] += self.elements[0 * 4 + 2] * tx + self.elements[1 * 4 + 2] * ty + self.elements[2 * 4 + 2] * tz; + self.elements[3 * 4 + 3] += self.elements[0 * 4 + 3] * tx + self.elements[1 * 4 + 3] * ty + self.elements[2 * 4 + 3] * tz; + return self; + }); + + $loc.rotate = new Sk.builtin.func(function (self, angle, x, y, z) { + var mag = Math.sqrt(x * x + y * y + z * z); + var sinAngle = Math.sin(angle * Math.PI / 180.0); + var cosAngle = Math.cos(angle * Math.PI / 180.0); + + if (mag > 0) { + var xx, yy, zz, xy, yz, zx, xs, ys, zs; + var oneMinusCos; + var rotMat; + + x /= mag; + y /= mag; + z /= mag; + + xx = x * x; + yy = y * y; + zz = z * z; + xy = x * y; + yz = y * z; + zx = z * x; + xs = x * sinAngle; + ys = y * sinAngle; + zs = z * sinAngle; + oneMinusCos = 1.0 - cosAngle; + + rotMat = Sk.misceval.callsimArray(mod.Mat44); + + rotMat.elements[0 * 4 + 0] = (oneMinusCos * xx) + cosAngle; + rotMat.elements[0 * 4 + 1] = (oneMinusCos * xy) - zs; + rotMat.elements[0 * 4 + 2] = (oneMinusCos * zx) + ys; + rotMat.elements[0 * 4 + 3] = 0.0; + + rotMat.elements[1 * 4 + 0] = (oneMinusCos * xy) + zs; + rotMat.elements[1 * 4 + 1] = (oneMinusCos * yy) + cosAngle; + rotMat.elements[1 * 4 + 2] = (oneMinusCos * yz) - xs; + rotMat.elements[1 * 4 + 3] = 0.0; + + rotMat.elements[2 * 4 + 0] = (oneMinusCos * zx) - ys; + rotMat.elements[2 * 4 + 1] = (oneMinusCos * yz) + xs; + rotMat.elements[2 * 4 + 2] = (oneMinusCos * zz) + cosAngle; + rotMat.elements[2 * 4 + 3] = 0.0; + + rotMat.elements[3 * 4 + 0] = 0.0; + rotMat.elements[3 * 4 + 1] = 0.0; + rotMat.elements[3 * 4 + 2] = 0.0; + rotMat.elements[3 * 4 + 3] = 1.0; + + rotMat = rotMat.multiply(self); + self.elements = rotMat.elements; + } + return self; + }); + + $loc.multiply = new Sk.builtin.func(function (self, right) { + var tmp = Sk.misceval.callsimArray(mod.Mat44); + + for (var i = 0; i < 4; i++) { + tmp.elements[i * 4 + 0] = + (self.elements[i * 4 + 0] * right.elements[0 * 4 + 0]) + + (self.elements[i * 4 + 1] * right.elements[1 * 4 + 0]) + + (self.elements[i * 4 + 2] * right.elements[2 * 4 + 0]) + + (self.elements[i * 4 + 3] * right.elements[3 * 4 + 0]); + + tmp.elements[i * 4 + 1] = + (self.elements[i * 4 + 0] * right.elements[0 * 4 + 1]) + + (self.elements[i * 4 + 1] * right.elements[1 * 4 + 1]) + + (self.elements[i * 4 + 2] * right.elements[2 * 4 + 1]) + + (self.elements[i * 4 + 3] * right.elements[3 * 4 + 1]); + + tmp.elements[i * 4 + 2] = + (self.elements[i * 4 + 0] * right.elements[0 * 4 + 2]) + + (self.elements[i * 4 + 1] * right.elements[1 * 4 + 2]) + + (self.elements[i * 4 + 2] * right.elements[2 * 4 + 2]) + + (self.elements[i * 4 + 3] * right.elements[3 * 4 + 2]); + + tmp.elements[i * 4 + 3] = + (self.elements[i * 4 + 0] * right.elements[0 * 4 + 3]) + + (self.elements[i * 4 + 1] * right.elements[1 * 4 + 3]) + + (self.elements[i * 4 + 2] * right.elements[2 * 4 + 3]) + + (self.elements[i * 4 + 3] * right.elements[3 * 4 + 3]); + } + + self.elements = tmp.elements; + return self; + }); + + /* Following gluLookAt implementation is adapted from + * the Mesa 3D Graphics library. http://www.mesa3d.org + */ + // todo; rewrite this with proper vec/mat ops + $loc.lookAt = new Sk.builtin.func(function (self, eyeX, eyeY, eyeZ, + centerX, centerY, centerZ, + upX, upY, upZ) { + /* Z vector */ + var z = [ + eyeX - centerX, + eyeY - centerY, + eyeZ - centerZ + ]; + var mag = Math.sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]); + if (mag) { + z[0] /= mag; + z[1] /= mag; + z[2] /= mag; + } + + /* Y vector */ + var y = [upX, upY, upZ]; + + /* X vector = Y cross Z */ + var x = []; + x[0] = y[1] * z[2] - y[2] * z[1]; + x[1] = -y[0] * z[2] + y[2] * z[0]; + x[2] = y[0] * z[1] - y[1] * z[0]; + + /* Recompute Y = Z cross X */ + y[0] = z[1] * x[2] - z[2] * x[1]; + y[1] = -z[0] * x[2] + z[2] * x[0]; + y[2] = z[0] * x[1] - z[1] * x[0]; + + /* mpichler, 19950515 */ + /* cross product gives area of parallelogram, which is < 1.0 for + * non-perpendicular unit-length vectors; so normalize x, y here + */ + + mag = Math.sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]); + if (mag) { + x[0] /= mag; + x[1] /= mag; + x[2] /= mag; + } + + mag = Math.sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]); + if (mag) { + y[0] /= mag; + y[1] /= mag; + y[2] /= mag; + } + + var lookAt = Sk.misceval.callsimArray(mod.Mat44); + lookAt.elements[0 * 4 + 0] = x[0]; + lookAt.elements[1 * 4 + 0] = x[1]; + lookAt.elements[2 * 4 + 0] = x[2]; + lookAt.elements[3 * 4 + 0] = 0.; + lookAt.elements[0 * 4 + 1] = y[0]; + lookAt.elements[1 * 4 + 1] = y[1]; + lookAt.elements[2 * 4 + 1] = y[2]; + lookAt.elements[3 * 4 + 1] = 0.; + lookAt.elements[0 * 4 + 2] = z[0]; + lookAt.elements[1 * 4 + 2] = z[1]; + lookAt.elements[2 * 4 + 2] = z[2]; + lookAt.elements[3 * 4 + 2] = 0.; + lookAt.elements[0 * 4 + 3] = 0.; + lookAt.elements[1 * 4 + 3] = 0.; + lookAt.elements[2 * 4 + 3] = 0.; + lookAt.elements[3 * 4 + 3] = 1.; + + // log(lookAt.elements); + + lookAt = lookAt.multiply(self); + self.elements = lookAt.elements; + self.translate(-eyeX, -eyeY, -eyeZ); + + // log(this.elements); + + return self; + }); + }, + "Mat44", []); + + // todo; should probably put this in a math package + mod.Mat33 = Sk.misceval.buildClass(mod, function ($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self) { + Sk.misceval.callsimArray($loc.loadIdentity, [self]); + }); + + $loc.loadIdentity = new Sk.builtin.func(function (self) { + self.elements = [1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0]; + }); + }, + "Mat33", []); + + mod.Vec3 = Sk.misceval.buildClass(mod, function ($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self, x, y, z) { + self.x = x; + self.y = y; + self.z = z; + }); + $loc.__sub__ = new Sk.builtin.func(function (self, other) { + return Sk.misceval.callsimArray(mod.Vec3, [self.x - other.x, self.y - other.y, self.z - other.z]); }); + }, + "Vec3", []); + + mod.cross = new Sk.builtin.func(function (v1, v2) { + Sk.asserts.assert(v1 instanceof mod.Vec3 && v2 instanceof mod.Vec3); + return Sk.misceval.callsimArray(mod.Vec3, [ + v1.y * v2.z - v1.z * v2.y, + v1.z * v2.x - v1.x * v2.z, + v1.x * v2.y - v1.y * v2.x]); + }); return mod; }; diff --git a/src/lib/webgl/matrix4.js b/src/lib/webgl/matrix4.js index 236a3d9afa..5aabf2dc18 100644 --- a/src/lib/webgl/matrix4.js +++ b/src/lib/webgl/matrix4.js @@ -1,6 +1,5 @@ // more from 'tdl' -var $builtinmodule = function(name) -{ +var $builtinmodule = function (name) { var mod = {}; var temp0v3_ = new Float32Array(3); @@ -15,304 +14,302 @@ var $builtinmodule = function(name) var temp1m4_ = new Float32Array(16); var temp2m4_ = new Float32Array(16); - var normalize = function(dst, a) { + var normalize = function (dst, a) { var n = 0.0; var aLength = a.length; - for (var i = 0; i < aLength; ++i) + for (var i = 0; i < aLength; ++i) { n += a[i] * a[i]; + } n = Math.sqrt(n); if (n > 0.00001) { - for (var i = 0; i < aLength; ++i) + for (var i = 0; i < aLength; ++i) { dst[i] = a[i] / n; + } } else { - for (var i = 0; i < aLength; ++i) + for (var i = 0; i < aLength; ++i) { dst[i] = 0; + } } return dst; }; - var cross = function(dst, a, b) { + var cross = function (dst, a, b) { dst[0] = a[1] * b[2] - a[2] * b[1]; dst[1] = a[2] * b[0] - a[0] * b[2]; dst[2] = a[0] * b[1] - a[1] * b[0]; return dst; }; - var subVector = function(dst, a, b) { + var subVector = function (dst, a, b) { var aLength = a.length; - for (var i = 0; i < aLength; ++i) + for (var i = 0; i < aLength; ++i) { dst[i] = a[i] - b[i]; + } return dst; }; - var dot = function(a, b) { + var dot = function (a, b) { return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); }; - mod.lookAt = new Sk.builtin.func(function(view, eye, target, up) - { - var t0 = temp0v3_; - var t1 = temp1v3_; - var t2 = temp2v3_; + mod.lookAt = new Sk.builtin.func(function (view, eye, target, up) { + var t0 = temp0v3_; + var t1 = temp1v3_; + var t2 = temp2v3_; - var vz = normalize(t0, subVector(t0, eye.v, target.v)); - var vx = normalize(t1, cross(t1, up.v, vz)); - var vy = cross(t2, vz, vx); + var vz = normalize(t0, subVector(t0, eye.v, target.v)); + var vx = normalize(t1, cross(t1, up.v, vz)); + var vy = cross(t2, vz, vx); - var dst = view.v; - dst[ 0] = vx[0]; - dst[ 1] = vy[0]; - dst[ 2] = vz[0]; - dst[ 3] = 0; - dst[ 4] = vx[1]; - dst[ 5] = vy[1]; - dst[ 6] = vz[1]; - dst[ 7] = 0; - dst[ 8] = vx[2]; - dst[ 9] = vy[2]; - dst[10] = vz[2]; - dst[11] = 0; - dst[12] = -dot(vx, eye.v); - dst[13] = -dot(vy, eye.v); - dst[14] = -dot(vz, eye.v); - dst[15] = 1; + var dst = view.v; + dst[0] = vx[0]; + dst[1] = vy[0]; + dst[2] = vz[0]; + dst[3] = 0; + dst[4] = vx[1]; + dst[5] = vy[1]; + dst[6] = vz[1]; + dst[7] = 0; + dst[8] = vx[2]; + dst[9] = vy[2]; + dst[10] = vz[2]; + dst[11] = 0; + dst[12] = -dot(vx, eye.v); + dst[13] = -dot(vy, eye.v); + dst[14] = -dot(vz, eye.v); + dst[15] = 1; - return view; - }); + return view; + }); - mod.perspective = new Sk.builtin.func(function(proj, angle, aspect, near, far) - { - var f = Math.tan(Math.PI * 0.5 - 0.5 * (angle * Math.PI / 180)); - var rangeInv = 1.0 / (near - far); + mod.perspective = new Sk.builtin.func(function (proj, angle, aspect, near, far) { + var f = Math.tan(Math.PI * 0.5 - 0.5 * (angle * Math.PI / 180)); + var rangeInv = 1.0 / (near - far); - var dst = proj.v; + var dst = proj.v; - dst[0] = f / aspect; - dst[1] = 0; - dst[2] = 0; - dst[3] = 0; + dst[0] = f / aspect; + dst[1] = 0; + dst[2] = 0; + dst[3] = 0; - dst[4] = 0; - dst[5] = f; - dst[6] = 0; - dst[7] = 0; + dst[4] = 0; + dst[5] = f; + dst[6] = 0; + dst[7] = 0; - dst[8] = 0; - dst[9] = 0; - dst[10] = (near + far) * rangeInv; - dst[11] = -1; + dst[8] = 0; + dst[9] = 0; + dst[10] = (near + far) * rangeInv; + dst[11] = -1; - dst[12] = 0; - dst[13] = 0; - dst[14] = near * far * rangeInv * 2; - dst[15] = 0; + dst[12] = 0; + dst[13] = 0; + dst[14] = near * far * rangeInv * 2; + dst[15] = 0; - return proj; - }); + return proj; + }); // builds, not appending - mod.rotationY = new Sk.builtin.func(function(target, angle) - { - var dst = target.v; - var c = Math.cos(angle * Math.PI / 180); - var s = Math.sin(angle * Math.PI / 180); + mod.rotationY = new Sk.builtin.func(function (target, angle) { + var dst = target.v; + var c = Math.cos(angle * Math.PI / 180); + var s = Math.sin(angle * Math.PI / 180); - dst[ 0] = c; - dst[ 1] = 0; - dst[ 2] = -s; - dst[ 3] = 0; - dst[ 4] = 0; - dst[ 5] = 1; - dst[ 6] = 0; - dst[ 7] = 0; - dst[ 8] = s; - dst[ 9] = 0; - dst[10] = c; - dst[11] = 0; - dst[12] = 0; - dst[13] = 0; - dst[14] = 0; - dst[15] = 1; + dst[0] = c; + dst[1] = 0; + dst[2] = -s; + dst[3] = 0; + dst[4] = 0; + dst[5] = 1; + dst[6] = 0; + dst[7] = 0; + dst[8] = s; + dst[9] = 0; + dst[10] = c; + dst[11] = 0; + dst[12] = 0; + dst[13] = 0; + dst[14] = 0; + dst[15] = 1; - return target; - }); + return target; + }); - mod.identity = new Sk.builtin.func(function(target) - { - var dst = target.v; - dst[ 0] = 1; - dst[ 1] = 0; - dst[ 2] = 0; - dst[ 3] = 0; - dst[ 4] = 0; - dst[ 5] = 1; - dst[ 6] = 0; - dst[ 7] = 0; - dst[ 8] = 0; - dst[ 9] = 0; - dst[10] = 1; - dst[11] = 0; - dst[12] = 0; - dst[13] = 0; - dst[14] = 0; - dst[15] = 1; - return target; - }); + mod.identity = new Sk.builtin.func(function (target) { + var dst = target.v; + dst[0] = 1; + dst[1] = 0; + dst[2] = 0; + dst[3] = 0; + dst[4] = 0; + dst[5] = 1; + dst[6] = 0; + dst[7] = 0; + dst[8] = 0; + dst[9] = 0; + dst[10] = 1; + dst[11] = 0; + dst[12] = 0; + dst[13] = 0; + dst[14] = 0; + dst[15] = 1; + return target; + }); // row major - mod.mul = new Sk.builtin.func(function(target, x, y) - { - var dst = target.v; - var a = x.v; - var b = y.v; - var a00 = a[0]; - var a01 = a[1]; - var a02 = a[2]; - var a03 = a[3]; - var a10 = a[ 4 + 0]; - var a11 = a[ 4 + 1]; - var a12 = a[ 4 + 2]; - var a13 = a[ 4 + 3]; - var a20 = a[ 8 + 0]; - var a21 = a[ 8 + 1]; - var a22 = a[ 8 + 2]; - var a23 = a[ 8 + 3]; - var a30 = a[12 + 0]; - var a31 = a[12 + 1]; - var a32 = a[12 + 2]; - var a33 = a[12 + 3]; - var b00 = b[0]; - var b01 = b[1]; - var b02 = b[2]; - var b03 = b[3]; - var b10 = b[ 4 + 0]; - var b11 = b[ 4 + 1]; - var b12 = b[ 4 + 2]; - var b13 = b[ 4 + 3]; - var b20 = b[ 8 + 0]; - var b21 = b[ 8 + 1]; - var b22 = b[ 8 + 2]; - var b23 = b[ 8 + 3]; - var b30 = b[12 + 0]; - var b31 = b[12 + 1]; - var b32 = b[12 + 2]; - var b33 = b[12 + 3]; - dst[ 0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30; - dst[ 1] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31; - dst[ 2] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32; - dst[ 3] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33; - dst[ 4] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30; - dst[ 5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31; - dst[ 6] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32; - dst[ 7] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33; - dst[ 8] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30; - dst[ 9] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31; - dst[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32; - dst[11] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33; - dst[12] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30; - dst[13] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31; - dst[14] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32; - dst[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33; - return target; - }); + mod.mul = new Sk.builtin.func(function (target, x, y) { + var dst = target.v; + var a = x.v; + var b = y.v; + var a00 = a[0]; + var a01 = a[1]; + var a02 = a[2]; + var a03 = a[3]; + var a10 = a[4 + 0]; + var a11 = a[4 + 1]; + var a12 = a[4 + 2]; + var a13 = a[4 + 3]; + var a20 = a[8 + 0]; + var a21 = a[8 + 1]; + var a22 = a[8 + 2]; + var a23 = a[8 + 3]; + var a30 = a[12 + 0]; + var a31 = a[12 + 1]; + var a32 = a[12 + 2]; + var a33 = a[12 + 3]; + var b00 = b[0]; + var b01 = b[1]; + var b02 = b[2]; + var b03 = b[3]; + var b10 = b[4 + 0]; + var b11 = b[4 + 1]; + var b12 = b[4 + 2]; + var b13 = b[4 + 3]; + var b20 = b[8 + 0]; + var b21 = b[8 + 1]; + var b22 = b[8 + 2]; + var b23 = b[8 + 3]; + var b30 = b[12 + 0]; + var b31 = b[12 + 1]; + var b32 = b[12 + 2]; + var b33 = b[12 + 3]; + dst[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30; + dst[1] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31; + dst[2] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32; + dst[3] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33; + dst[4] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30; + dst[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31; + dst[6] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32; + dst[7] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33; + dst[8] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30; + dst[9] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31; + dst[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32; + dst[11] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33; + dst[12] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30; + dst[13] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31; + dst[14] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32; + dst[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33; + return target; + }); - mod.invert = new Sk.builtin.func(function(target, mat) - { - var dst = target.v; - var m = mat.v; - var m00 = m[0 * 4 + 0]; - var m01 = m[0 * 4 + 1]; - var m02 = m[0 * 4 + 2]; - var m03 = m[0 * 4 + 3]; - var m10 = m[1 * 4 + 0]; - var m11 = m[1 * 4 + 1]; - var m12 = m[1 * 4 + 2]; - var m13 = m[1 * 4 + 3]; - var m20 = m[2 * 4 + 0]; - var m21 = m[2 * 4 + 1]; - var m22 = m[2 * 4 + 2]; - var m23 = m[2 * 4 + 3]; - var m30 = m[3 * 4 + 0]; - var m31 = m[3 * 4 + 1]; - var m32 = m[3 * 4 + 2]; - var m33 = m[3 * 4 + 3]; - var tmp_0 = m22 * m33; - var tmp_1 = m32 * m23; - var tmp_2 = m12 * m33; - var tmp_3 = m32 * m13; - var tmp_4 = m12 * m23; - var tmp_5 = m22 * m13; - var tmp_6 = m02 * m33; - var tmp_7 = m32 * m03; - var tmp_8 = m02 * m23; - var tmp_9 = m22 * m03; - var tmp_10 = m02 * m13; - var tmp_11 = m12 * m03; - var tmp_12 = m20 * m31; - var tmp_13 = m30 * m21; - var tmp_14 = m10 * m31; - var tmp_15 = m30 * m11; - var tmp_16 = m10 * m21; - var tmp_17 = m20 * m11; - var tmp_18 = m00 * m31; - var tmp_19 = m30 * m01; - var tmp_20 = m00 * m21; - var tmp_21 = m20 * m01; - var tmp_22 = m00 * m11; - var tmp_23 = m10 * m01; + mod.invert = new Sk.builtin.func(function (target, mat) { + var dst = target.v; + var m = mat.v; + var m00 = m[0 * 4 + 0]; + var m01 = m[0 * 4 + 1]; + var m02 = m[0 * 4 + 2]; + var m03 = m[0 * 4 + 3]; + var m10 = m[1 * 4 + 0]; + var m11 = m[1 * 4 + 1]; + var m12 = m[1 * 4 + 2]; + var m13 = m[1 * 4 + 3]; + var m20 = m[2 * 4 + 0]; + var m21 = m[2 * 4 + 1]; + var m22 = m[2 * 4 + 2]; + var m23 = m[2 * 4 + 3]; + var m30 = m[3 * 4 + 0]; + var m31 = m[3 * 4 + 1]; + var m32 = m[3 * 4 + 2]; + var m33 = m[3 * 4 + 3]; + var tmp_0 = m22 * m33; + var tmp_1 = m32 * m23; + var tmp_2 = m12 * m33; + var tmp_3 = m32 * m13; + var tmp_4 = m12 * m23; + var tmp_5 = m22 * m13; + var tmp_6 = m02 * m33; + var tmp_7 = m32 * m03; + var tmp_8 = m02 * m23; + var tmp_9 = m22 * m03; + var tmp_10 = m02 * m13; + var tmp_11 = m12 * m03; + var tmp_12 = m20 * m31; + var tmp_13 = m30 * m21; + var tmp_14 = m10 * m31; + var tmp_15 = m30 * m11; + var tmp_16 = m10 * m21; + var tmp_17 = m20 * m11; + var tmp_18 = m00 * m31; + var tmp_19 = m30 * m01; + var tmp_20 = m00 * m21; + var tmp_21 = m20 * m01; + var tmp_22 = m00 * m11; + var tmp_23 = m10 * m01; - var t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) - - (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31); - var t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) - - (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31); - var t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) - - (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31); - var t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) - - (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21); + var t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) - + (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31); + var t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) - + (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31); + var t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) - + (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31); + var t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) - + (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21); - var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3); + var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3); - dst[ 0] = d * t0; - dst[ 1] = d * t1; - dst[ 2] = d * t2; - dst[ 3] = d * t3; - dst[ 4] = d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) - - (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)); - dst[ 5] = d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) - - (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)); - dst[ 6] = d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) - - (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)); - dst[ 7] = d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) - - (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20)); - dst[ 8] = d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) - - (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)); - dst[ 9] = d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) - - (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)); - dst[10] = d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) - - (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)); - dst[11] = d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) - - (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23)); - dst[12] = d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) - - (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)); - dst[13] = d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) - - (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)); - dst[14] = d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) - - (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)); - dst[15] = d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) - - (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02)); - return target; - }); + dst[0] = d * t0; + dst[1] = d * t1; + dst[2] = d * t2; + dst[3] = d * t3; + dst[4] = d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) - + (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)); + dst[5] = d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) - + (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)); + dst[6] = d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) - + (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)); + dst[7] = d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) - + (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20)); + dst[8] = d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) - + (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)); + dst[9] = d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) - + (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)); + dst[10] = d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) - + (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)); + dst[11] = d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) - + (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23)); + dst[12] = d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) - + (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)); + dst[13] = d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) - + (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)); + dst[14] = d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) - + (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)); + dst[15] = d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) - + (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02)); + return target; + }); - mod.transpose = new Sk.builtin.func(function(target, mat) - { - var dst = target.v; - var m = mat.v; - for (var j = 0; j < 4; ++j) { - for (var i = 0; i < 4; ++i) - dst[j * 4 + i] = m[i * 4 + j]; - } - return dst; - }); + mod.transpose = new Sk.builtin.func(function (target, mat) { + var dst = target.v; + var m = mat.v; + for (var j = 0; j < 4; ++j) { + for (var i = 0; i < 4; ++i) { + dst[j * 4 + i] = m[i * 4 + j]; + } + } + return dst; + }); return mod; }; diff --git a/src/lib/webgl/models.js b/src/lib/webgl/models.js index 037b2e7ffd..44ddc181aa 100644 --- a/src/lib/webgl/models.js +++ b/src/lib/webgl/models.js @@ -1,10 +1,9 @@ // most of this file is from/based on 'tdl' -var $builtinmodule = function(name) -{ +var $builtinmodule = function (name) { var mod = {}; - var Buffer = function(array, opt_target) { + var Buffer = function (array, opt_target) { var target = opt_target || gl.ARRAY_BUFFER; var buf = gl.createBuffer(); this.target = target; @@ -28,142 +27,137 @@ var $builtinmodule = function(name) } }; - Buffer.prototype.set = function(array) { + Buffer.prototype.set = function (array) { gl.bindBuffer(this.target, this.buf); gl.bufferData(this.target, array.buffer, gl.STATIC_DRAW); - } + }; - Buffer.prototype.type = function() { + Buffer.prototype.type = function () { return this.type_; }; - Buffer.prototype.numComponents = function() { + Buffer.prototype.numComponents = function () { return this.numComponents_; }; - Buffer.prototype.numElements = function() { + Buffer.prototype.numElements = function () { return this.numElements_; }; - Buffer.prototype.totalComponents = function() { + Buffer.prototype.totalComponents = function () { return this.totalComponents_; }; - Buffer.prototype.buffer = function() { + Buffer.prototype.buffer = function () { return this.buf; }; - Buffer.prototype.stride = function() { + Buffer.prototype.stride = function () { return 0; }; - Buffer.prototype.offset = function() { + Buffer.prototype.offset = function () { return 0; }; - - mod.Model = Sk.misceval.buildClass(mod, function($gbl, $loc) - { - $loc.__init__ = new Sk.builtin.func(function(self, shader, arrays, textures) - { - self.buffers = {}; - var setBuffer = function(name, array) - { - var target = (name == 'indices') ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER; - b = self.buffers[name]; - if (!b) - b = new Buffer(array, target); - else - b.set(array); - self.buffers[name] = b; - }; - for (name in arrays) - setBuffer(name, arrays[name]); - - var textureUnits = {}; - var unit = 0; - for (var texture in textures) - { - textureUnits[texture] = unit++; - } - - self.mode = gl.TRIANGLES; - self.textures = textures.v; - self.textureUnits = textureUnits; - self.shader = shader; - }); - - /** - * Sets up the shared parts of drawing this model. Uses the - * program, binds the buffers, sets the textures. - * - * @param {!Object.} uniforms An object of names to - * values to set on this models uniforms. - */ - $loc.drawPrep = new Sk.builtin.func(function(self, uniforms) - { - var shader = self.shader; - var buffers = self.buffers; - var textures = self.textures; - - uniforms = Sk.ffi.remapToJs(uniforms); - - Sk.misceval.callsimArray(shader.use, [shader]); - - for (var buffer in buffers) { - var b = buffers[buffer]; - if (buffer == 'indices') { - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.buffer()); - } else { - var attrib = shader.attrib[buffer]; - if (attrib) { - attrib(b); - } - } - } - - for (var texture in textures) { - var unit = self.textureUnits[texture]; - shader.setUniform$impl(shader, textuer, unit); - textures[texture].bindToUnit(unit); - } - - for (var uniform in uniforms) { - shader.setUniform$impl(shader, uniform, uniforms[uniform]); - } - }); - - /** - * Draws this model. - * - * After calling tdl.models.Model.drawPrep you can call this - * function multiple times to draw this model. - * - * @param {!Object.} uniforms An object of names to - * values to set on this models uniforms. - */ - $loc.draw = new Sk.builtin.func(function(self, uniforms, opt_textures) - { - var shader = self.shader; - uniforms = Sk.ffi.remapToJs(uniforms); - for (uniform in uniforms) { - shader.setUniform$impl(shader, uniform, uniforms[uniform]); - } - - if (opt_textures) { - for (var texture in opt_textures) { - var unit = self.textureUnits[texture]; - shader.setUniform$impl(shader, texture, unit); - opt_textures[texture].bindToUnit(unit); - } + mod.Model = Sk.misceval.buildClass(mod, function ($gbl, $loc) { + $loc.__init__ = new Sk.builtin.func(function (self, shader, arrays, textures) { + self.buffers = {}; + var setBuffer = function (name, array) { + var target = (name == "indices") ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER; + b = self.buffers[name]; + if (!b) { + b = new Buffer(array, target); + } else { + b.set(array); + } + self.buffers[name] = b; + }; + for (name in arrays) { + setBuffer(name, arrays[name]); + } + + var textureUnits = {}; + var unit = 0; + for (var texture in textures) { + textureUnits[texture] = unit++; + } + + self.mode = gl.TRIANGLES; + self.textures = textures.v; + self.textureUnits = textureUnits; + self.shader = shader; + }); + + /** + * Sets up the shared parts of drawing this model. Uses the + * program, binds the buffers, sets the textures. + * + * @param {!Object.} uniforms An object of names to + * values to set on this models uniforms. + */ + $loc.drawPrep = new Sk.builtin.func(function (self, uniforms) { + var shader = self.shader; + var buffers = self.buffers; + var textures = self.textures; + + uniforms = Sk.ffi.remapToJs(uniforms); + + Sk.misceval.callsimArray(shader.use, [shader]); + + for (var buffer in buffers) { + var b = buffers[buffer]; + if (buffer == "indices") { + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, b.buffer()); + } else { + var attrib = shader.attrib[buffer]; + if (attrib) { + attrib(b); } - - var buffers = self.buffers; - gl.drawElements(self.mode, buffers.indices.totalComponents(), gl.UNSIGNED_SHORT, 0); - }); - }, - 'Model', []); + } + } + + for (var texture in textures) { + var unit = self.textureUnits[texture]; + shader.setUniform$impl(shader, textuer, unit); + textures[texture].bindToUnit(unit); + } + + for (var uniform in uniforms) { + shader.setUniform$impl(shader, uniform, uniforms[uniform]); + } + }); + + /** + * Draws this model. + * + * After calling tdl.models.Model.drawPrep you can call this + * function multiple times to draw this model. + * + * @param {!Object.} uniforms An object of names to + * values to set on this models uniforms. + */ + $loc.draw = new Sk.builtin.func(function (self, uniforms, opt_textures) { + var shader = self.shader; + uniforms = Sk.ffi.remapToJs(uniforms); + for (uniform in uniforms) { + shader.setUniform$impl(shader, uniform, uniforms[uniform]); + } + + if (opt_textures) { + for (var texture in opt_textures) { + var unit = self.textureUnits[texture]; + shader.setUniform$impl(shader, texture, unit); + opt_textures[texture].bindToUnit(unit); + } + } + + var buffers = self.buffers; + gl.drawElements(self.mode, buffers.indices.totalComponents(), gl.UNSIGNED_SHORT, 0); + }); + }, + 'Model', []); return mod; }; diff --git a/src/lib/webgl/primitives.js b/src/lib/webgl/primitives.js index ae90262abb..b624e2b2d6 100644 --- a/src/lib/webgl/primitives.js +++ b/src/lib/webgl/primitives.js @@ -1,11 +1,9 @@ // most of this file is from 'tdl' -var $builtinmodule = function(name) -{ +var $builtinmodule = function (name) { var mod = {}; - /** * AttribBuffer manages a TypedArray as an array of vectors. * @@ -16,9 +14,9 @@ var $builtinmodule = function(name) * create. Default = 'Float32Array'. * @param {!Array.} opt_data The data for the array. */ - var AttribBuffer = function( - numComponents, numElements, opt_type) { - opt_type = opt_type || 'Float32Array'; + var AttribBuffer = function ( + numComponents, numElements, opt_type) { + opt_type = opt_type || "Float32Array"; var type = window[opt_type]; if (numElements.length) { this.buffer = new type(numElements); @@ -33,15 +31,15 @@ var $builtinmodule = function(name) this.type = opt_type; }; - AttribBuffer.prototype.stride = function() { + AttribBuffer.prototype.stride = function () { return 0; }; - AttribBuffer.prototype.offset = function() { + AttribBuffer.prototype.offset = function () { return 0; }; - AttribBuffer.prototype.getElement = function(index) { + AttribBuffer.prototype.getElement = function (index) { var offset = index * this.numComponents; var value = []; for (var ii = 0; ii < this.numComponents; ++ii) { @@ -50,25 +48,25 @@ var $builtinmodule = function(name) return value; }; - AttribBuffer.prototype.setElement = function(index, value) { + AttribBuffer.prototype.setElement = function (index, value) { var offset = index * this.numComponents; for (var ii = 0; ii < this.numComponents; ++ii) { this.buffer[offset + ii] = value[ii]; } }; - AttribBuffer.prototype.clone = function() { + AttribBuffer.prototype.clone = function () { var copy = new AttribBuffer( - this.numComponents, this.numElements, this.type); + this.numComponents, this.numElements, this.type); copy.pushArray(this); return copy; - } + }; - AttribBuffer.prototype.push = function(value) { + AttribBuffer.prototype.push = function (value) { this.setElement(this.cursor++, value); }; - AttribBuffer.prototype.pushArray = function(array) { + AttribBuffer.prototype.pushArray = function (array) { // this.buffer.set(array, this.cursor * this.numComponents); // this.cursor += array.numElements; for (var ii = 0; ii < array.numElements; ++ii) { @@ -77,23 +75,23 @@ var $builtinmodule = function(name) }; AttribBuffer.prototype.pushArrayWithOffset = - function(array, offset) { - for (var ii = 0; ii < array.numElements; ++ii) { - var elem = array.getElement(ii); - for (var jj = 0; jj < offset.length; ++jj) { - elem[jj] += offset[jj]; + function (array, offset) { + for (var ii = 0; ii < array.numElements; ++ii) { + var elem = array.getElement(ii); + for (var jj = 0; jj < offset.length; ++jj) { + elem[jj] += offset[jj]; + } + this.push(elem); } - this.push(elem); - } - }; + }; /** - * Computes the extents - * @param {!AttribBuffer} positions The positions - * @return {!{min: !tdl.math.Vector3, max:!tdl.math.Vector3}} - * The min and max extents. - */ - AttribBuffer.prototype.computeExtents = function() { + * Computes the extents + * @param {!AttribBuffer} positions The positions + * @return {!{min: !tdl.math.Vector3, max:!tdl.math.Vector3}} + * The min and max extents. + */ + AttribBuffer.prototype.computeExtents = function () { var numElements = this.numElements; var numComponents = this.numComponents; var minExtent = this.getElement(0); @@ -117,79 +115,78 @@ var $builtinmodule = function(name) * @return {!Object.} The * created plane vertices. */ - mod.createCube = new Sk.builtin.func(function(size) - { - var CUBE_FACE_INDICES_ = [ - [3, 7, 5, 1], - [0, 4, 6, 2], - [6, 7, 3, 2], - [0, 1, 5, 4], - [5, 7, 6, 4], - [2, 3, 1, 0] - ]; - - var k = size / 2; - - var cornerVertices = [ - [-k, -k, -k], - [+k, -k, -k], - [-k, +k, -k], - [+k, +k, -k], - [-k, -k, +k], - [+k, -k, +k], - [-k, +k, +k], - [+k, +k, +k] - ]; - - var faceNormals = [ - [+1, +0, +0], - [-1, +0, +0], - [+0, +1, +0], - [+0, -1, +0], - [+0, +0, +1], - [+0, +0, -1] - ]; - - var uvCoords = [ - [0, 0], - [1, 0], - [1, 1], - [0, 1] - ]; - - var numVertices = 6 * 4; - var positions = new AttribBuffer(3, numVertices); - var normals = new AttribBuffer(3, numVertices); - var texCoords = new AttribBuffer(2, numVertices); - var indices = new AttribBuffer(3, 6 * 2, 'Uint16Array'); - - for (var f = 0; f < 6; ++f) { - var faceIndices = CUBE_FACE_INDICES_[f]; - for (var v = 0; v < 4; ++v) { - var position = cornerVertices[faceIndices[v]]; - var normal = faceNormals[f]; - var uv = uvCoords[v]; - - // Each face needs all four vertices because the normals and texture - // coordinates are not all the same. - positions.push(position); - normals.push(normal); - texCoords.push(uv); - - } - // Two triangles make a square face. - var offset = 4 * f; - indices.push([offset + 0, offset + 1, offset + 2]); - indices.push([offset + 0, offset + 2, offset + 3]); - } + mod.createCube = new Sk.builtin.func(function (size) { + var CUBE_FACE_INDICES_ = [ + [3, 7, 5, 1], + [0, 4, 6, 2], + [6, 7, 3, 2], + [0, 1, 5, 4], + [5, 7, 6, 4], + [2, 3, 1, 0] + ]; + + var k = size / 2; + + var cornerVertices = [ + [-k, -k, -k], + [+k, -k, -k], + [-k, +k, -k], + [+k, +k, -k], + [-k, -k, +k], + [+k, -k, +k], + [-k, +k, +k], + [+k, +k, +k] + ]; + + var faceNormals = [ + [+1, +0, +0], + [-1, +0, +0], + [+0, +1, +0], + [+0, -1, +0], + [+0, +0, +1], + [+0, +0, -1] + ]; + + var uvCoords = [ + [0, 0], + [1, 0], + [1, 1], + [0, 1] + ]; + + var numVertices = 6 * 4; + var positions = new AttribBuffer(3, numVertices); + var normals = new AttribBuffer(3, numVertices); + var texCoords = new AttribBuffer(2, numVertices); + var indices = new AttribBuffer(3, 6 * 2, "Uint16Array"); + + for (var f = 0; f < 6; ++f) { + var faceIndices = CUBE_FACE_INDICES_[f]; + for (var v = 0; v < 4; ++v) { + var position = cornerVertices[faceIndices[v]]; + var normal = faceNormals[f]; + var uv = uvCoords[v]; + + // Each face needs all four vertices because the normals and texture + // coordinates are not all the same. + positions.push(position); + normals.push(normal); + texCoords.push(uv); + + } + // Two triangles make a square face. + var offset = 4 * f; + indices.push([offset + 0, offset + 1, offset + 2]); + indices.push([offset + 0, offset + 2, offset + 3]); + } - return { - position: positions, - normal: normals, - texCoord: texCoords, - indices: indices - }; - }); + return { + position: positions, + normal: normals, + texCoord: texCoords, + indices: indices + }; + }); return mod; }; diff --git a/src/list.js b/src/list.js index 1949323fc3..423b2be100 100644 --- a/src/list.js +++ b/src/list.js @@ -1,440 +1,484 @@ /** * @constructor - * @param {Array.=} L - * @param {boolean=} canSuspend (defaults to true in this case, as list() is used directly from Python) - * @extends Sk.builtin.object + * @param {Array} L + * + * @extends {Sk.builtin.object} */ -Sk.builtin.list = function (L, canSuspend) { - var v, it, thisList; - - if (this instanceof Sk.builtin.list) { - canSuspend = canSuspend || false; - } else { - // Default to true in this case, because 'list' gets called directly from Python - return new Sk.builtin.list(L, canSuspend || true); - } - - this.__class__ = Sk.builtin.list; - - if (L === undefined) { - v = []; - } else if (Object.prototype.toString.apply(L) === "[object Array]") { - v = L; - } else if (Sk.builtin.checkIterable(L)) { - v = []; - it = Sk.abstr.iter(L); - - thisList = this; +Sk.builtin.list = Sk.abstr.buildNativeClass("list", { + constructor: function list(L) { + // this is an internal function and should be called with an array object + if (L === undefined) { + L = []; + } - return (function next(i) { - while(true) { - if (i instanceof Sk.misceval.Suspension) { - return new Sk.misceval.Suspension(next, i); - } else if (i === undefined) { - // done! - thisList.v = v; - return thisList; + Sk.asserts.assert(Array.isArray(L) && this instanceof Sk.builtin.list, "bad call to list, use 'new' with an Array"); + this.v = L; + }, + slots: /** @lends {Sk.builtin.list.prototype}*/ { + tp$getattr: Sk.generic.getAttr, + tp$as_sequence_or_mapping: true, + tp$hash: Sk.builtin.none.none$, + tp$doc: + "Built-in mutable sequence.\n\nIf no argument is given, the constructor creates a new empty list.\nThe argument must be an iterable if specified.", + tp$new: Sk.generic.new, + tp$init: function (args, kwargs) { + // this will be an Sk.builtin.list.prototype or a sk$klass.prototype that inherits from Sk.builtin.list.prototype + Sk.abstr.checkNoKwargs("list", kwargs); + Sk.abstr.checkArgsLen("list", args, 0, 1); + const self = this; + let L = Sk.misceval.arrayFromIterable(args[0], true); + return Sk.misceval.chain(L, (l) => { + self.v = l; + return Sk.builtin.none.none$; + }); + }, + $r: function () { + if (this.$entered_repr !== undefined) { + return new Sk.builtin.str("[...]"); + } + this.$entered_repr = true; + const ret = new Sk.builtin.str("[" + this.v.map((x) => Sk.misceval.objectRepr(x)).join(", ") + "]"); + this.$entered_repr = undefined; + return ret; + }, + tp$richcompare: function (w, op) { + // if the comparison allows for equality then short-circuit it here + if (this === w && Sk.misceval.opAllowsEquality(op)) { + return true; + } + if (!(w instanceof Sk.builtin.list)) { + if (Sk.__future__.python3) { + return Sk.builtin.NotImplemented.NotImplemented$; + } + return op === "NotEq" ? true : false; // py 2 mode... + } + let i; + const v = this.v; + w = w.v; + const vl = v.length; + const wl = w.length; + if (vl != wl && (op === "Eq" || op === "NotEq")) { + /* Shortcut: if the lengths differ, the lists differ */ + return op === "Eq" ? false : true; + } + for (i = 0; i < vl && i < wl; ++i) { + if (v[i] === w[i] || Sk.misceval.richCompareBool(v[i], w[i], "Eq")) { + continue; } else { - v.push(i); - i = it.tp$iternext(canSuspend); + break; + } + } + if (i >= vl || i >= wl) { + // no more items to compare, compare sizes + switch (op) { + case "Lt": + return vl < wl; + case "LtE": + return vl <= wl; + case "Eq": + return vl === wl; + case "NotEq": + return vl !== wl; + case "Gt": + return vl > wl; + case "GtE": + return vl >= wl; + default: + Sk.asserts.fail(); + } + } + // we have an item that's different + // shortcuts for eq/not + if (op === "Eq") { + return false; + } + if (op === "NotEq") { + return true; + } + // or, compare the differing element using the proper operator + return v[i] === w[i] || Sk.misceval.richCompareBool(v[i], w[i], op); + }, + tp$iter: function () { + return new Sk.builtin.list_iter_(this); + }, + + // sequence and mapping slots + sq$length: function () { + return this.v.length; + }, + sq$concat: function (other) { + if (!(other instanceof Sk.builtin.list)) { + throw new Sk.builtin.TypeError("can only concatenate list to list"); + } + return new Sk.builtin.list(this.v.concat(other.v)); + }, + sq$contains: function (item) { + for (let it = this.tp$iter(), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { + if (i === item || Sk.misceval.richCompareBool(i, item, "Eq")) { + return true; } } - })(it.tp$iternext(canSuspend)); - } else { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(L)+ "' " +"object is not iterable"); - } - - this["v"] = this.v = v; - return this; -}; - -Sk.abstr.setUpInheritance("list", Sk.builtin.list, Sk.builtin.seqtype); -Sk.abstr.markUnhashable(Sk.builtin.list); - -Sk.builtin.list.prototype.list_concat_ = function (other) { - // other not a list - var i; - var ret; - if (!other.__class__ || other.__class__ != Sk.builtin.list) { - throw new Sk.builtin.TypeError("can only concatenate list to list"); - } - - ret = this.v.slice(); - for (i = 0; i < other.v.length; ++i) { - ret.push(other.v[i]); - } - return new Sk.builtin.list(ret, false); -}; - -Sk.builtin.list.prototype.list_extend_ = function (other) { - var it, i; - var newb; - if (!Sk.builtin.checkIterable(other)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(other) + - "' object is not iterable"); - } - - if (this == other) { - // Handle extending list with itself - newb = []; - for (it = Sk.abstr.iter(other), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - newb.push(i); - } - - // Concatenate - this.v.push.apply(this.v, newb); - } else { - for (it = Sk.abstr.iter(other), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - this.v.push(i); - } - } - - return this; -}; - -Sk.builtin.list.prototype.list_del_item_ = function (i) { - i = Sk.builtin.asnum$(i); - if (i < 0 || i >= this.v.length) { - throw new Sk.builtin.IndexError("list assignment index out of range"); - } - this.list_del_slice_(i, i + 1); -}; - -Sk.builtin.list.prototype.list_del_slice_ = function (ilow, ihigh) { - var args; - ilow = Sk.builtin.asnum$(ilow); - ihigh = Sk.builtin.asnum$(ihigh); - args = []; - args.unshift(ihigh - ilow); - args.unshift(ilow); - this.v.splice.apply(this.v, args); -}; - -Sk.builtin.list.prototype.list_ass_item_ = function (i, v) { - i = Sk.builtin.asnum$(i); - if (i < 0 || i >= this.v.length) { - throw new Sk.builtin.IndexError("list assignment index out of range"); - } - this.v[i] = v; -}; - -Sk.builtin.list.prototype.list_ass_slice_ = function (ilow, ihigh, v) { - var args; - ilow = Sk.builtin.asnum$(ilow); - ihigh = Sk.builtin.asnum$(ihigh); - - if (Sk.builtin.checkIterable(v)) { - args = new Sk.builtin.list(v, false).v.slice(0); - } else { - throw new Sk.builtin.TypeError("can only assign an iterable"); - } - args.unshift(ihigh - ilow); - args.unshift(ilow); - this.v.splice.apply(this.v, args); -}; - -Sk.builtin.list.prototype["$r"] = function () { - var it, i; - var ret = []; - for (it = Sk.abstr.iter(this), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - if(i === this) { - ret.push("[...]"); - } else { - ret.push(Sk.misceval.objectRepr(i).v); - } - } - return new Sk.builtin.str("[" + ret.join(", ") + "]"); -}; - -Sk.builtin.list.prototype.tp$richcompare = function (w, op) { - // todo; can't figure out where cpy handles this silly case (test/run/t96.py) - // perhaps by trapping a stack overflow? otherwise i'm not sure for more - // complicated cases. bleh - // - // if the comparison allows for equality then short-circuit it here - var k; - var i; - var wl; - var vl; - var v; - if (this === w && Sk.misceval.opAllowsEquality(op)) { - return true; - } - - // w not a list - if (!w.__class__ || w.__class__ != Sk.builtin.list) { - // shortcuts for eq/not - if (op === "Eq") { return false; - } - if (op === "NotEq") { - return true; - } - - // todo; other types should have an arbitrary order - return false; - } - - v = this.v; - w = w.v; - vl = v.length; - wl = w.length; - - for (i = 0; i < vl && i < wl; ++i) { - k = Sk.misceval.richCompareBool(v[i], w[i], "Eq"); - if (!k) { - break; - } - } - - if (i >= vl || i >= wl) { - // no more items to compare, compare sizes - switch (op) { - case "Lt": - return vl < wl; - case "LtE": - return vl <= wl; - case "Eq": - return vl === wl; - case "NotEq": - return vl !== wl; - case "Gt": - return vl > wl; - case "GtE": - return vl >= wl; - default: - Sk.asserts.fail(); - } - } - - // we have an item that's different - - // shortcuts for eq/not - if (op === "Eq") { - return false; - } - if (op === "NotEq") { - return true; - } - - // or, compare the differing element using the proper operator - return Sk.misceval.richCompareBool(v[i], w[i], op); -}; - -Sk.builtin.list.prototype.__iter__ = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("__iter__", arguments.length, 0, 0, true, false); - return new Sk.builtin.list_iter_(self); + }, + sq$repeat: function (n) { + if (!Sk.misceval.isIndex(n)) { + throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '" + Sk.abstr.typeName(n) + "'"); + } + n = Sk.misceval.asIndex(n); + if (typeof n !== "number") { + throw new Sk.builtin.OverflowError("cannot fit '" + Sk.abstr.typeName(n) + "' into an index-sized integer"); + } + const ret = []; + for (let i = 0; i < n; ++i) { + for (let j = 0; j < this.v.length; ++j) { + ret.push(this.v[j]); + } + } + return new Sk.builtin.list(ret); + }, + mp$subscript: function (index) { + if (Sk.misceval.isIndex(index)) { + let i = Sk.misceval.asIndexOrThrow(index); + if (typeof i !== "number") { + throw new Sk.builtin.IndexError("cannot fit '" + Sk.abstr.typeName(index) + "' into an index-sized integer"); + } + if (i < 0) { + i = this.v.length + i; + } + if (i < 0 || i >= this.v.length) { + throw new Sk.builtin.IndexError("list index out of range"); + } + return this.v[i]; + } else if (index.constructor === Sk.builtin.slice) { + const ret = []; + const lst = this.v; + index.sssiter$(lst.length, (i) => { + ret.push(lst[i]); + }); + return new Sk.builtin.list(ret); + } + throw new Sk.builtin.TypeError("list indices must be integers, not " + Sk.abstr.typeName(index)); + }, + mp$ass_subscript: function (index, value) { + if (value === undefined) { + this.del$subscript(index); + } else { + this.ass$subscript(index, value); + } + return Sk.builtin.none.none$; + }, + nb$inplace_add: function (other) { + other = Sk.misceval.arrayFromIterable(other); + this.v.push(...other); + return this; + }, + nb$inplace_multiply: function (n) { + if (!Sk.misceval.isIndex(n)) { + throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '" + Sk.abstr.typeName(n) + "'"); + } + n = Sk.misceval.asIndex(n); + if (typeof n !== "number") { + throw new Sk.builtin.OverflowError("cannot fit '" + Sk.abstr.typeName(n) + "' into an index-sized integer"); + } + const len = this.v.length; + for (let i = 1; i < n; ++i) { + for (let j = 0; j < len; ++j) { + this.v.push(this.v[j]); + } + } + return this; + }, + }, + methods: /** @lends {Sk.builtin.list.prototype}*/ { + __reversed__: { + $meth: function () { + return new Sk.builtin.reverselist_iter_(this); + }, + $flags: {NoArgs: true}, + $textsig: "($self, /)", + $doc: "Return a reverse iterator over the list.", + }, + clear: { + $meth: function () { + this.v = []; + return Sk.builtin.none.none$; + }, + $flags: {NoArgs: true}, + $textsig: "($self, /)", + $doc: "Remove all items from list.", + }, + copy: { + $meth: function () { + return new Sk.builtin.list(this.v.slice(0)); + }, + $flags: {NoArgs: true}, + $textsig: "($self, /)", + $doc: "Return a shallow copy of the list.", + }, + append: { + $meth: function (item) { + this.v.push(item); + return Sk.builtin.none.none$; + }, + $flags: {OneArg: true}, + $textsig: "($self, object, /)", + $doc: "Append object to the end of the list.", + }, + insert: { + $meth: function (i, x) { + if (!Sk.builtin.checkNumber(i)) { + throw new Sk.builtin.TypeError("an integer is required"); + } + i = Sk.builtin.asnum$(i); + if (i < 0) { + i = i + this.v.length; + } + if (i < 0) { + i = 0; + } else if (i > this.v.length) { + i = this.v.length; + } + this.v.splice(i, 0, x); + return Sk.builtin.none.none$; + }, + $flags: {MinArgs: 2, MaxArgs: 2}, + $textsig: "($self, index, object, /)", + $doc: "Insert object before index.", + }, + extend: { + $meth: function (iterable) { + this.nb$inplace_add(iterable); + return Sk.builtin.none.none$; + }, + $flags: {OneArg: true}, + $textsig: "($self, iterable, /)", + $doc: "Extend list by appending elements from the iterable.", + }, + pop: { + $meth: function (i) { + if (i === undefined) { + i = this.v.length - 1; + } + if (!Sk.builtin.checkNumber(i)) { + throw new Sk.builtin.TypeError("an integer is required"); + } + i = Sk.builtin.asnum$(i); + if (i < 0) { + i = i + this.v.length; + } + if (i < 0 || i >= this.v.length) { + throw new Sk.builtin.IndexError("pop index out of range"); + } + const res = this.v[i]; + this.v.splice(i, 1); + return res; + }, + $flags: {MinArgs: 0, MaxArgs: 1}, + $textsig: "($self, index=-1, /)", + $doc: "Remove and return item at index (default last).\n\nRaises IndexError if list is empty or index is out of range.", + }, + remove: { + $meth: function (item) { + const idx = this.$index(item); + this.v.splice(Sk.builtin.asnum$(idx), 1); + return Sk.builtin.none.none$; + }, + $flags: {OneArg: true}, + $textsig: "($self, value, /)", + $doc: "Remove first occurrence of value.\n\nRaises ValueError if the value is not present.", + }, + sort: { + $meth: function (args, kwargs) { + Sk.abstr.checkNoArgs("sort", args); + const key_reverse = Sk.abstr.copyKeywordsToNamedArgs("sort", ["key", "reverse"], [], kwargs, [ + Sk.builtin.none.none$, + Sk.builtin.bool.false$, + ]); + const key = key_reverse[0]; + const reverse = key_reverse[1]; + return this.$list_sort(undefined, key, reverse); + }, + $flags: {FastCall: true}, + $textsig: "($self, /, *, key=None, reverse=False)", + $doc: "Stable sort *IN PLACE*.", + }, + index: { + $meth: function (value, start, stop) { + return this.$index(value, start, stop); + }, + $flags: {MinArgs: 1, MaxArgs: 3}, + $textsig: "($self, value, start=0, stop=sys.maxsize, /)", + $doc: "Return first index of value.\n\nRaises ValueError if the value is not present.", + }, + count: { + $meth: function (item) { + let count = 0; + const len = this.v.length; + const obj = this.v; + for (let i = 0; i < len; ++i) { + if (Sk.misceval.richCompareBool(obj[i], item, "Eq")) { + count += 1; + } + } + return new Sk.builtin.int_(count); + }, + $flags: {OneArg: true}, + $textsig: "($self, value, /)", + $doc: "Return number of occurrences of value.", + }, + reverse: { + $meth: function () { + return this.$list_reverse(); + }, + $flags: {NoArgs: true}, + $textsig: "($self, /)", + $doc: "Reverse *IN PLACE*.", + }, + }, + proto: /** @lends {Sk.builtin.list.prototype}*/ { + sk$asarray: function () { + return this.v.slice(0); + }, + }, }); -Sk.builtin.list.prototype.tp$iter = function () { - return new Sk.builtin.list_iter_(this); -}; - -Sk.builtin.list.prototype.sq$length = function () { - return this.v.length; -}; -Sk.builtin.list.prototype.sq$concat = Sk.builtin.list.prototype.list_concat_; -Sk.builtin.list.prototype.nb$add = Sk.builtin.list.prototype.list_concat_; -Sk.builtin.list.prototype.nb$inplace_add = Sk.builtin.list.prototype.list_extend_; -Sk.builtin.list.prototype.sq$repeat = function (n) { - var j; - var i; - var ret; - if (!Sk.misceval.isIndex(n)) { - throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '" + Sk.abstr.typeName(n) + "'"); - } - - n = Sk.misceval.asIndex(n); - ret = []; - for (i = 0; i < n; ++i) { - for (j = 0; j < this.v.length; ++j) { - ret.push(this.v[j]); - } - } - return new Sk.builtin.list(ret, false); -}; -Sk.builtin.list.prototype.nb$multiply = Sk.builtin.list.prototype.sq$repeat; -Sk.builtin.list.prototype.nb$inplace_multiply = function(n) { - var j; - var i; - var len; - if (!Sk.misceval.isIndex(n)) { - throw new Sk.builtin.TypeError("can't multiply sequence by non-int of type '" + Sk.abstr.typeName(n) + "'"); - } - - // works on list itself --> inplace - n = Sk.misceval.asIndex(n); - len = this.v.length; - for (i = 1; i < n; ++i) { - for (j = 0; j < len; ++j) { - this.v.push(this.v[j]); - } - } - - return this; -}; - -/* - Sk.builtin.list.prototype.sq$item = list_item; - Sk.builtin.list.prototype.sq$slice = list_slice; - */ -Sk.builtin.list.prototype.sq$ass_item = Sk.builtin.list.prototype.list_ass_item_; -Sk.builtin.list.prototype.sq$del_item = Sk.builtin.list.prototype.list_del_item_; -Sk.builtin.list.prototype.sq$ass_slice = Sk.builtin.list.prototype.list_ass_slice_; -Sk.builtin.list.prototype.sq$del_slice = Sk.builtin.list.prototype.list_del_slice_; - -Sk.builtin.list.prototype.sq$contains = function (item) { - var it, i; - - for (it = this.tp$iter(), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - if (Sk.misceval.richCompareBool(i, item, "Eq")) { - return true; - } - } - return false; -}; - -Sk.builtin.list.prototype.__contains__ = new Sk.builtin.func(function(self, item) { - Sk.builtin.pyCheckArgsLen("__contains__", arguments.length, 2, 2); - return new Sk.builtin.bool(self.sq$contains(item)); -}); +Sk.exportSymbol("Sk.builtin.list", Sk.builtin.list); -/* - Sk.builtin.list.prototype.sq$inplace_concat = list_inplace_concat; - Sk.builtin.list.prototype.sq$inplace_repeat = list_inplace_repeat; +/** + * @function + * @param {Sk.builtin.object} index + * @param {Sk.builtin.object} value + * + * @description + * called by mp$ass_subscript when assigning a value rather than deleting + * */ - -Sk.builtin.list.prototype.list_subscript_ = function (index) { - var ret; - var i; +Sk.builtin.list.prototype.ass$subscript = function (index, value) { if (Sk.misceval.isIndex(index)) { - i = Sk.misceval.asIndex(index); - if (i !== undefined) { - if (i < 0) { - i = this.v.length + i; - } - if (i < 0 || i >= this.v.length) { - throw new Sk.builtin.IndexError("list index out of range"); - } - return this.v[i]; + let i = Sk.misceval.asIndexOrThrow(index); + if (typeof i !== "number") { + throw new Sk.builtin.IndexError("cannot fit '" + Sk.abstr.typeName(index) + "' into an index-sized integer"); } - } else if (index instanceof Sk.builtin.slice) { - ret = []; - index.sssiter$(this, function (i, wrt) { - ret.push(wrt.v[i]); - }); - return new Sk.builtin.list(ret, false); - } - - throw new Sk.builtin.TypeError("list indices must be integers, not " + Sk.abstr.typeName(index)); -}; - -Sk.builtin.list.prototype.list_ass_subscript_ = function (index, value) { - var i; - var j; - var tosub; - var indices; - if (Sk.misceval.isIndex(index)) { - i = Sk.misceval.asIndex(index); if (i !== undefined) { if (i < 0) { i = this.v.length + i; } - this.list_ass_item_(i, value); + this.ass$item(i, value); return; } } else if (index instanceof Sk.builtin.slice) { - indices = index.slice_indices_(this.v.length); + const indices = index.$slice_indices(this.v.length); if (indices[2] === 1) { - this.list_ass_slice_(indices[0], indices[1], value); + this.ass$slice(indices[0], indices[1], value); } else { - tosub = []; - index.sssiter$(this, function (i, wrt) { + const tosub = []; + index.sssiter$(this.v.length, (i) => { tosub.push(i); }); - j = 0; + let j = 0; if (tosub.length !== value.v.length) { - throw new Sk.builtin.ValueError("attempt to assign sequence of size " + value.v.length + " to extended slice of size " + tosub.length); + throw new Sk.builtin.ValueError( + "attempt to assign sequence of size " + value.v.length + " to extended slice of size " + tosub.length + ); } - for (i = 0; i < tosub.length; ++i) { + for (let i = 0; i < tosub.length; ++i) { this.v.splice(tosub[i], 1, value.v[j]); j += 1; } } return; } - throw new Sk.builtin.TypeError("list indices must be integers, not " + Sk.abstr.typeName(index)); }; -Sk.builtin.list.prototype.list_del_subscript_ = function (index) { - var offdir; - var dec; - var self; - var indices; - var i; +/** + * @function + * @param {Sk.builtin.object} index + * + * @description + * called by mp$ass_subscript when deleting an index/slice + * + */ +Sk.builtin.list.prototype.del$subscript = function (index) { if (Sk.misceval.isIndex(index)) { - i = Sk.misceval.asIndex(index); + let i = Sk.misceval.asIndex(index); if (i !== undefined) { if (i < 0) { i = this.v.length + i; } - this.list_del_item_(i); + this.del$item(i); return; } } else if (index instanceof Sk.builtin.slice) { - indices = index.slice_indices_(this.v.length); + const indices = index.$slice_indices(this.v.length); if (indices[2] === 1) { - this.list_del_slice_(indices[0], indices[1]); + this.del$slice(indices[0], indices[1]); } else { - self = this; - dec = 0; // offset of removal for next index (because we'll have removed, but the iterator is giving orig indices) - offdir = indices[2] > 0 ? 1 : 0; - index.sssiter$(this, function (i, wrt) { - self.v.splice(i - dec, 1); + const lst = this.v; + let dec = 0; // offset of removal for next index (because we'll have removed, but the iterator is giving orig indices) + const offdir = indices[2] > 0 ? 1 : 0; + index.sssiter$(lst.length, (i) => { + lst.splice(i - dec, 1); dec += offdir; }); } return; } + throw new Sk.builtin.TypeError("list indices must be integers, not " + Sk.abstr.typeName(index)); +}; - throw new Sk.builtin.TypeError("list indices must be integers, not " + typeof index); +Sk.builtin.list.prototype.del$item = function (i) { + i = Sk.builtin.asnum$(i); + if (i < 0 || i >= this.v.length) { + throw new Sk.builtin.IndexError("list assignment index out of range"); + } + this.del$slice(i, i + 1); }; -Sk.builtin.list.prototype.mp$subscript = Sk.builtin.list.prototype.list_subscript_; -Sk.builtin.list.prototype.mp$ass_subscript = Sk.builtin.list.prototype.list_ass_subscript_; -Sk.builtin.list.prototype.mp$del_subscript = Sk.builtin.list.prototype.list_del_subscript_; +Sk.builtin.list.prototype.del$slice = function (ilow, ihigh) { + ilow = Sk.builtin.asnum$(ilow); + ihigh = Sk.builtin.asnum$(ihigh); + const args = []; + args.unshift(ihigh - ilow); + args.unshift(ilow); + this.v.splice.apply(this.v, args); +}; -Sk.builtin.list.prototype.__getitem__ = new Sk.builtin.func(function (self, index) { - return Sk.builtin.list.prototype.list_subscript_.call(self, index); -}); +Sk.builtin.list.prototype.ass$item = function (i, v) { + i = Sk.builtin.asnum$(i); + if (i < 0 || i >= this.v.length) { + throw new Sk.builtin.IndexError("list assignment index out of range"); + } + this.v[i] = v; +}; -Sk.builtin.list.prototype.__setitem__ = new Sk.builtin.func(function (self, index, val) { - return Sk.builtin.list.prototype.list_ass_subscript_.call(self, index, val); -}); +Sk.builtin.list.prototype.ass$slice = function (ilow, ihigh, v) { + const args = []; + ilow = Sk.builtin.asnum$(ilow); + ihigh = Sk.builtin.asnum$(ihigh); -Sk.builtin.list.prototype.__delitem__ = new Sk.builtin.func(function (self, index) { - return Sk.builtin.list.prototype.list_del_subscript_.call(self, index); -}); + if (Sk.builtin.checkIterable(v)) { + const iter = Sk.abstr.iter(v); + for (let i = iter.tp$iternext(); i !== undefined; i = iter.tp$iternext()) { + args.push(i); + } + } else { + throw new Sk.builtin.TypeError("can only assign an iterable"); + } + args.unshift(ihigh - ilow); + args.unshift(ilow); + this.v.splice.apply(this.v, args); +}; /** - * @param {?=} self - * @param {?=} cmp optional - * @param {?=} key optional - * @param {?=} reverse optional + * @param {?=} cmp optional (not supported in py3) + * @param {?=} key optional (keyword only argument in py3) + * @param {?=} reverse optional (keyword only argument in py3) */ -Sk.builtin.list.prototype.list_sort_ = function sort(self, cmp, key, reverse) { - var mucked; - var j; - var keyvalue; - var item; - var i; - var zero; - var timsort; - var has_key = key !== undefined && key !== null && key !== Sk.builtin.none.none$; - var has_cmp = cmp !== undefined && cmp !== null && cmp !== Sk.builtin.none.none$; - var rev; - +Sk.builtin.list.prototype.$list_sort = function sort(cmp, key, reverse) { + const has_key = key != null && key !== Sk.builtin.none.none$; + const has_cmp = cmp != null && cmp !== Sk.builtin.none.none$; + let rev, item; if (reverse === undefined) { rev = false; } else if (reverse === Sk.builtin.none.none$) { @@ -442,11 +486,10 @@ Sk.builtin.list.prototype.list_sort_ = function sort(self, cmp, key, reverse) { } else { rev = Sk.misceval.isTrue(reverse); } + const timsort = new Sk.builtin.timSort(this); - timsort = new Sk.builtin.timSort(self); - - self.v = []; - zero = new Sk.builtin.int_(0); + this.v = []; + const zero = new Sk.builtin.int_(0); if (has_key) { if (has_cmp) { @@ -459,9 +502,9 @@ Sk.builtin.list.prototype.list_sort_ = function sort(self, cmp, key, reverse) { return Sk.misceval.richCompareBool(a[0], b[0], "Lt"); }; } - for (i = 0; i < timsort.listlength; i++) { + for (let i = 0; i < timsort.listlength; i++) { item = timsort.list.v[i]; - keyvalue = Sk.misceval.callsimArray(key, [item]); + const keyvalue = Sk.misceval.callsimArray(key, [item]); timsort.list.v[i] = [keyvalue, item]; } } else if (has_cmp) { @@ -472,25 +515,25 @@ Sk.builtin.list.prototype.list_sort_ = function sort(self, cmp, key, reverse) { } if (rev) { - timsort.list.list_reverse_(timsort.list); + timsort.list.$list_reverse(); } timsort.sort(); if (rev) { - timsort.list.list_reverse_(timsort.list); + timsort.list.$list_reverse(); } if (has_key) { - for (j = 0; j < timsort.listlength; j++) { + for (let j = 0; j < timsort.listlength; j++) { item = timsort.list.v[j][1]; timsort.list.v[j] = item; } } - mucked = self.sq$length() > 0; + const mucked = this.sq$length() > 0; - self.v = timsort.list.v; + this.v = timsort.list.v; if (mucked) { throw new Sk.builtin.OperationError("list modified during sort"); @@ -498,106 +541,16 @@ Sk.builtin.list.prototype.list_sort_ = function sort(self, cmp, key, reverse) { return Sk.builtin.none.none$; }; -Sk.builtin.list.prototype.list_sort_.co_varnames = ["__self__", "cmp", "key", "reverse"]; -Sk.builtin.list.prototype.list_sort_.$defaults = [Sk.builtin.none.none$, Sk.builtin.none.none$, false]; /** - * @param {Sk.builtin.list=} self optional + * @this {Sk.builtin.list} **/ -Sk.builtin.list.prototype.list_reverse_ = function (self) { - var i; - var newarr; - var old; - var len; - Sk.builtin.pyCheckArgsLen("reverse", arguments.length, 1, 1); - - len = self.v.length; - old = self.v; - newarr = []; - for (i = len - 1; i > -1; --i) { - newarr.push(old[i]); - } - self["v"] = newarr; +Sk.builtin.list.prototype.$list_reverse = function () { + this.v.reverse(); return Sk.builtin.none.none$; }; -//Sk.builtin.list.prototype.__reversed__ = todo; - -Sk.builtin.list.prototype["append"] = new Sk.builtin.func(function (self, item) { - Sk.builtin.pyCheckArgsLen("append", arguments.length, 2, 2); - - self.v.push(item); - return Sk.builtin.none.none$; -}); - -Sk.builtin.list.prototype["insert"] = new Sk.builtin.func(function (self, i, x) { - Sk.builtin.pyCheckArgsLen("insert", arguments.length, 3, 3); - if (!Sk.builtin.checkNumber(i)) { - throw new Sk.builtin.TypeError("an integer is required"); - } - - i = Sk.builtin.asnum$(i); - if (i < 0) { - i = i + self.v.length; - } - if (i < 0) { - i = 0; - } else if (i > self.v.length) { - i = self.v.length; - } - self.v.splice(i, 0, x); - return Sk.builtin.none.none$; -}); - -Sk.builtin.list.prototype["extend"] = new Sk.builtin.func(function (self, b) { - Sk.builtin.pyCheckArgsLen("extend", arguments.length, 2, 2); - self.list_extend_(b); - return Sk.builtin.none.none$; -}); - -Sk.builtin.list.prototype["pop"] = new Sk.builtin.func(function (self, i) { - var ret; - Sk.builtin.pyCheckArgsLen("pop", arguments.length, 1, 2); - if (i === undefined) { - i = self.v.length - 1; - } - - if (!Sk.builtin.checkNumber(i)) { - throw new Sk.builtin.TypeError("an integer is required"); - } - - i = Sk.builtin.asnum$(i); - if (i < 0) { - i = i + self.v.length; - } - if ((i < 0) || (i >= self.v.length)) { - throw new Sk.builtin.IndexError("pop index out of range"); - } - ret = self.v[i]; - self.v.splice(i, 1); - return ret; -}); - -Sk.builtin.list.prototype["remove"] = new Sk.builtin.func(function (self, item) { - var idx; - Sk.builtin.pyCheckArgsLen("remove", arguments.length, 2, 2); - - idx = Sk.builtin.list.prototype["index"].func_code(self, item); - self.v.splice(Sk.builtin.asnum$(idx), 1); - return Sk.builtin.none.none$; -}); - -Sk.builtin.list.prototype.clear$ = function (self) { - Sk.builtin.pyCheckArgsLen("clear", arguments.length, 1, 1); - self.v = []; - return Sk.builtin.none.none$; -}; - -Sk.builtin.list.prototype["index"] = new Sk.builtin.func(function (self, item, start, stop) { - var i; - var obj; - var len; - Sk.builtin.pyCheckArgsLen("index", arguments.length, 2, 4); +Sk.builtin.list.prototype.$index = function (item, start, stop) { if (start !== undefined && !Sk.builtin.checkInt(start)) { throw new Sk.builtin.TypeError("slice indices must be integers"); } @@ -605,103 +558,38 @@ Sk.builtin.list.prototype["index"] = new Sk.builtin.func(function (self, item, s throw new Sk.builtin.TypeError("slice indices must be integers"); } - len = self.v.length; - obj = self.v; + const len = this.v.length; + const obj = this.v; - start = (start === undefined) ? 0 : start.v; + start = start === undefined ? 0 : start.v; if (start < 0) { - start = ((start + len) >= 0) ? start + len : 0; + start = start + len >= 0 ? start + len : 0; } - stop = (stop === undefined) ? len : stop.v; + stop = stop === undefined ? len : stop.v; if (stop < 0) { - stop = ((stop + len) >= 0) ? stop + len : 0; + stop = stop + len >= 0 ? stop + len : 0; } - for (i = start; i < stop; ++i) { + for (let i = start; i < stop; ++i) { if (Sk.misceval.richCompareBool(obj[i], item, "Eq")) { return new Sk.builtin.int_(i); } } throw new Sk.builtin.ValueError("list.index(x): x not in list"); -}); - -Sk.builtin.list.prototype["count"] = new Sk.builtin.func(function (self, item) { - var i; - var count; - var obj; - var len; - Sk.builtin.pyCheckArgsLen("count", arguments.length, 2, 2); - - len = self.v.length; - obj = self.v; - count = 0; - for (i = 0; i < len; ++i) { - if (Sk.misceval.richCompareBool(obj[i], item, "Eq")) { - count += 1; - } - } - return new Sk.builtin.int_(count); -}); - -Sk.builtin.list.prototype["copy"] = new Sk.builtin.func(function (self) { - var it; - var k; - var items; - Sk.builtin.pyCheckArgsLen("copy", arguments.length - 1, 0, 0); - - items = []; - for (it = Sk.abstr.iter(self), k = it.tp$iternext(); - k !== undefined; - k = it.tp$iternext()) { - items.push(k); - - } - return new Sk.builtin.list(items); - -}); - -Sk.builtin.list.prototype["reverse"] = new Sk.builtin.func(Sk.builtin.list.prototype.list_reverse_); -Sk.builtin.list.prototype["sort"] = new Sk.builtin.func(Sk.builtin.list.prototype.list_sort_); - -Sk.exportSymbol("Sk.builtin.list", Sk.builtin.list); - -/** - * @constructor - * @param {Object} lst - */ -Sk.builtin.list_iter_ = function (lst) { - if (!(this instanceof Sk.builtin.list_iter_)) { - return new Sk.builtin.list_iter_(lst); - } - this.$index = 0; - this.lst = lst.v.slice(); - this.sq$length = this.lst.length; - this.tp$iter = this; - this.tp$iternext = function () { - if (this.$index >= this.sq$length) { - return undefined; - } - return this.lst[this.$index++]; - }; - this.$r = function () { - return new Sk.builtin.str("listiterator"); - }; - return this; }; -Sk.abstr.setUpInheritance("listiterator", Sk.builtin.list_iter_, Sk.builtin.object); - -Sk.builtin.list_iter_.prototype.__class__ = Sk.builtin.list_iter_; - -Sk.builtin.list_iter_.prototype.__iter__ = new Sk.builtin.func(function (self) { - return self; -}); - -Sk.builtin.list_iter_.prototype.next$ = function (self) { - var ret = self.tp$iternext(); - if (ret === undefined) { - throw new Sk.builtin.StopIteration(); - } - return ret; +Sk.builtin.list.py2$methods = { + sort: { + $name: "sort", + $meth: function (cmp, key, reverse) { + return this.$list_sort(cmp, key, reverse); + }, + $flags: { + NamedArgs: ["cmp", "key", "reverse"], + Defaults: [Sk.builtin.none.none$, Sk.builtin.none.none$, false], //use false since bool not defined yet + }, + $textsig: "($self, cmp=None, key=None, reverse=False)", + $doc: "Stable sort *IN PLACE*.", + }, }; diff --git a/src/long.js b/src/long.js index edd6e8a55b..63271adf3e 100644 --- a/src/long.js +++ b/src/long.js @@ -1,844 +1,30 @@ -/* global Sk: true, goog:true */ - -// long aka "bignumber" implementation -// -// Using javascript BigInteger by Tom Wu /** * @constructor * Sk.builtin.lng * * @description - * Constructor for Python long. Also used for builtin long(). - * - * @extends {Sk.builtin.numtype} + * This is only for backward compatibility with py2. + * We take the approach of using a trivial subclass with int and overriding a few methods * - * @param {*} x Object or number to convert to Python long. - * @param {number=} base Optional base. - * @return {Sk.builtin.lng} Python long + * @param {Number|String|BigInt} x */ -Sk.builtin.lng = function (x, base) { /* long is a reserved word */ - base = Sk.builtin.asnum$(base); - if (!(this instanceof Sk.builtin.lng)) { - return new Sk.builtin.lng(x, base); - } - - - if (x === undefined) { - this.biginteger = new Sk.builtin.biginteger(0); - return this; - } - if (x instanceof Sk.builtin.lng) { - this.biginteger = x.biginteger.clone(); - return this; - } - if (x instanceof Sk.builtin.biginteger) { - this.biginteger = x; - return this; - } - if (x instanceof String || typeof x === "string") { - return Sk.longFromStr(x, base); - } - if (x instanceof Sk.builtin.str) { - return Sk.longFromStr(x.v, base); - } - - if ((x !== undefined) && (!Sk.builtin.checkString(x) && !Sk.builtin.checkNumber(x))) { - if (x === true) { - x = 1; - } else if (x === false) { - x = 0; - } else { - throw new Sk.builtin.TypeError("long() argument must be a string or a number, not '" + Sk.abstr.typeName(x) + "'"); - } - } - - x = Sk.builtin.asnum$nofloat(x); - this.biginteger = new Sk.builtin.biginteger(x); - return this; -}; - -Sk.abstr.setUpInheritance("long", Sk.builtin.lng, Sk.builtin.numtype); - -/* NOTE: See constants used for kwargs in constants.js */ - -Sk.builtin.lng.prototype.tp$index = function () { - return parseInt(this.str$(10, true), 10); -}; - -Sk.builtin.lng.prototype.tp$hash = function () { - return new Sk.builtin.int_(this.tp$index()); -}; - -Sk.builtin.lng.prototype.nb$int_ = function() { - if (this.cantBeInt()) { - return new Sk.builtin.lng(this); - } - - return new Sk.builtin.int_(this.toInt$()); -}; - -Sk.builtin.lng.prototype.__format__= function (obj, format_spec) { - var formatstr; - Sk.builtin.pyCheckArgsLen("__format__", arguments.length, 2, 2); - - if (!Sk.builtin.checkString(format_spec)) { - if (Sk.__future__.exceptions) { - throw new Sk.builtin.TypeError("format() argument 2 must be str, not " + Sk.abstr.typeName(format_spec)); - } else { - throw new Sk.builtin.TypeError("format expects arg 2 to be string or unicode, not " + Sk.abstr.typeName(format_spec)); - } - } else { - formatstr = Sk.ffi.remapToJs(format_spec); - if (formatstr !== "") { - throw new Sk.builtin.NotImplementedError("format spec is not yet implemented"); - } - } - - return new Sk.builtin.str(obj); -}; - -Sk.builtin.lng.prototype.round$ = function (self, ndigits) { - Sk.builtin.pyCheckArgsLen("__round__", arguments.length, 1, 2); - - var result, multiplier, number, num10, rounded, bankRound, ndigs; - - if ((ndigits !== undefined) && !Sk.misceval.isIndex(ndigits)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(ndigits) + "' object cannot be interpreted as an index"); - } - - number = Sk.builtin.asnum$(self); - if (ndigits === undefined) { - ndigs = 0; - } else { - ndigs = Sk.misceval.asIndex(ndigits); - } - - if (Sk.__future__.bankers_rounding) { - num10 = number * Math.pow(10, ndigs); - rounded = Math.round(num10); - bankRound = (((((num10>0)?num10:(-num10))%1)===0.5)?(((0===(rounded%2)))?rounded:(rounded-1)):rounded); - result = bankRound / Math.pow(10, ndigs); - return new Sk.builtin.lng(result); - } else { - multiplier = Math.pow(10, ndigs); - result = Math.round(number * multiplier) / multiplier; - - return new Sk.builtin.lng(result); - } -}; - -Sk.builtin.lng.prototype.__index__ = new Sk.builtin.func(function(self) { - return self.nb$int_(self); -}); - -Sk.builtin.lng.prototype.nb$lng_ = function () { - return this; -}; - -Sk.builtin.lng.prototype.nb$float_ = function() { - return new Sk.builtin.float_(Sk.ffi.remapToJs(this)); -}; - -// Threshold to determine when types should be converted to long -//Sk.builtin.lng.threshold$ = Sk.builtin.int_.threshold$; - -Sk.builtin.lng.MAX_INT$ = new Sk.builtin.lng(Sk.builtin.int_.threshold$); -Sk.builtin.lng.MIN_INT$ = new Sk.builtin.lng(-Sk.builtin.int_.threshold$); - -Sk.builtin.lng.prototype.cantBeInt = function () { - return (this.longCompare(Sk.builtin.lng.MAX_INT$) > 0) || (this.longCompare(Sk.builtin.lng.MIN_INT$) < 0); -}; - -Sk.builtin.lng.fromInt$ = function (ival) { - return new Sk.builtin.lng(ival); -}; - -// js string (not Sk.builtin.str) -> long. used to create longs in transformer, respects -// 0x, 0o, 0b, etc. -Sk.longFromStr = function (s, base) { - // l/L are valid digits with base >= 22 - // Sk.asserts.assert(s.charAt(s.length - 1) !== "L" && s.charAt(s.length - 1) !== 'l', "L suffix should be removed before here"); - - var parser = function (s, base) { - if (base === 10) { - return new Sk.builtin.biginteger(s); - } - return new Sk.builtin.biginteger(s, base); +Sk.builtin.lng = Sk.abstr.buildNativeClass("long", { + base: Sk.builtin.int_, // not technically correct but makes backward compatibility easy + constructor: function lng(x) { + Sk.builtin.int_.call(this, x); + }, + slots: { + $r: function () { + return new Sk.builtin.str(this.v.toString() + "L"); }, - biginteger = Sk.str2number(s, base, parser, function (x) { - return x.negate(); - }, "long"); - - return new Sk.builtin.lng(biginteger); -}; -Sk.exportSymbol("Sk.longFromStr", Sk.longFromStr); - -Sk.builtin.lng.prototype.toInt$ = function () { - return this.biginteger.intValue(); -}; - -Sk.builtin.lng.prototype.clone = function () { - return new Sk.builtin.lng(this); -}; - -Sk.builtin.lng.prototype.conjugate = new Sk.builtin.func(function (self) { - return self.clone(); + tp$as_number: true, + nb$negative: function () { + return new Sk.builtin.lng(intProto.nb$negative.call(this).v); + }, + nb$positive: function () { + return new Sk.builtin.lng(intProto.nb$positive.call(this).v); + }, + }, }); -Sk.builtin.lng.prototype.nb$add = function (other) { - var thisAsFloat; - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.str$(10, true)); - return thisAsFloat.nb$add(other); - } - - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - return new Sk.builtin.lng(this.biginteger.add(other.biginteger)); - } - - if (other instanceof Sk.builtin.biginteger) { - return new Sk.builtin.lng(this.biginteger.add(other)); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.lng.prototype.nb$reflected_add = function (other) { - // Should not automatically call this.nb$add, as nb$add may have - // been overridden by a subclass - return Sk.builtin.lng.prototype.nb$add.call(this, other); -}; - -Sk.builtin.lng.prototype.nb$inplace_add = Sk.builtin.lng.prototype.nb$add; - -Sk.builtin.lng.prototype.nb$subtract = function (other) { - var thisAsFloat; - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.str$(10, true)); - return thisAsFloat.nb$subtract(other); - } - - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - return new Sk.builtin.lng(this.biginteger.subtract(other.biginteger)); - } - - if (other instanceof Sk.builtin.biginteger) { - return new Sk.builtin.lng(this.biginteger.subtract(other)); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.lng.prototype.nb$reflected_subtract = function (other) { - // Should not automatically call this.nb$add, as nb$add may have - // been overridden by a subclass - var negative_this = this.nb$negative(); - return Sk.builtin.lng.prototype.nb$add.call(negative_this, other); -}; - -Sk.builtin.lng.prototype.nb$inplace_subtract = Sk.builtin.lng.prototype.nb$subtract; - -Sk.builtin.lng.prototype.nb$multiply = function (other) { - var thisAsFloat; - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.str$(10, true)); - return thisAsFloat.nb$multiply(other); - } - - if (other instanceof Sk.builtin.int_) { - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - return new Sk.builtin.lng(this.biginteger.multiply(other.biginteger)); - } - - if (other instanceof Sk.builtin.biginteger) { - return new Sk.builtin.lng(this.biginteger.multiply(other)); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** @override */ -Sk.builtin.lng.prototype.nb$reflected_multiply = function (other) { - // Should not automatically call this.nb$multiply, as nb$multiply may have - // been overridden by a subclass - return Sk.builtin.lng.prototype.nb$multiply.call(this, other); -}; - -Sk.builtin.lng.prototype.nb$inplace_multiply = Sk.builtin.lng.prototype.nb$multiply; - -Sk.builtin.lng.prototype.nb$divide = function (other) { - var thisAsFloat, thisneg, otherneg, result; - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.str$(10, true)); - return thisAsFloat.nb$divide(other); - } - - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - // Standard, long result mode - - if (other instanceof Sk.builtin.lng) { - // Special logic to round DOWN towards negative infinity for negative results - thisneg = this.nb$isnegative(); - otherneg = other.nb$isnegative(); - if ((thisneg && !otherneg) || (otherneg && !thisneg)) { - result = this.biginteger.divideAndRemainder(other.biginteger); - // If remainder is zero or positive, just return division result - if (result[1].trueCompare(Sk.builtin.biginteger.ZERO) === 0) { - // No remainder, just return result - return new Sk.builtin.lng(result[0]); - } - // Reminder... subtract 1 from the result (like rounding to neg infinity) - result = result[0].subtract(Sk.builtin.biginteger.ONE); - return new Sk.builtin.lng(result); - } - return new Sk.builtin.lng(this.biginteger.divide(other.biginteger)); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$reflected_divide = function (other) { - var thisneg, otherneg, result; - - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - // Standard, long result mode - if (other instanceof Sk.builtin.lng) { - return other.nb$divide(this); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$floor_divide = function (other) { - var thisAsFloat; - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.str$(10, true)); - return thisAsFloat.nb$floor_divide(other); - } - - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - // Standard, long result mode - if (other instanceof Sk.builtin.lng) { - return other.nb$divide(this); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$divmod = function (other) { - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - return new Sk.builtin.tuple([ - this.nb$floor_divide(other), - this.nb$remainder(other) - ]); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$reflected_divmod = function (other) { - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - return new Sk.builtin.tuple([ - other.nb$floor_divide(this), - other.nb$remainder(this) - ]); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$inplace_divide = Sk.builtin.lng.prototype.nb$divide; - -Sk.builtin.lng.prototype.nb$floor_divide = Sk.builtin.lng.prototype.nb$divide; - -Sk.builtin.lng.prototype.nb$reflected_floor_divide = Sk.builtin.lng.prototype.nb$reflected_divide; - -Sk.builtin.lng.prototype.nb$inplace_floor_divide = Sk.builtin.lng.prototype.nb$floor_divide; - -Sk.builtin.lng.prototype.nb$remainder = function (other) { - var thisAsFloat, tmp; - - if (this.biginteger.trueCompare(Sk.builtin.biginteger.ZERO) === 0) { - if (other instanceof Sk.builtin.float_) { - return new Sk.builtin.float_(0); - } - return new Sk.builtin.lng(0); - } - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.str$(10, true)); - return thisAsFloat.nb$remainder(other); - } - - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - - tmp = new Sk.builtin.lng(this.biginteger.remainder(other.biginteger)); - if (this.nb$isnegative()) { - if (other.nb$ispositive() && tmp.nb$nonzero()) { - tmp = tmp.nb$add(other).nb$remainder(other); - } - } else { - if (other.nb$isnegative() && tmp.nb$nonzero()) { - tmp = tmp.nb$add(other); - } - } - return tmp; - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$reflected_remainder = function (other) { - if (other instanceof Sk.builtin.int_) { - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - return other.nb$remainder(this); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$inplace_remainder = Sk.builtin.lng.prototype.nb$remainder; - -Sk.builtin.lng.prototype.nb$divmod = function (other) { - var thisAsFloat; - - if (other === Sk.builtin.bool.true$) { - other = new Sk.builtin.lng(1); - } - - if (other === Sk.builtin.bool.false$) { - other = new Sk.builtin.lng(0); - } - - if (other instanceof Sk.builtin.int_) { - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - return new Sk.builtin.tuple([ - this.nb$floor_divide(other), - this.nb$remainder(other) - ]); - } - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this.str$(10, true)); - return thisAsFloat.nb$divmod(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** - * @param {number|Object} n - * @param {number|Object=} mod - * @suppress {checkTypes} - */ -Sk.builtin.lng.prototype.nb$power = function (n, mod) { - var thisAsFloat; - if (mod !== undefined) { - n = new Sk.builtin.biginteger(Sk.builtin.asnum$(n)); - mod = new Sk.builtin.biginteger(Sk.builtin.asnum$(mod)); - - return new Sk.builtin.lng(this.biginteger.modPowInt(n, mod)); - } - - if (n instanceof Sk.builtin.float_ || - (n instanceof Sk.builtin.int_ && n.v < 0)) { - thisAsFloat = new Sk.builtin.float_(this.str$(10, true)); - return thisAsFloat.nb$power(n); - } - - if (n instanceof Sk.builtin.int_) { - // Promote an int to long - n = new Sk.builtin.lng(n.v); - } - - if (n instanceof Sk.builtin.lng) { - if (mod !== undefined) { - n = new Sk.builtin.biginteger(Sk.builtin.asnum$(n)); - mod = new Sk.builtin.biginteger(Sk.builtin.asnum$(mod)); - - return new Sk.builtin.lng(this.biginteger.modPowInt(n, mod)); - } - - if (n.nb$isnegative()) { - thisAsFloat = new Sk.builtin.float_(this.str$(10, true)); - return thisAsFloat.nb$power(n); - } - return new Sk.builtin.lng(this.biginteger.pow(n.biginteger)); - } - - if (n instanceof Sk.builtin.biginteger) { - if (mod !== undefined) { - mod = new Sk.builtin.biginteger(Sk.builtin.asnum$(mod)); - - return new Sk.builtin.lng(this.biginteger.modPowInt(n, mod)); - } - - if (n.isnegative()) { - thisAsFloat = new Sk.builtin.float_(this.str$(10, true)); - return thisAsFloat.nb$power(n); - } - return new Sk.builtin.lng(this.biginteger.pow(n)); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$reflected_power = function (n, mod) { - if (n instanceof Sk.builtin.int_) { - // Promote an int to long - n = new Sk.builtin.lng(n.v); - } - - if (n instanceof Sk.builtin.lng) { - return n.nb$power(this, mod); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$inplace_power = Sk.builtin.lng.prototype.nb$power; - -/** - * Compute the absolute value of this instance and return. - * - * Javascript function, returns Python object. - * - * @return {Sk.builtin.lng} The absolute value - */ -Sk.builtin.lng.prototype.nb$abs = function () { - return new Sk.builtin.lng(this.biginteger.bnAbs()); -}; - -Sk.builtin.lng.prototype.nb$lshift = function (other) { - - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - if (other.biginteger.signum() < 0) { - throw new Sk.builtin.ValueError("negative shift count"); - } - return new Sk.builtin.lng(this.biginteger.shiftLeft(other.biginteger)); - } - if (other instanceof Sk.builtin.biginteger) { - if (other.signum() < 0) { - throw new Sk.builtin.ValueError("negative shift count"); - } - return new Sk.builtin.lng(this.biginteger.shiftLeft(other)); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$reflected_lshift = function (other) { - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - return other.nb$lshift(this); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$inplace_lshift = Sk.builtin.lng.prototype.nb$lshift; - -Sk.builtin.lng.prototype.nb$rshift = function (other) { - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - if (other.biginteger.signum() < 0) { - throw new Sk.builtin.ValueError("negative shift count"); - } - return new Sk.builtin.lng(this.biginteger.shiftRight(other.biginteger)); - } - if (other instanceof Sk.builtin.biginteger) { - if (other.signum() < 0) { - throw new Sk.builtin.ValueError("negative shift count"); - } - return new Sk.builtin.lng(this.biginteger.shiftRight(other)); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$reflected_rshift = function (other) { - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - return other.nb$rshift(this); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$inplace_rshift = Sk.builtin.lng.prototype.nb$rshift; - -Sk.builtin.lng.prototype.nb$and = function (other) { - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - return new Sk.builtin.lng(this.biginteger.and(other.biginteger)); - } - if (other instanceof Sk.builtin.biginteger) { - return new Sk.builtin.lng(this.biginteger.and(other)); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$reflected_and = Sk.builtin.lng.prototype.nb$and; - -Sk.builtin.lng.prototype.nb$inplace_and = Sk.builtin.lng.prototype.nb$and; - -Sk.builtin.lng.prototype.nb$or = function (other) { - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - return new Sk.builtin.lng(this.biginteger.or(other.biginteger)); - } - if (other instanceof Sk.builtin.biginteger) { - return new Sk.builtin.lng(this.biginteger.or(other)); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - - -Sk.builtin.lng.prototype.nb$reflected_or = Sk.builtin.lng.prototype.nb$or; - -Sk.builtin.lng.prototype.nb$inplace_or = Sk.builtin.lng.prototype.nb$or; - -Sk.builtin.lng.prototype.nb$xor = function (other) { - if (other instanceof Sk.builtin.int_) { - // Promote an int to long - other = new Sk.builtin.lng(other.v); - } - - if (other instanceof Sk.builtin.lng) { - return new Sk.builtin.lng(this.biginteger.xor(other.biginteger)); - } - if (other instanceof Sk.builtin.biginteger) { - return new Sk.builtin.lng(this.biginteger.xor(other)); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -Sk.builtin.lng.prototype.nb$reflected_xor = Sk.builtin.lng.prototype.nb$xor; - -Sk.builtin.lng.prototype.nb$inplace_xor = Sk.builtin.lng.prototype.nb$xor; - -/** - * @override - * - * @return {Sk.builtin.lng} A copy of this instance with the value negated. - */ -Sk.builtin.lng.prototype.nb$negative = function () { - return new Sk.builtin.lng(this.biginteger.negate()); -}; - -Sk.builtin.lng.prototype.nb$invert = function () { - return new Sk.builtin.lng(this.biginteger.not()); -}; - -Sk.builtin.lng.prototype.nb$positive = function () { - return this.clone(); -}; - -Sk.builtin.lng.prototype.nb$nonzero = function () { - return this.biginteger.trueCompare(Sk.builtin.biginteger.ZERO) !== 0; -}; - -Sk.builtin.lng.prototype.nb$isnegative = function () { - return this.biginteger.isnegative(); -}; - -Sk.builtin.lng.prototype.nb$ispositive = function () { - return !this.biginteger.isnegative(); -}; - -Sk.builtin.lng.prototype.longCompare = function (other) { - var otherAsLong, thisAsFloat; - - if (typeof other === "number") { - other = new Sk.builtin.lng(other); - } - - if (other instanceof Sk.builtin.int_ || - (other instanceof Sk.builtin.float_ && other.v % 1 === 0)) { - otherAsLong = new Sk.builtin.lng(other.v); - return this.longCompare(otherAsLong); - } - - if (other instanceof Sk.builtin.float_) { - thisAsFloat = new Sk.builtin.float_(this); - return thisAsFloat.numberCompare(other); - } - - if (other instanceof Sk.builtin.lng) { - return this.biginteger.subtract(other.biginteger); - } else if (other instanceof Sk.builtin.biginteger) { - return this.biginteger.subtract(other); - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -//tests fail if === -Sk.builtin.lng.prototype.ob$eq = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.longCompare(other) == 0); //jshint ignore:line - } else if (other instanceof Sk.builtin.none) { - return Sk.builtin.bool.false$; - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; - -Sk.builtin.lng.prototype.ob$ne = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.longCompare(other) != 0); //jshint ignore:line - } else if (other instanceof Sk.builtin.none) { - return Sk.builtin.bool.true$; - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; - -Sk.builtin.lng.prototype.ob$lt = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.longCompare(other) < 0); - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; - -Sk.builtin.lng.prototype.ob$le = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.longCompare(other) <= 0); - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; - -Sk.builtin.lng.prototype.ob$gt = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.longCompare(other) > 0); - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; - -Sk.builtin.lng.prototype.ob$ge = function (other) { - if (other instanceof Sk.builtin.int_ || other instanceof Sk.builtin.lng || - other instanceof Sk.builtin.float_) { - return new Sk.builtin.bool(this.longCompare(other) >= 0); - } else { - return Sk.builtin.NotImplemented.NotImplemented$; - } -}; - -Sk.builtin.lng.prototype.$r = function () { - var suffix; - if (Sk.__future__.python3) { - suffix = ""; - } else { - suffix = "L"; - } - return new Sk.builtin.str(this.str$(10, true) + suffix); -}; - -Sk.builtin.lng.prototype.tp$str = function () { - return new Sk.builtin.str(this.str$(10, true)); -}; - -Sk.builtin.lng.prototype.str$ = function (base, sign) { - var work; - if (sign === undefined) { - sign = true; - } - - work = sign ? this.biginteger : this.biginteger.abs(); - - if (base === undefined || base === 10) { - return work.toString(); - } - - // Another base... convert... - return work.toString(base); -}; +const intProto = Sk.builtin.int_.prototype; diff --git a/src/main.js b/src/main.js index 4c45e3237a..d8f2841136 100644 --- a/src/main.js +++ b/src/main.js @@ -9,33 +9,40 @@ require("./util.js"); // Global support functions Sk.global["strftime"] = require("strftime"); Sk.global["strptime"] = require("../support/time-helpers/strptime.js"); +Sk.global["JSBI"] = require("jsbi"); require("setimmediate"); // Skulpt require("assert"); require("./env.js"); require("./type.js"); +require("./generic.js"); +require("./check.js"); require("./abstract.js"); require("./object.js"); +require("./slotdefs.js"); +require("./descr.js"); +// can only setUpMethods / setUpGetsets / setUpSlots from now. require("./function.js"); +require("./sk_method.js"); +// can only do setUpSlots with tp$new from now since __new__ is a sk_method require("./builtin.js"); require("./fromcodepoint.js"); require("./errors.js"); require("./method.js"); require("./misceval.js"); -require("./seqtype.js"); +require("./simple_iterators.js"); require("./list.js"); require("./str.js"); require("./formatting.js"); require("./tuple.js"); require("./dict.js"); -require("./numtype.js"); -require("./biginteger.js"); +require("./dictviews.js"); +require("./mappingproxy.js"); +require("./property_class_static.js"); require("./int.js"); require("./bool.js"); require("./float.js"); -require("./number.js"); -require("./long.js"); require("./complex.js"); require("./slice.js"); require("./set.js"); @@ -45,12 +52,8 @@ require("./structseq.js"); require("./generator.js"); require("./file.js"); require("./ffi.js"); -require("./iterator.js"); -require("./enumerate.js"); -require("./filter.js"); -require("./zip.js"); -require("./map.js"); -//require("./classmethod.js"); +require("./range.js"); +require("./iteratorobjects.js"); require("./token.js"); require("./tokenize.js"); require("../gen/parse_tables.js"); @@ -61,8 +64,7 @@ require("./symtable.js"); require("./compile.js"); require("./import.js"); require("./timsort.js"); -require("./sorted.js"); -require("./typeobject.js"); +require("./super.js"); require("./builtindict.js"); require("./constants.js"); require("./internalpython.js"); diff --git a/src/map.js b/src/map.js index e373ff140d..71d9c9ee64 100644 --- a/src/map.js +++ b/src/map.js @@ -3,7 +3,7 @@ * @param {Object} iterable * @extends Sk.builtin.object */ -Sk.builtin.map_ = function map_ (fun, seq) { +Sk.builtin.map_ = function map_(fun, seq) { var next; var args; var getnext; diff --git a/src/mappingproxy.js b/src/mappingproxy.js new file mode 100644 index 0000000000..9edf9c33f6 --- /dev/null +++ b/src/mappingproxy.js @@ -0,0 +1,95 @@ +/** + * + * @constructor + * + * @param {Object} d + * + * @description + * This should be called with the prototype of a type object + * It returns a mapping proxy + * useful for when we do typeobject.__dict__ + * or module.__dict__ since a module $d is an object literal + * + * In Python this can technically be called with a dict + * but we don't implement this feature. If you tried to call this in Skulpt + * You would get an error because object's new property won't allow any arguments + * + * Technically we can also have any hashable item as a key - we also ignore this implementation detail + * + */ +Sk.builtin.mappingproxy = Sk.abstr.buildNativeClass("mappingproxy", { + constructor: function mappingproxy(d) { + Sk.asserts.assert(this instanceof Sk.builtin.mappingproxy, "bad call to mapping proxy, use 'new'"); + this.mapping = Object.create(null); // create from null to avoid name conflicts or prototype issues + d = d || {}; + const d_copy = {...d}; + // we make a shallow copy in order to ignore inherited attributes from the prototype + // a lot of the $ properties are enumerable so won't be copied here + // also constructor is enumerable so no need to worry about that + this.size = 0; + for (let key in d_copy) { + const k = Sk.unfixReserved(key); + if (!k.includes("$")) { + this.mapping[k] = d_copy[key]; + this.size++; + } + } + }, + slots: { + tp$getattr: Sk.generic.getAttr, + tp$as_sequence_or_mapping: true, + tp$hash: Sk.builtin.none.none$, + $r: function () { + const bits = []; + for (let k in this.mapping) { + bits.push("'" + k + "': " + Sk.misceval.objectRepr(this.mapping[k])); + } + const repr = "mappingproxy({" + bits.join(", ") + "}"; + return new Sk.builtin.str(repr); + }, + mp$subscript: function (key) { + const res = this.mp$lookup(key); + if (res !== undefined) { + return res; + } else { + throw new Sk.builtin.KeyError(Sk.misceval.objectRepr(key)); + } + }, + sq$contains: function (key) { + return this.mp$lookup(key) !== undefined; + }, + sq$length: function () { + return this.get$size(); + }, + tp$iter: function () { + return new Sk.builtin.dict_iter_(this); + }, + }, + methods: { + get: Sk.builtin.dict.prototype.get.d$def, // just use the descriptor defn for get + keys: Sk.builtin.dict.prototype.keys.d$def, + items: Sk.builtin.dict.prototype.items.d$def, + values: Sk.builtin.dict.prototype.values.d$def, + }, + proto: { + mp$lookup: function (key) { + if (Sk.builtin.checkString(key)) { + return this.mapping[key.$jsstr()]; + } else { + return undefined; + } + }, + sk$asarray: function () { + return Object.keys(this.mapping).map((key) => new Sk.builtin.str(key)); + }, + get$size: function () { + // useful for using dict key iterators + return this.size; + }, + }, + flags: { + sk$acceptable_as_base_class: false, + }, +}); + +Sk.exportSymbol("Sk.builtin.mappingproxy", Sk.builtin.mappingproxy); diff --git a/src/method.js b/src/method.js index 0cb1239486..548e2d4321 100644 --- a/src/method.js +++ b/src/method.js @@ -1,137 +1,92 @@ /** * @constructor * - * @param {Sk.builtin.func|Sk.builtin.method} func - * @param {Object} self - * @param {Sk.builtin.type|Sk.builtin.none} klass - * @param {boolean=} builtin - * - * co_varnames and co_name come from generated code, must access as dict. + * @param {Sk.builtin.func} func + * @param {Sk.builtin.object} self + * */ -Sk.builtin.method = function (func, self, klass, builtin) { - if (!(this instanceof Sk.builtin.method)) { - Sk.builtin.pyCheckArgsLen("method", arguments.length, 2, 3); - if (!Sk.builtin.checkCallable(func)) { - throw new Sk.builtin.TypeError("First argument must be callable"); - } - if (self.ob$type === undefined) { - throw new Sk.builtin.TypeError("Second argument must be object of known type"); - } - return new Sk.builtin.method(func, self, klass); - } - this.tp$name = func.tp$name; - this.im_func = func; - this.im_self = self || Sk.builtin.none.none$; - this.im_class = klass || Sk.builtin.none.none$; - this.im_builtin = builtin; - this["$d"] = { - im_func: func, - im_self: self, - im_class: klass - }; -}; - -Sk.exportSymbol("Sk.builtin.method", Sk.builtin.method); -Sk.abstr.setUpInheritance("instancemethod", Sk.builtin.method, Sk.builtin.object); - -Sk.builtin.method.prototype.tp$name = "method"; - -Sk.builtin.method.prototype.ob$eq = function (other) { - if (((this.im_self == Sk.builtin.none.none$) && (other.im_self != Sk.builtin.none.none$)) || ((other.im_self == Sk.builtin.none.none$) && (this.im_self != Sk.builtin.none.none$))) { - return false; - } - try { - return Sk.misceval.richCompareBool(this.im_self, other.im_self, "Eq", false) && (this.im_func == other.im_func); - } catch (x) { - return false; - } -}; - -Sk.builtin.method.prototype.ob$ne = function (other) { - return !(this.ob$eq(other)); -}; - -Sk.builtin.method.prototype.tp$hash = function () { - var selfhash, funchash; - - if (this.im_self == Sk.builtin.none.none$) { - selfhash = 0; - } else { - selfhash = Sk.builtin.asnum$(Sk.builtin.hash(this.im_self)); - } - funchash = Sk.builtin.asnum$(Sk.builtin.hash(this.im_func)); - - return new Sk.builtin.int_(selfhash + funchash); -}; - -Sk.builtin.method.prototype.tp$call = function (args, kw) { - // Sk.asserts.assert(this.im_func instanceof Sk.builtin.func); - - // 'args' and 'kw' get mucked around with heavily in applyOrSuspend(); - // changing it here is OK. - if (this.im_self !== Sk.builtin.none.none$) { - args.unshift(this.im_self); - } - - // if there is no first argument or - // if the first argument is not a subclass of the class this method belongs to we throw an error - // unless it's a builtin method, because they shouldn't have been __get__ and left in this unbound - // state. - if (this.im_self === Sk.builtin.none.none$) { - var getMessage = (function (reason) { - return "unbound method " + this.tp$name + "() must be called with " + Sk.abstr.typeName(this.im_class) + " instance as first argument (got " + reason + " instead)"; - }).bind(this); - - if (args.length > 0) { - if (this.im_class != Sk.builtin.none.none$ && !Sk.builtin.issubclass(args[0].ob$type, this.im_class) && !this.im_builtin) { - throw new Sk.builtin.TypeError(getMessage(Sk.abstr.typeName(args[0].ob$type) + " instance")); +Sk.builtin.method = Sk.abstr.buildNativeClass("method", { + constructor: function method(func, self) { + Sk.asserts.assert(this instanceof Sk.builtin.method, "bad call to method constructor, use 'new'"); + this.im_func = func; + this.im_self = self; + }, + slots: { + $r: function () { + const def_name = "?"; + const func = this.im_func; + const self = this.im_self; + return new Sk.builtin.str(""); + }, + tp$hash: function () { + const selfhash = Sk.builtin.asnum$(Sk.builtin.hash(this.im_self)); + const funchash = Sk.builtin.asnum$(Sk.builtin.hash(this.im_func)); + return new Sk.builtin.int_(selfhash + funchash); + }, + tp$call: function (args, kwargs) { + return this.im_func.tp$call([this.im_self, ...args], kwargs); + }, + tp$new: function (args, kwargs) { + Sk.abstr.checkNoKwargs("method", kwargs); + Sk.abstr.checkArgsLen("method", args, 2, 2); + const func = args[0]; + const self = args[1]; + if (!Sk.builtin.checkCallable(func)) { + throw new Sk.builtin.TypeError("first argument must be callable"); + } + if (Sk.builtin.checkNone(self)) { + throw new Sk.builtin.TypeError("self must not be None"); + } + return new Sk.builtin.method(func, self); + }, + tp$richcompare: function (other, op) { + if ((op != "Eq" && op != "NotEq") || !(other instanceof Sk.builtin.method)) { + return Sk.builtin.NotImplemented.NotImplemented$; + } + let eq; + try { + eq = Sk.misceval.richCompareBool(this.im_self, other.im_self, "Eq", false) && this.im_func == other.im_func; + } catch (x) { + eq = false; + } + if (op == "Eq") { + return eq; + } else { + return !eq; + } + }, + tp$descr_get: function (obj, obtype) { + return this; + }, + tp$getattr: function (pyName, canSuspend) { + const descr = Sk.abstr.lookupSpecial(this, pyName); // true means we should mangle this pyName + if (descr !== undefined) { + const f = descr.tp$descr_get; + if (f !== undefined) { + return f.call(descr, this, this.ob$type); + } else { + return descr; + } } - } else { - throw new Sk.builtin.TypeError(getMessage("nothing")); - } - } - - // A method call is just a call to this.im_func with 'self' on the beginning of the args. - // Do the necessary. - return this.im_func.tp$call(args, kw); -}; - -Sk.builtin.method.prototype.tp$descr_get = function (obj, objtype) { - Sk.asserts.assert(obj !== undefined && objtype !== undefined); - return new Sk.builtin.method(this, obj, objtype, this.im_builtin); -}; - -Sk.builtin.method.pythonFunctions = ["__get__"]; - -Sk.builtin.method.prototype.__get__ = function __get__(self, instance, owner) { - Sk.builtin.pyCheckArgsLen("__get__", arguments.length, 1, 2, false, true); - if (instance === Sk.builtin.none.none$ && owner === Sk.builtin.none.none$) { - throw new Sk.builtin.TypeError("__get__(None, None) is invalid"); - } - - // if the owner is specified it needs to be a a subclass of im_self - if (owner && owner !== Sk.builtin.none.none$) { - if (Sk.builtin.issubclass(owner, self.im_class)) { - return self.tp$descr_get(instance, owner); - } - - // if it's not we're not bound - return self; - } - - // use the original type to get a bound object - return self.tp$descr_get(instance, Sk.builtin.none.none$); -}; - -Sk.builtin.method.prototype["$r"] = function () { - if (this.im_builtin) { - return new Sk.builtin.str(""); - } - - if (this.im_self === Sk.builtin.none.none$) { - return new Sk.builtin.str(""); - } - - var owner = this.im_class !== Sk.builtin.none.none$ ? Sk.abstr.typeName(this.im_class) : "?"; - return new Sk.builtin.str(""); -}; + return this.im_func.tp$getattr(pyName, canSuspend); + }, + }, + getsets: { + __func__: { + $get: function () { + return this.im_func; + }, + }, + __self__: { + $get: function () { + return this.im_self; + }, + }, + __doc__: { + $get: function () { + return this.im_func.tp$getattr(Sk.builtin.str.$doc); + }, + }, + }, + flags: {sk$suitable_as_base_class: false}, +}); diff --git a/src/misceval.js b/src/misceval.js index 9213a547cc..b66193c78e 100644 --- a/src/misceval.js +++ b/src/misceval.js @@ -1,8 +1,14 @@ /** * @namespace Sk.misceval * + * @description + * Various function protocols that include suspension aware options + * As well as handling some common pyObject operations to Javascript + * */ Sk.misceval = {}; +const JSBI = require("jsbi"); +/** @typedef {Sk.builtin.object}*/ var pyObject; /* Suspension object format: @@ -15,17 +21,20 @@ Sk.misceval = {}; */ /** - * + * @description * Hi kids lets make a suspension... + * * @constructor - * @param{function(?)=} resume A function to be called on resume. child is resumed first and its return value is passed to this function. - * @param{Object=} child A child suspension. 'optional' will be copied from here if supplied. - * @param{Object=} data Data attached to this suspension. Will be copied from child if not supplied. + * @param {function(?)=} resume A function to be called on resume. child is resumed first and its return value is passed to this function. + * @param {Object=} child A child suspension. 'optional' will be copied from here if supplied. + * @param {Object=} data Data attached to this suspension. Will be copied from child if not supplied. */ Sk.misceval.Suspension = function Suspension(resume, child, data) { this.$isSuspension = true; if (resume !== undefined && child !== undefined) { - this.resume = function() { return resume(child.resume()); }; + this.resume = function () { + return resume(child.resume()); + }; } this.child = child; this.optional = child !== undefined && child.optional; @@ -38,11 +47,11 @@ Sk.misceval.Suspension = function Suspension(resume, child, data) { Sk.exportSymbol("Sk.misceval.Suspension", Sk.misceval.Suspension); /** - * + * @description * Well this seems pretty obvious by the name what it should do.. * - * @param{Sk.misceval.Suspension} susp - * @param{string=} message + * @param {Sk.misceval.Suspension} susp + * @param {string=} message */ Sk.misceval.retryOptionalSuspensionOrThrow = function (susp, message) { while (susp instanceof Sk.misceval.Suspension) { @@ -56,109 +65,96 @@ Sk.misceval.retryOptionalSuspensionOrThrow = function (susp, message) { Sk.exportSymbol("Sk.misceval.retryOptionalSuspensionOrThrow", Sk.misceval.retryOptionalSuspensionOrThrow); /** + * @description * Check if the given object is valid to use as an index. Only ints, or if the object has an `__index__` method. - * @param o + * + * @param {pyObject} o - typically an {@link Sk.builtin.int_} legacy code might use a js number * @returns {boolean} */ Sk.misceval.isIndex = function (o) { - if (Sk.builtin.checkInt(o)) { - return true; + return o.nb$index !== undefined || typeof o === "number"; +}; +Sk.exportSymbol("Sk.misceval.isIndex", Sk.misceval.isIndex); + +/** + * @function + * + * @param {pyObject|number} obj - typically an {@link Sk.builtin.int_} legacy code might use a js number + * @param {string=} msg - an optional message when throwing the TypeError + * @throws {Sk.builtin.TypeError} + * + * @description + * requires a pyObject - returns a string or integer depending on the size. + * throws a generic error that the object cannot be interpreted as an index + * - converts the `Sk.builtin.int_` + * - if the number is too large to be safe returns a string + * @returns {number|string} + */ +Sk.misceval.asIndexOrThrow = function (obj, msg) { + let res; + if (obj.constructor === Sk.builtin.int_) { + // the fast case + res = obj.v; + } else if (typeof obj === "number") { + return obj; + } else if (obj.nb$index) { + res = obj.nb$index().v; + } else { + msg = msg || "'" + Sk.abstr.typeName(obj) + "' object cannot be interpreted as an index"; + throw new Sk.builtin.TypeError(msg); } - if (Sk.abstr.lookupSpecial(o, Sk.builtin.str.$index)) { - return true; + if (typeof res === "number") { + return res; } - return false; + return res.toString(); // then we definitely have a JSBI.BigInt so return it as a string. }; -Sk.exportSymbol("Sk.misceval.isIndex", Sk.misceval.isIndex); -Sk.misceval.asIndex = function (o) { - var idxfn, ret; - if (!Sk.misceval.isIndex(o)) { - return undefined; - } - if (o === null) { +Sk.misceval.asIndex = function (o) { + if (o === null || o === undefined) { return undefined; } - if (o === true) { - return 1; - } - if (o === false) { - return 0; - } if (typeof o === "number") { return o; } + let res; if (o.constructor === Sk.builtin.int_) { - return o.v; - } - if (o.constructor === Sk.builtin.lng) { - return o.tp$index(); + res = o.v; + } else if (o.nb$index) { + res = o.nb$index().v; // this slot will check the return value is an int. } - if (o.constructor === Sk.builtin.bool) { - return Sk.builtin.asnum$(o); - } - idxfn = Sk.abstr.lookupSpecial(o, Sk.builtin.str.$index); - if (idxfn) { - ret = Sk.misceval.callsimArray(idxfn, [o]); - if (!Sk.builtin.checkInt(ret)) { - throw new Sk.builtin.TypeError("__index__ returned non-(int,long) (type " + - Sk.abstr.typeName(ret) + ")"); - } - return Sk.builtin.asnum$(ret); + if (typeof res === "number") { + return res; + } else if (res instanceof JSBI) { + return res.toString(); } - Sk.asserts.fail("todo asIndex;"); + return res; }; /** * return u[v:w] + * @ignore */ Sk.misceval.applySlice = function (u, v, w, canSuspend) { - var ihigh; - var ilow; - if (u.sq$slice && Sk.misceval.isIndex(v) && Sk.misceval.isIndex(w)) { - ilow = Sk.misceval.asIndex(v); - if (ilow === undefined) { - ilow = 0; - } - ihigh = Sk.misceval.asIndex(w); - if (ihigh === undefined) { - ihigh = 1e100; - } - return Sk.abstr.sequenceGetSlice(u, ilow, ihigh); - } return Sk.abstr.objectGetItem(u, new Sk.builtin.slice(v, w, null), canSuspend); }; Sk.exportSymbol("Sk.misceval.applySlice", Sk.misceval.applySlice); /** * u[v:w] = x + * @ignore */ Sk.misceval.assignSlice = function (u, v, w, x, canSuspend) { - var slice; - var ihigh; - var ilow; - if (u.sq$ass_slice && Sk.misceval.isIndex(v) && Sk.misceval.isIndex(w)) { - ilow = Sk.misceval.asIndex(v) || 0; - ihigh = Sk.misceval.asIndex(w) || 1e100; - if (x === null) { - Sk.abstr.sequenceDelSlice(u, ilow, ihigh); - } else { - Sk.abstr.sequenceSetSlice(u, ilow, ihigh, x); - } + const slice = new Sk.builtin.slice(v, w); + if (x === null) { + return Sk.abstr.objectDelItem(u, slice); } else { - slice = new Sk.builtin.slice(v, w); - if (x === null) { - return Sk.abstr.objectDelItem(u, slice); - } else { - return Sk.abstr.objectSetItem(u, slice, x, canSuspend); - } + return Sk.abstr.objectSetItem(u, slice, x, canSuspend); } }; Sk.exportSymbol("Sk.misceval.assignSlice", Sk.misceval.assignSlice); /** - * Used by min() and max() to get an array from arbitrary input. * Note that this does no validation, just coercion. */ Sk.misceval.arrayFromArguments = function (args) { @@ -184,8 +180,7 @@ Sk.misceval.arrayFromArguments = function (args) { } else if (Sk.builtin.checkIterable(arg)) { // handle arbitrary iterable (strings, generators, etc.) res = []; - for (it = Sk.abstr.iter(arg), i = it.tp$iternext(); - i !== undefined; i = it.tp$iternext()) { + for (it = Sk.abstr.iter(arg), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { res.push(i); } return res; @@ -197,84 +192,86 @@ Sk.exportSymbol("Sk.misceval.arrayFromArguments", Sk.misceval.arrayFromArguments /** * for reversed comparison: Gt -> Lt, etc. + * @ignore */ Sk.misceval.swappedOp_ = { - "Eq" : "Eq", - "NotEq": "NotEq", - "Lt" : "GtE", - "LtE" : "Gt", - "Gt" : "LtE", - "GtE" : "Lt", - "Is" : "IsNot", - "IsNot": "Is", - "In_" : "NotIn", - "NotIn": "In_" + Eq: "Eq", + NotEq: "NotEq", + Lt: "GtE", + LtE: "Gt", + Gt: "LtE", + GtE: "Lt", + Is: "IsNot", + IsNot: "Is", + In_: "NotIn", + NotIn: "In_", +}; + +Sk.misceval.opSymbols = { + Eq: "==", + NotEq: "!=", + Lt: "<", + LtE: "<=", + Gt: ">", + GtE: ">=", + Is: "is", + IsNot: "is not", + In_: "in", + NotIn: "not in", }; /** -* @param{*} v -* @param{*} w -* @param{string} op -* @param{boolean=} canSuspend + * @function + * + * @param {pyObject} v + * @param {pyObject} w + * @param {string} op - `Eq`, `NotEq`, `Lt`, `LtE`, `Gt`, `GtE`, `Is`, `IsNot`, `In_`, `NotIn` + * @param {boolean=} canSuspend + * + * @returns {boolean} + * + * @todo This implementation overrides the return value from a user defined dunder method since it returns a boolean + * whereas Python will return the user defined return value. + * + * @throws {Sk.builtin.TypeError} */ Sk.misceval.richCompareBool = function (v, w, op, canSuspend) { // v and w must be Python objects. will return Javascript true or false for internal use only // if you want to return a value from richCompareBool to Python you must wrap as Sk.builtin.bool first - var wname, - vname, - ret, - swapped_method, - method, + Sk.asserts.assert(v.sk$object && w.sk$object, "JS object passed to richCompareBool"); + var ret, swapped_shortcut, - shortcut, - v_has_shortcut, - w_has_shortcut, - op2shortcut, - vcmp, - wcmp, - w_seq_type, - w_num_type, - v_seq_type, - v_num_type, - sequence_types, - numeric_types, - w_type, - v_type; - - Sk.asserts.assert((v !== null) && (v !== undefined), "passed null or undefined parameter to Sk.misceval.richCompareBool"); - Sk.asserts.assert((w !== null) && (w !== undefined), "passed null or undefined parameter to Sk.misceval.richCompareBool"); - - v_type = new Sk.builtin.type(v); - w_type = new Sk.builtin.type(w); - - // Python has specific rules when comparing two different builtin types + shortcut; + + const v_type = v.ob$type; + const w_type = w.ob$type; + + // Python 2 has specific rules when comparing two different builtin types // currently, this code will execute even if the objects are not builtin types // but will fall through and not return anything in this section - if ((v_type !== w_type) && - (op === "GtE" || op === "Gt" || op === "LtE" || op === "Lt")) { + if (!Sk.__future__.python3 && v_type !== w_type && (op === "GtE" || op === "Gt" || op === "LtE" || op === "Lt")) { // note: sets are omitted here because they can only be compared to other sets - numeric_types = [Sk.builtin.float_.prototype.ob$type, - Sk.builtin.int_.prototype.ob$type, - Sk.builtin.lng.prototype.ob$type, - Sk.builtin.bool.prototype.ob$type]; - sequence_types = [Sk.builtin.dict.prototype.ob$type, - Sk.builtin.enumerate.prototype.ob$type, - Sk.builtin.filter_.prototype.ob$type, - Sk.builtin.list.prototype.ob$type, - Sk.builtin.map_.prototype.ob$type, - Sk.builtin.str.prototype.ob$type, - Sk.builtin.tuple.prototype.ob$type, - Sk.builtin.zip_.prototype.ob$type]; - - v_num_type = numeric_types.indexOf(v_type); - v_seq_type = sequence_types.indexOf(v_type); - w_num_type = numeric_types.indexOf(w_type); - w_seq_type = sequence_types.indexOf(w_type); + const numeric_types = [Sk.builtin.float_, Sk.builtin.int_, Sk.builtin.lng, Sk.builtin.bool]; + const sequence_types = [ + Sk.builtin.dict, + Sk.builtin.enumerate, + Sk.builtin.filter_, + Sk.builtin.list, + Sk.builtin.map_, + Sk.builtin.str, + Sk.builtin.tuple, + Sk.builtin.zip_, + ]; + + const v_num_type = numeric_types.indexOf(v_type); + const v_seq_type = sequence_types.indexOf(v_type); + const w_num_type = numeric_types.indexOf(w_type); + const w_seq_type = sequence_types.indexOf(w_type); // NoneTypes are considered less than any other type in Python // note: this only handles comparing NoneType with any non-NoneType. // Comparing NoneType with NoneType is handled further down. - if (v_type === Sk.builtin.none.prototype.ob$type) { + if (v === Sk.builtin.none.none$) { switch (op) { case "Lt": return true; @@ -287,7 +284,7 @@ Sk.misceval.richCompareBool = function (v, w, op, canSuspend) { } } - if (w_type === Sk.builtin.none.prototype.ob$type) { + if (w === Sk.builtin.none.none$) { switch (op) { case "Lt": return false; @@ -343,29 +340,34 @@ Sk.misceval.richCompareBool = function (v, w, op, canSuspend) { } } - // handle identity and membership comparisons if (op === "Is") { - if (v instanceof Sk.builtin.int_ && w instanceof Sk.builtin.int_) { - return v.numberCompare(w) === 0; - } else if (v instanceof Sk.builtin.float_ && w instanceof Sk.builtin.float_) { - return v.numberCompare(w) === 0; - } else if (v instanceof Sk.builtin.lng && w instanceof Sk.builtin.lng) { - return v.longCompare(w) === 0; + if (v_type === w_type) { + if (v === w) { + return true; + } else if (v_type === Sk.builtin.float_) { + return v.v - w.v === 0; + } else if (v_type === Sk.builtin.int_) { + if (typeof v.v === "number" && typeof v.v === "number") { + return v.v === w.v; + } + return JSBI.equal(JSBI.BigInt(v.v), JSBI.BigInt(w.v)); + } } - - return v === w; + return false; } if (op === "IsNot") { - if (v instanceof Sk.builtin.int_ && w instanceof Sk.builtin.int_) { - return v.numberCompare(w) !== 0; - } else if (v instanceof Sk.builtin.float_ && w instanceof Sk.builtin.float_) { - return v.numberCompare(w) !== 0; - }else if (v instanceof Sk.builtin.lng && w instanceof Sk.builtin.lng) { - return v.longCompare(w) !== 0; + if (v_type !== w_type) { + return true; + } else if (v_type === Sk.builtin.float_) { + return v.v - w.v !== 0; + } else if (v_type === Sk.builtin.int_) { + if (typeof v.v === "number" && typeof v.v === "number") { + return v.v !== w.v; + } + return JSBI.notEqual(JSBI.BigInt(v.v), JSBI.BigInt(w.v)); } - return v !== w; } @@ -373,212 +375,158 @@ Sk.misceval.richCompareBool = function (v, w, op, canSuspend) { return Sk.misceval.chain(Sk.abstr.sequenceContains(w, v, canSuspend), Sk.misceval.isTrue); } if (op === "NotIn") { - return Sk.misceval.chain( - Sk.abstr.sequenceContains(w, v, canSuspend), - function(x) { return !Sk.misceval.isTrue(x); } - ); + return Sk.misceval.chain(Sk.abstr.sequenceContains(w, v, canSuspend), function (x) { + return !Sk.misceval.isTrue(x); + }); } // Call Javascript shortcut method if exists for either object - op2shortcut = { - "Eq" : "ob$eq", - "NotEq": "ob$ne", - "Gt" : "ob$gt", - "GtE" : "ob$ge", - "Lt" : "ob$lt", - "LtE" : "ob$le" + var op2shortcut = { + Eq: "ob$eq", + NotEq: "ob$ne", + Gt: "ob$gt", + GtE: "ob$ge", + Lt: "ob$lt", + LtE: "ob$le", }; + // tp richcompare and all respective shortcuts guaranteed because we inherit from object shortcut = op2shortcut[op]; - v_has_shortcut = v.constructor.prototype.hasOwnProperty(shortcut); - if (v_has_shortcut) { - if ((ret = v[shortcut](w)) !== Sk.builtin.NotImplemented.NotImplemented$) { - return Sk.misceval.isTrue(ret); - } + if ((ret = v[shortcut](w)) !== Sk.builtin.NotImplemented.NotImplemented$) { + return Sk.misceval.isTrue(ret); + // techincally this is not correct along with the compile code + // richcompare slots could return any pyObject ToDo - would require changing compile code } swapped_shortcut = op2shortcut[Sk.misceval.swappedOp_[op]]; - w_has_shortcut = w.constructor.prototype.hasOwnProperty(swapped_shortcut); - if (w_has_shortcut) { - - if ((ret = w[swapped_shortcut](v)) !== Sk.builtin.NotImplemented.NotImplemented$) { - return Sk.misceval.isTrue(ret); - } - } - - // use comparison methods if they are given for either object - if (v.tp$richcompare && (ret = v.tp$richcompare(w, op)) !== undefined) { - if (ret != Sk.builtin.NotImplemented.NotImplemented$) { - return Sk.misceval.isTrue(ret); - } - } - - if (w.tp$richcompare && (ret = w.tp$richcompare(v, Sk.misceval.swappedOp_[op])) !== undefined) { - if (ret != Sk.builtin.NotImplemented.NotImplemented$) { - return Sk.misceval.isTrue(ret); - } - } - - - // depending on the op, try left:op:right, and if not, then - // right:reversed-top:left - - method = Sk.abstr.lookupSpecial(v, Sk.misceval.op2method_[op]); - if (method && !v_has_shortcut) { - ret = Sk.misceval.callsimArray(method, [v, w]); - if (ret != Sk.builtin.NotImplemented.NotImplemented$) { - return Sk.misceval.isTrue(ret); - } - } - - swapped_method = Sk.abstr.lookupSpecial(w, Sk.misceval.op2method_[Sk.misceval.swappedOp_[op]]); - if (swapped_method && !w_has_shortcut) { - ret = Sk.misceval.callsimArray(swapped_method, [w, v]); - if (ret != Sk.builtin.NotImplemented.NotImplemented$) { - return Sk.misceval.isTrue(ret); - } - } - - vcmp = Sk.abstr.lookupSpecial(v, Sk.builtin.str.$cmp); - if (vcmp) { - try { - ret = Sk.misceval.callsimArray(vcmp, [v, w]); - if (Sk.builtin.checkNumber(ret)) { - ret = Sk.builtin.asnum$(ret); - if (op === "Eq") { - return ret === 0; - } else if (op === "NotEq") { - return ret !== 0; - } else if (op === "Lt") { - return ret < 0; - } else if (op === "Gt") { - return ret > 0; - } else if (op === "LtE") { - return ret <= 0; - } else if (op === "GtE") { - return ret >= 0; + if ((ret = w[swapped_shortcut](v)) !== Sk.builtin.NotImplemented.NotImplemented$) { + return Sk.misceval.isTrue(ret); + } + + if (!Sk.__future__.python3) { + const vcmp = Sk.abstr.lookupSpecial(v, Sk.builtin.str.$cmp); + if (vcmp) { + try { + ret = Sk.misceval.callsimArray(vcmp, [v, w]); + if (Sk.builtin.checkNumber(ret)) { + ret = Sk.builtin.asnum$(ret); + if (op === "Eq") { + return ret === 0; + } else if (op === "NotEq") { + return ret !== 0; + } else if (op === "Lt") { + return ret < 0; + } else if (op === "Gt") { + return ret > 0; + } else if (op === "LtE") { + return ret <= 0; + } else if (op === "GtE") { + return ret >= 0; + } } - } - if (ret !== Sk.builtin.NotImplemented.NotImplemented$) { + if (ret !== Sk.builtin.NotImplemented.NotImplemented$) { + throw new Sk.builtin.TypeError("comparison did not return an int"); + } + } catch (e) { throw new Sk.builtin.TypeError("comparison did not return an int"); } - } catch (e) { - throw new Sk.builtin.TypeError("comparison did not return an int"); } - } - - wcmp = Sk.abstr.lookupSpecial(w, Sk.builtin.str.$cmp); - if (wcmp) { - // note, flipped on return value and call - try { - ret = Sk.misceval.callsimArray(wcmp, [w, v]); - if (Sk.builtin.checkNumber(ret)) { - ret = Sk.builtin.asnum$(ret); - if (op === "Eq") { - return ret === 0; - } else if (op === "NotEq") { - return ret !== 0; - } else if (op === "Lt") { - return ret > 0; - } else if (op === "Gt") { - return ret < 0; - } else if (op === "LtE") { - return ret >= 0; - } else if (op === "GtE") { - return ret <= 0; + const wcmp = Sk.abstr.lookupSpecial(w, Sk.builtin.str.$cmp); + if (wcmp) { + // note, flipped on return value and call + try { + ret = Sk.misceval.callsimArray(wcmp, [w, v]); + if (Sk.builtin.checkNumber(ret)) { + ret = Sk.builtin.asnum$(ret); + if (op === "Eq") { + return ret === 0; + } else if (op === "NotEq") { + return ret !== 0; + } else if (op === "Lt") { + return ret > 0; + } else if (op === "Gt") { + return ret < 0; + } else if (op === "LtE") { + return ret >= 0; + } else if (op === "GtE") { + return ret <= 0; + } } - } - if (ret !== Sk.builtin.NotImplemented.NotImplemented$) { + if (ret !== Sk.builtin.NotImplemented.NotImplemented$) { + throw new Sk.builtin.TypeError("comparison did not return an int"); + } + } catch (e) { throw new Sk.builtin.TypeError("comparison did not return an int"); } - } catch (e) { - throw new Sk.builtin.TypeError("comparison did not return an int"); } - } - - // handle special cases for comparing None with None or Bool with Bool - if (((v instanceof Sk.builtin.none) && (w instanceof Sk.builtin.none)) || - ((v instanceof Sk.builtin.bool) && (w instanceof Sk.builtin.bool))) { - // Javascript happens to return the same values when comparing null - // with null or true/false with true/false as Python does when - // comparing None with None or True/False with True/False + if ((v instanceof Sk.builtin.none && w instanceof Sk.builtin.none)) { + // Javascript happens to return the same values when comparing null + // with null or true/false with true/false as Python does when + // comparing None with None or True/False with True/False - if (op === "Eq") { - return v.v === w.v; - } - if (op === "NotEq") { - return v.v !== w.v; - } - if (op === "Gt") { - return v.v > w.v; - } - if (op === "GtE") { - return v.v >= w.v; - } - if (op === "Lt") { - return v.v < w.v; - } - if (op === "LtE") { - return v.v <= w.v; + if (op === "Eq") { + return v.v === w.v; + } + if (op === "NotEq") { + return v.v !== w.v; + } + if (op === "Gt") { + return v.v > w.v; + } + if (op === "GtE") { + return v.v >= w.v; + } + if (op === "Lt") { + return v.v < w.v; + } + if (op === "LtE") { + return v.v <= w.v; + } } } - // handle equality comparisons for any remaining objects if (op === "Eq") { - if ((v instanceof Sk.builtin.str) && (w instanceof Sk.builtin.str)) { - return v.v === w.v; - } return v === w; } if (op === "NotEq") { - if ((v instanceof Sk.builtin.str) && (w instanceof Sk.builtin.str)) { - return v.v !== w.v; - } return v !== w; } - vname = Sk.abstr.typeName(v); - wname = Sk.abstr.typeName(w); - throw new Sk.builtin.TypeError("'" + "OPERATION SYMBOL" + "' not supported between instances of '" + vname + "' and '" + wname + "'"); + const vname = Sk.abstr.typeName(v); + const wname = Sk.abstr.typeName(w); + throw new Sk.builtin.TypeError("'" + Sk.misceval.opSymbols[op] + "' not supported between instances of '" + vname + "' and '" + wname + "'"); //throw new Sk.builtin.ValueError("don't know how to compare '" + vname + "' and '" + wname + "'"); }; Sk.exportSymbol("Sk.misceval.richCompareBool", Sk.misceval.richCompareBool); -Sk.misceval.objectRepr = function (v) { - Sk.asserts.assert(v !== undefined, "trying to repr undefined"); - if ((v === null) || (v instanceof Sk.builtin.none)) { - return new Sk.builtin.str("None"); - } else if (v === true) { - // todo; these should be consts - return new Sk.builtin.str("True"); - } else if (v === false) { - return new Sk.builtin.str("False"); - } else if (typeof v === "number") { - return new Sk.builtin.str("" + v); - } else if (typeof v === "string") { - return new Sk.builtin.str(v); - } else if (!v["$r"]) { - if (v.tp$name) { - return new Sk.builtin.str("<" + v.tp$name + " object>"); - } else { - return new Sk.builtin.str(""); - } - } else if (v.constructor === Sk.builtin.float_) { - if (v.v === Infinity) { - return new Sk.builtin.str("inf"); - } else if (v.v === -Infinity) { - return new Sk.builtin.str("-inf"); - } else { - return v["$r"](); - } - } else if (v.constructor === Sk.builtin.int_) { - return v["$r"](); + +/** + * @function + * @description + * calls the __repr__ of a pyObject or returns `` if a JS object was passed + * @param {*} obj + * @returns {string} + * + */ +Sk.misceval.objectRepr = function (obj) { + Sk.asserts.assert(obj !== undefined, "trying to repr undefined"); + if (obj !== null && obj.$r) { + return obj.$r().v; } else { - return v["$r"](); + try { + // str goes through the common javascript cases or throws a TypeError; + return new Sk.builtin.str(obj).v; + } catch (e) { + if (e instanceof Sk.builtin.TypeError) { + return ""; + } else { + throw e; + } + } } }; Sk.exportSymbol("Sk.misceval.objectRepr", Sk.misceval.objectRepr); @@ -594,6 +542,14 @@ Sk.misceval.opAllowsEquality = function (op) { }; Sk.exportSymbol("Sk.misceval.opAllowsEquality", Sk.misceval.opAllowsEquality); + +/** + * @function + * @description + * Decides whether a pyObject is True or not + * @returns {boolean} + * @param {*} x + */ Sk.misceval.isTrue = function (x) { var ret; if (x === true) { @@ -605,58 +561,27 @@ Sk.misceval.isTrue = function (x) { if (x === null) { return false; } - if (x.constructor === Sk.builtin.none) { + if (x === undefined) { return false; } - - if (x.constructor === Sk.builtin.NotImplemented) { - return false; - } - if (x.constructor === Sk.builtin.bool) { - return x.v; - } - if (typeof x === "number") { - return x !== 0; - } - if (x instanceof Sk.builtin.lng) { - return x.nb$nonzero(); - } - if (x.constructor === Sk.builtin.int_) { return x.v !== 0; } - if (x.constructor === Sk.builtin.float_) { - return x.v !== 0; + if (x === Sk.builtin.none.none$) { + return false; } - if (Sk.__future__.python3) { - if (x.__bool__) { - ret = Sk.misceval.callsimArray(x.__bool__, [x]); - if (!(ret instanceof Sk.builtin.bool)) { - throw new Sk.builtin.TypeError("__bool__ should return bool, returned " + Sk.abstr.typeName(ret)); - } - return ret.v; - } - } else { - if (x.__nonzero__) { - ret = Sk.misceval.callsimArray(x.__nonzero__, [x]); - if (!Sk.builtin.checkInt(ret)) { - throw new Sk.builtin.TypeError("__nonzero__ should return an int"); - } - return Sk.builtin.asnum$(ret) !== 0; - } + if (x === Sk.builtin.NotImplemented.NotImplemented$) { + return false; } - if (x.__len__) { - ret = Sk.misceval.callsimArray(x.__len__, [x]); - if (!Sk.builtin.checkInt(ret)) { - throw new Sk.builtin.TypeError("__len__ should return an int"); - } - return Sk.builtin.asnum$(ret) !== 0; + if (typeof x === "number") { + return x !== 0; } - if (x.mp$length) { - return Sk.builtin.asnum$(x.mp$length()) !== 0; + if (x.nb$bool) { + return x.nb$bool(); } if (x.sq$length) { - return Sk.builtin.asnum$(x.sq$length()) !== 0; + ret = x.sq$length(); // the slot wrapper takes care of the error message + return Sk.builtin.asnum$(ret) !== 0; } return true; }; @@ -679,38 +604,39 @@ Sk.misceval.print_ = function (x) { s = new Sk.builtin.str(x); - return Sk.misceval.chain(Sk.importModule("sys", false, true), function(sys) { - return Sk.misceval.apply(sys["$d"]["stdout"]["write"], undefined, undefined, undefined, [sys["$d"]["stdout"], s]); - }, function () { - if (s.v.length === 0 || !isspace(s.v[s.v.length - 1]) || s.v[s.v.length - 1] === " ") { - Sk.misceval.softspace_ = true; + return Sk.misceval.chain( + Sk.importModule("sys", false, true), + function (sys) { + return Sk.misceval.apply(sys["$d"]["stdout"]["write"], undefined, undefined, undefined, [sys["$d"]["stdout"], s]); + }, + function () { + if (s.v.length === 0 || !isspace(s.v[s.v.length - 1]) || s.v[s.v.length - 1] === " ") { + Sk.misceval.softspace_ = true; + } } - }); + ); }; Sk.exportSymbol("Sk.misceval.print_", Sk.misceval.print_); /** + * @function + * @description + * Get a python object from a given namespace * @param {string} name * @param {Object=} other generally globals + * @example + * Sk.misceval.loadname("foo", Sk.globals); */ Sk.misceval.loadname = function (name, other) { - var builtinModule, bi; + var bi; var v = other[name]; if (v !== undefined) { - if (typeof v === "function" && v["$d"] === undefined && v["tp$name"] === undefined) { - return v(); - } + // if (typeof v === "function" && v["$d"] === undefined && v["tp$name"] === undefined) { + // return v(); + // } return v; } - // Check if we've overridden the builtin via the builtin's module - if (other["__builtins__"] !== undefined) { - builtinModule = other["__builtins__"].mp$lookup(new Sk.builtin.str(name)); - if (builtinModule !== undefined) { - return builtinModule; - } - } - bi = Sk.builtins[name]; if (bi !== undefined) { return bi; @@ -792,7 +718,8 @@ Sk.exportSymbol("Sk.misceval.loadname", Sk.misceval.loadname); * @param {...*} args stuff to pass it * * - * TODO I think all the above is out of date. + * @todo I think all the above is out of date. + * @ignore */ Sk.misceval.call = function (func, kwdict, varargseq, kws, args) { args = Array.prototype.slice.call(arguments, 4); @@ -810,7 +737,7 @@ Sk.exportSymbol("Sk.misceval.call", Sk.misceval.call); * @param {...*} args stuff to pass it * * - * TODO I think all the above is out of date. + * @todo I think all the above is out of date. */ Sk.misceval.callAsync = function (suspensionHandlers, func, kwdict, varargseq, kws, args) { @@ -820,7 +747,6 @@ Sk.misceval.callAsync = function (suspensionHandlers, func, kwdict, varargseq, k }; Sk.exportSymbol("Sk.misceval.callAsync", Sk.misceval.callAsync); - Sk.misceval.callOrSuspend = function (func, kwdict, varargseq, kws, args) { args = Array.prototype.slice.call(arguments, 4); // todo; possibly inline apply to avoid extra stack frame creation @@ -831,6 +757,7 @@ Sk.exportSymbol("Sk.misceval.callOrSuspend", Sk.misceval.callOrSuspend); /** * @param {Object} func the thing to call * @param {...*} args stuff to pass it + * @ignore */ Sk.misceval.callsim = function (func, args) { args = Array.prototype.slice.call(arguments, 1); @@ -839,15 +766,18 @@ Sk.misceval.callsim = function (func, args) { Sk.exportSymbol("Sk.misceval.callsim", Sk.misceval.callsim); /** - * @param {Object} func the thing to call + * @param {Object=} func the thing to call * @param {Array=} args an array of arguments to pass to the func + * @param {Array=} kws an array of string/pyObject pairs to pass to the func as kwargs * - * Does the same thing as callsim without expensive call to Array.slice. + * @description + * Call a pyObject - if the object is not callable will throw a TypeError * Requires args to be a Javascript array. + * kws should be an array of string/pyObject pairs as key/values */ -Sk.misceval.callsimArray = function(func, args) { +Sk.misceval.callsimArray = function (func, args, kws) { var argarray = args ? args : []; - return Sk.misceval.apply(func, undefined, undefined, undefined, argarray); + return Sk.misceval.apply(func, undefined, undefined, kws, argarray); }; Sk.exportSymbol("Sk.misceval.callsimArray", Sk.misceval.callsimArray); @@ -862,10 +792,11 @@ Sk.misceval.callsimAsync = function (suspensionHandlers, func, args) { }; Sk.exportSymbol("Sk.misceval.callsimAsync", Sk.misceval.callsimAsync); - /** * @param {Object} func the thing to call * @param {...*} args stuff to pass it + * @deprecated + * @ignore */ Sk.misceval.callsimOrSuspend = function (func, args) { args = Array.prototype.slice.call(arguments, 1); @@ -874,20 +805,34 @@ Sk.misceval.callsimOrSuspend = function (func, args) { Sk.exportSymbol("Sk.misceval.callsimOrSuspend", Sk.misceval.callsimOrSuspend); /** - * @param {Object} func the thing to call + * @description + * Does the same thing as callsimOrSuspend without expensive call to + * Array.slice. Requires args+kws to be Javascript arrays. + * The preferred method for calling a pyObject. + * + * @param {Object=} func the thing to call * @param {Array=} args an array of arguments to pass to the func + * @param {Array=} kws an array of keyword arguments to pass to the func * - * Does the same thing as callsimOrSuspend without expensive call to - * Array.slice. Requires args to be a Javascript array. */ -Sk.misceval.callsimOrSuspendArray = function (func, args) { - var argarray = args ? args : []; - return Sk.misceval.applyOrSuspend(func, undefined, undefined, undefined, argarray); +Sk.misceval.callsimOrSuspendArray = function (func, args, kws) { + if (!args) { + args = []; + } + if (func.tp$call) { + return func.tp$call(args, kws); + } else { + // Slow path handles things like calling native JS fns + // (perhaps we should stop supporting that), and weird + // detection of the __call__ method (everything should use tp$call) + return Sk.misceval.applyOrSuspend(func, undefined, undefined, kws, args); + } }; Sk.exportSymbol("Sk.misceval.callsimOrSuspendArray", Sk.misceval.callsimOrSuspendArray); /** * Wrap Sk.misceval.applyOrSuspend, but throw an error if we suspend + * @ignore */ Sk.misceval.apply = function (func, kwdict, varargseq, kws, args) { var r = Sk.misceval.applyOrSuspend(func, kwdict, varargseq, kws, args); @@ -932,15 +877,15 @@ Sk.exportSymbol("Sk.misceval.apply", Sk.misceval.apply); * @param{function()} suspendablefn returns either a result or a Suspension * @param{Object=} suspHandlers an object map of suspension handlers */ -Sk.misceval.asyncToPromise = function(suspendablefn, suspHandlers) { - return new Promise(function(resolve, reject) { +Sk.misceval.asyncToPromise = function (suspendablefn, suspHandlers) { + return new Promise(function (resolve, reject) { try { var r = suspendablefn(); - (function handleResponse (r) { + (function handleResponse(r) { try { // jsh*nt insists these be defined outside the loop - var resume = function() { + var resume = function () { try { handleResponse(r.resume()); } catch (e) { @@ -951,7 +896,7 @@ Sk.misceval.asyncToPromise = function(suspendablefn, suspHandlers) { try { r.data["result"] = x; resume(); - } catch(e) { + } catch (e) { reject(e); } }; @@ -959,14 +904,12 @@ Sk.misceval.asyncToPromise = function(suspendablefn, suspHandlers) { try { r.data["error"] = e; resume(); - } catch(ex) { + } catch (ex) { reject(ex); } }; - while (r instanceof Sk.misceval.Suspension) { - var handler = suspHandlers && (suspHandlers[r.data["type"]] || suspHandlers["*"]); if (handler) { @@ -980,36 +923,31 @@ Sk.misceval.asyncToPromise = function(suspendablefn, suspHandlers) { if (r.data["type"] == "Sk.promise") { r.data["promise"].then(resumeWithData, resumeWithError); return; - } else if (r.data["type"] == "Sk.yield") { // Assumes all yields are optional, as Sk.setTimeout might // not be able to yield. //Sk.setTimeout(resume, 0); Sk.global["setImmediate"](resume); return; - } else if (r.data["type"] == "Sk.delay") { //Sk.setTimeout(resume, 1); Sk.global["setImmediate"](resume); return; - } else if (r.optional) { // Unhandled optional suspensions just get // resumed immediately, and we go around the loop again. r = r.resume(); - } else { // Unhandled, non-optional suspension. - throw new Sk.builtin.SuspensionError("Unhandled non-optional suspension of type '"+r.data["type"]+"'"); + throw new Sk.builtin.SuspensionError("Unhandled non-optional suspension of type '" + r.data["type"] + "'"); } } resolve(r); - } catch(e) { + } catch (e) { reject(e); } })(r); - } catch (e) { reject(e); } @@ -1018,7 +956,7 @@ Sk.misceval.asyncToPromise = function(suspendablefn, suspHandlers) { Sk.exportSymbol("Sk.misceval.asyncToPromise", Sk.misceval.asyncToPromise); Sk.misceval.applyAsync = function (suspHandlers, func, kwdict, varargseq, kws, args) { - return Sk.misceval.asyncToPromise(function() { + return Sk.misceval.asyncToPromise(function () { return Sk.misceval.applyOrSuspend(func, kwdict, varargseq, kws, args); }, suspHandlers); }; @@ -1047,13 +985,18 @@ Sk.exportSymbol("Sk.misceval.applyAsync", Sk.misceval.applyAsync); Sk.misceval.chain = function (initialValue, chainedFns) { // We try to minimise overhead when nothing suspends (the common case) - var i = 1, value = initialValue, j, fs; + var i = 1, + value = initialValue, + j, + fs; while (true) { if (i == arguments.length) { return value; } - if (value && value.$isSuspension) { break; } // oops, slow case + if (value && value.$isSuspension) { + break; + } // oops, slow case value = arguments[i](value); i++; } @@ -1066,7 +1009,7 @@ Sk.misceval.chain = function (initialValue, chainedFns) { fs = new Array(arguments.length - i); for (j = 0; j < arguments.length - i; j++) { - fs[j] = arguments[i+j]; + fs[j] = arguments[i + j]; } j = 0; @@ -1086,7 +1029,6 @@ Sk.misceval.chain = function (initialValue, chainedFns) { }; Sk.exportSymbol("Sk.misceval.chain", Sk.misceval.chain); - /** * Catch any exceptions thrown by a function, or by resuming any suspension it * returns. @@ -1103,13 +1045,15 @@ Sk.misceval.tryCatch = function (tryFn, catchFn) { try { r = tryFn(); - } catch(e) { + } catch (e) { return catchFn(e); } if (r instanceof Sk.misceval.Suspension) { var susp = new Sk.misceval.Suspension(undefined, r); - susp.resume = function() { return Sk.misceval.tryCatch(r.resume, catchFn); }; + susp.resume = function () { + return Sk.misceval.tryCatch(r.resume, catchFn); + }; return susp; } else { return r; @@ -1118,6 +1062,8 @@ Sk.misceval.tryCatch = function (tryFn, catchFn) { Sk.exportSymbol("Sk.misceval.tryCatch", Sk.misceval.tryCatch); /** + * @function + * @description * Perform a suspension-aware for-each on an iterator, without * blowing up the stack. * forFn() is called for each element in the iterator, with two @@ -1136,15 +1082,15 @@ Sk.exportSymbol("Sk.misceval.tryCatch", Sk.misceval.tryCatch); * iterFor() on infinite iterators. * * @param {*} iter - * @param {function(*,*=)} forFn + * @param {function(pyObject,*=)} forFn * @param {*=} initialValue */ Sk.misceval.iterFor = function (iter, forFn, initialValue) { var prevValue = initialValue; - var breakOrIterNext = function(r) { + var breakOrIterNext = function (r) { prevValue = r; - return (r instanceof Sk.misceval.Break) ? r : iter.tp$iternext(true); + return r instanceof Sk.misceval.Break ? r : iter.tp$iternext(true); }; return (function nextStep(i) { @@ -1157,16 +1103,48 @@ Sk.misceval.iterFor = function (iter, forFn, initialValue) { return i.brValue; } - i = Sk.misceval.chain( - forFn(i, prevValue), - breakOrIterNext - ); + i = Sk.misceval.chain(forFn(i, prevValue), breakOrIterNext); } return prevValue; })(iter.tp$iternext(true)); }; Sk.exportSymbol("Sk.misceval.iterFor", Sk.misceval.iterFor); +/** + * @function + * + * @description + * Convert a Python iterable into a javascript array + * + * @param {pyObject} iterable + * @param {boolean=} canSuspend - Can this funciton suspend + * + * @returns {!Array} + */ +Sk.misceval.arrayFromIterable = function (iterable, canSuspend) { + if (iterable === undefined) { + return []; + } + const hptype = iterable.hp$type || undefined; + if (hptype === undefined && iterable.sk$asarray !== undefined) { + return iterable.sk$asarray(); + } + const L = []; + const ret = Sk.misceval.iterFor(Sk.abstr.iter(iterable), (i) => { + L.push(i); + }); + if (ret === undefined) { + return L; + } else if (canSuspend) { + return Sk.misceval.chain(ret, () => { + return L; + }); + } + Sk.misceval.retryOptionalSuspensionOrThrow(ret); + return L; +}; +Sk.exportSymbol("Sk.misceval.arrayFromIterable", Sk.misceval.arrayFromIterable); + /** * A special value to return from an iterFor() function, * to abort the iteration. Optionally supply a value for iterFor() to return @@ -1175,7 +1153,7 @@ Sk.exportSymbol("Sk.misceval.iterFor", Sk.misceval.iterFor); * @constructor * @param {*=} brValue */ -Sk.misceval.Break = function(brValue) { +Sk.misceval.Break = function (brValue) { if (!(this instanceof Sk.misceval.Break)) { return new Sk.misceval.Break(brValue); } @@ -1187,12 +1165,14 @@ Sk.exportSymbol("Sk.misceval.Break", Sk.misceval.Break); /** * same as Sk.misceval.call except args is an actual array, rather than * varargs. + * @deprecated + * @ignore */ Sk.misceval.applyOrSuspend = function (func, kwdict, varargseq, kws, args) { var fcall; var it, i; - if (func === null || func instanceof Sk.builtin.none) { + if (func == null || func === Sk.builtin.none.none$) { throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(func) + "' object is not callable"); } @@ -1209,7 +1189,7 @@ Sk.misceval.applyOrSuspend = function (func, kwdict, varargseq, kws, args) { } if (kwdict) { - for (it = Sk.abstr.iter(kwdict), i = it.tp$iternext(); i!== undefined; i = it.tp$iternext()) { + for (it = Sk.abstr.iter(kwdict), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { if (!Sk.builtin.checkString(i)) { throw new Sk.builtin.TypeError("Function keywords must be strings"); } @@ -1237,10 +1217,10 @@ Sk.exportSymbol("Sk.misceval.applyOrSuspend", Sk.misceval.applyOrSuspend); /** * Do the boilerplate suspension stuff. */ -Sk.misceval.promiseToSuspension = function(promise) { +Sk.misceval.promiseToSuspension = function (promise) { var suspension = new Sk.misceval.Suspension(); - suspension.resume = function() { + suspension.resume = function () { if (suspension.data["error"]) { throw suspension.data["error"]; } @@ -1250,7 +1230,7 @@ Sk.misceval.promiseToSuspension = function(promise) { suspension.data = { type: "Sk.promise", - promise: promise + promise: promise, }; return suspension; @@ -1258,6 +1238,8 @@ Sk.misceval.promiseToSuspension = function(promise) { Sk.exportSymbol("Sk.misceval.promiseToSuspension", Sk.misceval.promiseToSuspension); /** + * @function + * @description * Constructs a class object given a code object representing the body * of the class, the name of the class, and the list of bases. * @@ -1287,7 +1269,10 @@ Sk.misceval.buildClass = function (globals, func, name, bases, cell) { // new Syntax would be different // file's __name__ is class's __module__ - locals.__module__ = globals["__name__"]; + if (globals["__name__"]) { + // some js modules haven't set their module name and we shouldn't set a dictionary value to be undefined that should be equivalent to deleting a value; + locals.__module__ = globals["__name__"]; + } var _name = new Sk.builtin.str(name); var _bases = new Sk.builtin.tuple(bases); var _locals = []; @@ -1310,15 +1295,15 @@ Sk.misceval.buildClass = function (globals, func, name, bases, cell) { }; Sk.exportSymbol("Sk.misceval.buildClass", Sk.misceval.buildClass); -Sk.misceval.handleTraceback = function(err,currLineNo,currColNo,currSource,filename, scopeName) { - if(err instanceof Sk.builtin.TimeLimitError){ - Sk.execStart=Date.now(); - Sk.execPaused=0; +Sk.misceval.handleTraceback = function (err, currLineNo, currColNo, currSource, filename, scopeName) { + if (err instanceof Sk.builtin.TimeoutError) { + Sk.execStart = Date.now(); + Sk.execPaused = 0; } - if(!(err instanceof Sk.builtin.BaseException)) { - err=new Sk.builtin.ExternalError(err); + if (!(err instanceof Sk.builtin.BaseException)) { + err = new Sk.builtin.ExternalError(err); } - Sk.err=err; + Sk.err = err; err.traceback.push({ lineno: currLineNo, colno: currColNo, @@ -1330,25 +1315,25 @@ Sk.misceval.handleTraceback = function(err,currLineNo,currColNo,currSource,filen }; Sk.exportSymbol("Sk.misceval.handleTraceback", Sk.misceval.handleTraceback); -Sk.misceval.startTimer = function() { +Sk.misceval.startTimer = function () { if (typeof Sk.execStart === "undefined") { Sk.execStart = Date.now(); - Sk.execPaused=0; + Sk.execPaused = 0; } }; Sk.exportSymbol("Sk.misceval.startTimer", Sk.misceval.startTimer); -Sk.misceval.pauseTimer = function() { - Sk.execPaused=Date.now(); +Sk.misceval.pauseTimer = function () { + Sk.execPaused = Date.now(); }; Sk.exportSymbol("Sk.misceval.pauseTimer", Sk.misceval.pauseTimer); -Sk.misceval.unpauseTimer = function() { - Sk.execPaused=Date.now()-Sk.execPaused; +Sk.misceval.unpauseTimer = function () { + Sk.execPaused = Date.now() - Sk.execPaused; }; Sk.exportSymbol("Sk.misceval.unpauseTimer", Sk.misceval.unpauseTimer); -Sk.misceval.errorUL = function(mangled) { - return new Sk.builtin.UnboundLocalError('local variable "'+mangled+ '" referenced before assignment'); +Sk.misceval.errorUL = function (mangled) { + return new Sk.builtin.UnboundLocalError("local variable \"" + mangled + "\" referenced before assignment"); }; Sk.exportSymbol("Sk.misceval.errorUL", Sk.misceval.errorUL); \ No newline at end of file diff --git a/src/module.js b/src/module.js index d9022ef6b0..7f54dee027 100644 --- a/src/module.js +++ b/src/module.js @@ -1,17 +1,59 @@ /** * @constructor + * @extends {Sk.builtin.object} */ -Sk.builtin.module = function module (name) { - if (!(this instanceof Sk.builtin.module)) { - return new Sk.builtin.module(name); +Sk.builtin.module = Sk.abstr.buildNativeClass("module", { + constructor: function module_ () {}, + slots: { + tp$doc: "Create a module object.\n\nThe name must be a string; the optional doc argument can have any type.", + tp$init: function(args, kwargs) { + Sk.abstr.checkArgsLen(this.tp$name, args, 1, 3); + this["$d"] = { + "__name__": args[0], + "__package__": Sk.builtin.none.none$, + }; + return Sk.builtin.none.none$; + }, + tp$getattr: function (pyName, canSuspend) { + var jsMangled = pyName.$mangled; + const ret = this.$d[jsMangled]; + if (ret !== undefined) { + return ret; + } + // technically this is the wrong way round but its seems performance wise better + // to just return the module elements before checking for descriptors + const descr = this.ob$type.$typeLookup(pyName); + if (descr !== undefined) { + const f = descr.tp$descr_get; + if (f) { + return f.call(descr, this, this.ob$type, canSuspend); + } + } + }, + $r: function () { + let get = (s) => { + let v = this.tp$getattr(new Sk.builtin.str(s)); + return Sk.misceval.objectRepr(v || Sk.builtin.str.$emptystr); + }; + const _name = get("__name__"); + let _file = get("__file__"); + if (_file === "''") { + _file = "(built-in)"; + } else { + _file = "from " + _file; + } + return new Sk.builtin.str(""); + } + }, + getsets: { + __dict__: { + $get: function () { + // modules in skulpt have a $d as a js object so just return it as a mapping proxy; + // TODO we should really have a dict object + return new Sk.builtin.mappingproxy(this.$d); + } + } } - this["$d"] = {__name__: name}; - this["$d"]["__dict__"] = this["$d"]; - return this; -}; -Sk.exportSymbol("Sk.builtin.module", Sk.builtin.module); +}); -Sk.builtin.module.prototype.ob$type = Sk.builtin.type.makeIntoTypeObj("module", Sk.builtin.module); -Sk.builtin.module.prototype.tp$getattr = Sk.builtin.object.prototype.GenericGetAttr; -Sk.builtin.module.prototype.tp$setattr = Sk.builtin.object.prototype.GenericSetAttr; -Sk.builtin.module.prototype.tp$name = "module"; +Sk.exportSymbol("Sk.builtin.module", Sk.builtin.module); diff --git a/src/object.js b/src/object.js index 2ec918c400..fffb56c64d 100644 --- a/src/object.js +++ b/src/object.js @@ -1,522 +1,213 @@ /** + * * @constructor - * Sk.builtin.object * * @description * Constructor for Python object. All Python classes (builtin and user-defined) * should inherit from this class. * - * @return {Sk.builtin.object} Python object */ -Sk.builtin.object = function () { - if (!(this instanceof Sk.builtin.object)) { - return new Sk.builtin.object(); - } - - return this; +Sk.builtin.object = function object() { + Sk.asserts.assert(this instanceof Sk.builtin.object, "bad call to object, use 'new'"); }; -Sk.builtin.object.prototype.__init__ = function __init__() { - return Sk.builtin.none.none$; -}; -Sk.builtin.object.prototype.__init__.co_kwargs = 1; - -Sk.builtin._tryGetSubscript = function(dict, pyName) { - try { - return dict.mp$subscript(pyName); - } catch (x) { - return undefined; - } -}; -Sk.exportSymbol("Sk.builtin._tryGetSubscript", Sk.builtin._tryGetSubscript); - +// now that object has been created we setup the base inheritances +// between type and object +Sk.abstr.setUpBaseInheritance(); /** - * Get an attribute - * @param {Object} pyName Python string name of the attribute - * @param {boolean=} canSuspend Can we return a suspension? - * @return {undefined} + * worth noting that we don't use the new api for object since descr_objects are not yet initialized + * object, type, NoneType, NotImplemented, + * slot_wrapper, methods_descriptor, getsets_desciptor, class_descriptor + * will be fully initialized in the import.js doOneTimeInitialization + * @ignore */ -Sk.builtin.object.prototype.GenericGetAttr = function (pyName, canSuspend) { - var res; - var f; - var descr; - var tp; - var dict; - var getf; - var jsName = pyName.$jsstr(); - - tp = this.ob$type; - Sk.asserts.assert(tp !== undefined, "object has no ob$type!"); - dict = this["$d"] || this.constructor["$d"]; - //print("getattr", tp.tp$name, name); - - if (jsName === "__class__") { - if (tp !== null) { - return tp; - } - }/* else if (jsName === "__name__") { - if (this.tp$name !== null) { - return Sk.ffi.remapToPy(this.tp$name); - } - }*/ +Sk.builtin.object.prototype.tp$doc = "The most base type"; - // todo; assert? force? - if (dict) { - if (dict.mp$lookup) { - res = dict.mp$lookup(pyName); - } else if (dict.mp$subscript) { - res = Sk.builtin._tryGetSubscript(dict, pyName); - } else if (typeof dict === "object") { - res = dict[jsName]; +Sk.builtin.object.prototype.tp$new = function (args, kwargs) { + // see cypthon object_new for algorithm details we do two versions one for prototypical and one for not + if (args.length || (kwargs && kwargs.length)) { + if (this.tp$new !== Sk.builtin.object.prototype.tp$new) { + throw new Sk.builtin.TypeError("object.__new__() takes exactly one argument (the type to instantiate)"); } - if (res !== undefined) { - return res; + if (this.tp$init === Sk.builtin.object.prototype.tp$init) { + throw new Sk.builtin.TypeError(Sk.abstr.typeName(this) + "() takes no arguments"); } } - - descr = Sk.builtin.type.typeLookup(tp, pyName); - - // otherwise, look in the type for a descr - if (descr !== undefined && descr !== null) { - f = descr.tp$descr_get; - // todo - data descriptors (ie those with tp$descr_set too) get a different lookup priority - - if (f) { - // non-data descriptor - return f.call(descr, this, this.ob$type, canSuspend); - } - } - - if (descr !== undefined) { - return descr; - } - - // OK, try __getattr__ - - descr = Sk.builtin.type.typeLookup(tp, Sk.builtin.str.$getattr); - if (descr !== undefined && descr !== null) { - f = descr.tp$descr_get; - if (f) { - getf = f.call(descr, this, this.ob$type); - } else { - getf = descr; - } - - res = Sk.misceval.tryCatch(function() { - return Sk.misceval.callsimOrSuspendArray(getf, [pyName]); - }, function(e) { - if (e instanceof Sk.builtin.AttributeError) { - return undefined; - } else { - throw e; - } - }); - return canSuspend ? res : Sk.misceval.retryOptionalSuspensionOrThrow(res); - } - - - return undefined; -}; -Sk.exportSymbol("Sk.builtin.object.prototype.GenericGetAttr", Sk.builtin.object.prototype.GenericGetAttr); - -Sk.builtin.object.prototype.GenericPythonGetAttr = function(self, pyName) { - var r = Sk.builtin.object.prototype.GenericGetAttr.call(self, pyName, true); - if (r === undefined) { - throw new Sk.builtin.AttributeError(pyName); - } - return r; + return new this.constructor(); }; -Sk.exportSymbol("Sk.builtin.object.prototype.GenericPythonGetAttr", Sk.builtin.object.prototype.GenericPythonGetAttr); /** - * @param {Object} pyName - * @param {Object} value - * @param {boolean=} canSuspend - * @return {undefined} + * @param {Array} args + * @param {Array=} kwargs */ -Sk.builtin.object.prototype.GenericSetAttr = function (pyName, value, canSuspend) { - var objname = Sk.abstr.typeName(this); - var jsName = pyName.$jsstr(); - var dict; - var tp = this.ob$type; - var descr; - var f; - - Sk.asserts.assert(tp !== undefined, "object has no ob$type!"); - - dict = this["$d"] || this.constructor["$d"]; - - if (jsName == "__class__") { - if (value.tp$mro === undefined || value.tp$name === undefined) { - throw new Sk.builtin.TypeError( - "attempted to assign non-class to __class__"); - } - this.ob$type = value; - this.tp$name = value.tp$name; - return; - } - - descr = Sk.builtin.type.typeLookup(tp, pyName); - - // otherwise, look in the type for a descr - if (descr !== undefined && descr !== null) { - f = descr.tp$descr_set; - // todo; is this the right lookup priority for data descriptors? - if (f) { - return f.call(descr, this, value, canSuspend); +Sk.builtin.object.prototype.tp$init = function (args, kwargs) { + // see cypthon object_init for algorithm details + if (args.length || (kwargs && kwargs.length)) { + if (this.tp$init !== Sk.builtin.object.prototype.tp$init) { + throw new Sk.builtin.TypeError("object.__init__() takes exactly one argument (the instance to initialize)"); } - } - - if (dict.mp$ass_subscript) { - if (this instanceof Sk.builtin.object && !(this.ob$type.sk$klass) && - dict.mp$lookup(pyName) === undefined) { - // Cannot add new attributes to a builtin object - throw new Sk.builtin.AttributeError("'" + objname + "' object has no attribute '" + Sk.unfixReserved(jsName) + "'"); + if (this.tp$new === Sk.builtin.object.prototype.tp$new) { + throw new Sk.builtin.TypeError(Sk.abstr.typeName(this) + ".__init__() takes exactly one argument (the instance to initialize)"); } - dict.mp$ass_subscript(pyName, value); - } else if (typeof dict === "object") { - dict[jsName] = value; } -}; -Sk.exportSymbol("Sk.builtin.object.prototype.GenericSetAttr", Sk.builtin.object.prototype.GenericSetAttr); - -Sk.builtin.object.prototype.GenericPythonSetAttr = function(self, pyName, value) { - return Sk.builtin.object.prototype.GenericSetAttr.call(self, pyName, value, true); -}; -Sk.exportSymbol("Sk.builtin.object.prototype.GenericPythonSetAttr", Sk.builtin.object.prototype.GenericPythonSetAttr); - -Sk.builtin.object.prototype.HashNotImplemented = function () { - throw new Sk.builtin.TypeError("unhashable type: '" + Sk.abstr.typeName(this) + "'"); -}; - -Sk.builtin.object.prototype.tp$getattr = Sk.builtin.object.prototype.GenericGetAttr; -Sk.builtin.object.prototype.tp$setattr = Sk.builtin.object.prototype.GenericSetAttr; - -// Although actual attribute-getting happens in pure Javascript via tp$getattr, classes -// overriding __getattribute__ etc need to be able to call object.__getattribute__ etc from Python -Sk.builtin.object.prototype["__getattribute__"] = Sk.builtin.object.prototype.GenericPythonGetAttr; -Sk.builtin.object.prototype["__setattr__"] = Sk.builtin.object.prototype.GenericPythonSetAttr; - -/** - * The name of this class. - * @type {string} - */ -Sk.builtin.object.prototype.tp$name = "object"; - -/** - * The type object of this class. - * @type {Sk.builtin.type|Object} - */ -Sk.builtin.object.prototype.ob$type = Sk.builtin.type.makeIntoTypeObj("object", Sk.builtin.object); -Sk.builtin.object.prototype.ob$type.sk$klass = undefined; // Nonsense for closure compiler -Sk.builtin.object.prototype.tp$descr_set = undefined; // Nonsense for closure compiler - -/** Default implementations of dunder methods found in all Python objects */ -/** - * Default implementation of __new__ just calls the class constructor - * @name __new__ - * @memberOf Sk.builtin.object.prototype - * @instance - */ -Sk.builtin.object.prototype["__new__"] = function (cls) { - Sk.builtin.pyCheckArgsLen("__new__", arguments.length, 1, 1, false, false); - - return new cls([], []); -}; - -/** - * Python wrapper for `__repr__` method. - * @name __repr__ - * @memberOf Sk.builtin.object.prototype - * @instance - */ -Sk.builtin.object.prototype["__repr__"] = function (self) { - Sk.builtin.pyCheckArgsLen("__repr__", arguments.length, 0, 0, false, true); - - return self["$r"](); + return Sk.builtin.none.none$; }; +Sk.builtin.object.prototype.tp$getattr = Sk.generic.getAttr; +Sk.builtin.object.prototype.tp$setattr = Sk.generic.setAttr; -Sk.builtin.object.prototype["__format__"] = function (self, format_spec) { - var formatstr; - Sk.builtin.pyCheckArgsLen("__format__", arguments.length, 2, 2); - - if (!Sk.builtin.checkString(format_spec)) { - if (Sk.__future__.exceptions) { - throw new Sk.builtin.TypeError("format() argument 2 must be str, not " + Sk.abstr.typeName(format_spec)); - } else { - throw new Sk.builtin.TypeError("format expects arg 2 to be string or unicode, not " + Sk.abstr.typeName(format_spec)); - } - } else { - formatstr = Sk.ffi.remapToJs(format_spec); - if (formatstr !== "") { - throw new Sk.builtin.NotImplementedError("format spec is not yet implemented"); - } +Sk.builtin.object.prototype.$r = function () { + const mod = Sk.abstr.lookupSpecial(this, Sk.builtin.str.$module); + let cname = ""; + if (mod && Sk.builtin.checkString(mod)) { + cname = mod.v + "."; } - - return new Sk.builtin.str(self); -}; - - -/** - * Python wrapper for `__str__` method. - * @name __str__ - * @memberOf Sk.builtin.object.prototype - * @instance - */ -Sk.builtin.object.prototype["__str__"] = function (self) { - Sk.builtin.pyCheckArgsLen("__str__", arguments.length, 0, 0, false, true); - - return self["$r"](); -}; - -/** - * Python wrapper for `__hash__` method. - * @name __hash__ - * @memberOf Sk.builtin.object.prototype - * @instance - */ -Sk.builtin.object.prototype["__hash__"] = function (self) { - Sk.builtin.pyCheckArgsLen("__hash__", arguments.length, 0, 0, false, true); - - return self.tp$hash(); -}; - -/** - * Python wrapper for `__eq__` method. - * @name __eq__ - * @memberOf Sk.builtin.object.prototype - * @instance - */ -Sk.builtin.object.prototype["__eq__"] = function (self, other) { - Sk.builtin.pyCheckArgsLen("__eq__", arguments.length, 1, 1, false, true); - - return self.ob$eq(other); -}; - -/** - * Python wrapper for `__ne__` method. - * @name __ne__ - * @memberOf Sk.builtin.object.prototype - * @instance - */ -Sk.builtin.object.prototype["__ne__"] = function (self, other) { - Sk.builtin.pyCheckArgsLen("__ne__", arguments.length, 1, 1, false, true); - - return self.ob$ne(other); -}; - -/** - * Python wrapper for `__lt__` method. - * @name __lt__ - * @memberOf Sk.builtin.object.prototype - * @instance - */ -Sk.builtin.object.prototype["__lt__"] = function (self, other) { - Sk.builtin.pyCheckArgsLen("__lt__", arguments.length, 1, 1, false, true); - - return self.ob$lt(other); + return new Sk.builtin.str("<" + cname + Sk.abstr.typeName(this) + " object>"); }; -/** - * Python wrapper for `__le__` method. - * @name __le__ - * @memberOf Sk.builtin.object.prototype - * @instance - */ -Sk.builtin.object.prototype["__le__"] = function (self, other) { - Sk.builtin.pyCheckArgsLen("__le__", arguments.length, 1, 1, false, true); - - return self.ob$le(other); +Sk.builtin.object.prototype.tp$str = function () { + // if we're calling this function then the object has no __str__ or tp$str defined + return this.$r(); }; -/** - * Python wrapper for `__gt__` method. - * @name __gt__ - * @memberOf Sk.builtin.object.prototype - * @instance - */ -Sk.builtin.object.prototype["__gt__"] = function (self, other) { - Sk.builtin.pyCheckArgsLen("__gt__", arguments.length, 1, 1, false, true); - - return self.ob$gt(other); -}; - -/** - * Python wrapper for `__ge__` method. - * @name __ge__ - * @memberOf Sk.builtin.object.prototype - * @instance - */ -Sk.builtin.object.prototype["__ge__"] = function (self, other) { - Sk.builtin.pyCheckArgsLen("__ge__", arguments.length, 1, 1, false, true); - - return self.ob$ge(other); -}; - -/** Default implementations of Javascript functions used in dunder methods */ - -/** - * Return the string representation of this instance. - * - * Javascript function, returns Python object. - * - * @name $r - * @memberOf Sk.builtin.object.prototype - * @return {Sk.builtin.str} The Python string representation of this instance. - */ -Sk.builtin.object.prototype["$r"] = function () { - return new Sk.builtin.str(""); -}; - -Sk.builtin.hashCount = 1; -Sk.builtin.idCount = 1; - /** * Return the hash value of this instance. * * Javascript function, returns Python object. * * @return {Sk.builtin.int_} The hash value + * @ignore */ Sk.builtin.object.prototype.tp$hash = function () { if (!this.$savedHash_) { this.$savedHash_ = new Sk.builtin.int_(Sk.builtin.hashCount++); } - return this.$savedHash_; }; -/** - * Perform equality check between this instance and a Python object (i.e. this == other). - * - * Implements `__eq__` dunder method. - * - * Javascript function, returns Python object. - * - * @param {Object} other The Python object to check for equality. - * @return {(Sk.builtin.bool|Sk.builtin.NotImplemented)} true if equal, false otherwise - */ -Sk.builtin.object.prototype.ob$eq = function (other) { - if (this === other) { - return Sk.builtin.bool.true$; - } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** - * Perform non-equality check between this instance and a Python object (i.e. this != other). - * - * Implements `__ne__` dunder method. - * - * Javascript function, returns Python object. - * - * @param {Object} other The Python object to check for non-equality. - * @return {(Sk.builtin.bool|Sk.builtin.NotImplemented)} true if not equal, false otherwise - */ -Sk.builtin.object.prototype.ob$ne = function (other) { - if (this === other) { - return Sk.builtin.bool.false$; +Sk.builtin.object.prototype.tp$richcompare = function (other, op) { + let res; + switch (op) { + case "Eq": + res = this === other || Sk.builtin.NotImplemented.NotImplemented$; + break; + case "NotEq": + // use tp$richcompare here... because CPython does. ob$eq breaks some tests for NotEq subclasses + res = this.tp$richcompare(other, "Eq"); + if (res !== Sk.builtin.NotImplemented.NotImplemented$) { + res = !Sk.misceval.isTrue(res); + } + break; + default: + res = Sk.builtin.NotImplemented.NotImplemented$; } - - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** - * Determine if this instance is less than a Python object (i.e. this < other). - * - * Implements `__lt__` dunder method. - * - * Javascript function, returns Python object. - * - * @param {Object} other The Python object to compare. - * @return {(Sk.builtin.bool|Sk.builtin.NotImplemented)} true if this < other, false otherwise - */ -Sk.builtin.object.prototype.ob$lt = function (other) { - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** - * Determine if this instance is less than or equal to a Python object (i.e. this <= other). - * - * Implements `__le__` dunder method. - * - * Javascript function, returns Python object. - * - * @param {Object} other The Python object to compare. - * @return {(Sk.builtin.bool|Sk.builtin.NotImplemented)} true if this <= other, false otherwise - */ -Sk.builtin.object.prototype.ob$le = function (other) { - return Sk.builtin.NotImplemented.NotImplemented$; + return res; }; -/** - * Determine if this instance is greater than a Python object (i.e. this > other). - * - * Implements `__gt__` dunder method. - * - * Javascript function, returns Python object. - * - * @param {Object} other The Python object to compare. - * @return {(Sk.builtin.bool|Sk.builtin.NotImplemented)} true if this > other, false otherwise - */ -Sk.builtin.object.prototype.ob$gt = function (other) { - return Sk.builtin.NotImplemented.NotImplemented$; -}; - -/** - * Determine if this instance is greater than or equal to a Python object (i.e. this >= other). - * - * Implements `__ge__` dunder method. - * - * Javascript function, returns Python object. - * - * @param {Object} other The Python object to compare. - * @return {(Sk.builtin.bool|Sk.builtin.NotImplemented)} true if this >= other, false otherwise - */ -Sk.builtin.object.prototype.ob$ge = function (other) { - return Sk.builtin.NotImplemented.NotImplemented$; +Sk.builtin.object.prototype.tp$getsets = { + __class__: { + $get: function () { + return this.ob$type; + }, + $set: function (value) { + if (!Sk.builtin.checkClass(value)) { + throw new Sk.builtin.TypeError("__class__ must be set to a class, not '" + Sk.abstr.typeName(value) + "' object"); + } + if (!this.hp$type || !value.sk$klass) { + throw new Sk.builtin.TypeError(" __class__ assignment only supported for heap types or ModuleType subclasses"); + } + Object.setPrototypeOf(this, value.prototype); + return; + }, + $doc: "the object's class", + }, +}; + +Sk.builtin.object.prototype.tp$methods = { + __dir__: { + $meth: function __dir__() { + let dir = []; + if (this.$d) { + if (this.$d instanceof Sk.builtin.dict) { + dir = this.$d.sk$asarray(); + } else { + for (let key in this.$d) { + dir.push(new Sk.builtin.str(key)); + } + } + } + // here we use the type.__dir__ implementation + const type_dir = Sk.misceval.callsimArray(Sk.builtin.type.prototype.__dir__, [this.ob$type]); + // put the dict keys before the prototype keys + dir.push(...type_dir.v); + type_dir.v = dir; + return type_dir; + }, + $flags: {NoArgs: true}, + $doc: "Default dir() implementation.", + }, + __format__: { + $meth: function (format_spec) { + let formatstr; + if (!Sk.builtin.checkString(format_spec)) { + if (Sk.__future__.exceptions) { + throw new Sk.builtin.TypeError("format() argument 2 must be str, not " + Sk.abstr.typeName(format_spec)); + } else { + throw new Sk.builtin.TypeError("format expects arg 2 to be string or unicode, not " + Sk.abstr.typeName(format_spec)); + } + } else { + formatstr = Sk.ffi.remapToJs(format_spec); + if (formatstr !== "") { + throw new Sk.builtin.NotImplementedError("format spec is not yet implemented"); + } + } + return this.tp$str(); + }, + $flags: {OneArg: true}, + $doc: "Default object formatter.", + }, }; -// Wrap the following functions in Sk.builtin.func once that class is initialized -/** - * Array of all the Python functions which are methods of this class. - * @type {Array} - */ -Sk.builtin.object.pythonFunctions = [ - "__repr__", "__str__", "__hash__", - "__eq__", "__ne__", "__lt__", "__le__", - "__gt__", "__ge__", "__getattribute__", - "__setattr__", "__format__" -]; +Sk.builtin.hashCount = 1; +Sk.builtin.idCount = 1; /** * @constructor * Sk.builtin.none - * * @extends {Sk.builtin.object} + * + * @description + * It would be rare to call this as a constructor since it returns {@link Sk.builtin.none.none$} */ Sk.builtin.none = function () { - this.v = null; + return Sk.builtin.none.none$; // always return the same object }; Sk.abstr.setUpInheritance("NoneType", Sk.builtin.none, Sk.builtin.object); -/** @override */ -Sk.builtin.none.prototype["$r"] = function () { return new Sk.builtin.str("None"); }; +Sk.builtin.none.prototype.$r = function () { + return new Sk.builtin.str("None"); +}; -/** @override */ Sk.builtin.none.prototype.tp$hash = function () { return new Sk.builtin.int_(0); }; +Sk.builtin.none.prototype.tp$new = function (args, kwargs) { + Sk.abstr.checkNoArgs("NoneType", args, kwargs); + return Sk.builtin.none.none$; +}; + /** - * Python None constant. + * Python None value. * @type {Sk.builtin.none} + * @member {Sk.builtin.none} + * @suppress {checkTypes} */ -Sk.builtin.none.none$ = new Sk.builtin.none(); +Sk.builtin.none.none$ = Object.create(Sk.builtin.none.prototype, { + v: {value: null, enumerable: true}, +}); /** * @constructor @@ -524,17 +215,28 @@ Sk.builtin.none.none$ = new Sk.builtin.none(); * * @extends {Sk.builtin.object} */ -Sk.builtin.NotImplemented = function() { }; +Sk.builtin.NotImplemented = function () { + return Sk.builtin.NotImplemented.NotImplemented$; // always return the same object +}; Sk.abstr.setUpInheritance("NotImplementedType", Sk.builtin.NotImplemented, Sk.builtin.object); -/** @override */ -Sk.builtin.NotImplemented.prototype["$r"] = function () { return new Sk.builtin.str("NotImplemented"); }; +Sk.builtin.NotImplemented.prototype.$r = function () { + return new Sk.builtin.str("NotImplemented"); +}; +Sk.builtin.NotImplemented.prototype.tp$new = function (args, kwargs) { + Sk.abstr.checkNoArgs("NotImplementedType", args, kwargs); + return Sk.builtin.NotImplemented.NotImplemented$; +}; /** * Python NotImplemented constant. * @type {Sk.builtin.NotImplemented} + * @member {Sk.builtin.NotImplemented} + * @suppress {checkTypes} */ -Sk.builtin.NotImplemented.NotImplemented$ = new Sk.builtin.NotImplemented(); +Sk.builtin.NotImplemented.NotImplemented$ = Object.create(Sk.builtin.NotImplemented.prototype, { + v: {value: null, enumerable: true}, +}); Sk.exportSymbol("Sk.builtin.none", Sk.builtin.none); Sk.exportSymbol("Sk.builtin.NotImplemented", Sk.builtin.NotImplemented); diff --git a/src/parser.js b/src/parser.js index b22bd55492..86d2b9a682 100644 --- a/src/parser.js +++ b/src/parser.js @@ -14,7 +14,7 @@ * * can throw SyntaxError */ -function Parser (filename, grammar) { +function Parser(filename, grammar) { this.filename = filename; this.grammar = grammar; this.comments = {}; @@ -43,24 +43,24 @@ Parser.prototype.setup = function (start) { //print("START:"+start); newnode = - { - type : start, - value : null, - context : null, - children: [] - }; + { + type: start, + value: null, + context: null, + children: [] + }; stackentry = - { - dfa : this.grammar.dfas[start], - state: 0, - node : newnode - }; + { + dfa: this.grammar.dfas[start], + state: 0, + node: newnode + }; this.stack = [stackentry]; this.used_names = {}; Sk._setupTokenRegexes(); }; -function findInDfa (a, obj) { +function findInDfa(a, obj) { var i = a.length; while (i--) { if (a[i][0] === obj[0] && a[i][1] === obj[1]) { @@ -71,7 +71,7 @@ function findInDfa (a, obj) { } // Add a comment -Parser.prototype.addcomment = function(value, start, end, line) { +Parser.prototype.addcomment = function (value, start, end, line) { this.comments[start] = value; }; @@ -172,7 +172,7 @@ Parser.prototype.classify = function (type, value, context) { ilabel = this.grammar.keywords.hasOwnProperty(value) && this.grammar.keywords[value]; /* Check for handling print as an builtin function */ - if(value === "print" && (this.p_flags & Parser.CO_FUTURE_PRINT_FUNCTION || Sk.__future__.print_function === true)) { + if (value === "print" && (this.p_flags & Parser.CO_FUTURE_PRINT_FUNCTION || Sk.__future__.print_function === true)) { ilabel = false; // ilabel determines if the value is a keyword } @@ -186,7 +186,7 @@ Parser.prototype.classify = function (type, value, context) { // throw new Sk.builtin.SyntaxError("bad token", type, value, context); // Questionable modification to put line number in position 2 // like everywhere else and filename in position 1. - let descr = "#"+type; + let descr = "#" + type; for (let i in Sk.token.tokens) { if (Sk.token.tokens[i] == type) { descr = i; @@ -206,21 +206,21 @@ Parser.prototype.shift = function (type, value, newstate, context) { var node = this.stack[this.stack.length - 1].node; //print("context", context); var newnode = { - type : type, - value : value, - lineno : context[0][0], + type: type, + value: value, + lineno: context[0][0], col_offset: context[0][1], - end_lineno : context[1][0], + end_lineno: context[1][0], end_col_offset: context[1][1], - children : null + children: null }; if (newnode) { node.children.push(newnode); } this.stack[this.stack.length - 1] = { - dfa : dfa, + dfa: dfa, state: newstate, - node : node + node: node }; }; @@ -229,23 +229,23 @@ Parser.prototype.push = function (type, newdfa, newstate, context) { var dfa = this.stack[this.stack.length - 1].dfa; var node = this.stack[this.stack.length - 1].node; var newnode = { - type : type, - value : null, - lineno : context[0][0], + type: type, + value: null, + lineno: context[0][0], col_offset: context[0][1], - end_lineno : context[1][0], + end_lineno: context[1][0], end_col_offset: context[1][1], - children : [] + children: [] }; this.stack[this.stack.length - 1] = { - dfa : dfa, + dfa: dfa, state: newstate, - node : node + node: node }; this.stack.push({ - dfa : newdfa, + dfa: newdfa, state: 0, - node : newnode + node: newnode }); }; @@ -281,7 +281,7 @@ Parser.prototype.pop = function () { * @param {string} filename * @param {string=} style root of parse tree (optional) */ -function makeParser (filename, style) { +function makeParser(filename, style) { if (style === undefined) { style = "file_input"; } @@ -296,8 +296,8 @@ function makeParser (filename, style) { } -Sk.parse = function parse (filename, input) { - +Sk.parse = function parse(filename, input) { + var T_COMMENT = Sk.token.tokens.T_COMMENT; var T_NL = Sk.token.tokens.T_NL; var T_OP = Sk.token.tokens.T_OP; @@ -315,12 +315,12 @@ Sk.parse = function parse (filename, input) { function readline(input) { var lines = input.split("\n").reverse();//.map(function (l) { return l + "\n"; }); - return function() { + return function () { if (lines.length === 0) { throw new Sk.builtin.Exception("EOF"); } - return lines.pop()+"\n"; + return lines.pop() + "\n"; }; } @@ -345,7 +345,7 @@ Sk.parse = function parse (filename, input) { lineno += 1; column = 0; } - + if (tokenInfo.type === T_COMMENT) { parser.addcomment(tokenInfo.string, tokenInfo.start, tokenInfo.end, tokenInfo.line); } @@ -373,7 +373,7 @@ Sk.parse = function parse (filename, input) { return result; }; -Sk.parseTreeDump = function parseTreeDump (n, indent) { +Sk.parseTreeDump = function parseTreeDump(n, indent) { //return JSON.stringify(n, null, 2); var i; var ret; diff --git a/src/pgen/ast/asdl.py b/src/pgen/ast/asdl.py index 6f060e0e22..00d3c09a6f 100644 --- a/src/pgen/ast/asdl.py +++ b/src/pgen/ast/asdl.py @@ -1,4 +1,4 @@ -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Parser for ASDL [1] definition files. Reads in an ASDL description and parses # it into an AST that describes it. # @@ -18,7 +18,7 @@ # # [1] "The Zephyr Abstract Syntax Description Language" by Wang, et. al. See # http://asdl.sourceforge.net/ -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- from collections import namedtuple import re @@ -36,10 +36,12 @@ builtin_types = {'identifier', 'string', 'bytes', 'int', 'object', 'singleton', 'constant'} + class AST: def __repr__(self): raise NotImplementedError + class Module(AST): def __init__(self, name, dfns): self.name = name @@ -49,6 +51,7 @@ def __init__(self, name, dfns): def __repr__(self): return 'Module({0.name}, {0.dfns})'.format(self) + class Type(AST): def __init__(self, name, value): self.name = name @@ -57,6 +60,7 @@ def __init__(self, name, value): def __repr__(self): return 'Type({0.name}, {0.value})'.format(self) + class Constructor(AST): def __init__(self, name, fields=None): self.name = name @@ -65,6 +69,7 @@ def __init__(self, name, fields=None): def __repr__(self): return 'Constructor({0.name}, {0.fields})'.format(self) + class Field(AST): def __init__(self, type, name=None, seq=False, opt=False): self.type = type @@ -84,6 +89,7 @@ def __repr__(self): else: return 'Field({0.type}, {0.name}{1})'.format(self, extra) + class Sum(AST): def __init__(self, types, attributes=None): self.types = types @@ -95,6 +101,7 @@ def __repr__(self): else: return 'Sum({0.types})'.format(self) + class Product(AST): def __init__(self, fields, attributes=None): self.fields = fields @@ -106,6 +113,7 @@ def __repr__(self): else: return 'Product({0.fields})'.format(self) + # A generic visitor for the meta-AST that describes ASDL. This can be used by # emitters. Note that this visitor does not provide a generic visit method, so a # subclass needs to define visit methods from visitModule to as deep as the @@ -114,6 +122,7 @@ def __repr__(self): class VisitorBase(object): """Generic tree visitor for ASTs.""" + def __init__(self): self.cache = {} @@ -131,11 +140,13 @@ def visit(self, obj, *args): print("Error visiting %r: %s" % (obj, e)) raise + class Check(VisitorBase): """A visitor that checks a parsed ASDL tree for correctness. Errors are printed and accumulated. """ + def __init__(self): super(Check, self).__init__() self.cons = {} @@ -174,6 +185,7 @@ def visitProduct(self, prod, name): for f in prod.fields: self.visit(f, name) + def check(mod): """Check the parsed ASDL tree for correctness. @@ -190,6 +202,7 @@ def check(mod): print('Undefined type {}, used in {}'.format(t, uses)) return not v.errors + # The ASDL parser itself comes next. The only interesting external interface # here is the top-level parse function. @@ -199,6 +212,7 @@ def parse(filename): parser = ASDLParser() return parser.parse(f.read()) + # Types for describing tokens in an ASDL specification. class TokenKind: """TokenKind is provides a scope for enumerated token kinds.""" @@ -206,11 +220,13 @@ class TokenKind: LParen, RParen, LBrace, RBrace) = range(11) operator_table = { - '=': Equals, ',': Comma, '?': Question, '|': Pipe, '(': LParen, - ')': RParen, '*': Asterisk, '{': LBrace, '}': RBrace} + '=': Equals, ',': Comma, '?': Question, '|': Pipe, '(': LParen, + ')': RParen, '*': Asterisk, '{': LBrace, '}': RBrace} + Token = namedtuple('Token', 'kind value lineno') + class ASDLSyntaxError(Exception): def __init__(self, msg, lineno=None): self.msg = msg @@ -219,6 +235,7 @@ def __init__(self, msg, lineno=None): def __str__(self): return 'Syntax error on line {0.lineno}: {0.msg}'.format(self) + def tokenize_asdl(buf): """Tokenize the given buffer. Yield Token objects.""" for lineno, line in enumerate(buf.splitlines(), 1): @@ -241,6 +258,7 @@ def tokenize_asdl(buf): raise ASDLSyntaxError('Invalid operator %s' % c, lineno) yield Token(op_kind, c, lineno) + class ASDLParser: """Parser for ASDL files. @@ -248,6 +266,7 @@ class ASDLParser: This is a simple recursive descent parser that uses tokenize_asdl for the lexing. """ + def __init__(self): self._tokenizer = None self.cur_token = None @@ -289,12 +308,12 @@ def _parse_type(self): # Otherwise it's a sum. Look for ConstructorId sumlist = [Constructor(self._match(TokenKind.ConstructorId), self._parse_optional_fields())] - while self.cur_token.kind == TokenKind.Pipe: + while self.cur_token.kind == TokenKind.Pipe: # More constructors self._advance() sumlist.append(Constructor( - self._match(TokenKind.ConstructorId), - self._parse_optional_fields())) + self._match(TokenKind.ConstructorId), + self._parse_optional_fields())) return Sum(sumlist, self._parse_optional_attributes()) def _parse_product(self): @@ -307,7 +326,7 @@ def _parse_fields(self): typename = self._advance() is_seq, is_opt = self._parse_optional_field_quantifier() id = (self._advance() if self.cur_token.kind in self._id_kinds - else None) + else None) fields.append(Field(typename, id, seq=is_seq, opt=is_opt)) if self.cur_token.kind == TokenKind.RParen: break @@ -361,8 +380,8 @@ def _match(self, kind): * Reads in the next token """ if (isinstance(kind, tuple) and self.cur_token.kind in kind or - self.cur_token.kind == kind - ): + self.cur_token.kind == kind + ): value = self.cur_token.value self._advance() return value @@ -373,4 +392,4 @@ def _match(self, kind): def _at_keyword(self, keyword): return (self.cur_token.kind == TokenKind.TypeId and - self.cur_token.value == keyword) \ No newline at end of file + self.cur_token.value == keyword) diff --git a/src/pgen/ast/asdl_js.py b/src/pgen/ast/asdl_js.py index ed9bf289bf..fa8faad09e 100755 --- a/src/pgen/ast/asdl_js.py +++ b/src/pgen/ast/asdl_js.py @@ -11,6 +11,7 @@ TABSIZE = 4 MAX_COL = 80 + def get_c_type(name): """Return a string for the C name of the type. @@ -25,6 +26,7 @@ def get_c_type(name): else: return "%s_ty" % name + def reflow_lines(s, depth): """Reflow the line s indented depth tabs. @@ -53,20 +55,21 @@ def reflow_lines(s, depth): # find new size based on brace j = cur.find('{', 0, i) if j >= 0: - j += 2 # account for the brace and the space after it + j += 2 # account for the brace and the space after it size -= j padding = " " * j else: j = cur.find('(', 0, i) if j >= 0: - j += 1 # account for the paren (no space after it) + j += 1 # account for the paren (no space after it) size -= j padding = " " * j - cur = cur[i+1:] + cur = cur[i + 1:] else: lines.append(padding + cur) return lines + def is_simple(sum): """Return True if a sum is a simple. @@ -166,7 +169,7 @@ def visitType(self, type): def visitSum(self, sum, name): if is_simple(sum): - pass # XXX + pass # XXX else: for t in sum.types: self.visit(t, name, sum.attributes) @@ -213,10 +216,10 @@ def emit_function(self, name, ctype, args, attrs, union=1): else: argstr = "PyArena *arena" margs = "a0" - for i in range(1, len(args)+1): + for i in range(1, len(args) + 1): margs += ", a%d" % i self.emit("#define %s(%s) _Py_%s(%s)" % (name, margs, name, margs), 0, - reflow = 0) + reflow=0) self.emit("%s _Py_%s(%s);" % (ctype, name, argstr), 0) def visitProduct(self, prod, name): @@ -230,6 +233,7 @@ class FunctionVisitor(PrototypeVisitor): def emit_function(self, name, ctype, args, attrs, union=1): def emit(s, depth=0, reflow=1): self.emit(s, depth, reflow) + argstr = ", ".join(["/* {%s} */ %s" % (atype, aname) for atype, aname, opt in args + attrs]) emit("/** @constructor */") @@ -247,10 +251,10 @@ def emit(s, depth=0, reflow=1): emit("}") emit("") - def emit_body_union(self, name, args, attrs): def emit(s, depth=0, reflow=1): self.emit(s, depth, reflow) + for argtype, argname, opt in args: emit("this.%s = %s;" % (argname, argname), 1) for argtype, argname, opt in attrs: @@ -259,10 +263,12 @@ def emit(s, depth=0, reflow=1): def emit_body_struct(self, name, args, attrs): def emit(s, depth=0, reflow=1): self.emit(s, depth, reflow) + for argtype, argname, opt in args: emit("this.%s = %s;" % (argname, argname), 1) assert not attrs + class PickleVisitor(EmitVisitor): def visitModule(self, mod): @@ -290,18 +296,19 @@ def cleanName(name): if name[-1] == "_": return name[:-1] return name -class FieldNamesVisitor(PickleVisitor): +class FieldNamesVisitor(PickleVisitor): """note that trailing comma is bad in IE so we have to fiddle a bit to avoid it""" def visitProduct(self, prod, name): if prod.fields: self.emit('Sk.astnodes.%s.prototype._astname = "%s";' % (name, cleanName(name)), 0) - self.emit("Sk.astnodes.%s.prototype._fields = [" % name,0) + self.emit("Sk.astnodes.%s.prototype._fields = [" % name, 0) c = 0 for f in prod.fields: c += 1 - self.emit('"%s", function(n) { return n.%s; }%s' % (f.name, f.name, "," if c < len(prod.fields) else ""), 1) + self.emit( + '"%s", function(n) { return n.%s; }%s' % (f.name, f.name, "," if c < len(prod.fields) else ""), 1) self.emit("];", 0) def visitSum(self, sum, name): @@ -320,11 +327,14 @@ def visitConstructor(self, cons, name): c = 0 for t in cons.fields: c += 1 - self.emit('"%s", function(n) { return n.%s; }%s' % (t.name, t.name, "," if c < len(cons.fields) else ""), 1) - self.emit("];",0) + self.emit( + '"%s", function(n) { return n.%s; }%s' % (t.name, t.name, "," if c < len(cons.fields) else ""), 1) + self.emit("];", 0) + _SPECIALIZED_SEQUENCES = ('stmt', 'expr') + def find_sequence(fields, doing_specialization): """Return True if any field uses a sequence.""" for f in fields: @@ -335,6 +345,7 @@ def find_sequence(fields, doing_specialization): return True return False + def has_sequence(types, doing_specialization): for t in types: if find_sequence(t.fields, doing_specialization): @@ -351,8 +362,10 @@ def visit(self, object): v.visit(object) v.emit("", 0) + common_msg = "/* File automatically generated by %s. */\n\n" + def main(asdlfile, outputfile): argv0 = sys.argv[0] components = argv0.split(os.sep) @@ -367,28 +380,30 @@ def main(asdlfile, outputfile): f.write(auto_gen_msg) f.write("/* Object that holds all nodes */\n"); f.write("Sk.astnodes = {};\n\n"); - + c = ChainOfVisitors(TypeDefVisitor(f), ) c.visit(mod) - f.write("\n"*5) + f.write("\n" * 5) f.write("/* ---------------------- */\n") f.write("/* constructors for nodes */\n") f.write("/* ---------------------- */\n") - f.write("\n"*5) + f.write("\n" * 5) v = ChainOfVisitors( FunctionVisitor(f), FieldNamesVisitor(f), - ) + ) v.visit(mod) f.write('Sk.exportSymbol("Sk.astnodes", Sk.astnodes);\n'); - + f.close() + if __name__ == "__main__": import sys + if len(sys.argv) != 3: print("usage: asdl_js.py input.asdl output.js") raise SystemExit() diff --git a/src/pgen/ast/spark.py b/src/pgen/ast/spark.py index b064d62ec6..3cfdbb446a 100644 --- a/src/pgen/ast/spark.py +++ b/src/pgen/ast/spark.py @@ -24,6 +24,7 @@ import re import string + def _namelist(instance): namelist, namedict, classlist = [], {}, [instance.__class__] for c in classlist: @@ -35,14 +36,15 @@ def _namelist(instance): namedict[name] = 1 return namelist + class GenericScanner: def __init__(self, flags=0): pattern = self.reflect() - self.re = re.compile(pattern, re.VERBOSE|flags) + self.re = re.compile(pattern, re.VERBOSE | flags) self.index2func = {} for name, number in self.re.groupindex.items(): - self.index2func[number-1] = getattr(self, 't_' + name) + self.index2func[number - 1] = getattr(self, 't_' + name) def makeRE(self, name): doc = getattr(self, name).__doc__ @@ -59,7 +61,8 @@ def reflect(self): return string.join(rv, '|') def error(self, s, pos): - print "Lexical error at position %s" % pos + print + "Lexical error at position %s" % pos raise SystemExit def tokenize(self, s): @@ -78,9 +81,11 @@ def tokenize(self, s): def t_default(self, s): r'( . | \n )+' - print "Specification error: unmatched input" + print + "Specification error: unmatched input" raise SystemExit + # # Extracted from GenericParser and made global so that [un]picking works. # @@ -89,6 +94,7 @@ def __init__(self, stateno, items): self.T, self.complete, self.items = [], [], items self.stateno = stateno + class GenericParser: # # An Earley parser, as per J. Earley, "An Efficient Context-Free @@ -129,7 +135,7 @@ def __getstate__(self): self.makeNewRules() self.ruleschanged = 0 self.edges, self.cores = {}, {} - self.states = { 0: self.makeState0() } + self.states = {0: self.makeState0()} self.makeState(0, self._BOF) # # XXX - should find a better way to do this.. @@ -156,7 +162,7 @@ def __setstate__(self, D): self.rule2func = {} self.rule2name = {} self.collectRules() - start = D['rules'][self._START][0][1][1] # Blech. + start = D['rules'][self._START][0][1][1] # Blech. self.augment(start) D['rule2func'] = self.rule2func D['makeSet'] = self.makeSet_fast @@ -167,7 +173,8 @@ def __setstate__(self, D): # thee not with this; nor shall thee toucheth the _preprocess # argument to addRule. # - def preprocess(self, rule, func): return rule, func + def preprocess(self, rule, func): + return rule, func def addRule(self, doc, func, _preprocess=1): fn = func @@ -176,12 +183,12 @@ def addRule(self, doc, func, _preprocess=1): index = [] for i in range(len(rules)): if rules[i] == '::=': - index.append(i-1) + index.append(i - 1) index.append(len(rules)) - for i in range(len(index)-1): + for i in range(len(index) - 1): lhs = rules[index[i]] - rhs = rules[index[i]+2:index[i+1]] + rhs = rules[index[i] + 2:index[i + 1]] rule = (lhs, tuple(rhs)) if _preprocess: @@ -190,7 +197,7 @@ def addRule(self, doc, func, _preprocess=1): if self.rules.has_key(lhs): self.rules[lhs].append(rule) else: - self.rules[lhs] = [ rule ] + self.rules[lhs] = [rule] self.rule2func[rule] = fn self.rule2name[rule] = func.__name__[2:] self.ruleschanged = 1 @@ -269,37 +276,38 @@ def makeNewRules(self): while i < n: sym = rhs[i] if not self.rules.has_key(sym) or \ - not self.nullable[sym]: + not self.nullable[sym]: candidate = 0 i = i + 1 continue newrhs = list(rhs) - newrhs[i] = self._NULLABLE+sym + newrhs[i] = self._NULLABLE + sym newrule = (lhs, tuple(newrhs)) - worklist.append((newrule, i+1, + worklist.append((newrule, i + 1, candidate, oldrule)) candidate = 0 i = i + 1 else: if candidate: - lhs = self._NULLABLE+lhs + lhs = self._NULLABLE + lhs rule = (lhs, rhs) if self.newrules.has_key(lhs): self.newrules[lhs].append(rule) else: - self.newrules[lhs] = [ rule ] + self.newrules[lhs] = [rule] self.new2old[rule] = oldrule def typestring(self, token): return None def error(self, token): - print "Syntax error at or near `%s' token" % token + print + "Syntax error at or near `%s' token" % token raise SystemExit def parse(self, tokens): - sets = [ [(1,0), (2,0)] ] + sets = [[(1, 0), (2, 0)]] self.links = {} if self.ruleschanged: @@ -309,7 +317,7 @@ def parse(self, tokens): self.makeNewRules() self.ruleschanged = 0 self.edges, self.cores = {}, {} - self.states = { 0: self.makeState0() } + self.states = {0: self.makeState0()} self.makeState(0, self._BOF) for i in xrange(len(tokens)): @@ -322,17 +330,17 @@ def parse(self, tokens): sets.append([]) self.makeSet(None, sets, len(tokens)) - #_dump(tokens, sets, self.states) + # _dump(tokens, sets, self.states) finalitem = (self.finalState(tokens), 0) if finalitem not in sets[-2]: if len(tokens) > 0: - self.error(tokens[i-1]) + self.error(tokens[i - 1]) else: self.error(None) return self.buildTree(self._START, finalitem, - tokens, len(sets)-2) + tokens, len(sets) - 2) def isnullable(self, sym): # @@ -358,8 +366,8 @@ def makeState(self, state, sym): kitems = [] for rule, pos in self.states[state].items: lhs, rhs = rule - if rhs[pos:pos+1] == (sym,): - kitems.append((rule, self.skip(rule, pos+1))) + if rhs[pos:pos + 1] == (sym,): + kitems.append((rule, self.skip(rule, pos + 1))) core = kitems core.sort() @@ -371,7 +379,7 @@ def makeState(self, state, sym): # \epsilon-nonkernel state together; we'll need it right away. # k = self.cores[tcore] = len(self.states) - K, NK = _State(k, kitems), _State(k+1, []) + K, NK = _State(k, kitems), _State(k + 1, []) self.states[k] = K predicted = {} @@ -469,7 +477,7 @@ def add(self, set, item, i=None, predecessor=None, causal=None): self.links[key].append((predecessor, causal)) def makeSet(self, token, sets, i): - cur, next = sets[i], sets[i+1] + cur, next = sets[i], sets[i + 1] ttype = token is not None and self.typestring(token) or None if ttype is not None: @@ -483,10 +491,10 @@ def makeSet(self, token, sets, i): add = fn(state, arg) for k in add: if k is not None: - self.add(next, (k, parent), i+1, ptr) + self.add(next, (k, parent), i + 1, ptr) nk = self.goto(k, None) if nk is not None: - self.add(next, (nk, i+1)) + self.add(next, (nk, i + 1)) if parent == i: continue @@ -512,7 +520,7 @@ def makeSet_fast(self, token, sets, i): # then duplicates and inlines code to boost speed at the # cost of extreme ugliness. # - cur, next = sets[i], sets[i+1] + cur, next = sets[i], sets[i + 1] ttype = token is not None and self.typestring(token) or None for item in cur: @@ -521,33 +529,33 @@ def makeSet_fast(self, token, sets, i): if ttype is not None: k = self.edges.get((state, ttype), None) if k is not None: - #self.add(next, (k, parent), i+1, ptr) - #INLINED --v + # self.add(next, (k, parent), i+1, ptr) + # INLINED --v new = (k, parent) - key = (new, i+1) + key = (new, i + 1) if new not in next: self.links[key] = [] next.append(new) self.links[key].append((ptr, None)) - #INLINED --^ - #nk = self.goto(k, None) + # INLINED --^ + # nk = self.goto(k, None) nk = self.edges.get((k, None), None) if nk is not None: - #self.add(next, (nk, i+1)) - #INLINED --v - new = (nk, i+1) + # self.add(next, (nk, i+1)) + # INLINED --v + new = (nk, i + 1) if new not in next: next.append(new) - #INLINED --^ + # INLINED --^ else: add = self.gotoST(state, token) for k in add: if k is not None: - self.add(next, (k, parent), i+1, ptr) - #nk = self.goto(k, None) + self.add(next, (k, parent), i + 1, ptr) + # nk = self.goto(k, None) nk = self.edges.get((k, None), None) if nk is not None: - self.add(next, (nk, i+1)) + self.add(next, (nk, i + 1)) if parent == i: continue @@ -556,30 +564,30 @@ def makeSet_fast(self, token, sets, i): lhs, rhs = rule for pitem in sets[parent]: pstate, pparent = pitem - #k = self.goto(pstate, lhs) + # k = self.goto(pstate, lhs) k = self.edges.get((pstate, lhs), None) if k is not None: why = (item, i, rule) pptr = (pitem, parent) - #self.add(cur, (k, pparent), + # self.add(cur, (k, pparent), # i, pptr, why) - #INLINED --v + # INLINED --v new = (k, pparent) key = (new, i) if new not in cur: self.links[key] = [] cur.append(new) self.links[key].append((pptr, why)) - #INLINED --^ - #nk = self.goto(k, None) + # INLINED --^ + # nk = self.goto(k, None) nk = self.edges.get((k, None), None) if nk is not None: - #self.add(cur, (nk, i)) - #INLINED --v + # self.add(cur, (nk, i)) + # INLINED --v new = (nk, i) if new not in cur: cur.append(new) - #INLINED --^ + # INLINED --^ def predecessor(self, key, causal): for p, c in self.links[key]: @@ -604,12 +612,12 @@ def deriveEpsilon(self, nt): rule = self.ambiguity(self.newrules[nt]) else: rule = self.newrules[nt][0] - #print rule + # print rule rhs = rule[1] attr = [None] * len(rhs) - for i in range(len(rhs)-1, -1, -1): + for i in range(len(rhs) - 1, -1, -1): attr[i] = self.deriveEpsilon(rhs[i]) return self.rule2func[self.new2old[rule]](attr) @@ -623,19 +631,19 @@ def buildTree(self, nt, item, tokens, k): rule = choices[0] if len(choices) > 1: rule = self.ambiguity(choices) - #print rule + # print rule rhs = rule[1] attr = [None] * len(rhs) - for i in range(len(rhs)-1, -1, -1): + for i in range(len(rhs) - 1, -1, -1): sym = rhs[i] if not self.newrules.has_key(sym): if sym != self._BOF: - attr[i] = tokens[k-1] + attr[i] = tokens[k - 1] key = (item, k) item, k = self.predecessor(key, None) - #elif self.isnullable(sym): + # elif self.isnullable(sym): elif self._NULLABLE == sym[0:len(self._NULLABLE)]: attr[i] = self.deriveEpsilon(sym) else: @@ -660,7 +668,7 @@ def ambiguity(self, rules): sortlist.append((len(rhs), name)) name2index[name] = i sortlist.sort() - list = map(lambda (a,b): b, sortlist) + list = map(lambda (a, b): b, sortlist) return rules[name2index[self.resolve(list)]] def resolve(self, list): @@ -671,6 +679,7 @@ def resolve(self, list): # return list[0] + # # GenericASTBuilder automagically constructs a concrete/abstract syntax tree # for a given input. The extra argument is a class (not an instance!) @@ -686,8 +695,8 @@ def __init__(self, AST, start): def preprocess(self, rule, func): rebind = lambda lhs, self=self: \ - lambda args, lhs=lhs, self=self: \ - self.buildASTNode(args, lhs) + lambda args, lhs=lhs, self=self: \ + self.buildASTNode(args, lhs) lhs, rhs = rule return rule, rebind(lhs) @@ -700,13 +709,15 @@ def buildASTNode(self, args, lhs): children.append(self.terminal(arg)) return self.nonterminal(lhs, children) - def terminal(self, token): return token + def terminal(self, token): + return token def nonterminal(self, type, args): rv = self.AST(type) rv[:len(args)] = args return rv + # # GenericASTTraversal is a Visitor pattern according to Design Patterns. For # each node it attempts to invoke the method n_, falling @@ -720,6 +731,7 @@ def nonterminal(self, type, args): class GenericASTTraversalPruningException: pass + class GenericASTTraversal: def __init__(self, ast): self.ast = ast @@ -766,10 +778,10 @@ def postorder(self, node=None): else: self.default(node) - def default(self, node): pass + # # GenericASTMatcher. AST nodes must have "__getitem__" and "__cmp__" # implemented. @@ -784,8 +796,8 @@ def __init__(self, start, ast): def preprocess(self, rule, func): rebind = lambda func, self=self: \ - lambda args, func=func, self=self: \ - self.foundMatch(args, func) + lambda args, func=func, self=self: \ + self.foundMatch(args, func) lhs, rhs = rule rhslist = list(rhs) rhslist.reverse() @@ -823,17 +835,25 @@ def resolve(self, list): # return list[-1] + def _dump(tokens, sets, states): for i in range(len(sets)): - print 'set', i + print + 'set', i for item in sets[i]: - print '\t', item + print + '\t', item for (lhs, rhs), pos in states[item[0]].items: - print '\t\t', lhs, '::=', - print string.join(rhs[:pos]), - print '.', - print string.join(rhs[pos:]) + print + '\t\t', lhs, '::=', + print + string.join(rhs[:pos]), + print + '.', + print + string.join(rhs[pos:]) if i < len(tokens): print - print 'token', str(tokens[i]) + print + 'token', str(tokens[i]) print diff --git a/src/pgen/parser/grammar.py b/src/pgen/parser/grammar.py index bb2617d374..db3a514af0 100644 --- a/src/pgen/parser/grammar.py +++ b/src/pgen/parser/grammar.py @@ -118,7 +118,8 @@ def genjs(self): return ( "Sk.ParseTables = {\n" + "sym:\n" + - pformat(self.symbol2number).replace("'","") + # NOTE don't quote LHS, closure compiler won't rename through strings as props + pformat(self.symbol2number).replace("'", + "") + # NOTE don't quote LHS, closure compiler won't rename through strings as props ",\n" + "number2symbol:\n" + pformat(self.number2symbol) + @@ -142,7 +143,7 @@ def genjs(self): str(self.start) + "\n};\n" # ick, tuple -> list and None -> null - ).replace("(", "[").replace(")", "]").replace("None", "null") + ).replace("(", "[").replace(")", "]").replace("None", "null") # Map from operator to number (since tokenize doesn't do this) diff --git a/src/pgen/parser/main.py b/src/pgen/parser/main.py index b96534c037..588b87d219 100644 --- a/src/pgen/parser/main.py +++ b/src/pgen/parser/main.py @@ -1,5 +1,6 @@ import pgen import sys + out = """// generated by pgen/main.py Sk.OpMap = { "(": Sk.token.tokens.T_LPAR, @@ -48,8 +49,9 @@ "**=": Sk.token.tokens.T_DOUBLESTAREQUAL, "//": Sk.token.tokens.T_DOUBLESLASH, "//=": Sk.token.tokens.T_DOUBLESLASHEQUAL, -"->": Sk.token.tokens.T_RARROW +"->": Sk.token.tokens.T_RARROW, +"...": Sk.token.tokens.T_ELLIPSIS }; """ + \ - pgen.generate_grammar().genjs() + pgen.generate_grammar().genjs() open(sys.argv[1], "w").write(out) diff --git a/src/pgen/parser/pgen.py b/src/pgen/parser/pgen.py index 24563edbad..1ba241ffbe 100644 --- a/src/pgen/parser/pgen.py +++ b/src/pgen/parser/pgen.py @@ -4,9 +4,11 @@ # Pgen imports import grammar, token, tokenize + class PgenGrammar(grammar.Grammar): pass + class ParserGenerator(object): def __init__(self, filename, stream=None): @@ -17,11 +19,11 @@ def __init__(self, filename, stream=None): self.filename = filename self.stream = stream self.generator = tokenize.generate_tokens(stream.readline) - self.gettoken() # Initialize lookahead + self.gettoken() # Initialize lookahead self.dfas, self.startsymbol = self.parse() if close_stream is not None: close_stream() - self.first = {} # map from symbol name to set of tokens + self.first = {} # map from symbol name to set of tokens self.addfirstsets() def make_grammar(self): @@ -96,7 +98,7 @@ def make_label(self, c, label): return ilabel else: # An operator (any non-numeric token) - itoken = grammar.opmap[value] # Fails if unknown token + itoken = grammar.opmap[value] # Fails if unknown token if itoken in c.tokens: return c.tokens[itoken] else: @@ -110,11 +112,11 @@ def addfirstsets(self): for name in names: if name not in self.first: self.calcfirst(name) - #print name, self.first[name].keys() + # print name, self.first[name].keys() def calcfirst(self, name): dfa = self.dfas[name] - self.first[name] = None # dummy to detect left recursion + self.first[name] = None # dummy to detect left recursion state = dfa[0] totalset = {} overlapcheck = {} @@ -154,14 +156,14 @@ def parse(self): self.expect(token.OP, ":") a, z = self.parse_rhs() self.expect(token.NEWLINE) - #self.dump_nfa(name, a, z) + # self.dump_nfa(name, a, z) dfa = self.make_dfa(a, z) - #self.dump_dfa(name, dfa) + # self.dump_dfa(name, dfa) oldlen = len(dfa) self.simplify_dfa(dfa) newlen = len(dfa) dfas[name] = dfa - #print name, oldlen, newlen + # print name, oldlen, newlen if startsymbol is None: startsymbol = name return dfas, startsymbol @@ -173,10 +175,12 @@ def make_dfa(self, start, finish): # values. assert isinstance(start, NFAState) assert isinstance(finish, NFAState) + def closure(state): base = {} addclosure(state, base) return base + def addclosure(state, base): assert isinstance(state, NFAState) if state in base: @@ -185,8 +189,9 @@ def addclosure(state, base): for label, next in state.arcs: if label is None: addclosure(next, base) + states = [DFAState(closure(start), finish)] - for state in states: # NB states grows while we're iterating + for state in states: # NB states grows while we're iterating arcs = {} for nfastate in state.nfaset: for label, next in nfastate.arcs: @@ -200,7 +205,7 @@ def addclosure(state, base): st = DFAState(nfaset, finish) states.append(st) state.addarc(st, label) - return states # List of DFAState instances; first one is start + return states # List of DFAState instances; first one is start def dump_nfa(self, name, start, finish): print("Dump of NFA for", name) @@ -236,10 +241,10 @@ def simplify_dfa(self, dfa): while changes: changes = False for i, state_i in enumerate(dfa): - for j in range(i+1, len(dfa)): + for j in range(i + 1, len(dfa)): state_j = dfa[j] if state_i == state_j: - #print " unify", i, j + # print " unify", i, j del dfa[j] for state in dfa: state.unifystate(state_j, state_i) @@ -323,7 +328,7 @@ def gettoken(self): while tup[0] in (tokenize.COMMENT, tokenize.NL): tup = next(self.generator) self.type, self.value, self.begin, self.end, self.line = tup - #print token.tok_name[self.type], repr(self.value) + # print token.tok_name[self.type], repr(self.value) def raise_error(self, msg, *args): if args: @@ -334,16 +339,18 @@ def raise_error(self, msg, *args): raise SyntaxError(msg, (self.filename, self.end[0], self.end[1], self.line)) + class NFAState(object): def __init__(self): - self.arcs = [] # list of (label, NFAState) pairs + self.arcs = [] # list of (label, NFAState) pairs def addarc(self, next, label=None): assert label is None or isinstance(label, str) assert isinstance(next, NFAState) self.arcs.append((label, next)) + class DFAState(object): def __init__(self, nfaset, final): @@ -352,7 +359,7 @@ def __init__(self, nfaset, final): assert isinstance(final, NFAState) self.nfaset = nfaset self.isfinal = final in nfaset - self.arcs = {} # map from label to DFAState + self.arcs = {} # map from label to DFAState def addarc(self, next, label): assert isinstance(label, str) @@ -379,6 +386,7 @@ def __eq__(self, other): return False return True + def generate_grammar(filename="Grammar.txt"): p = ParserGenerator(filename) return p.make_grammar() diff --git a/src/pgen/parser/token.py b/src/pgen/parser/token.py index e3875b748c..0ab5ad5908 100755 --- a/src/pgen/parser/token.py +++ b/src/pgen/parser/token.py @@ -9,7 +9,7 @@ # # ./python Lib/token.py -#--start constants-- +# --start constants-- ENDMARKER = 0 NAME = 1 NUMBER = 2 @@ -72,19 +72,22 @@ ERRORTOKEN = 58 N_TOKENS = 59 NT_OFFSET = 256 -#--end constants-- +# --end constants-- tok_name = {value: name for name, value in globals().items() if isinstance(value, int) and not name.startswith('_')} __all__.extend(tok_name.values()) + def ISTERMINAL(x): return x < NT_OFFSET + def ISNONTERMINAL(x): return x >= NT_OFFSET + def ISEOF(x): return x == ENDMARKER @@ -113,7 +116,7 @@ def _main(): if match: name, val = match.group(1, 2) val = int(val) - tokens[val] = name # reverse so we can sort them... + tokens[val] = name # reverse so we can sort them... keys = sorted(tokens.keys()) # load the output skeleton from the target: try: @@ -143,4 +146,4 @@ def _main(): if __name__ == "__main__": - _main() \ No newline at end of file + _main() diff --git a/src/pgen/parser/tokenize.py b/src/pgen/parser/tokenize.py index bdbe9c56e0..a8d84820ab 100644 --- a/src/pgen/parser/tokenize.py +++ b/src/pgen/parser/tokenize.py @@ -34,17 +34,23 @@ # Meredydd changed - why were we using a Python 2 token table from the # filesystem that disagrees with our local token.py? -#from lib2to3.pgen2.token import * +# from lib2to3.pgen2.token import * # from import token # __all__ = [x for x in dir(token) if x[0] != '_'] + ["tokenize", # "generate_tokens", "untokenize"] # del token from token import * + def group(*choices): return '(' + '|'.join(choices) + ')' + + def any(*choices): return group(*choices) + '*' + + def maybe(*choices): return group(*choices) + '?' + Whitespace = r'[ \f\t]*' Comment = r'#[^\r\n]*' Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment) @@ -138,20 +144,24 @@ def maybe(*choices): return group(*choices) + '?' "ur'", 'ur"', "Ur'", 'Ur"', "uR'", 'uR"', "UR'", 'UR"', "br'", 'br"', "Br'", 'Br"', - "bR'", 'bR"', "BR'", 'BR"', ): + "bR'", 'bR"', "BR'", 'BR"',): single_quoted[t] = t tabsize = 8 + class TokenError(Exception): pass + class StopTokenizing(Exception): pass -def printtoken(type, token, xxx_todo_changeme, xxx_todo_changeme1, line): # for testing + +def printtoken(type, token, xxx_todo_changeme, xxx_todo_changeme1, line): # for testing (srow, scol) = xxx_todo_changeme (erow, ecol) = xxx_todo_changeme1 print("%d,%d-%d,%d:\t%s\t%s" % \ - (srow, scol, erow, ecol, tok_name[type], repr(token))) + (srow, scol, erow, ecol, tok_name[type], repr(token))) + def tokenize(readline, tokeneater=printtoken): """ @@ -171,11 +181,13 @@ def tokenize(readline, tokeneater=printtoken): except StopTokenizing: pass + # backwards compatible interface def tokenize_loop(readline, tokeneater): for token_info in generate_tokens(readline): tokeneater(*token_info) + class Untokenizer: def __init__(self): @@ -232,8 +244,10 @@ def compat(self, token, iterable): startline = False toks_append(tokval) + cookie_re = re.compile("coding[:=]\s*([-\w.]+)") + def detect_encoding(readline): """ The detect_encoding() function is used to detect the encoding that should @@ -253,6 +267,7 @@ def detect_encoding(readline): """ bom_found = False encoding = None + def read_or_stop(): try: return readline() @@ -301,6 +316,7 @@ def find_cookie(line): return 'utf-8', [first, second] + def untokenize(iterable): """Transform tokens back into Python source code. @@ -322,6 +338,7 @@ def untokenize(iterable): ut = Untokenizer() return ut.untokenize(iterable) + def generate_tokens(readline): """ The generate_tokens() generator requires one argment, readline, which @@ -344,7 +361,7 @@ def generate_tokens(readline): contline = None indents = [0] - while 1: # loop over lines in stream + while 1: # loop over lines in stream try: line = readline() except StopIteration: @@ -352,7 +369,7 @@ def generate_tokens(readline): lnum = lnum + 1 pos, max = 0, len(line) - if contstr: # continued string + if contstr: # continued string if not line: raise TokenError("EOF in multi-line string", strstart) endmatch = endprog.match(line) @@ -364,7 +381,7 @@ def generate_tokens(readline): contline = None elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n': yield (ERRORTOKEN, contstr + line, - strstart, (lnum, len(line)), contline) + strstart, (lnum, len(line)), contline) contstr = '' contline = None continue @@ -376,15 +393,19 @@ def generate_tokens(readline): elif parenlev == 0 and not continued: # new statement if not line: break column = 0 - while pos < max: # measure leading whitespace - if line[pos] == ' ': column = column + 1 - elif line[pos] == '\t': column = (column/tabsize + 1)*tabsize - elif line[pos] == '\f': column = 0 - else: break + while pos < max: # measure leading whitespace + if line[pos] == ' ': + column = column + 1 + elif line[pos] == '\t': + column = (column / tabsize + 1) * tabsize + elif line[pos] == '\f': + column = 0 + else: + break pos = pos + 1 if pos == max: break - if line[pos] in '#\r\n': # skip comments or blank lines + if line[pos] in '#\r\n': # skip comments or blank lines if line[pos] == '#': comment_token = line[pos:].rstrip('\r\n') nl_pos = pos + len(comment_token) @@ -397,7 +418,7 @@ def generate_tokens(readline): (lnum, pos), (lnum, len(line)), line) continue - if column > indents[-1]: # count indents or dedents + if column > indents[-1]: # count indents or dedents indents.append(column) yield (INDENT, line[:pos], (lnum, 0), (lnum, pos), line) while column < indents[-1]: @@ -408,20 +429,20 @@ def generate_tokens(readline): indents = indents[:-1] yield (DEDENT, '', (lnum, pos), (lnum, pos), line) - else: # continued statement + else: # continued statement if not line: raise TokenError("EOF in multi-line statement", (lnum, 0)) continued = 0 while pos < max: pseudomatch = pseudoprog.match(line, pos) - if pseudomatch: # scan for tokens + if pseudomatch: # scan for tokens start, end = pseudomatch.span(1) spos, epos, pos = (lnum, start), (lnum, end), end token, initial = line[start:end], line[start] if initial in numchars or \ - (initial == '.' and token != '.'): # ordinary number + (initial == '.' and token != '.'): # ordinary number yield (NUMBER, token, spos, epos, line) elif initial in '\r\n': newline = NEWLINE @@ -434,47 +455,53 @@ def generate_tokens(readline): elif token in triple_quoted: endprog = endprogs[token] endmatch = endprog.match(line, pos) - if endmatch: # all on one line + if endmatch: # all on one line pos = endmatch.end(0) token = line[start:pos] yield (STRING, token, spos, (lnum, pos), line) else: - strstart = (lnum, start) # multiple lines + strstart = (lnum, start) # multiple lines contstr = line[start:] contline = line break elif initial in single_quoted or \ - token[:2] in single_quoted or \ - token[:3] in single_quoted: - if token[-1] == '\n': # continued string + token[:2] in single_quoted or \ + token[:3] in single_quoted: + if token[-1] == '\n': # continued string strstart = (lnum, start) endprog = (endprogs[initial] or endprogs[token[1]] or endprogs[token[2]]) contstr, needcont = line[start:], 1 contline = line break - else: # ordinary string + else: # ordinary string yield (STRING, token, spos, epos, line) - elif initial in namechars: # ordinary name + elif initial in namechars: # ordinary name yield (NAME, token, spos, epos, line) - elif initial == '\\': # continued stmt + elif initial == '\\': # continued stmt # This yield is new; needed for better idempotency: yield (NL, token, spos, (lnum, pos), line) continued = 1 else: - if initial in '([{': parenlev = parenlev + 1 - elif initial in ')]}': parenlev = parenlev - 1 + if initial in '([{': + parenlev = parenlev + 1 + elif initial in ')]}': + parenlev = parenlev - 1 yield (OP, token, spos, epos, line) else: yield (ERRORTOKEN, line[pos], - (lnum, pos), (lnum, pos+1), line) + (lnum, pos), (lnum, pos + 1), line) pos = pos + 1 - for indent in indents[1:]: # pop remaining indent levels + for indent in indents[1:]: # pop remaining indent levels yield (DEDENT, '', (lnum, 0), (lnum, 0), '') yield (ENDMARKER, '', (lnum, 0), (lnum, 0), '') -if __name__ == '__main__': # testing + +if __name__ == '__main__': # testing import sys - if len(sys.argv) > 1: tokenize(open(sys.argv[1]).readline) - else: tokenize(sys.stdin.readline) + + if len(sys.argv) > 1: + tokenize(open(sys.argv[1]).readline) + else: + tokenize(sys.stdin.readline) diff --git a/src/print.js b/src/print.js index b504626a89..e27ac3cb5b 100644 --- a/src/print.js +++ b/src/print.js @@ -4,27 +4,28 @@ anything for the internal implementation */ -var print_f = function function_print(kwa) { - Sk.builtin.pyCheckArgsLen("print", arguments.length, 0, Infinity, true, false); - var args = Array.prototype.slice.call(arguments, 1); - var kwargs = new Sk.builtins.dict(kwa); - var _kwargs = Sk.ffi.remapToJs(kwargs); +Sk.builtin.print = function print(args, kwargs) { + const kwarg_vals = Sk.abstr.copyKeywordsToNamedArgs("print", ["sep", "end", "file"], [], kwargs, [ + Sk.builtin.none.none$, + Sk.builtin.none.none$, + Sk.builtin.none.none$, + ]); // defaults, null for None var kw_list = { - "sep": " ", - "end": "\n", - "file": null + sep: " ", + end: "\n", + file: null, }; var remap_val; var is_none; // check for sep; string or None - remap_val = kwargs.mp$lookup(new Sk.builtin.str("sep")); - if(remap_val !== undefined) { + remap_val = kwarg_vals[0]; + if (remap_val !== undefined) { is_none = Sk.builtin.checkNone(remap_val); - if(Sk.builtin.checkString(remap_val) || is_none) { + if (is_none || Sk.builtin.checkString(remap_val)) { kw_list["sep"] = is_none ? kw_list["sep"] : Sk.ffi.remapToJs(remap_val); // only reassign for string } else { throw new Sk.builtin.TypeError("sep must be None or a string, not " + Sk.abstr.typeName(remap_val)); @@ -32,10 +33,10 @@ var print_f = function function_print(kwa) { } // check for end; string or None - remap_val = kwargs.mp$lookup(new Sk.builtin.str("end")); - if(remap_val !== undefined) { + remap_val = kwarg_vals[1]; + if (remap_val !== undefined) { is_none = Sk.builtin.checkNone(remap_val); - if(Sk.builtin.checkString(remap_val) || is_none) { + if (is_none || Sk.builtin.checkString(remap_val)) { kw_list["end"] = is_none ? kw_list["end"] : Sk.ffi.remapToJs(remap_val); // only reassign for string } else { throw new Sk.builtin.TypeError("end must be None or a string, not " + Sk.abstr.typeName(remap_val)); @@ -44,10 +45,10 @@ var print_f = function function_print(kwa) { // check for file // allow None, though just keep null or check if value has attribute write - remap_val = kwargs.mp$lookup(new Sk.builtin.str("file")); - if(remap_val !== undefined) { + remap_val = kwarg_vals[2]; + if (remap_val !== undefined) { is_none = Sk.builtin.checkNone(remap_val); - if(is_none || remap_val.tp$getattr(new Sk.builtin.str("write")) !== undefined) { + if (is_none || remap_val.tp$getattr("write") !== undefined) { kw_list["file"] = is_none ? kw_list["file"] : remap_val; } else { throw new Sk.builtin.AttributeError("'" + Sk.abstr.typeName(remap_val) + "' object has no attribute 'write'"); @@ -57,35 +58,30 @@ var print_f = function function_print(kwa) { // loop through outputs and create output string var s = ""; var i; - for(i = 0; i < args.length; i++) { - s += (new Sk.builtin.str(args[i])).v; // get str repr + for (i = 0; i < args.length; i++) { + s += new Sk.builtin.str(args[i]).v; // get str repr s += kw_list.sep; } - if(args.length > 0 && kw_list.sep.length > 0) { - s = s.substring(0, s.length-kw_list.sep.length); + if (args.length > 0 && kw_list.sep.length > 0) { + s = s.substring(0, s.length - kw_list.sep.length); } s += kw_list.end; - if(kw_list.file !== null) { + if (kw_list.file !== null) { // currently not tested, though it seems that we need to see how we should access the write function in a correct manner Sk.misceval.callsimArray(kw_list.file.write, [kw_list.file, new Sk.builtin.str(s)]); // callsim to write function } else { - return Sk.misceval.chain(Sk.importModule("sys", false, true), function(sys) { - return Sk.misceval.apply(sys["$d"]["stdout"]["write"], undefined, undefined, undefined, [sys["$d"]["stdout"], new Sk.builtin.str(s)]); + if (Sk.globals.sys !== undefined) { + const sys = Sk.globals.sys; + return Sk.misceval.callsimOrSuspendArray(sys.$d.stdout.write, [sys["$d"]["stdout"], new Sk.builtin.str(s)]); + } + return Sk.misceval.chain(Sk.importModule("sys", false, true), function (sys) { + return Sk.misceval.callsimOrSuspendArray(sys["$d"]["stdout"]["write"], [sys["$d"]["stdout"], new Sk.builtin.str(s)]); }); } // ToDo: - // cpython print function may receive another flush kwarg that flushes the output stream immediatelly + // cpython print function may receive another flush kwarg that flushes the output stream immediately return Sk.builtin.none.none$; }; - -print_f.co_kwargs = true; -print_f.co_name = new Sk.builtin.str("print"); -Sk.builtin.print = new Sk.builtin.func(print_f); - -Sk.builtin.print.__doc__ = new Sk.builtin.str("print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n\nPrints the values to a stream, or to sys.stdout by default.\nOptional keyword arguments:\nfile: a file-like object (stream); defaults to the current sys.stdout.\nsep: string inserted between values, default a space.\nend: string appended after the last value, default a newline.\nflush: whether to forcibly flush the stream."); - - -Sk.builtin.input.co_name = new Sk.builtin.str("input"); diff --git a/src/property_class_static.js b/src/property_class_static.js new file mode 100644 index 0000000000..93e46f2b10 --- /dev/null +++ b/src/property_class_static.js @@ -0,0 +1,206 @@ +/** + * @constructor + * @param {Sk.builtin.func} fget + * @param {Sk.builtin.func} fset + * @param {Sk.builtin.func} fdel + * @param {Sk.builtin.str} doc + */ +Sk.builtin.property = Sk.abstr.buildNativeClass("property", { + constructor: function property(fget, fset, fdel, doc) { + // this can be uses as an internal function + // typically these properties will be set in the init method + this.prop$get = fget || Sk.builtin.none.none$; + this.prop$set = fset || Sk.builtin.none.none$; + this.prop$del = fdel || Sk.builtin.none.none$; + this.prop$doc = doc || (fget && fget.$doc) || Sk.builtin.none.none$; + }, + slots: { + tp$getattr: Sk.generic.getAttr, + tp$new: Sk.generic.new, + tp$init: function (args, kwargs) { + args = Sk.abstr.copyKeywordsToNamedArgs( + "property", + ["fget", "fset", "fdel", "doc"], + args, + kwargs, + new Array(4).fill(Sk.builtin.none.none$) + ); + + this.prop$get = args[0]; + this.prop$set = args[1]; + this.prop$del = args[2]; + if (Sk.builtin.checkNone(args[3])) { + if (!Sk.builtin.checkNone(args[0])) { + this.prop$doc = args[0].$doc || args[3]; + } + } else { + this.prop$doc = args[3]; + } + return Sk.builtin.none.none$; + }, + tp$doc: + "Property attribute.\n\n fget\n function to be used for getting an attribute value\n fset\n function to be used for setting an attribute value\n fdel\n function to be used for del'ing an attribute\n doc\n docstring\n\nTypical use is to define a managed attribute x:\n\nclass C(object):\n def getx(self): return self._x\n def setx(self, value): self._x = value\n def delx(self): del self._x\n x = property(getx, setx, delx, 'I'm the 'x' property.')\n\nDecorators make defining new properties or modifying existing ones easy:\n\nclass C(object):\n @property\n def x(self):\n 'I am the 'x' property.'\n return self._x\n @x.setter\n def x(self, value):\n self._x = value\n @x.deleter\n def x(self):\n del self._x", + tp$descr_get: function (obj, type) { + if (obj === null) { + return this; + } + if (this.prop$get === undefined) { + throw new Sk.builtin.AttributeError("unreadable attribute"); + } + return Sk.misceval.callsimOrSuspendArray(this.prop$get, [obj]); + }, + tp$descr_set: function (obj, value) { + let func; + if (value == null) { + func = this.prop$del; + } else { + func = this.prop$set; + } + if (Sk.builtin.checkNone(func)) { + const msg = value == null ? "delete" : "set"; + throw new Sk.builtin.AttributeError("can't " + msg + " attribute"); + } + if (!func.tp$call) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(func) + "' is not callable"); + } + + if (value == null) { + return func.tp$call([obj]); + } else { + return func.tp$call([obj, value]); + } + }, + }, + methods: { + getter: { + $meth: function (fget) { + return new Sk.builtin.property(fget, this.prop$set, this.prop$del, this.prop$doc); + }, + $flags: {OneArg: true}, + }, + setter: { + $meth: function (fset) { + return new Sk.builtin.property(this.prop$get, fset, this.prop$del, this.prop$doc); + }, + $flags: {OneArg: true}, + }, + deleter: { + $meth: function (fdel) { + return new Sk.builtin.property(this.prop$get, this.prop$set, fdel, this.prop$doc); + }, + $flags: {OneArg: true}, + }, + }, + getsets: { + fget: { + $get: function () { + return this.prop$get; + }, + }, + fset: { + $get: function () { + return this.prop$set; + }, + }, + fdel: { + $get: function () { + return this.prop$del; + }, + }, + __doc__: { + $get: function () { + return this.prop$doc; + }, + $set: function (value) { + this.prop$doc = value; + } + }, + }, +}); + +/** + * @constructor + * @param {Sk.builtin.func} callable + */ + +Sk.builtin.classmethod = Sk.abstr.buildNativeClass("classmethod", { + constructor: function classmethod(callable) { + // this can be used as an internal function + // typically callable will be set in the init method if being called by python + this.cm$callable = callable; + this.$d = new Sk.builtin.dict(); + }, + slots: { + tp$getattr: Sk.generic.getAttr, + tp$new: Sk.generic.new, + tp$init: function (args, kwargs) { + Sk.abstr.checkNoKwargs("classmethod", kwargs); + Sk.abstr.checkArgsLen("classmethod", args, 1, 1); + this.cm$callable = args[0]; + return Sk.builtin.none.none$; + }, + tp$doc: + "classmethod(function) -> method\n\nConvert a function to be a class method.\n\nA class method receives the class as implicit first argument,\njust like an instance method receives the instance.\nTo declare a class method, use this idiom:\n\n class C:\n @classmethod\n def f(cls, arg1, arg2, ...):\n ...\n\nIt can be called either on the class (e.g. C.f()) or on an instance\n(e.g. C().f()). The instance is ignored except for its class.\nIf a class method is called for a derived class, the derived class\nobject is passed as the implied first argument.\n\nClass methods are different than C++ or Java static methods.\nIf you want those, see the staticmethod builtin.", + tp$descr_get: function (obj, type) { + if (this.cm$callable === undefined) { + throw new Sk.builtin.RuntimeError("uninitialized classmethod object"); + } + if (type === undefined) { + type = obj.ob$type; + } + const f = this.cm$callable.tp$descr_get; + if (f) { + return f.call(this.cm$callable, type); + } + return new Sk.builtin.method(this.cm$callable, type); + }, + }, + getsets: { + __func__: { + $get: function () { + return this.cm$callable; + }, + }, + __dict__: Sk.generic.getSetDict, + }, +}); + +/** + * @constructor + * @param {Sk.builtin.func} callable + */ + +Sk.builtin.staticmethod = Sk.abstr.buildNativeClass("staticmethod", { + constructor: function staticmethod(callable) { + // this can be used as an internal function + // typically callable will be set in the init method if being called by python + this.sm$callable = callable; + this.$d = new Sk.builtin.dict(); + }, + slots: { + tp$getattr: Sk.generic.getAttr, + tp$new: Sk.generic.new, + tp$init: function (args, kwargs) { + Sk.abstr.checkNoKwargs("staticmethod", kwargs); + Sk.abstr.checkArgsLen("staticmethod", args, 1, 1); + this.sm$callable = args[0]; + return Sk.builtin.none.none$; + }, + tp$doc: + "staticmethod(function) -> method\n\nConvert a function to be a static method.\n\nA static method does not receive an implicit first argument.\nTo declare a static method, use this idiom:\n\n class C:\n @staticmethod\n def f(arg1, arg2, ...):\n ...\n\nIt can be called either on the class (e.g. C.f()) or on an instance\n(e.g. C().f()). The instance is ignored except for its class.\n\nStatic methods in Python are similar to those found in Java or C++.\nFor a more advanced concept, see the classmethod builtin.", + tp$descr_get: function (obj, type) { + if (this.sm$callable === undefined) { + throw new Sk.builtin.RuntimeError("uninitialized staticmethod object"); + } + return this.sm$callable; + }, + }, + getsets: { + __func__: { + $get: function () { + return this.sm$callable; + }, + }, + __dict__: Sk.generic.getSetDict, + }, +}); diff --git a/src/range.js b/src/range.js new file mode 100644 index 0000000000..ad5697ef35 --- /dev/null +++ b/src/range.js @@ -0,0 +1,265 @@ +const JSBI = require("jsbi"); + +/** + * @constructor + * @param {number} start + * @param {number} stop + * @param {number} step + * @param {Object} lst + */ +Sk.builtin.range_ = Sk.abstr.buildNativeClass("range", { + constructor: function range(start, stop, step, lst) { + this.start = start; + this.stop = stop; + this.step = step; + this.v = lst; + }, + slots: { + tp$getattr: Sk.generic.getAttr, + tp$as_sequence_or_mapping: true, + tp$doc: + "range(stop) -> range object\nrange(start, stop[, step]) -> range object\n\nReturn an object that produces a sequence of integers from start (inclusive)\nto stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.\nstart defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.\nThese are exactly the valid indices for a list of 4 elements.\nWhen step is given, it specifies the increment (or decrement).", + tp$new: function (args, kwargs) { + Sk.abstr.checkNoKwargs("range", kwargs); + Sk.abstr.checkArgsLen("range", args, 1, 3); + return rangeFromPy(args[0], args[1], args[2]); + }, + $r: function () { + let name = "range(" + this.start + ", " + this.stop; + if (this.step != 1) { + name += ", " + this.step; + } + name += ")"; + return new Sk.builtin.str(name); + }, + tp$richcompare: function (w, op) { + if ((op !== "Eq" && op !== "NotEq") || w.ob$type !== Sk.builtin.range_) { + return Sk.builtin.NotImplemented.NotImplemented$; + } + w = new Sk.builtin.list(w.v); + return new Sk.builtin.list(this.v).tp$richcompare(w, op); + }, + tp$iter: function () { + return new Sk.builtin.range_iter_(this); + }, + nb$bool: function () { + return this.v.length !== 0; + }, + // sequence and mapping slots + sq$contains: function (item) { + const lst = this.v; + for (let i = 0; i < lst.length; i++) { + if (Sk.misceval.richCompareBool(item, lst[i], "Eq")) { + return true; + } + } + return false; + }, + sq$length: function () { + return this.v.length; + }, + mp$subscript: function (index) { + if (Sk.misceval.isIndex(index)) { + let i = Sk.misceval.asIndex(index); + if (i < 0) { + i = this.v.length + i; + } + if (i < 0 || i >= this.v.length) { + throw new Sk.builtin.IndexError("range object index out of range"); + } + return this.v[i]; + } else if (index.constructor === Sk.builtin.slice) { + const ret = []; + const lst = this.v; + index.sssiter$(lst.length, (i) => { + ret.push(lst[i]); + }); + const sss = index.$slice_indices(); + const start = Sk.misceval.asIndex(lst[sss[0]]) || this.start; + const stop = Sk.misceval.asIndex(lst[sss[1]]) || this.stop; + let step; + if (typeof this.step === "number") { + step = sss[2] * this.step; + } else { + step = JSBI.multiply(this.step, JSBI.BigInt(sss[2])); + } + return new Sk.builtin.range_(start, stop, step, ret); + } + throw new Sk.builtin.TypeError("range indices must be integers or slices, not " + Sk.abstr.typeName(index)); + }, + }, + getsets: { + start: { + $get: function () { + return new Sk.builtin.int_(this.start); + }, + }, + step: { + $get: function () { + return new Sk.builtin.int_(this.step); + }, + }, + stop: { + $get: function () { + return new Sk.builtin.int_(this.stop); + }, + }, + }, + methods: { + __reversed__: { + $meth: function () { + return new Sk.builtin.revereserange_iter_(this); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Return a reverse iterator.", + }, + // __reduce__: { + // $meth: methods.__reduce__, + // $flags:{}, + // $textsig: null, + // $doc: "" }, + count: { + $meth: function (item) { + const lst = this.v; + let count = 0; + for (let i = 0; i < lst.length; i++) { + if (Sk.misceval.richCompareBool(item, lst[i], "Eq")) { + count++; + } + } + return new Sk.builtin.int_(count); + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "rangeobject.count(value) -> integer -- return number of occurrences of value", + }, + index: { + $meth: function (item) { + const lst = this.v; + for (let i = 0; i < lst.length; i++) { + if (Sk.misceval.richCompareBool(item, lst[i], "Eq")) { + return new Sk.builtin.int_(i); + } + } + throw new Sk.builtin.ValueError(Sk.misceval.objectRepr(item) + "is not in range"); + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "rangeobject.index(value, [start, [stop]]) -> integer -- return index of value.\nRaise ValueError if the value is not present.", + }, + }, + proto: { + sk$asarray: function () { + return this.v.slice(0); + }, + }, + flags: { + sk$acceptable_as_base_class: false, + }, +}); + +function rangeFromPy(start, stop, step) { + start = start === undefined ? start : Sk.misceval.asIndexOrThrow(start); + stop = stop === undefined ? stop : Sk.misceval.asIndexOrThrow(stop); + step = step === undefined ? step : Sk.misceval.asIndexOrThrow(step); + if (stop === undefined && step === undefined) { + stop = start; + start = 0; + step = 1; + } else if (step === undefined) { + step = 1; + } else if (step === 0) { + throw new Sk.builtin.ValueError("range() step argument must not be zero"); + } + const ret = []; + if (typeof start === "number" && typeof stop === "number" && typeof step === "number") { + if (step > 0) { + for (let i = start; i < stop; i += step) { + ret.push(new Sk.builtin.int_(i)); + } + } else { + for (let i = start; i > stop; i += step) { + ret.push(new Sk.builtin.int_(i)); + } + } + } else { + // This is going to be slow + let i; + start = i = JSBI.BigInt(start); + step = JSBI.BigInt(step); + stop = JSBI.BigInt(stop); + if (!step.sign) { + while (JSBI.lessThan(i, stop)) { + ret.push(new Sk.builtin.int_(convertIfSafe(i))); + i = JSBI.add(i, step); + } + } else { + while (JSBI.greaterThan(i, stop)) { + ret.push(new Sk.builtin.int_(convertIfSafe(i))); + i = JSBI.add(i, step); + } + } + start = convertIfSafe(start); + step = convertIfSafe(step); + stop = convertIfSafe(stop); + } + return new Sk.builtin.range_(start, stop, step, ret); +} + +Sk.builtin.range_iter_ = Sk.abstr.buildIteratorClass("range_iterator", { + constructor: function range_iter_(range_obj) { + this.$index = 0; + this.$seq = range_obj.v; + }, + iternext: function () { + return this.$seq[this.$index++]; + // we could check that the index is not outside of range + // but it will still return undefined so no need? + }, + methods: { + __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef, + }, + flags: {sk$acceptable_as_base_class: false}, +}); + +Sk.builtin.revereserange_iter_ = Sk.abstr.buildIteratorClass("range_reverseiterator", { + constructor: function range_iter(range_obj) { + this.$seq = range_obj.v; + this.$index = this.$seq.length - 1; + }, + iternext: function () { + if (this.$index < 0) { + return undefined; + } + return this.$seq[this.$index--]; + }, + methods: { + __length_hint__: Sk.generic.iterReverseLengthHintMethodDef + }, + flags: {sk$acceptable_as_base_class: false}, +}); + +const MaxSafeBig = JSBI.BigInt(Number.MAX_SAFE_INTEGER); +const MaxSafeBigNeg = JSBI.BigInt(-Number.MAX_SAFE_INTEGER); +function convertIfSafe(v) { + if (JSBI.lessThan(v, MaxSafeBig) && JSBI.greaterThan(v, MaxSafeBigNeg)) { + return JSBI.toNumber(v); + } + return v; +} + +/** + * + * @description + * Python 2 implementations of range and xrange + * + * @param {*} start + * @param {*} stop + * @param {*} step + * @ignore + */ +Sk.builtin.range = Sk.builtin.xrange = function range(start, stop, step) { + const ret = rangeFromPy(start, stop, step); + return new Sk.builtin.list(ret.v); +}; diff --git a/src/seqtype.js b/src/seqtype.js index 0835460ea4..68548b8a9e 100644 --- a/src/seqtype.js +++ b/src/seqtype.js @@ -30,7 +30,7 @@ Sk.builtin.seqtype.prototype["__len__"] = new Sk.builtin.func(function (self) { Sk.builtin.pyCheckArgsLen("__len__", arguments.length, 0, 0, false, true); - return new Sk.builtin.int_(self.sq$length()); + return new Sk.builtin.int_(self.sq$length()); }); @@ -128,6 +128,6 @@ Sk.builtin.seqtype.prototype["__rmul__"] = new Sk.builtin.func(function (self, n Sk.builtin.pyCheckArgsLen("__rmul__", arguments.length, 1, 1, false, true); - return self.sq$repeat(n); + return self.sq$repeat(n); }); diff --git a/src/set.js b/src/set.js index ad5ec19a6f..ce3772a3fe 100644 --- a/src/set.js +++ b/src/set.js @@ -1,466 +1,482 @@ /** + * * @constructor - * @param {Array.} S + * @param {Array} S + * + * @description + * internally call new Sk.builtin.set with an array of python objects */ -Sk.builtin.set = function (S) { - var it, i; - var S_list; - if (!(this instanceof Sk.builtin.set)) { - Sk.builtin.pyCheckArgsLen("set", arguments.length, 0, 1); - return new Sk.builtin.set(S); - } - - - if (typeof(S) === "undefined") { - S = []; - } - - this.set_reset_(); - S_list = new Sk.builtin.list(S); - // python sorts sets on init, but not thereafter. - // Skulpt seems to init a new set each time you add/remove something - //Sk.builtin.list.prototype['sort'].func_code(S); - for (it = Sk.abstr.iter(S_list), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - Sk.builtin.set.prototype["add"].func_code(this, i); - } - - this.__class__ = Sk.builtin.set; - - this["v"] = this.v; - return this; -}; -Sk.abstr.setUpInheritance("set", Sk.builtin.set, Sk.builtin.object); -Sk.abstr.markUnhashable(Sk.builtin.set); - -Sk.builtin.set.prototype.set_reset_ = function () { - this.v = new Sk.builtin.dict([]); -}; - -Sk.builtin.set.prototype["$r"] = function () { - var it, i; - var ret = []; - for (it = Sk.abstr.iter(this), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - ret.push(Sk.misceval.objectRepr(i).v); - } - - if(Sk.__future__.set_repr) { - if (ret.length === 0) { - return new Sk.builtin.str("set()"); - } else { - return new Sk.builtin.str("{" + ret.join(", ") + "}"); - } - } else { - return new Sk.builtin.str("set([" + ret.join(", ") + "])"); - } -}; - -Sk.builtin.set.prototype.ob$eq = function (other) { - - if (this === other) { - return Sk.builtin.bool.true$; - } - - if (!(other instanceof Sk.builtin.set)) { - return Sk.builtin.bool.false$; - } - - if (Sk.builtin.set.prototype.sq$length.call(this) !== - Sk.builtin.set.prototype.sq$length.call(other)) { - return Sk.builtin.bool.false$; - } - - return this["issubset"].func_code(this, other); -}; - -Sk.builtin.set.prototype.ob$ne = function (other) { - - if (this === other) { - return Sk.builtin.bool.false$; - } - - if (!(other instanceof Sk.builtin.set)) { - return Sk.builtin.bool.true$; - } - - if (Sk.builtin.set.prototype.sq$length.call(this) !== - Sk.builtin.set.prototype.sq$length.call(other)) { - return Sk.builtin.bool.true$; - } - - if (this["issubset"].func_code(this, other).v) { - return Sk.builtin.bool.false$; - } else { - return Sk.builtin.bool.true$; - } -}; - -Sk.builtin.set.prototype.ob$lt = function (other) { - - if (this === other) { - return Sk.builtin.bool.false$; - } - - if (Sk.builtin.set.prototype.sq$length.call(this) >= - Sk.builtin.set.prototype.sq$length.call(other)) { - return Sk.builtin.bool.false$; - } - - return this["issubset"].func_code(this, other); -}; - -Sk.builtin.set.prototype.ob$le = function (other) { - - if (this === other) { - return Sk.builtin.bool.true$; - } - - if (Sk.builtin.set.prototype.sq$length.call(this) > - Sk.builtin.set.prototype.sq$length.call(other)) { - return Sk.builtin.bool.false$; - } - - return this["issubset"].func_code(this, other); -}; - -Sk.builtin.set.prototype.ob$gt = function (other) { - - if (this === other) { - return Sk.builtin.bool.false$; - } - - if (Sk.builtin.set.prototype.sq$length.call(this) <= - Sk.builtin.set.prototype.sq$length.call(other)) { - return Sk.builtin.bool.false$; - } - - return this["issuperset"].func_code(this, other); -}; - -Sk.builtin.set.prototype.ob$ge = function (other) { - - if (this === other) { - return Sk.builtin.bool.true$; - } - - if (Sk.builtin.set.prototype.sq$length.call(this) < - Sk.builtin.set.prototype.sq$length.call(other)) { - return Sk.builtin.bool.false$; - } - - return this["issuperset"].func_code(this, other); -}; - -Sk.builtin.set.prototype.nb$and = function(other){ - return this["intersection"].func_code(this, other); -}; - -Sk.builtin.set.prototype.nb$or = function(other){ - return this["union"].func_code(this, other); -}; - -Sk.builtin.set.prototype.nb$xor = function(other){ - return this["symmetric_difference"].func_code(this, other); -}; - -Sk.builtin.set.prototype.nb$subtract = function(other){ - return this["difference"].func_code(this, other); -}; - -Sk.builtin.set.prototype["__iter__"] = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("__iter__", arguments.length, 0, 0, false, true); - return new Sk.builtin.set_iter_(self); -}); - -Sk.builtin.set.prototype.tp$iter = function () { - return new Sk.builtin.set_iter_(this); -}; - -Sk.builtin.set.prototype.sq$length = function () { - return this["v"].mp$length(); -}; - -Sk.builtin.set.prototype.sq$contains = function(ob) { - return this["v"].sq$contains(ob); -}; - -Sk.builtin.set.prototype["isdisjoint"] = new Sk.builtin.func(function (self, other) { - // requires all items in self to not be in other - var isIn; - var it, item; - - Sk.builtin.pyCheckArgsLen("isdisjoint", arguments.length, 2, 2); - if (!Sk.builtin.checkIterable(other)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(other) + "' object is not iterable"); - } - - for (it = Sk.abstr.iter(self), item = it.tp$iternext(); item !== undefined; item = it.tp$iternext()) { - isIn = Sk.abstr.sequenceContains(other, item); - if (isIn) { - return Sk.builtin.bool.false$; - } - } - return Sk.builtin.bool.true$; -}); - -Sk.builtin.set.prototype["issubset"] = new Sk.builtin.func(function (self, other) { - var isIn; - var it, item; - var selfLength, otherLength; - - Sk.builtin.pyCheckArgsLen("issubset", arguments.length, 2, 2); - if (!Sk.builtin.checkIterable(other)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(other) + "' object is not iterable"); - } - - selfLength = self.sq$length(); - otherLength = other.sq$length(); - - if (selfLength > otherLength) { - // every item in this set can't be in other if it's shorter! - return Sk.builtin.bool.false$; - } - for (it = Sk.abstr.iter(self), item = it.tp$iternext(); item !== undefined; item = it.tp$iternext()) { - isIn = Sk.abstr.sequenceContains(other, item); - if (!isIn) { - return Sk.builtin.bool.false$; - } - } - return Sk.builtin.bool.true$; -}); - -Sk.builtin.set.prototype["issuperset"] = new Sk.builtin.func(function (self, other) { - Sk.builtin.pyCheckArgsLen("issuperset", arguments.length, 2, 2); - return Sk.builtin.set.prototype["issubset"].func_code(other, self); -}); - -Sk.builtin.set.prototype["union"] = new Sk.builtin.func(function (self) { - var S, i, new_args; - - Sk.builtin.pyCheckArgsLen("union", arguments.length, 1); - - S = Sk.builtin.set.prototype["copy"].func_code(self); - new_args = [S]; - for (i = 1; i < arguments.length; i++) { - new_args.push(arguments[i]); - } - - Sk.builtin.set.prototype["update"].func_code.apply(null, new_args); - return S; -}); - -Sk.builtin.set.prototype["intersection"] = new Sk.builtin.func(function (self) { - var S, i, new_args; - - Sk.builtin.pyCheckArgsLen("intersection", arguments.length, 1); - - S = Sk.builtin.set.prototype["copy"].func_code(self); - new_args = [S]; - for (i = 1; i < arguments.length; i++) { - new_args.push(arguments[i]); - } - - Sk.builtin.set.prototype["intersection_update"].func_code.apply(null, new_args); - return S; -}); - -Sk.builtin.set.prototype["difference"] = new Sk.builtin.func(function (self, other) { - var S, i, new_args; - - Sk.builtin.pyCheckArgsLen("difference", arguments.length, 2); - - S = Sk.builtin.set.prototype["copy"].func_code(self); - new_args = [S]; - for (i = 1; i < arguments.length; i++) { - new_args.push(arguments[i]); - } - - Sk.builtin.set.prototype["difference_update"].func_code.apply(null, new_args); - return S; -}); - -Sk.builtin.set.prototype["symmetric_difference"] = new Sk.builtin.func(function (self, other) { - var it, item, S; - - Sk.builtin.pyCheckArgsLen("symmetric_difference", arguments.length, 2, 2); - - S = Sk.builtin.set.prototype["union"].func_code(self, other); - for (it = Sk.abstr.iter(S), item = it.tp$iternext(); item !== undefined; item = it.tp$iternext()) { - if (Sk.abstr.sequenceContains(self, item) && Sk.abstr.sequenceContains(other, item)) { - Sk.builtin.set.prototype["discard"].func_code(S, item); - } - } - return S; -}); - -Sk.builtin.set.prototype["copy"] = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("copy", arguments.length, 1, 1); - return new Sk.builtin.set(self); -}); - -Sk.builtin.set.prototype["update"] = new Sk.builtin.func(function (self, other) { - var i, it, item, arg; - - Sk.builtin.pyCheckArgsLen("update", arguments.length, 2); - - for (i = 1; i < arguments.length; i++) { - arg = arguments[i]; - if (!Sk.builtin.checkIterable(arg)) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(arg) + "' object is not iterable"); +Sk.builtin.set = Sk.abstr.buildNativeClass("set", { + constructor: function set(S) { + if (S === undefined) { + S = []; } - for (it = Sk.abstr.iter(arg), item = it.tp$iternext(); - item !== undefined; - item = it.tp$iternext()) { - Sk.builtin.set.prototype["add"].func_code(self, item); + Sk.asserts.assert(Array.isArray(S) && this instanceof Sk.builtin.set, "Bad call to set - must be called with an Array and 'new'"); + const L = []; + for (let i = 0; i < S.length; i++) { + L.push(S[i]); + L.push(true); } - } - - return Sk.builtin.none.none$; -}); - -Sk.builtin.set.prototype["intersection_update"] = new Sk.builtin.func(function (self, other) { - var i, it, item; - - Sk.builtin.pyCheckArgsLen("intersection_update", arguments.length, 2); - for (i = 1; i < arguments.length; i++) { - if (!Sk.builtin.checkIterable(arguments[i])) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(arguments[i]) + - "' object is not iterable"); - } - } - - for (it = Sk.abstr.iter(self), item = it.tp$iternext(); item !== undefined; item = it.tp$iternext()) { - for (i = 1; i < arguments.length; i++) { - if (!Sk.abstr.sequenceContains(arguments[i], item)) { - Sk.builtin.set.prototype["discard"].func_code(self, item); - break; + this.v = new Sk.builtin.dict(L); + }, + slots: /**@lends {Sk.builtin.set.prototype}*/ { + tp$getattr: Sk.generic.getAttr, + tp$as_number: true, + tp$as_sequence_or_mapping: true, + tp$hash: Sk.builtin.none.none$, + tp$doc: "set() -> new empty set object\nset(iterable) -> new set object\n\nBuild an unordered collection of unique elements.", + tp$init: function (args, kwargs) { + Sk.abstr.checkNoKwargs("set", kwargs); + Sk.abstr.checkArgsLen("set", args, 0, 1); + return Sk.builtin.set.prototype.update.$meth.call(this, ...args); + }, + tp$new: Sk.generic.new, + $r: function () { + const ret = this.sk$asarray().map((x) => Sk.misceval.objectRepr(x)); + if (Sk.__future__.python3) { + if (ret.length === 0) { + return new Sk.builtin.str(Sk.abstr.typeName(this) + "()"); + } else if (this.ob$type !== Sk.builtin.set) { + // then we are a subclass of set + return new Sk.builtin.str(Sk.abstr.typeName(this) + "({" + ret.join(", ") + "})"); + } else { + return new Sk.builtin.str("{" + ret.join(", ") + "}"); + } + } else { + return new Sk.builtin.str(Sk.abstr.typeName(this) + "([" + ret.join(", ") + "])"); } - } - } - return Sk.builtin.none.none$; -}); - -Sk.builtin.set.prototype["difference_update"] = new Sk.builtin.func(function (self, other) { - var i, it, item; - - Sk.builtin.pyCheckArgsLen("difference_update", arguments.length, 2); - for (i = 1; i < arguments.length; i++) { - if (!Sk.builtin.checkIterable(arguments[i])) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(arguments[i]) + - "' object is not iterable"); - } - } - - for (it = Sk.abstr.iter(self), item = it.tp$iternext(); item !== undefined; item = it.tp$iternext()) { - for (i = 1; i < arguments.length; i++) { - if (Sk.abstr.sequenceContains(arguments[i], item)) { - Sk.builtin.set.prototype["discard"].func_code(self, item); - break; + }, + tp$iter: function () { + return new Sk.builtin.set_iter_(this); + }, + tp$richcompare: function (other, op) { + if (!Sk.builtin.checkAnySet(other)) { + return Sk.builtin.NotImplemented.NotImplemented$; } - } - } - return Sk.builtin.none.none$; -}); - -Sk.builtin.set.prototype["symmetric_difference_update"] = new Sk.builtin.func(function (self, other) { - Sk.builtin.pyCheckArgsLen("symmetric_difference_update", arguments.length, 2, 2); - - var sd = Sk.builtin.set.prototype["symmetric_difference"].func_code(self, other); - self.set_reset_(); - Sk.builtin.set.prototype["update"].func_code(self, sd); - return Sk.builtin.none.none$; -}); - - -Sk.builtin.set.prototype["add"] = new Sk.builtin.func(function (self, item) { - Sk.builtin.pyCheckArgsLen("add", arguments.length, 2, 2); - - self.v.mp$ass_subscript(item, true); - return Sk.builtin.none.none$; -}); - -Sk.builtin.set.prototype["discard"] = new Sk.builtin.func(function (self, item) { - Sk.builtin.pyCheckArgsLen("discard", arguments.length, 2, 2); - - Sk.builtin.dict.prototype["pop"].func_code(self.v, item, - Sk.builtin.none.none$); - return Sk.builtin.none.none$; -}); - -Sk.builtin.set.prototype["pop"] = new Sk.builtin.func(function (self) { - var it, item; - - Sk.builtin.pyCheckArgsLen("pop", arguments.length, 1, 1); - - if (self.sq$length() === 0) { - throw new Sk.builtin.KeyError("pop from an empty set"); - } - - it = Sk.abstr.iter(self); - item = it.tp$iternext(); - Sk.builtin.set.prototype["discard"].func_code(self, item); - return item; -}); - -Sk.builtin.set.prototype["remove"] = new Sk.builtin.func(function (self, item) { - Sk.builtin.pyCheckArgsLen("remove", arguments.length, 2, 2); - - self.v.mp$del_subscript(item); - return Sk.builtin.none.none$; + switch (op) { + case "Eq": + if (this.get$size() !== other.get$size()) { + return false; + } + if (this === other) { + return true; + } + return Sk.misceval.isTrue(this.issubset.$meth.call(this, other)); + case "NotEq": + const res = this.tp$richcompare(other, "Eq"); + if (res === Sk.builtin.NotImplemented.NotImplemented$) { + return res; + } + return !res; + case "LtE": + if (this === other) { + return true; + } + return Sk.misceval.isTrue(this.issubset.$meth.call(this, other)); + case "GtE": + if (this === other) { + return true; + } + return Sk.misceval.isTrue(this.issuperset.$meth.call(this, other)); + case "Lt": + if (this.get$size() >= other.get$size()) { + return false; + } + return Sk.misceval.isTrue(this.issubset.$meth.call(this, other)); + case "Gt": + if (this.get$size() <= other.get$size()) { + return false; + } + return Sk.misceval.isTrue(this.issuperset.$meth.call(this, other)); + } + }, + // number slots + nb$subtract: numberSlot(function (other) { + return this.difference.$meth.call(this, other); + }), + nb$and: numberSlot(function (other) { + return this.intersection.$meth.call(this, other); + }), + nb$or: numberSlot(function (other) { + return this.union.$meth.call(this, other); + }), + nb$xor: numberSlot(function (other) { + return this.symmetric_difference.$meth.call(this, other); + }), + nb$inplace_subtract: numberSlot(function (other) { + return this.difference_update.$meth.call(this, other); + }), + nb$inplace_and: numberSlot(function (other) { + return this.intersection_update.$meth.call(this, other); + }), + nb$inplace_or: numberSlot(function (other) { + return this.update.$meth.call(this, other); + }), + nb$inplace_xor: numberSlot(function (other) { + return this.symmetric_difference_update.$meth.call(this, other); + }), + // sequence or mapping slots + sq$length: function () { + return this.get$size(); + }, + sq$contains: function (ob) { + return this.v.sq$contains(ob); + }, + }, + methods: /**@lends {Sk.builtin.set.prototype}*/ { + add: { + $meth: function (item) { + this.v.mp$ass_subscript(item, true); + return Sk.builtin.none.none$; + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "Add an element to a set.\n\nThis has no effect if the element is already present.", + }, + clear: { + $meth: function () { + this.v = new Sk.builtin.dict([]); + return Sk.builtin.none.none$; + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Remove all elements from this set.", + }, + copy: { + $meth: function () { + return new this.constructor(this.sk$asarray()); + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Return a shallow copy of a set.", + }, + discard: { + $meth: function (item) { + Sk.misceval.callsimArray(this.v.pop, [this.v, item, Sk.builtin.none.none$]); + return Sk.builtin.none.none$; + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "Remove an element from a set if it is a member.\n\nIf the element is not a member, do nothing.", + }, + difference: { + $meth: function (...args) { + const tmp = this.copy.$meth.call(this); // could be a set or a frozenset + Sk.builtin.set.prototype.difference_update.$meth.call(tmp, ...args); + return tmp; + }, + $flags: {MinArgs: 0}, + $textsig: null, + $doc: "Return the difference of two or more sets as a new set.\n\n(i.e. all elements that are in this set but not the others.)", + }, + difference_update: { + $meth: function (...args) { + if (!args.length) { + return Sk.builtin.none.none$; + } + // we don't use the iterator since a set shouldn't change size during iteration + const vals = this.sk$asarray(); + const discard = Sk.builtin.set.prototype.discard.$meth; + for (let j = 0; j < vals.length; j++) { + const item = vals[j]; + for (let i = 0; i < args.length; i++) { + if (Sk.abstr.sequenceContains(args[i], item)) { + discard.call(this, item); + break; + } + } + } + return Sk.builtin.none.none$; + }, + $flags: {MinArgs: 0}, + $textsig: null, + $doc: "Remove all elements of another set from this set.", + }, + intersection: { + $meth: function (...args) { + const tmp = this.copy.$meth.call(this); + Sk.builtin.set.prototype.intersection_update.$meth.call(tmp, ...args); + return tmp; + }, + $flags: {MinArgs: 0}, + $textsig: null, + $doc: "Return the intersection of two sets as a new set.\n\n(i.e. all elements that are in both sets.)", + }, + intersection_update: { + $meth: function (...args) { + if (!args.length) { + return Sk.builtin.none.none$; + } + const vals = this.sk$asarray(); + const discard = Sk.builtin.set.prototype.discard.$meth; + for (let j = 0; j < vals.length; j++) { + const item = vals[j]; + for (let i = 0; i < args.length; i++) { + if (!Sk.abstr.sequenceContains(args[i], item)) { + discard.call(this, item); + break; + } + } + } + return Sk.builtin.none.none$; + }, + $flags: {MinArgs: 0}, + $textsig: null, + $doc: "Update a set with the intersection of itself and another.", + }, + isdisjoint: { + $meth: function (other) { + // requires all items in this to not be in other + let isIn; + other = Sk.misceval.arrayFromIterable(other); + for (let i = 0; i < other.length; i++) { + isIn = this.sq$contains(other[i]); + if (isIn) { + return Sk.builtin.bool.false$; + } + } + return Sk.builtin.bool.true$; + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "Return True if two sets have a null intersection.", + }, + issubset: { + $meth: function (other) { + if (!Sk.builtin.checkAnySet(other)) { + other = new Sk.builtin.set(Sk.misceval.arrayFromIterable(other)); + } + let isIn; + const thisLength = this.get$size(); + const otherLength = this.get$size(); + if (thisLength > otherLength) { + // every item in this set can't be in other if it's shorter! + return Sk.builtin.bool.false$; + } + for (let it = this.tp$iter(), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { + isIn = other.sq$contains(i); + if (!isIn) { + return Sk.builtin.bool.false$; + } + } + return Sk.builtin.bool.true$; + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "Report whether another set contains this set.", + }, + issuperset: { + $meth: function (other) { + if (!Sk.builtin.checkAnySet(other)) { + other = new Sk.builtin.set(Sk.misceval.arrayFromIterable(other)); + } + return other.issubset.$meth.call(other, this); + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "Report whether this set contains another set.", + }, + pop: { + $meth: function () { + if (this.get$size() === 0) { + throw new Sk.builtin.KeyError("pop from an empty set"); + } + const vals = this.sk$asarray(); + const item = vals[0]; + this.discard.$meth.call(this, item); //can only be called by a set not a frozen set + return item; + }, + $flags: {NoArgs: true}, + $textsig: null, + $doc: "Remove and return an arbitrary set element.\nRaises KeyError if the set is empty.", + }, + // __reduce__: { + // $meth: methods.$__reduce__, + // $flags:{}, + // $textsig: null, + // $doc: "Return state information for pickling." }, + remove: { + $meth: function (item) { + return this.v.mp$ass_subscript(item); + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "Remove an element from a set; it must be a member.\n\nIf the element is not a member, raise a KeyError.", + }, + // __sizeof__: { + // $meth: methods.$__sizeof__, + // $flags:{}, + // $textsig: null, + // $doc: "S.__sizeof__() -> size of S in memory, in bytes" }, + symmetric_difference: { + $meth: function (other) { + const S = this.union.$meth.call(this, other); + const vals = S.sk$asarray(); + const discard = Sk.builtin.set.prototype.discard.$meth; + for (let i = 0; i < vals.length; i++) { + const item = vals[i]; + if (Sk.abstr.sequenceContains(this, item) && Sk.abstr.sequenceContains(other, item)) { + discard.call(S, item); + } + } + return S; + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "Return the symmetric difference of two sets as a new set.\n\n(i.e. all elements that are in exactly one of the sets.)", + }, + symmetric_difference_update: { + $meth: function (other) { + const sd = this.symmetric_difference.$meth.call(this, other); + this.clear.$meth.call(this); + this.update.$meth.call(this, sd); + return Sk.builtin.none.none$; + }, + $flags: {OneArg: true}, + $textsig: null, + $doc: "Update a set with the symmetric difference of itself and another.", + }, + union: { + $meth: function (...args) { + const S = this.copy.$meth.call(this); + Sk.builtin.set.prototype.update.$meth.call(S, ...args); + return S; + }, + $flags: {MinArgs: 0}, + $textsig: null, + $doc: "Return the union of sets as a new set.\n\n(i.e. all elements that are in either set.)", + }, + update: { + $meth: function (...args) { + const add = Sk.builtin.set.prototype.add.$meth; + for (let i = 0; i < args.length; i++) { + for (let it = Sk.abstr.iter(args[i]), item = it.tp$iternext(); item !== undefined; item = it.tp$iternext()) { + add.call(this, item); + } + } + return Sk.builtin.none.none$; + }, + $flags: {MinArgs: 0}, + $textsig: null, + $doc: "Update a set with the union of itself and others.", + }, + }, + proto: /**@lends {Sk.builtin.set.prototype}*/ { + sk$asarray: function () { + return this.v.sk$asarray(); + }, + get$size: function () { + // this method cannot be overriden by subclasses + return this.v.sq$length(); + }, + }, }); Sk.exportSymbol("Sk.builtin.set", Sk.builtin.set); +const set_proto = Sk.builtin.set.prototype; /** * @constructor - * @param {Object} obj + * @param {Array.} S */ -Sk.builtin.set_iter_ = function (obj) { - var allkeys, k, i, bucket, buckets; - if (!(this instanceof Sk.builtin.set_iter_)) { - return new Sk.builtin.set_iter_(obj); - } - this.$obj = obj; - this.tp$iter = this; - allkeys = []; - buckets = obj.v.buckets; - for (k in buckets) { - if (buckets.hasOwnProperty(k)) { - bucket = buckets[k]; - if (bucket && bucket.$hash !== undefined && bucket.items !== undefined) { - // skip internal stuff. todo; merge pyobj and this - for (i = 0; i < bucket.items.length; i++) { - allkeys.push(bucket.items[i].lhs); - } - } +Sk.builtin.frozenset = Sk.abstr.buildNativeClass("frozenset", { + constructor: function frozenset(S) { + // takes in an array of py objects + if (S === undefined) { + S = []; } - } - this.$index = 0; - this.$keys = allkeys; - this.tp$iternext = function () { - if (this.$index >= this.$keys.length) { - return undefined; + Sk.asserts.assert( + Array.isArray(S) && this instanceof Sk.builtin.frozenset, + "bad call to frozen set - must be called with an Array and 'new'" + ); + const L = []; + for (let i = 0; i < S.length; i++) { + L.push(S[i]); + L.push(true); } - return this.$keys[this.$index++]; - }; - this.$r = function () { - return new Sk.builtin.str("setiterator"); - }; - return this; -}; - -Sk.abstr.setUpInheritance("setiterator", Sk.builtin.set_iter_, Sk.builtin.object); - -Sk.builtin.set_iter_.prototype.__class__ = Sk.builtin.set_iter_; - -Sk.builtin.set_iter_.prototype.__iter__ = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("__iter__", arguments.length, 0, 0, true, false); - return self; + this.v = new Sk.builtin.dict(L); + }, + slots: /**@lends {Sk.builtin.frozenset.prototype}*/ { + tp$getattr: Sk.generic.getAttr, + tp$as_number: true, + tp$as_sequence_or_mapping: true, + tp$doc: + "frozenset() -> empty frozenset object\nfrozenset(iterable) -> frozenset object\n\nBuild an immutable unordered collection of unique elements.", + tp$hash: function () { + // numbers taken from Cpython 2.7 hash function + let hash = 1927868237; + const entries = this.sk$asarray(); + hash *= entries.length + 1; + for (let i = 0; i < entries.length; i++) { + const h = Sk.builtin.hash(entries[i]).v; + hash ^= (h ^ (h << 16) ^ 89869747) * 3644798167; + } + hash = hash * 69069 + 907133923; + hash = new Sk.builtin.int_(hash); + this.$savedHash_ = hash; + return hash; + }, + /** + * @param {Array} args + * @param {Array=} kwargs + */ + tp$new: function (args, kwargs) { + if (this !== Sk.builtin.frozenset.prototype) { + return this.$subtype_new(args, kwargs); + } + Sk.abstr.checkNoKwargs("frozenset", kwargs); + Sk.abstr.checkArgsLen("frozenset", args, 0, 1); + const arg = args[0]; + if (arg !== undefined && arg.ob$type === Sk.builtin.frozenset) { + return arg; + } + const S = Sk.misceval.arrayFromIterable(arg); + return new Sk.builtin.frozenset(S); + }, + $r: set_proto.$r, + tp$iter: set_proto.tp$iter, + tp$richcompare: set_proto.tp$richcompare, + // number slots + nb$subtract: set_proto.nb$subtract, + nb$and: set_proto.nb$and, + nb$or: set_proto.nb$or, + nb$xor: set_proto.nb$xor, + // as mapping + sq$length: set_proto.sq$length, + sq$contains: set_proto.sq$contains, + }, + methods: /**@lends {Sk.builtin.frozenset.prototype}*/ { + copy: set_proto.copy.d$def, + difference: set_proto.difference.d$def, + intersection: set_proto.intersection.d$def, + isdisjoint: set_proto.isdisjoint.d$def, + issubset: set_proto.issubset.d$def, + issuperset: set_proto.issuperset.d$def, + // __reduce__: set_proto.__reduce__, + // __sizeof__: set_proto.__sizeof__, + symmetric_difference: set_proto.symmetric_difference.d$def, + union: set_proto.union.d$def, + }, + proto: /**@lends {Sk.builtin.frozenset.prototype}*/ { + $subtype_new: function (args, kwargs) { + const instance = new this.constructor(); + // pass the args but ignore the kwargs for subtyping + const frozenset = Sk.builtin.frozenset.prototype.tp$new(args); + instance.v = frozenset.v; + return instance; + }, + sk$asarray: set_proto.sk$asarray, + get$size: set_proto.get$size, + }, }); -Sk.builtin.set_iter_.prototype.next$ = function (self) { - var ret = self.tp$iternext(); - if (ret === undefined) { - throw new Sk.builtin.StopIteration(); - } - return ret; -}; +Sk.exportSymbol("Sk.builtin.frozenset", Sk.builtin.frozenset); + +function numberSlot(f) { + return function (other) { + if (!Sk.builtin.checkAnySet(other)) { + return Sk.builtin.NotImplemented.NotImplemented$; + } + return f.call(this, other); + }; +} diff --git a/src/simple_iterators.js b/src/simple_iterators.js new file mode 100644 index 0000000000..624916a202 --- /dev/null +++ b/src/simple_iterators.js @@ -0,0 +1,261 @@ +/** + * + * @constructor + * + * @param {Function} fn + * @param {boolean=} [handlesOwnSuspensions=false] - Does it handle its own suspension? + * + * @description + * Create a generic Python iterator that repeatedly calls a given JS function + * until it returns 'undefined'. This function is useful for user defined Native classes + * + * @example + * // some immutable tuple like class where the v property is an array + * MyClass.prototype.tp$iter = function() { + * let i = 0; + * const len = this.v.length; + * const self = this; + * return new Sk.generic.iterator(() => i >= len ? self.v[i++] : undefined); + * } + * @extends {Sk.builtin.object} + * + */ +Sk.generic.iterator = Sk.abstr.buildIteratorClass("iterator", { + constructor: function iterator(fn, handlesOwnSuspensions) { + this.tp$iternext = handlesOwnSuspensions ? fn : function (canSuspend) { + let x = fn(); + if (canSuspend || !x.$isSuspension) { + return x; + } else { + return Sk.misceval.retryOptionalSuspensionOrThrow(x); + } + }; + }, + iternext: function (canSuspend) { /* keep slot __next__ happy */ + return this.tp$iternext(canSuspend); + }, + flags: {sk$acceptable_as_base_class: false}, +}); + + + + +/** + * + * @constructor + * @extends {Sk.builtin.object} + * @param {Sk.builtin.func} callable + * @param {Sk.builtin.object} sentinel - if reached returns undefined + * @private + */ +Sk.builtin.callable_iter_ = Sk.abstr.buildIteratorClass("callable_iterator", { + constructor: function callable_iter(callable, sentinel) { + if (!Sk.builtin.checkCallable(callable)) { + throw new Sk.builtin.TypeError("iter(v, w): v must be callable"); + } + this.$callable = callable; + this.$sentinel = sentinel; + this.$flag = false; + }, + iternext: function (canSuspend) { + let ret; + if (this.$flag === true) { + // Iterator has already completed + return undefined; + } + if (canSuspend) { + ret = Sk.misceval.callsimOrSuspendArray(this.$callable, []); + const self = this; + return Sk.misceval.chain(ret, function (r) { + if (Sk.misceval.richCompareBool(r, self.$sentinel, "Eq", true)) { + self.$flag = true; + return undefined; + } else { + return r; + } + }); + } else { + ret = Sk.misceval.callsimArray(this.$callable, []); + if (Sk.misceval.richCompareBool(ret, this.$sentinel, "Eq", false)) { + this.$flag = true; + return undefined; + } else { + return ret; + } + } + }, + flags: {sk$acceptable_as_base_class: false}, +}); + + + +/** + * @constructor + * @extends {Sk.builtin.object} + * @param {Sk.builtin.list} lst + * @private + */ +Sk.builtin.list_iter_ = Sk.abstr.buildIteratorClass("list_iterator", { + constructor: function list_iter_(lst) { + this.$index = 0; + this.$seq = lst.v; + this.$done = false; // the list can change size but once we've consumed the iterator we must stop + }, + iternext: function () { + if (this.$index >= this.$seq.length || this.$done) { + this.$done = true; + return undefined; + } + return this.$seq[this.$index++]; + }, + methods: { + __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef, + }, + flags: {sk$acceptable_as_base_class: false}, +}); + + +/** + * @constructor + * @extends {Sk.builtin.object} + * @param {Sk.builtin.list} lst + * @private + */ +Sk.builtin.reverselist_iter_ = Sk.abstr.buildIteratorClass("list_reverseiterator", { + constructor: function reverselist_iter_(lst) { + this.$index = lst.v.length - 1; + this.$seq = lst.v; + }, + iternext: function () { + if (this.$index < 0) { + return undefined; + } + return this.$seq[this.$index--]; + }, + methods: { + __length_hint__: Sk.generic.iterReverseLengthHintMethodDef + }, + flags: {sk$acceptable_as_base_class: false}, +}); + +/** + * @constructor + * @extends {Sk.builtin.object} + * @param {Sk.builtin.set|Sk.builtin.frozenset} set or frozenset + * @private + */ +Sk.builtin.set_iter_ = Sk.abstr.buildIteratorClass("set_iterator", { + constructor: function set_iter_(set) { + this.$index = 0; + this.$seq = set.sk$asarray(); + this.$orig = set; + }, + iternext: Sk.generic.iterNextWithArrayCheckSize, + methods: { + __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef, + }, + flags: {sk$acceptable_as_base_class: false}, +}); + +/** + * @constructor + * @extends {Sk.builtin.object} + * @param {Sk.builtin.object} seq + * @private + */ +Sk.builtin.seq_iter_ = Sk.abstr.buildIteratorClass("iterator", { + constructor: function seq_iter(seq) { + this.$index = 0; + this.$seq = seq; + }, + iternext: function (canSuspend) { + let ret; + try { + ret = this.$seq.mp$subscript( + new Sk.builtin.int_(this.$index), + canSuspend + ); + } catch (e) { + if ( + e instanceof Sk.builtin.IndexError || + e instanceof Sk.builtin.StopIteration + ) { + return undefined; + } else { + throw e; + } + } + this.$index++; + return ret; + }, + methods: { + __length_hint__: { + $flags: {NoArgs: true}, + $meth: function () { + if (this.$seq.sq$length) { + // sq$length will return Sk.miseval.asIndex + return this.$seq.sq$length() - this.$index; + } else { + throw new Sk.builtin.NotImplementedError( + "len is not implemented for " + Sk.abstr.typeName(this.$seq) + ); + } + }, + }, + }, + flags: {sk$acceptable_as_base_class: false}, +}); + +/** + * @constructor + * @extends {Sk.builtin.object} + * @param {Sk.builtin.str} str + * @private + */ +Sk.builtin.str_iter_ = Sk.abstr.buildIteratorClass("str_iterator", { + constructor: function str_iter_(str) { + this.$index = 0; + this.$seq = str.v.slice(0); + this.$length = str.sq$length(); + }, + iternext: function () { + if (this.$index >= this.$length) { + return undefined; + } + return new Sk.builtin.str(this.$seq.substr(this.$index++, 1)); + }, + methods: { + __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef, + }, + flags: {sk$acceptable_as_base_class: false}, +}); + +/** + * @constructor + * @extends {Sk.builtin.object} + * @param {Sk.builtin.tuple} tuple + * @private + */ +Sk.builtin.tuple_iter_ = Sk.abstr.buildIteratorClass("tuple_iterator", { + constructor: function tuple_iter_(tuple) { + this.$index = 0; + this.$seq = tuple.sk$asarray(); + }, + iternext: Sk.generic.iterNextWithArray, + methods: { + __length_hint__: Sk.generic.iterLengthHintWithArrayMethodDef, + }, + flags: {sk$acceptable_as_base_class: false}, +}); + + + + + +Sk.exportSymbol("Sk.builtin.callable_iter_", Sk.builtin.callable_iter_); +Sk.exportSymbol("Sk.builtin.dict_iter_", Sk.builtin.dict_iter_); +Sk.exportSymbol("Sk.builtin.list_iter_", Sk.builtin.list_iter_); +Sk.exportSymbol("Sk.builtin.set_iter_", Sk.builtin.set_iter_); +Sk.exportSymbol("Sk.builtin.seq_iter_", Sk.builtin.seq_iter_); +Sk.exportSymbol("Sk.builtin.str_iter_", Sk.builtin.str_iter_); +Sk.exportSymbol("Sk.builtin.tuple_iter_", Sk.builtin.tuple_iter_); diff --git a/src/sk_method.js b/src/sk_method.js new file mode 100644 index 0000000000..f38d286e3b --- /dev/null +++ b/src/sk_method.js @@ -0,0 +1,138 @@ +/** + * @constructor + * Sk.builtin.sk_method + * + * @description + * this constructor is used by all builtin functions or methods + * the tp$call method is defined based on the flags + * + * A good way to determine the flags is to look at the textsignature of a function + * or find the equivalent function in CPython and map the flags to skulpt flags + * flags: { + * NoArgs: true, raises exception if there are args or kwargs (METH_NOARGS) + * OneArg: true, raises exception if there is more than one Arg (METH_O) + * + * MinArgs: int (also assumes noKwargs) + * MaxArgs: int optional (used in conjuntiontion with MinArgs) + * + * NamedArgs: Array e.g. [null, null, "name1", "name2"] + * use null for posonly args + * ensures that the total number of args (including kwargs) equals the named args + * the call sig will check the kwarg names are valid + * the call sig applies Defaults (if set) to any named args + * Defaults: Array (used in conjunction with NamedArgs, can use [undefined] see dict.pop for use case) + * + * FastCall && NoKwargs: true, check NoKewords and pass args the function will handle these (METH_FASTCALL) + * FastCall: pass args, kwargs - the function will handle this (METH_FASTCALL || KEYWORDS) + * + * if no flags are set then the tp$call = function.prototype.tp$call + * + * @param {Object} method_def + * @param {*} self + * @param {string=} module + */ +Sk.builtin.sk_method = Sk.abstr.buildNativeClass("builtin_function_or_method", { + constructor: function builtin_function_or_method(method_def, self, module) { + // here we set this.$meth binding it's call signature to self + this.$meth = method_def.$meth.bind(self); + this.$doc = method_def.$doc; + this.$self = self; + this.$module = module ? new Sk.builtin.str(module) : Sk.builtin.none.none$; + this.$name = method_def.$name || method_def.$meth.name || ""; + + // useful to set the $textsig to determine the correct flags + this.$textsig = method_def.$textsig; + + // override the default tp$call method if there is a valid flag + const flags = method_def.$flags || {}; + this.$flags = flags; + + if (flags.FastCall && flags.NoKwargs) { + this.tp$call = this.$fastCallNoKwargs; + } else if (flags.FastCall) { + this.tp$call = this.$meth; + } else if (flags.NoArgs) { + this.tp$call = this.$callNoArgs; + } else if (flags.OneArg) { + this.tp$call = this.$callOneArg; + } else if (flags.NamedArgs) { + this.tp$call = this.$callNamedArgs; + } else if (flags.MinArgs !== undefined) { + this.tp$call = this.$callMinArgs; + } else { + this.func_code = this.$meth; + this.tp$call = this.$defaultCallMethod; + } + }, + proto: { + $fastCallNoKwargs: function (args, kwargs) { + Sk.abstr.checkNoKwargs(this.$name, kwargs); + return this.$meth(args); + }, + $callNoArgs: function (args, kwargs) { + Sk.abstr.checkNoArgs(this.$name, args, kwargs); + return this.$meth(); + }, + $callOneArg: function (args, kwargs) { + Sk.abstr.checkOneArg(this.$name, args, kwargs); + return this.$meth(args[0]); + }, + $callNamedArgs: function (args, kwargs) { + args = Sk.abstr.copyKeywordsToNamedArgs(this.$name, this.$flags.NamedArgs, args, kwargs, this.$flags.Defaults); + return this.$meth(...args); + }, + $callMinArgs: function (args, kwargs) { + Sk.abstr.checkNoKwargs(this.$name, kwargs); + Sk.abstr.checkArgsLen(this.$name, args, this.$flags.MinArgs, this.$flags.MaxArgs); + return this.$meth(...args); + }, + $defaultCallMethod: function (args, kwargs) { + // default implementation for all currently created functions that have yet to be be converted + // and don't utilise flagged calls + if (this.$self !== undefined) { + return Sk.builtin.func.prototype.tp$call.call(this, [this.$self, ...args], kwargs); + } + return Sk.builtin.func.prototype.tp$call.call(this, args, kwargs); + }, + $memoiseFlags: Sk.builtin.func.prototype.$memoiseFlags, + $resolveArgs: Sk.builtin.func.prototype.$resolveArgs, + }, + flags: {sk$acceptable_as_base_class: false}, + slots: { + tp$getattr: Sk.generic.getAttr, + $r: function () { + if (this.$self === undefined) { + return new Sk.builtin.str(""); + } + return new Sk.builtin.str(""); + }, + tp$call: function (args, kwargs) { + return this.tp$call(args, kwargs); + }, + }, + getsets: { + __module__: { + $get: function () { + return this.$module; + }, + $set: function (value) { + this.$module = value; + }, + }, + __doc__: { + $get: function () { + return this.$doc ? new Sk.builtin.str(this.$doc) : Sk.builtin.none.none$; + }, + }, + __name__: { + $get: function () { + return new Sk.builtin.str(this.$name); + }, + }, + __text_signature__: { + $get: function () { + return new Sk.builtin.str(this.$textsig); + }, + }, + }, +}); diff --git a/src/slice.js b/src/slice.js index 691c058695..d5bbb4c71c 100644 --- a/src/slice.js +++ b/src/slice.js @@ -1,183 +1,168 @@ +/** @typedef {Sk.builtin.object} */ var pyObject; + /** * @constructor - * @param {Object} start - * @param {Object=} stop - * @param {Object=} step + * @extends {Sk.builtin.object} + * @param {pyObject} start + * @param {pyObject=} stop + * @param {pyObject=} step */ -Sk.builtin.slice = function slice (start, stop, step) { - Sk.builtin.pyCheckArgsLen("slice", arguments.length, 1, 3, false, false); - - if ((step !== undefined) && Sk.misceval.isIndex(step) && (Sk.misceval.asIndex(step) === 0)) { - throw new Sk.builtin.ValueError("slice step cannot be zero"); - } - - if (!(this instanceof Sk.builtin.slice)) { - return new Sk.builtin.slice(start, stop, step); - } - - - if (stop === undefined && step === undefined) { - stop = start; - start = Sk.builtin.none.none$; - } - if (stop === undefined) { - stop = Sk.builtin.none.none$; - } - if (step === undefined) { - step = Sk.builtin.none.none$; - } - this.start = start; - this.stop = stop; - this.step = step; - - this.__class__ = Sk.builtin.slice; - - this["$d"] = new Sk.builtin.dict([Sk.builtin.slice$start, this.start, - Sk.builtin.slice$stop, this.stop, - Sk.builtin.slice$step, this.step]); - - return this; -}; - -Sk.abstr.setUpInheritance("slice", Sk.builtin.slice, Sk.builtin.object); - -Sk.builtin.slice.prototype["$r"] = function () { - var a = Sk.builtin.repr(this.start).v; - var b = Sk.builtin.repr(this.stop).v; - var c = Sk.builtin.repr(this.step).v; - return new Sk.builtin.str("slice(" + a + ", " + b + ", " + c + ")"); -}; - -Sk.builtin.slice.prototype.tp$richcompare = function (w, op) { - // w not a slice - var t1, t2; - if (!w.__class__ || w.__class__ != Sk.builtin.slice) { - // shortcuts for eq/not - if (op === "Eq") { - return false; - } - if (op === "NotEq") { - return true; - } - - // todo; other types should have an arbitrary order - return false; - } - - // This is how CPython does it - t1 = new Sk.builtin.tuple([this.start, this.stop, this.step]); - t2 = new Sk.builtin.tuple([w.start, w.stop, w.step]); - - return t1.tp$richcompare(t2, op); -}; - -/* Internal indices function */ -Sk.builtin.slice.prototype.slice_indices_ = function (length) { - var start, stop, step; - - if (Sk.builtin.checkNone(this.start)) { - start = null; - } else if (Sk.misceval.isIndex(this.start)) { - start = Sk.misceval.asIndex(this.start); - } else { - throw new Sk.builtin.TypeError("slice indices must be integers or None"); - } - - if (Sk.builtin.checkNone(this.stop)) { - stop = null; - } else if (Sk.misceval.isIndex(this.stop)) { - stop = Sk.misceval.asIndex(this.stop); - } else { - throw new Sk.builtin.TypeError("slice indices must be integers or None"); - } - - if (Sk.builtin.checkNone(this.step)) { - step = null; - } else if (Sk.misceval.isIndex(this.step)) { - step = Sk.misceval.asIndex(this.step); - } else { - throw new Sk.builtin.TypeError("slice indices must be integers or None"); - } - - // this seems ugly, better way? - if (step === null) { - step = 1; - } - if (step > 0) { - if (start === null) { - start = 0; +Sk.builtin.slice = Sk.abstr.buildNativeClass("slice", { + constructor: function slice(start, stop, step) { + if (stop === undefined && step === undefined) { + stop = start; + start = Sk.builtin.none.none$; } - if (stop === null) { - stop = length; + if (stop === undefined) { + stop = Sk.builtin.none.none$; } - if (stop > length) { - stop = length; + if (step === undefined) { + step = Sk.builtin.none.none$; } - if (start < 0) { - start = length + start; - if (start < 0) { - start = 0; + this.start = start; + this.stop = stop; + this.step = step; + }, + slots: /**@lends {Sk.builtin.slice.prototype} */ { + tp$getattr: Sk.generic.getAttr, + tp$doc: "slice(stop)\nslice(start, stop[, step])\n\nCreate a slice object. This is used for extended slicing (e.g. a[0:10:2]).", + tp$hash: Sk.builtin.none.none$, + tp$new: function (args, kwargs) { + Sk.abstr.checkNoKwargs("slice", kwargs); + Sk.abstr.checkArgsLen("slice", args, 1, 3); + return new Sk.builtin.slice(...args); + }, + $r: function () { + const a = Sk.misceval.objectRepr(this.start); + const b = Sk.misceval.objectRepr(this.stop); + const c = Sk.misceval.objectRepr(this.step); + return new Sk.builtin.str("slice(" + a + ", " + b + ", " + c + ")"); + }, + tp$richcompare: function (w, op) { + // w not a slice - it's not subclassable so no need to use instanceof here + if (w.ob$type !== Sk.builtin.slice) { + return Sk.builtin.NotImplemented.NotImplemented$; } - } - if (stop < 0) { - stop = length + stop; - } - } else { - if (start === null) { - start = length - 1; - } - if (start >= length) { - start = length - 1; - } - if (stop === null) { - stop = -1; - } else if (stop < 0) { - stop = length + stop; - if (stop < 0) { - stop = -1; + // This is how CPython does it + const t1 = new Sk.builtin.tuple([this.start, this.stop, this.step]); + const t2 = new Sk.builtin.tuple([w.start, w.stop, w.step]); + return t1.tp$richcompare(t2, op); + }, + }, + getsets: /**@lends {Sk.builtin.slice.prototype} */{ + start: { + $get: function () { + return this.start; + }, + }, + step: { + $get: function () { + return this.step; + }, + }, + stop: { + $get: function () { + return this.stop; + }, + }, + }, + methods: /**@lends {Sk.builtin.slice.prototype} */{ + indices: { + $meth: function indices(length) { + length = Sk.misceval.asIndexOrThrow(length); + if (length < 0) { + throw new Sk.builtin.TypeError("length should not be negative"); + } + const sss = this.$slice_indices(length); + return new Sk.builtin.tuple([new Sk.builtin.int_(sss[0]), new Sk.builtin.int_(sss[1]), new Sk.builtin.int_(sss[2])]); + }, + $doc: + "S.indices(len) -> (start, stop, stride)\n\nAssuming a sequence of length len, calculate the start and stop\nindices, and the stride length of the extended slice described by\nS. Out of bounds indices are clipped in a manner consistent with the\nhandling of normal slices.", + $textsig: null, + $flags: {OneArg: true}, + }, + }, + proto: /**@lends {Sk.builtin.slice.prototype} */{ + $slice_indices: function (length) { + let start, stop, step; + const msg = "slice indices must be integers or None or have an __index__ method"; + if (Sk.builtin.checkNone(this.step)) { + step = 1; + } else { + step = Sk.misceval.asIndexOrThrow(this.step, msg); + if (step === 0) { + throw new Sk.builtin.ValueError("slice step cannot be zero"); + } } - } - if (start < 0) { - start = length + start; - } - } - - return [start, stop, step]; -}; - -Sk.builtin.slice.prototype["indices"] = new Sk.builtin.func(function (self, length) { - Sk.builtin.pyCheckArgsLen("indices", arguments.length, 2, 2, false, false); - - length = Sk.builtin.asnum$(length); - var sss = self.slice_indices_(length); - - return new Sk.builtin.tuple([ - new Sk.builtin.int_(sss[0]), - new Sk.builtin.int_(sss[1]), - new Sk.builtin.int_(sss[2]) - ]); -}); - -Sk.builtin.slice.prototype.sssiter$ = function (wrt, f) { - var i; - var wrtv = Sk.builtin.asnum$(wrt); - var sss = this.slice_indices_(typeof wrtv === "number" ? wrtv : wrt.v.length); - if (sss[2] > 0) { - for (i = sss[0]; i < sss[1]; i += sss[2]) { - if (f(i, wrtv) === false) { - return; + if (Sk.builtin.checkNone(this.start)) { + start = null; + } else { + start = Sk.misceval.asIndexOrThrow(this.start, msg); } - } // wrt or wrtv? RNL - } else { - for (i = sss[0]; i > sss[1]; i += sss[2]) { - if (f(i, wrtv) === false) { - return; + if (Sk.builtin.checkNone(this.stop)) { + stop = null; + } else { + stop = Sk.misceval.asIndexOrThrow(this.stop, msg); } - } // wrt or wrtv? RNL - } -}; + if (step > 0) { + if (start === null) { + start = 0; + } else if (start < 0) { + start = length + start; + if (start < 0) { + start = 0; + } + } + if (stop === null) { + stop = length; + } else if (stop > length) { + stop = length; + } else if (stop < 0) { + stop = length + stop; + } + } else { + if (start === null) { + start = length - 1; + } else if (start >= length) { + start = length - 1; + } else if (start < 0) { + start = length + start; + } + if (stop === null) { + stop = -1; + } else if (stop < 0) { + stop = length + stop; + if (stop < 0) { + stop = -1; + } + } + } -Sk.builtin.slice$start = new Sk.builtin.str("start"); -Sk.builtin.slice$stop = new Sk.builtin.str("stop"); -Sk.builtin.slice$step = new Sk.builtin.str("step"); + return [start, stop, step]; + }, + /** + * used by objects like str, list, tuple that can return a slice + * @param {number} len + * @param {Function} f + */ + sssiter$: function (len, f) { + const sss = this.$slice_indices(len); + const start = sss[0]; + const stop = sss[1]; + const step = sss[2]; + if (step > 0) { + for (let i = start; i < stop; i += step) { + f(i); + } + } else { + for (let i = start; i > stop; i += step) { + f(i); + } + } + }, + }, + flags: { + sk$acceptable_as_base_class: false, + }, +}); diff --git a/src/slotdefs.js b/src/slotdefs.js new file mode 100644 index 0000000000..947aedd9c4 --- /dev/null +++ b/src/slotdefs.js @@ -0,0 +1,2172 @@ +/** @typedef {Sk.builtin.object} */ var pyObject; +/** @typedef {Sk.builtin.type|Function} */ var typeObject; + +/** + * @description + * Wrappers and slot functions + * + * A wrapper function wrapper a slot defined on the prototype of a builtin type object + * typically a a slot wrapper will be called with a self argument and args and kwargs + * + * self becomes this in when the slot wrapper is called + * the slot wrapper_descriptor object takes care of checking that self is an instance of the type object + * @param {*} self + * @param {Array} args + * @param {Array=} kwargs + * @ignore + */ +function wrapperCallNoArgs(self, args, kwargs) { + // this = the wrapped function + Sk.abstr.checkNoArgs(this.$name, args, kwargs); + const res = this.call(self); + if (res === undefined) { + return Sk.builtin.none.none$; + } + return res; +} +/** + * @param {*} self + * @param {Array} args + * @param {Array=} kwargs + */ +function wrapperFastCall(self, args, kwargs) { + // this = the wrapped function + const res = this.call(self, args, kwargs); + if (res === undefined) { + return Sk.builtin.none.none$; + } + return res; +} + +/** + * @param {*} self + * @param {Array} args + * @param {Array=} kwargs + */ +function wrapperCallOneArg(self, args, kwargs) { + // this = the wrapped function + Sk.abstr.checkOneArg(this.$name, args, kwargs); + const res = this.call(self, args[0]); + if (res === undefined) { + return Sk.builtin.none.none$; + } + return res; +} + +/** + * @param {*} self + * @param {!Array} args + * @param {Array=} kwargs + */ +function wrapperCallTernary(self, args, kwargs) { + // this = the wrapped function + // only used for __pow__ + Sk.abstr.checkNoKwargs(this.$name, kwargs); + Sk.abstr.checkArgsLen(this.$name, args, 1, 2); + const res = this.call(self, ...args); + if (res === undefined) { + return Sk.builtin.none.none$; + } + return res; +} +/** + * @param {*} self + * @param {Array} args + * @param {Array=} kwargs + */ +function wrapperSet(self, args, kwargs) { + Sk.abstr.checkNoKwargs(this.$name, kwargs); + Sk.abstr.checkArgsLen(this.$name, args, 2, 2); + this.call(self, args[0], args[1]); + return Sk.builtin.none.none$; +} +/** + * @param {*} self + * @param {Array} args + * @param {Array=} kwargs + */ +function wrapperRichCompare(self, args, kwargs) { + const res = wrapperCallOneArg.call(this, self, args, kwargs); + if (res === Sk.builtin.NotImplemented.NotImplemented$) { + return res; + } + return new Sk.builtin.bool(res); +} + +/** + * @description + * Slot functions are wrappers around an Sk.builtin.func + * if skulpt calls tp$init on a type object the slotFunc will call the Sk.builtin.func + * + * with most slots we take the approach that we know which dunderFunc will be called + * However some slots currently double up + * e.g. mp$ass_subscript is called by both __setitem__ and __delitem__ + * for these dual slots we need to do a typelookup + * + * __getattr__ is another complicated case and the algorithm largely follows Cpython's algorithm + * @ignore + */ +function slotFuncNoArgs(dunderFunc) { + return function () { + return Sk.misceval.callsimArray(dunderFunc, [this]); + }; +} + +/** + * @param {string} dunderName + * @param {Function} checkFunc + * @param {string} checkMsg + * @param {Function=} f + */ +function slotFuncNoArgsWithCheck(dunderName, checkFunc, checkMsg, f) { + return function (dunderFunc) { + return function () { + let res = Sk.misceval.callsimArray(dunderFunc, [this]); + if (!checkFunc(res)) { + throw new Sk.builtin.TypeError(dunderName + " should return " + checkMsg + " (returned " + Sk.abstr.typeName(res) + ")"); + } + // f is might be a function that changes the result to a js object like for nb$bool which returns a Boolean + if (f !== undefined) { + return f(res); + } + return res; + }; + }; +} + +function slotFuncOneArg(dunderFunc) { + return function (value) { + return Sk.misceval.callsimArray(dunderFunc, [this, value]); + }; +} + +function slotFuncGetAttribute(pyName, canSuspend) { + const func = Sk.abstr.lookupSpecial(this, Sk.builtin.str.$getattribute); + let res; + if (func instanceof Sk.builtin.wrapper_descriptor) { + return func.d$wrapped.call(this, pyName, canSuspend); + } else if (canSuspend) { + res = Sk.misceval.callsimOrSuspendArray(func, [this, pyName]); + } else { + res = Sk.misceval.callsimArray(func, [this, pyName]); + } + return res; +} + +function slotFuncFastCall(dunderFunc) { + return function (args, kwargs) { + return Sk.misceval.callsimOrSuspendArray(dunderFunc, [this, ...args], kwargs); + }; +} + +/** + * this is currently a bit of a hack + * in attempting to maintain dual slots like mp$ass_subscript for assigning and deleting + * this function has to do a type lookup... since it doesn't know in advance if it is being asked to set or delete + * @ignore + */ +function slotFuncSetDelete(set_name, del_name, error_msg) { + return function (dunderFunc) { + return function (pyObject, value, canSuspend) { + let res, dunderName; + if (value === undefined) { + dunderName = del_name; + error_msg = null; + } else { + dunderName = set_name; + } + const func = Sk.abstr.lookupSpecial(this, new Sk.builtin.str(dunderName)); + if (func instanceof Sk.builtin.wrapper_descriptor) { + return func.d$wrapped.call(this, pyObject, value); + } + const call_version = canSuspend ? Sk.misceval.callsimOrSuspendArray : Sk.misceval.callsimArray; + if (func !== undefined) { + res = value === undefined ? call_version(func, [this, pyObject]) : call_version(func, [this, pyObject, value]); + } else if (error_msg) { + throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(this) + "' object " + error_msg); + } else { + throw new Sk.builtin.AttributeError(dunderName); + } + return res; + }; + }; +} + +/** + * @namespace + * + * @description + * If you want to build a skulpt native class you need to understand slots + * Each dunder method in python is matched to a slot in skulpt {@link Sk.dunderToSkulpt} which is closely aligned to a Cpython slot + * + * Plenty examples exist in {@link Sk.builtin} + * + * If a user builds a `nativeClass` using {@link Sk.abstr.buildNativeClass } they define slots as javascript function + * Dunder Methods will be created as `slot_wrappers` + * + * If a user defines a class in Python or using {@link Sk.misceval.buildClass} + * Dunder Functions should be defined and slot funcs will be added + * + * Below is information about each slot function, should you decide to build a native class + * + * For mappings of slots to dunders see source code for {@link Sk.dunderToSkulpt} or [Sk.subSlots]{@link Sk.slots.subSlots} + * + */ +Sk.slots = Object.create(null); +const slots = Sk.slots; + +/** + * + * @memberof Sk.slots + * @member tp$doc + * @implements __doc__ + * @suppress {checkTypes} + * @type {string} + */ + +/** + * @memberof Sk.slots + * @method tp$init + * @implements __init__ + * @suppress {checkTypes} + * @param {Array} args + * @param {Array=} kwargs + * @returns {Sk.builtin.none} + */ +Sk.slots.__init__ = { + $name: "__init__", + $slot_name: "tp$init", + $slot_func: function (dunderFunc) { + return function tp$init(args, kwargs) { + let ret = Sk.misceval.callsimOrSuspendArray(dunderFunc, [this, ...args], kwargs); + return Sk.misceval.chain(ret, function (r) { + if (!Sk.builtin.checkNone(r) && r !== undefined) { + throw new Sk.builtin.TypeError("__init__() should return None, not " + Sk.abstr.typeName(r)); + } else { + return r; + } + }); + }; + }, + $wrapper: function (self, args, kwargs) { + // this = the wrapped function + this.call(self, args, kwargs); + return Sk.builtin.none.none$; + }, + $textsig: "($self, /, *args, **kwargs)", + $flags: {FastCall: true}, + $doc: "Initialize self. See help(type(self)) for accurate signature.", +}; + +/** + * @memberof Sk.slots + * @method tp$new + * @implements __new__ + * @suppress {checkTypes} + * @returns {pyObject} + * @param {Array} args + * @param {Array=} kwargs + * @description + * {@link Sk.generic.new} {@link Sk.generic.newMethodDef} are related implementations of `tp$mew` and `__new__` + * unusually `this = typeobject.prototype` since it is typically called like `typeobj.prototype.tp$new` and must + * be taken into when writing an implementation of `tp$new` + */ +slots.__new__ = { + $name: "__new__", + $slot_name: "tp$new", + $slot_func: function (dunderFunc) { + const tp$new = function (args, kwargs) { + return Sk.misceval.callsimOrSuspendArray(dunderFunc, [this.constructor, ...args], kwargs); + }; + tp$new.sk$static_new = false; // this is a flag used in the __new__ algorithm + return tp$new; + }, + $wrapper: null, // handled separately since it's not a slot wrapper but an sk_method + $textsig: "($self, /, *args, **kwargs)", + $flags: {FastCall: true}, + $doc: "Create and return a new object.", +}; + +/** + * @memberof Sk.slots + * @method tp$call + * @implements __call__ + * @suppress {checkTypes} + * @param {Array} args + * @param {Array=} kwargs + * + */ +slots.__call__ = { + $name: "__call__", + $slot_name: "tp$call", + $slot_func: slotFuncFastCall, + $wrapper: wrapperFastCall, + $textsig: "($self, /, *args, **kwargs)", + $flags: {FastCall: true}, + $doc: "Call self as a function.", +}; + +/** + * @memberof Sk.slots + * @method $r + * @implements __repr__ + * @suppress {checkTypes} + * @returns {Sk.builtin.str} + */ +slots.__repr__ = { + $name: "__repr__", + $slot_name: "$r", + $slot_func: slotFuncNoArgsWithCheck("__repr__", Sk.builtin.checkString, "str"), + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "Return repr(self).", +}; + +/** + * @memberof Sk.slots + * @method tp$str + * @implements `__str__` + * @suppress {checkTypes} + * @returns {Sk.builtin.str} + */ +slots.__str__ = { + $name: "__str__", + $slot_name: "tp$str", + $slot_func: slotFuncNoArgsWithCheck("__str__", Sk.builtin.checkString, "str"), + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "Return str(self).", +}; + +/** + * @memberof Sk.slots + * @method tp$hash + * @implements __hash__ + * @suppress {checkTypes} + * @returns {Sk.builtin.int_} + * @description + * To be unhashable set this slot to {@link Sk.builtin.none.none$} or call {@link Sk.abstr.markUnhashable} + */ +slots.__hash__ = { + $name: "__hash__", + $slot_name: "tp$hash", + $slot_func: slotFuncNoArgsWithCheck("__hash__", Sk.builtin.checkInt, "int"), + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "Return hash(self).", +}; + +// getters/setters/deletters + +/** + * @memberof Sk.slots + * @method tp$getattr + * @implements __getattribute__ + * @suppress {checkTypes} + * + * @param {Sk.builtin.str} pyName + * @param {boolean=} canSuspend + * + * @returns {pyObject|undefined} + * @description + * This slot will also be given to a pyObject which defines `__getattr__` + */ +slots.__getattribute__ = { + $name: "__getattribute__", + $slot_name: "tp$getattr", + $slot_func: function (dunderFunc) { + return function tp$getattr(pyName, canSuspend) { + const getattrFn = Sk.abstr.lookupSpecial(this, Sk.builtin.str.$getattr); + if (getattrFn === undefined) { + // we don't support dynamically created __getattr__ but hey... + this.constructor.prototype.tp$getattr = slotFuncGetAttribute; + return slotFuncGetAttribute.call(this, pyName, canSuspend); + } + const getattributeFn = Sk.abstr.lookupSpecial(this, Sk.builtin.str.$getattribute); + const self = this; + + let r = Sk.misceval.chain( + Sk.misceval.tryCatch( + () => { + if (getattributeFn instanceof Sk.builtin.wrapper_descriptor) { + return getattributeFn.d$wrapped.call(self, pyName, canSuspend); + } else { + return Sk.misceval.callsimOrSuspendArray(getattributeFn, [self, pyName]); + } + }, + function (e) { + if (e instanceof Sk.builtin.AttributeError) { + return undefined; + } else { + throw e; + } + } + ), + (val) => + Sk.misceval.tryCatch( + () => { + if (val !== undefined) { + return val; + } + return Sk.misceval.callsimOrSuspendArray(getattrFn, [self, pyName]); + }, + function (e) { + if (e instanceof Sk.builtin.AttributeError) { + return undefined; + } else { + throw e; + } + } + ) + ); + return canSuspend ? r : Sk.misceval.retryOptionalSuspensionOrThrow(r); + }; + }, + $wrapper: function (self, args, kwargs) { + // this = the wrapped function + Sk.abstr.checkOneArg(this.$name, args, kwargs); + const pyName = args[0]; + if (!Sk.builtin.checkString(pyName)) { + throw new Sk.builtin.TypeError("attribute name must be string, not '" + Sk.abstr.typeName(pyName) + "'"); + } + const res = this.call(self, pyName); + if (res === undefined) { + throw new Sk.builtin.AttributeError(Sk.abstr.typeName(self) + " has no attribute " + pyName.$jsstr()); + } + return res; + }, + $textsig: "($self, name, /)", + $flags: {OneArg: true}, + $doc: "Return getattr(self, name).", +}; + +slots.__getattr__ = { + $name: "__getattr__", + $slot_name: "tp$getattr", + $slot_func: slots.__getattribute__.$slot_func, + $wrapper: null, + $textsig: "($self, name, /)", + $flags: {OneArg: true}, + $doc: "Return getattr(self, name).", +}; +/** + * @suppress {checkTypes} + * @memberof Sk.slots + * @method tp$setattr + * @implements __setattr__ + * @param {Sk.builtin.str} pyName + * @param {pyObject|undefined} value undefined indicates the attribute is to be deleted + * @param {boolean=} canSuspend + * @description + * `tp$setattr` is responsible for throwing its own exceptions. It also implements __delattr__ + */ +slots.__setattr__ = { + $name: "__setattr__", + $slot_name: "tp$setattr", + $slot_func: slotFuncSetDelete("__setattr__", "__delattr__"), + // not need for an error message setattr is always defined on object + $wrapper: wrapperSet, + $textsig: "($self, name, value, /)", + $flags: {MinArgs: 2, MaxArgs: 2}, + $doc: "Implement setattr(self, name, value).", +}; + +slots.__delattr__ = { + $name: "__delattr__", + $slot_name: "tp$setattr", + $slot_func: slots.__setattr__.$slot_func, + $wrapper: wrapperCallOneArg, + $textsig: "($self, name, /)", + $flags: {OneArg: true}, + $doc: "Implement delattr(self, name).", +}; + +/** + * @memberof Sk.slots + * @method tp$descr_get + * @implements __get__ + * @suppress {checkTypes} + * @param {pyObject} obj + * @param {typeObject=} obtype + * @param {boolean=} canSuspend + */ +slots.__get__ = { + $name: "__get__", + $slot_name: "tp$descr_get", + $slot_func: function (dunderFunc) { + return function tp$descr_get(obj, obtype, canSuspend) { + const call_version = canSuspend ? Sk.misceval.callsimOrSuspendArray : Sk.misceval.callsimArray; + if (obj === null) { + obj = Sk.builtin.none.none$; + } + if (obtype == null) { + obtype = Sk.builtin.none.none$; + } + return call_version(dunderFunc, [this, obj, obtype]); + }; + }, + $wrapper: function (self, args, kwargs) { + Sk.abstr.checkNoKwargs(this.$name, kwargs); + Sk.abstr.checkArgsLen(this.$name, args, 1, 2); + let obj = args[0]; + let obtype = args[1]; + if (obj === Sk.builtin.none.none$) { + obj = null; + } + if (obtype === Sk.builtin.none.none$) { + obtype = null; + } + if (obtype === null && obj === null) { + throw new Sk.builtin.TypeError("__get__(None, None) is invalid"); + } + return this.call(self, obj, obtype); + }, + $textsig: "($self, instance, owner, /)", + $flags: {MinArgs: 2, MaxArgs: 2}, + $doc: "Return an attribute of instance, which is of type owner.", +}; +/** + * @memberof Sk.slots + * @method tp$descr_set + * @implements __set__ + * @suppress {checkTypes} + * @param {pyObject} obj + * @param {pyObject|undefined} value undefined will signals __delete__ + * @param {boolean=} canSuspend + * @description + * Also implements __delete__ + */ +slots.__set__ = { + $name: "__set__", + $slot_name: "tp$descr_set", + $slot_func: slotFuncSetDelete("__set__", "__delete__"), + $wrapper: wrapperSet, + $textsig: "($self, instance, value, /)", + $flags: {MinArgs: 2, MaxArgs: 2}, + $doc: "Set an attribute of instance to value.", +}; + +slots.__delete__ = { + $name: "__delete__", + $slot_name: "tp$descr_set", + $slot_func: slots.__set__.$slot_func, + $wrapper: wrapperCallOneArg, + $textsig: "($self, instance, /)", + $flags: {OneArg: true}, + $doc: "Delete an attribute of instance.", +}; + +/** + * @memberof Sk.slots + * @method tp$richcompare + * @implements __eq__ + * @suppress {checkTypes} + * @param {pyObject} other + * @param {string} opname "Eq", "NotEq", "Lt", "LtE", "Gt", "GtE" + * @returns {boolean} + * @description + * __eq__/__ne__/__lt__/__le__/__gt__/__ge__ + * Either define tp$richcompare or any of the `ob$*` slots + * If `tp$richcompare` is defined then the `nativeClass` will get wrapper functions into each `ob$*` slot + */ + + /** + * @memberof Sk.slots + * @method ob$eq + * @implements __eq__ + * @suppress {checkTypes} + * @returns {boolean} + */ + slots.__eq__ = { + $name: "__eq__", + $slot_name: "ob$eq", + $slot_func: slotFuncOneArg, + $wrapper: wrapperRichCompare, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self==value.", + }; + + /** + * @memberof Sk.slots + * @method ob$ge + * @implements __ge__ + * @suppress {checkTypes} + * @returns {boolean} + */ + slots.__ge__ = { + $name: "__ge__", + $slot_name: "ob$ge", + $slot_func: slotFuncOneArg, + $wrapper: wrapperRichCompare, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self>=value.", + }; + /** + * @memberof Sk.slots + * @method ob$gt + * @implements __gt__ + * @suppress {checkTypes} + * @returns {boolean} + */ + slots.__gt__ = { + $name: "__gt__", + $slot_name: "ob$gt", + $slot_func: slotFuncOneArg, + $wrapper: wrapperRichCompare, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self>value.", + }; + /** + * @memberof Sk.slots + * @method ob$le + * @implements __le__ + * @suppress {checkTypes} + * @returns {boolean} + */ + slots.__le__ = { + $name: "__le__", + $slot_name: "ob$le", + $slot_func: slotFuncOneArg, + $wrapper: wrapperRichCompare, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self<=value.", + }; + /** + * @memberof Sk.slots + * @method ob$lt + * @implements __lt__ + * @suppress {checkTypes} + * @returns {boolean} + */ + slots.__lt__ = { + $name: "__lt__", + $slot_name: "ob$lt", + $slot_func: slotFuncOneArg, + $wrapper: wrapperRichCompare, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self Sk.misceval.callsimOrSuspendArray(dunderFunc, [self]), + (e) => { + if (e instanceof Sk.builtin.StopIteration) { + return undefined; + } else { + throw e; + } + } + ); + } + try { + return Sk.misceval.callsimArray(dunderFunc, [this]); + } catch (e) { + if (e instanceof Sk.builtin.StopIteration) { + return undefined; + } else { + throw e; + } + } + }; + }, + /** + * + * @param {*} self + * @param {Array} args + * @param {Array|undefined=} kwargs + */ + $wrapper: function (self, args, kwargs) { + // this = the wrapped function + Sk.abstr.checkNoArgs(this.$name, args, kwargs); + return Sk.misceval.chain(this.call(self, true), (res) => { + if (res === undefined) { + throw new Sk.builtin.StopIteration(); + } + return res; + }); + }, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "Implement next(self).", +}; + +// sequence and mapping +/** + * @memberof Sk.slots + * @member tp$as_sequence_or_mapping + * @type {boolean} + * @description + * set `tp$as_sequence_or_mapping` to `true` in order for for {@link Sk.abstr.buildNativeClass} + * to acquire appropriate `slot_wrappers` for the slots + * - [sq$length]{@link Sk.slots.sq$length} + * - [sq$concat]{@link Sk.slots.sq$concat} + * - [sq$contains]{@link Sk.slots.sq$contains} + * - [sq$repeat]{@link Sk.slots.sq$repeat} + * - [mp$subscript]{@link Sk.slots.mp$subscript} + * - [mp$ass_subscript]{@link Sk.slots.mp$ass_subscript} + */ + +/** + * @memberof Sk.slots + * @method sq$concat + * @implements __add__ + * @suppress {checkTypes} + * @description defining `sq$concat` along with {@link Sk.slots.tp$as_sequence_or_mapping} will gain the slot + * `__add__`. + * note that this slot will be equivalent to the [nb$add]{@link Sk.slots.nb$add} slot + */ + +/** + * @memberof Sk.slots + * @method sq$repeat + * @implements __mul__/__rmul__ + * @suppress {checkTypes} + * @description defining `sq$repeat` along with {@link Sk.slots.tp$as_sequence_or_mapping} will gain the slots + * `__mul__` and `__rmul__` + * note that this slot will be equivalent to the [nb$multiply]{@link Sk.slots.nb$multiply} slot + */ + +/** + * @memberof Sk.slots + * @method sq$length + * @param {boolean=} canSuspend + * @implements __len__ + * @suppress {checkTypes} + * @returns {number} + */ +slots.__len__ = { + $name: "__len__", + $slot_name: "sq$length", + $slot_func: function (dunderFunc) { + return function sq$length(canSuspend) { + let res; + if (canSuspend) { + res = Sk.misceval.callsimOrSuspendArray(dunderFunc, [this]); + return Sk.misceval.chain(res, (r) => { + return Sk.misceval.asIndexOrThrow(r, "'" + Sk.abstr.typeName(r) + "' object cannot be interpreted as an integer"); + }); + } else { + res = Sk.misceval.callsimArray(dunderFunc, [this]); + return Sk.misceval.asIndexOrThrow(res, "'" + Sk.abstr.typeName(res) + "' object cannot be interpreted as an integer"); + } + }; + }, + $wrapper: function __len__(self, args, kwargs) { + Sk.abstr.checkNoArgs("__len__", args, kwargs); + return new Sk.builtin.int_(self.sq$length(true)); + }, + $flags: {NoArgs: true}, + $textsig: "($self, /)", + $doc: "Return len(self).", +}; + +/** + * @suppress {checkTypes} + * @memberof Sk.slots + * @method sq$contains + * + * @param {pyObject} key + * @param {boolean=} canSuspend + * + * @implements __contains__ + * @returns {boolean} + */ +slots.__contains__ = { + $name: "__contains__", + $slot_name: "sq$contains", + $slot_func: function (dunderFunc) { + return function sq$contains(key, canSuspend) { + let res = Sk.misceval.callsimOrSuspendArray(dunderFunc, [this, key]); + res = Sk.misceval.chain(res, (r) => Sk.misceval.isTrue(r)); + if (res.$isSuspension) { + return canSuspend ? res : Sk.misceval.retryOptionalSuspensionOrThrow(res); + } + return res; + }; + }, + $wrapper: function __contains__(self, args, kwargs) { + Sk.abstr.checkOneArg("__contains__", args, kwargs); + return new Sk.builtin.bool(this.call(self, args[0], true)); + }, + $textsig: "($self, key, /)", + $flags: {OneArg: true}, + $doc: "Return key in self.", +}; + +/** + * @memberof Sk.slots + * @method mp$subscript + * @param {pyObject} key - might be a pyStr, pyInt or pySlice + * @param {boolean=} canSuspend + * @implements __getitem__ + * @suppress {checkTypes} + * @returns {pyObject} + * @throws {Sk.builtin.TypeError} + */ +slots.__getitem__ = { + $name: "__getitem__", + $slot_name: "mp$subscript", + $slot_func: function (dunderFunc) { + return function mp$subscript(key, canSuspend) { + const call_version = canSuspend ? Sk.misceval.callsimOrSuspendArray : Sk.misceval.callsimArray; + return call_version(dunderFunc, [this, key]); + }; + }, + $wrapper: wrapperCallOneArg, + $textsig: "($self, key, /)", + $flags: {OneArg: true}, + $doc: "Return self[key].", +}; + +/** + * @memberof Sk.slots + * @method mp$ass_subscript + * @param {pyObject} item - might be a pyStr, pyInt or pySlice + * @param {pyObject|undefined} value - undefined indicates the item should be deleted + * @param {boolean=} canSuspend + * @implements __setitem__ + * @suppress {checkTypes} + * @returns {pyObject} + * @throws {Sk.builtin.TypeError} + * @description + * Also implements __delitem__ + */ +slots.__setitem__ = { + $name: "__setitem__", + $slot_name: "mp$ass_subscript", + $slot_func: slotFuncSetDelete("__setitem__", "__delitem__", "does not support item assignment"), + $wrapper: wrapperSet, + $textsig: "($self, key, value, /)", + $flags: {MinArgs: 2, MaxArgs: 2}, + $doc: "Set self[key] to value.", +}; + +slots.__delitem__ = { + $name: "__delitem__", + $slot_name: "mp$ass_subscript", + $slot_func: slots.__setitem__.$slot_func, + $wrapper: wrapperCallOneArg, + $textsig: "($self, key, /)", + $flags: {OneArg: true}, + $doc: "Delete self[key].", +}; + +// number slots +/** + * @memberof Sk.slots + * @member tp$as_number + * @type {boolean} + * @description + * set `tp$as_number` to `true` in order for for {@link Sk.abstr.buildNativeClass} + * to acquire appropriate `slot_wrappers` for number slots + * You can find an exhaustive list in the source code {@link Sk.slots} + * + * Examples: + * - [nb$add]{@link Sk.slots.nb$add} + * - [nb$int_]{@link Sk.slots.nb$int_} + * - [nb$divide]{@link Sk.slots.nb$divide} - note we do not use `nb$true_divide` + * - [nb$bool]{@link Sk.slots.nb$bool} - should return a js boolean + * + * You need not define `nb$reflected_*` slots unless your implementation is different from the default implementation + * Similarly `nb$inplace_` need not be defined unless the implementation is different from the usual slot. + * + */ + +/** + * @memberof Sk.slots + * @method nb$add + * @implements __add__ + * @suppress {checkTypes} + * @description + * the reflected slot will be defined if not set + * + */ +slots.__add__ = { + $name: "__add__", + $slot_name: "nb$add", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self+value.", +}; +/** + * @memberof Sk.slots + * @method nb$relfceted_add + * @implements __radd__ + * @suppress {checkTypes} + * @description + * the reflected slot will be defined if not set + * + */ +slots.__radd__ = { + $name: "__radd__", + $slot_name: "nb$reflected_add", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return value+self.", +}; +/** + * @memberof Sk.slots + * @method nb$inplace_add + * @implements __iadd__ + * @suppress {checkTypes} + * @description + * Only define this if your implementation is different from `nb$add` + * + */ +slots.__iadd__ = { + $name: "__iadd__", + $slot_name: "nb$inplace_add", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Implement self+=value.", +}; +/** + * @memberof Sk.slots + * @method nb$subtract + * @implements __sub__ + * @suppress {checkTypes} + * + */ +slots.__sub__ = { + $name: "__sub__", + $slot_name: "nb$subtract", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self-value.", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_subtract + * @implements __rsub__ + * @suppress {checkTypes} + */ +slots.__rsub__ = { + $name: "__rsub__", + $slot_name: "nb$reflected_subtract", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return value-self.", +}; +/** + * @memberof Sk.slots + * @method nb$inplace_multiply + * @implements __imul__ + * @suppress {checkTypes} + */ +slots.__imul__ = { + $name: "__imul__", + $slot_name: "nb$inplace_multiply", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Implement self*=value.", +}; +/** + * @memberof Sk.slots + * @method nb$multiply + * @implements __mul__ + * @suppress {checkTypes} + */ +slots.__mul__ = { + $name: "__mul__", + $slot_name: "nb$multiply", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self*value.", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_multiply + * @implements __rmul__ + * @suppress {checkTypes} + */ +slots.__rmul__ = { + $name: "__rmul__", + $slot_name: "nb$reflected_multiply", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return value*self.", +}; +/** + * @memberof Sk.slots + * @method nb$inplace_subtract + * @implements __isub__ + * @suppress {checkTypes} + */ +slots.__isub__ = { + $name: "__isub__", + $slot_name: "nb$inplace_subtract", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Implement self-=value.", +}; +/** + * @memberof Sk.slots + * @method nb$remainder + * @implements __mod__ + * @suppress {checkTypes} + */ +slots.__mod__ = { + $name: "__mod__", + $slot_name: "nb$remainder", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self%value.", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_remainder + * @implements __rmod__ + * @suppress {checkTypes} + */ +slots.__rmod__ = { + $name: "__rmod__", + $slot_name: "nb$reflected_remainder", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return value%self.", +}; +/** + * @memberof Sk.slots + * @method nb$inplace_remainder + * @implements __imod__ + * @suppress {checkTypes} + */ +slots.__imod__ = { + $name: "__imod__", + $slot_name: "nb$inplace_remainder", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Implement value%=self.", +}; +/** + * @memberof Sk.slots + * @method nb$divmod + * @implements __divmod__ + * @suppress {checkTypes} + */ +slots.__divmod__ = { + $name: "__divmod__", + $slot_name: "nb$divmod", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return divmod(self, value).", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_divmod + * @implements __rdivmod__ + * @suppress {checkTypes} + */ +slots.__rdivmod__ = { + $name: "__rdivmod__", + $slot_name: "nb$reflected_divmod", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return divmod(value, self)", +}; +/** + * @memberof Sk.slots + * @method nb$positive + * @implements __pos__ + * @suppress {checkTypes} + */ +slots.__pos__ = { + $name: "__pos__", + $slot_name: "nb$positive", + $slot_func: slotFuncNoArgs, + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "+self", +}; +/** + * @memberof Sk.slots + * @method nb$negative + * @implements __neg__ + * @suppress {checkTypes} + */ +slots.__neg__ = { + $name: "__neg__", + $slot_name: "nb$negative", + $slot_func: slotFuncNoArgs, + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "-self", +}; +/** + * @memberof Sk.slots + * @method nb$abs + * @implements __abs__ + * @suppress {checkTypes} + */ +slots.__abs__ = { + $name: "__abs__", + $slot_name: "nb$abs", + $slot_func: slotFuncNoArgs, + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "abs(self)", +}; +/** + * @memberof Sk.slots + * @method nb$bool + * @implements __bool__ + * @suppress {checkTypes} + * @returns {boolean} + */ +slots.__bool__ = { + $name: "__bool__", + $slot_name: "nb$bool", + $slot_func: slotFuncNoArgsWithCheck("__bool__", Sk.builtin.checkBool, "bool", (res) => res.v !== 0), + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "self != 0", +}; +/** + * @memberof Sk.slots + * @method nb$invert + * @implements __invert__ + * @suppress {checkTypes} + */ +slots.__invert__ = { + $name: "__invert__", + $slot_name: "nb$invert", + $slot_func: slotFuncNoArgs, + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "~self", +}; +/** + * @memberof Sk.slots + * @method nb$lshift + * @implements __lshift__ + * @suppress {checkTypes} + */ +slots.__lshift__ = { + $name: "__lshift__", + $slot_name: "nb$lshift", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self<>value.", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_rshift + * @implements __rrshift__ + * @suppress {checkTypes} + */ +slots.__rrshift__ = { + $name: "__rrshift__", + $slot_name: "nb$reflected_rshift", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return value>>self.", +}; +/** + * @memberof Sk.slots + * @method nb$inplace_lshift + * @implements __ilshift__ + * @suppress {checkTypes} + */ +slots.__ilshift__ = { + $name: "__ilshift__", + $slot_name: "nb$inplace_lshift", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Implement self<<=value.", +}; +/** + * @memberof Sk.slots + * @method nb$inplace_rshift + * @implements __irshift__ + * @suppress {checkTypes} + */ +slots.__irshift__ = { + $name: "__irshift__", + $slot_name: "nb$inplace_rshift", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Implement self=>>value.", +}; +/** + * @memberof Sk.slots + * @method nb$and + * @implements __and__ + * @suppress {checkTypes} + */ +slots.__and__ = { + $name: "__and__", + $slot_name: "nb$and", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self&value.", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_and + * @implements __rand__ + * @suppress {checkTypes} + */ +slots.__rand__ = { + $name: "__rand__", + $slot_name: "nb$refelcted_and", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return value&self.", +}; +/** + * @memberof Sk.slots + * @method nb$inplace_and + * @implements __iand__ + * @suppress {checkTypes} + */ +slots.__iand__ = { + $name: "__iand__", + $slot_name: "nb$and", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Implement self&=value.", +}; +/** + * @memberof Sk.slots + * @method nb$xor + * @implements __xor__ + * @suppress {checkTypes} + */ +slots.__xor__ = { + $name: "__xor__", + $slot_name: "nb$xor", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self^value.", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_xor + * @implements __rxor__ + * @suppress {checkTypes} + */ +slots.__rxor__ = { + $name: "__rxor__", + $slot_name: "nb$reflected_xor", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return value^self.", +}; +/** + * @memberof Sk.slots + * @method nb$inplace_xor + * @implements __ixor__ + * @suppress {checkTypes} + */ +slots.__ixor__ = { + $name: "__ixor__", + $slot_name: "nb$inplace_xor", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Implement self^=value.", +}; +/** + * @memberof Sk.slots + * @method nb$or + * @implements __or__ + * @suppress {checkTypes} + */ +slots.__or__ = { + $name: "__or__", + $slot_name: "nb$or", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self|value.", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_or + * @implements __ror__ + * @suppress {checkTypes} + */ +slots.__ror__ = { + $name: "__ror__", + $slot_name: "nb$reflected_or", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return value|self.", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_ior + * @implements __ior__ + * @suppress {checkTypes} + */ +slots.__ior__ = { + $name: "__ior__", + $slot_name: "nb$inplace_or", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Implement self|=value.", +}; +/** + * @memberof Sk.slots + * @method nb$int_ + * @implements __int__ + * @suppress {checkTypes} + */ +slots.__int__ = { + $name: "__int__", + $slot_name: "nb$int_", + $slot_func: slotFuncNoArgsWithCheck("__int__", Sk.builtin.checkInt, "int"), + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "int(self)", +}; +/** + * @memberof Sk.slots + * @method nb$float_ + * @implements __float__ + * @suppress {checkTypes} + */ +slots.__float__ = { + $name: "__float__", + $slot_name: "nb$float_", + $slot_func: slotFuncNoArgsWithCheck("__float__", Sk.builtin.checkFloat, "float"), + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "float(self)", +}; +/** + * @memberof Sk.slots + * @method nb$floor_divide + * @implements __floordiv__ + * @suppress {checkTypes} + */ +slots.__floordiv__ = { + $name: "__floordiv__", + $slot_name: "nb$floor_divide", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self//value.", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_floor_divide + * @implements __rfloordiv__ + * @suppress {checkTypes} + */ +slots.__rfloordiv__ = { + $name: "__rfloordiv__", + $slot_name: "nb$reflected_floor_divide", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return value//self.", +}; +/** + * @memberof Sk.slots + * @method nb$inplace_floor_divide + * @implements __ifloordiv__ + * @suppress {checkTypes} + */ +slots.__ifloordiv__ = { + $name: "__ifloordiv__", + $slot_name: "nb$inplace_floor_divide", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Implement self//=value.", +}; +/** + * @memberof Sk.slots + * @method nb$divide + * @implements __truediv__ + * @suppress {checkTypes} + */ +slots.__truediv__ = { + $name: "__truediv__", + $slot_name: "nb$divide", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self/value.", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_divide + * @implements __rtruediv__ + * @suppress {checkTypes} + */ +slots.__rtruediv__ = { + $name: "__rtruediv__", + $slot_name: "nb$reflected_divide", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return value/self.", +}; +/** + * @memberof Sk.slots + * @method nb$inplace_divide + * @implements __itruediv__ + * @suppress {checkTypes} + */ +slots.__itruediv__ = { + $name: "__itruediv__", + $slot_name: "nb$inplace_divide", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Implement self/=value.", +}; +/** + * @memberof Sk.slots + * @method nb$index + * @implements __index__ + * @suppress {checkTypes} + */ +slots.__index__ = { + $name: "__index__", + $slot_name: "nb$index", + $slot_func: slotFuncNoArgsWithCheck("__index__", Sk.builtin.checkInt, "int"), + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "Return self converted to an integer, if self is suitable for use as an index into a list.", +}; +/** + * @memberof Sk.slots + * @method nb$power + * @implements __pow__ + * @suppress {checkTypes} + */ +slots.__pow__ = { + $name: "__pow__", + $slot_name: "nb$power", + $slot_func: function (dunderFunc) { + return function (value, mod) { + if (mod == undefined) { + return Sk.misceval.callsimArray(dunderFunc, [this, value]); + } else { + return Sk.misceval.callsimArray(dunderFunc, [this, value, mod]); + } + }; + }, + $wrapper: wrapperCallTernary, + $textsig: "($self, value, mod=None, /)", + $flags: {MinArgs: 1, MaxArgs: 2}, + $doc: "Return pow(self, value, mod).", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_power + * @implements __rpow__ + * @suppress {checkTypes} + */ +slots.__rpow__ = { + $name: "__rpow__", + $slot_name: "nb$reflected_power", + $slot_func: slots.__pow__.$slot_func, + $wrapper: wrapperCallTernary, + $textsig: "($self, value, mod=None, /)", + $flags: {MinArgs: 1, MaxArgs: 2}, + $doc: "Return pow(value, self, mod).", +}; +/** + * @memberof Sk.slots + * @method nb$inplace_power + * @implements __ipow__ + * @suppress {checkTypes} + */ +slots.__ipow__ = { + $name: "__ipow__", + $slot_name: "nb$inplace_power", + $slot_func: slots.__pow__.$slot_func, + $wrapper: wrapperCallTernary, + $textsig: "($self, value, mod=None, /)", + $flags: {MinArgs: 1, MaxArgs: 2}, + $doc: "Implement **=", +}; +/** + * @memberof Sk.slots + * @method nb$matrix_multiply + * @implements __matmul__ + * @suppress {checkTypes} + */ +slots.__matmul__ = { + $name: "__matmul__", + $slot_name: "nb$matrix_multiply", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return self@value.", +}; +/** + * @memberof Sk.slots + * @method nb$reflected_matrix_multiply + * @implements __rmatmul__ + * @suppress {checkTypes} + */ +slots.__rmatmul__ = { + $name: "__rmatmul__", + $slot_name: "nb$reflected_matrix_multiply", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Return value@self.", +}; +/** + * @memberof Sk.slots + * @method nb$inplace_matrix_multiply + * @implements __imatmul__ + * @suppress {checkTypes} + */ +slots.__imatmul__ = { + $name: "__imatmul__", + $slot_name: "nb$inplace_matrix_multiply", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, value, /)", + $flags: {OneArg: true}, + $doc: "Implement self@=value.", +}; + +// py2 ONLY slots +slots.__long__ = { + $name: "__long__", + $slot_name: "nb$lng", + $slot_func: slotFuncNoArgsWithCheck("__long__", Sk.builtin.checkInt, "int"), + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "int(self)", +}; + +var py3$slots; +var py2$slots = { + next: { + $name: "next", + $slot_name: "tp$iternext", + $slot_func: slots.__next__.$slot_func, + $wrapper: slots.__next__.$wrapper, + $textsig: slots.__next__.$textsig, + $flags: slots.__next__.$flags, + }, + __nonzero__: { + $name: "__nonzero__", + $slot_name: "nb$bool", + $slot_func: slotFuncNoArgsWithCheck("__nonzero__", Sk.builtin.checkInt, "int", (res) => res.v !== 0), + $wrapper: wrapperCallNoArgs, + $textsig: "($self, /)", + $flags: {NoArgs: true}, + $doc: "x.__nonzero__() <==> x != 0", + }, + __div__: { + $name: "__div__", + $slot_name: "nb$divide", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, other/)", + $flags: {OneArg: true}, + $doc: "x.__div__(y) <==> x/y", + }, + __rdiv__: { + $name: "__rdiv__", + $slot_name: "nb$reflected_divide", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, other/)", + $flags: {OneArg: true}, + $doc: "x.__rdiv__(y) <==> x/y", + }, + __idiv__: { + $name: "__idiv__", + $slot_name: "nb$inplace_divide", + $slot_func: slotFuncOneArg, + $wrapper: wrapperCallOneArg, + $textsig: "($self, other/)", + $flags: {OneArg: true}, + $doc: "implement self /= other", + }, +}; +/** + * @memberof Sk.slots + * @member subSlots + * @description + * See the source code for a full list of slots split into apprpriate categories + */ +Sk.subSlots = { + main_slots: { + // nb we handle tp$new differently + // tp_slots + tp$init: "__init__", + tp$call: "__call__", + $r: "__repr__", + // tp$hash: "__hash__", // do tp$hash separately since it could be None + tp$str: "__str__", + + // getattribute, setattr, delattr + tp$getattr: "__getattribute__", + tp$setattr: ["__setattr__", "__delattr__"], + + // tp$richcompare + ob$eq: "__eq__", + ob$ne: "__ne__", + ob$lt: "__lt__", + ob$le: "__le__", + ob$gt: "__gt__", + ob$ge: "__ge__", + + // getters and setters + tp$descr_get: "__get__", + tp$descr_set: ["__set__", "__delete__"], + + // iter + tp$iter: "__iter__", + tp$iternext: "__next__", + }, + + number_slots: { + nb$abs: "__abs__", + nb$negative: "__neg__", + nb$positive: "__pos__", + nb$int_: "__int__", + nb$lng: "__long__", + nb$float_: "__float__", + nb$add: "__add__", + nb$reflected_add: "__radd__", + nb$inplace_add: "__iadd__", + nb$subtract: "__sub__", + nb$reflected_subtract: "__rsub__", + nb$inplace_subtract: "__isub__", + nb$multiply: "__mul__", + nb$reflected_multiply: "__rmul__", + nb$inplace_multiply: "__imul__", + nb$floor_divide: "__floordiv__", + nb$reflected_floor_divide: "__rfloordiv__", + nb$inplace_floor_divide: "__ifloordiv__", + nb$invert: "__invert__", + nb$remainder: "__mod__", + nb$reflected_remainder: "__rmod__", + nb$inplace_mod: "__imod__", + nb$divmod: "__divmod__", + nb$reflected_divmod: "__rdivmod__", + nb$power: "__pow__", + nb$reflected_power: "__rpow__", + nb$inplace_power: "__ipow__", + nb$divide: "__truediv__", // TODO: think about py2 vs py3 truediv vs div + nb$reflected_divide: "__rtruediv__", + nb$inplace_divide: "__itruediv__", + + nb$bool: "__bool__", + + nb$and: "__and__", + nb$reflected_and: "__rand__", + nb$inplace_and: "__iand__", + nb$or: "__or__", + nb$reflected_or: "__ror__", + nb$inplace_or: "__ior__", + nb$xor: "__xor__", + nb$reflected_xor: "__rxor__", + nb$inplace_xor: "__ixor__", + + nb$lshift: "__lshift__", + nb$reflected_lshift: "__rlshift__", + nb$rshift: "__rshift__", + nb$reflected_rshift: "__rrshift__", + nb$inplace_lshift: "__ilshift__", + nb$inplace_rshift: "__irshift__", + }, + + sequence_and_mapping_slots: { + // sequence and mapping slots + sq$length: "__len__", + sq$contains: "__contains__", + mp$subscript: "__getitem__", + mp$ass_subscript: ["__setitem__", "__delitem__"], + nb$add: "__add__", + nb$multiply: "__mul__", + nb$reflected_multiply: "__rmul__", + nb$inplace_add: "__iadd__", + nb$inplace_multiply: "__imul__", + }, +}; + +Sk.reflectedNumberSlots = { + nb$add: {reflected: "nb$reflected_add"}, + nb$subtract: { + reflected: "nb$reflected_subtract", + slot: function (other) { + if (other instanceof this.constructor) { + return other.nb$subtract(this); + } + return Sk.builtin.NotImplemented.NotImplemented$; + }, + }, + nb$multiply: {reflected: "nb$reflected_multiply"}, + nb$divide: { + reflected: "nb$reflected_divide", + slot: function (other) { + if (other instanceof this.constructor) { + return other.nb$divide(this); + } + return Sk.builtin.NotImplemented.NotImplemented$; + }, + }, + nb$floor_divide: { + reflected: "nb$reflected_floor_divide", + slot: function (other) { + if (other instanceof this.constructor) { + return other.nb$floor_divide(this); + } + return Sk.builtin.NotImplemented.NotImplemented$; + }, + }, + nb$remainder: { + reflected: "nb$reflected_remainder", + slot: function (other) { + if (other instanceof this.constructor) { + return other.nb$remainder(this); + } + return Sk.builtin.NotImplemented.NotImplemented$; + }, + }, + nb$divmod: { + reflected: "nb$reflected_divmod", + slot: function (other) { + if (other instanceof this.constructor) { + return other.nb$divmod(this); + } + return Sk.builtin.NotImplemented.NotImplemented$; + }, + }, + nb$power: { + reflected: "nb$reflected_power", + slot: function (other, mod) { + if (other instanceof this.constructor) { + return other.nb$power(this, mod); + } + return Sk.builtin.NotImplemented.NotImplemented$; + }, + }, + nb$and: {reflected: "nb$reflected_and"}, + nb$or: {reflected: "nb$reflected_or"}, + nb$xor: {reflected: "nb$reflected_xor"}, + nb$lshift: { + reflected: "nb$reflected_lshift", + slot: function (other) { + if (other instanceof this.constructor) { + return other.nb$lshift(this); + } + return Sk.builtin.NotImplemented.NotImplemented$; + }, + }, + nb$rshift: { + reflected: "nb$reflected_rshift", + slot: function (other) { + if (other instanceof this.constructor) { + return other.nb$rshift(this); + } + return Sk.builtin.NotImplemented.NotImplemented$; + }, + }, + nb$matrix_multiply: { + reflected: "nb$reflexted_matrix_multiply", + slot: function (other) { + if (other instanceof this.constructor) { + return other.nb$matrix_multiply(this); + } + return Sk.builtin.NotImplemented.NotImplemented$; + }, + }, +}; + +Sk.sequenceAndMappingSlots = { + sq$concat: ["nb$add"], + sq$repeat: ["nb$multiply", "nb$reflected_multiply"], + mp$length: ["sq$length"], +}; + +/** + * + * + * @member Sk.dunderToSkulpt + * + * Maps Python dunder names to the Skulpt Javascript function names that + * implement them. + * + * Note: __add__, __mul__, and __rmul__ can be used for either numeric or + * sequence types. Here, they default to the numeric versions (i.e. nb$add, + * nb$multiply, and nb$reflected_multiply). This works because Sk.abstr.binary_op_ + * checks for the numeric shortcuts and not the sequence shortcuts when computing + * a binary operation. + * + * Because many of these functions are used in contexts in which Skulpt does not + * [yet] handle suspensions, the assumption is that they must not suspend. However, + * some of these built-in functions are acquiring "canSuspend" arguments to signal + * where this is not the case. These need to be spliced out of the argument list before + * it is passed to python. Array values in this map contain [dunderName, argumentIdx], + * where argumentIdx specifies the index of the "canSuspend" boolean argument. + * + * @description + * A mapping of dunder names to skulpt slots + * + * @type {Object} + */ +Sk.dunderToSkulpt = { + __repr__: "$r", + __str__: "tp$str", + __init__: "tp$init", + __new__: "tp$new", + __hash__: "tp$hash", + __call__: "tp$call", + + __eq__: "ob$eq", + __ne__: "ob$ne", + __lt__: "ob$lt", + __le__: "ob$le", + __gt__: "ob$gt", + __ge__: "ob$ge", + + __abs__: "nb$abs", + __neg__: "nb$negative", + __pos__: "nb$positive", + __int__: "nb$int_", + __float__: "nb$float_", + + __add__: "nb$add", + __radd__: "nb$reflected_add", + __iadd__: "nb$inplace_add", + __sub__: "nb$subtract", + __rsub__: "nb$reflected_subtract", + __isub__: "nb$inplace_subtract", + __mul__: "nb$multiply", + __rmul__: "nb$reflected_multiply", + __imul__: "nb$inplace_multiply", + __truediv__: "nb$divide", + __rtruediv__: "nb$reflected_divide", + __itruediv__: "nb$inplace_divide", + __floordiv__: "nb$floor_divide", + __rfloordiv__: "nb$reflected_floor_divide", + __ifloordiv__: "nb$inplace_floor_divide", + __invert__: "nb$invert", + __mod__: "nb$remainder", + __rmod__: "nb$reflected_remainder", + __imod__: "nb$inplace_remainder", + __divmod__: "nb$divmod", + __rdivmod__: "nb$reflected_divmod", //no inplace divmod + __pow__: "nb$power", + __rpow__: "nb$reflected_power", + __ipow__: "nb$inplace_power", + + __bool__: "nb$bool", + // py2 only + __long__: "nb$lng", + + __lshift__: "nb$lshift", + __rlshift__: "nb$reflected_lshift", + __ilshift__: "nb$inplace_lshift", + __rshift__: "nb$rshift", + __rrshift__: "nb$reflected_rshift", + __irshift__: "nb$inplace_rshift", + + __and__: "nb$and", + __rand__: "nb$reflected_and", + __iand__: "nb$inplace_and", + __or__: "nb$or", + __ror__: "nb$reflected_or", + __ior__: "nb$inplace_or", + __xor__: "nb$xor", + __rxor__: "nb$reflected_xor", + __ixor__: "nb$inplace_xor", + + __matmul__: "nb$matrix_multiply", + __rmatmul__: "nb$reflected_matrix_multiply", + __imatmul__: "nb$inplace_matrix_multiply", + + __get__: "tp$descr_get", + __set__: "tp$descr_set", + __delete__: "tp$descr_set", + + __getattribute__: "tp$getattr", + __getattr__: "tp$getattr", + __setattr__: "tp$setattr", + __delattr__: "tp$setattr", + + __len__: "sq$length", + __contains__: "sq$contains", + __getitem__: "mp$subscript", + __setitem__: "mp$ass_subscript", + __delitem__: "mp$ass_subscript", +}; + +Sk.exportSymbol("Sk.setupDunderMethods", Sk.setupDunderMethods); + +Sk.setupDunderMethods = function (py3) { + const slots = Sk.slots; + if (py3 && py3$slots === undefined) { + // assume python3 switch version if we have to + return; + } + const classes_with_next = [ + Sk.builtin.list_iter_, + Sk.builtin.set_iter_, + Sk.builtin.str_iter_, + Sk.builtin.tuple_iter_, + Sk.builtin.generator, + Sk.builtin.enumerate, + Sk.builtin.filter_, + Sk.builtin.zip_, + Sk.builtin.reversed, + Sk.builtin.map_, + Sk.builtin.seq_iter_, + Sk.builtin.callable_iter_, + Sk.builtin.reverselist_iter_, + Sk.builtin.dict_iter_, + Sk.builtin.dict_itemiter_, + Sk.builtin.dict_valueiter_, + Sk.builtin.dict_reverse_iter_, + Sk.builtin.dict_reverse_itemiter_, + Sk.builtin.dict_reverse_valueiter_, + Sk.builtin.range_iter_, + Sk.builtin.revereserange_iter_, + Sk.generic.iterator, + ]; + const classes_with_bool = [Sk.builtin.int_, Sk.builtin.lng, Sk.builtin.float_, Sk.builtin.complex]; + const classes_with_divide = classes_with_bool; + const number_slots = Sk.subSlots.number_slots; + const main_slots = Sk.subSlots.main_slots; + const dunderToSkulpt = Sk.dunderToSkulpt; + + function switch_version(classes_with, old_meth, new_meth) { + for (let i = 0; i < classes_with.length; i++) { + const cls_proto = classes_with[i].prototype; + if (cls_proto.hasOwnProperty(new_meth)) { + continue; + } + cls_proto[new_meth] = cls_proto[old_meth]; + delete cls_proto[old_meth]; + } + } + + if (py3) { + Sk.builtin.str.$next = new Sk.builtin.str("__next__"); + dunderToSkulpt.__bool__ = "nb$bool"; + dunderToSkulpt.__next__ = "tp$iternext"; + + delete dunderToSkulpt.__nonzero__; + delete dunderToSkulpt.__div__; + delete dunderToSkulpt.__rdiv__; + delete dunderToSkulpt.__idiv__; + delete dunderToSkulpt.next; + + for (let slot_name in py3$slots) { + slots[slot_name] = py3$slots[slot_name]; + } + for (let slot_name in py2$slots) { + delete slots[slot_name]; + } + for (let i = 0; i < classes_with_divide.length; i++) { + const cls_proto = classes_with_divide[i].prototype; + delete cls_proto.__div__; + delete cls_proto.__rdiv__; + } + + main_slots.tp$iternext = "__next__"; + number_slots.nb$bool = "__bool__"; + switch_version(classes_with_next, "next", "__next__"); + switch_version(classes_with_bool, "__bool__", "__nonzero__"); + } else { + if (py3$slots === undefined) { + slots.py3$slots = { + __next__: slots.__next__, + }; + py3$slots = slots.py3$slots; + } + Sk.builtin.str.$next = new Sk.builtin.str("next"); + dunderToSkulpt.next = "tp$iternext"; + dunderToSkulpt.__nonzero__ = "nb$bool"; + dunderToSkulpt.__div__ = "nb$divide"; + dunderToSkulpt.__rdiv__ = "nb$reflected_divide"; + dunderToSkulpt.__idiv__ = "nb$inplace_divide"; + delete dunderToSkulpt.__bool__; + delete dunderToSkulpt.__next__; + + for (let slot_name in py2$slots) { + slots[slot_name] = py2$slots[slot_name]; + } + for (let slot_name in py3$slots) { + delete slots[slot_name]; + } + + main_slots.tp$iternext = "next"; + number_slots.nb$bool = "__nonzero__"; + switch_version(classes_with_next, "__next__", "next"); + switch_version(classes_with_bool, "__nonzero__", "__bool__"); + + for (let i = 0; i < classes_with_divide.length; i++) { + const cls = classes_with_divide[i]; + const cls_proto = cls.prototype; + if (cls_proto.hasOwnProperty("__div__")) { + continue; + } + cls_proto.__div__ = new Sk.builtin.wrapper_descriptor(cls, py2$slots.__div__, cls_proto.nb$divide); + cls_proto.__rdiv__ = new Sk.builtin.wrapper_descriptor(cls, py2$slots.__rdiv__, Sk.reflectedNumberSlots.nb$divide.slot); + } + } +}; diff --git a/src/sorted.js b/src/sorted.js index 000aceee1e..8e44fbdbc2 100644 --- a/src/sorted.js +++ b/src/sorted.js @@ -1,4 +1,4 @@ -Sk.builtin.sorted = function sorted (iterable, cmp, key, reverse) { +Sk.builtin.sorted = function sorted(iterable, cmp, key, reverse) { var arr; var next; var iter; diff --git a/src/str.js b/src/str.js index 002f690830..07b5aeb2fe 100755 --- a/src/str.js +++ b/src/str.js @@ -1,4 +1,12 @@ -Sk.builtin.interned = {}; +Sk.builtin.interned = Object.create(null); // avoid name conflicts with Object.prototype + +function getInterned(x) { + return Sk.builtin.interned[x]; +} + +function setInterned(x, pyStr) { + Sk.builtin.interned[x] = pyStr; +} /** * @constructor @@ -6,34 +14,27 @@ Sk.builtin.interned = {}; * @extends Sk.builtin.object */ Sk.builtin.str = function (x) { - var ret; - - Sk.builtin.pyCheckArgsLen("str", arguments.length, 0, 1); - - if (x === undefined) { - x = ""; - } - if (x instanceof Sk.builtin.str) { - return x; - } - if (!(this instanceof Sk.builtin.str)) { - return new Sk.builtin.str(x); - } - - - // convert to js string - if (x === true) { + // new Sk.builtin.str is an internal function called with a JS value x + // occasionally called with a python object and returns tp$str() or $r(); + Sk.asserts.assert(this instanceof Sk.builtin.str, "bad call to str - use 'new'"); + // Temporary + // Sk.asserts.assert(typeof this === "string" || this === undefined || this.sk$object, "str called with an invalid JS object"); + + let ret, interned; + if (typeof x === "string") { + // the common case + ret = x; + } else if (x === undefined) { + ret = ""; + } else if (x === null) { + ret = "None"; + } else if (x.tp$str !== undefined) { + // then we're a python object - all objects inherit from object which has tp$str + return x.tp$str(); + } else if (x === true) { ret = "True"; } else if (x === false) { ret = "False"; - } else if ((x === null) || (x instanceof Sk.builtin.none)) { - ret = "None"; - } else if (x instanceof Sk.builtin.bool) { - if (x.v) { - ret = "True"; - } else { - ret = "False"; - } } else if (typeof x === "number") { ret = x.toString(); if (ret === "Infinity") { @@ -41,40 +42,55 @@ Sk.builtin.str = function (x) { } else if (ret === "-Infinity") { ret = "-inf"; } - } else if (typeof x === "string") { - ret = x; - } else if (x.tp$str !== undefined) { - ret = x.tp$str(); - if (!(ret instanceof Sk.builtin.str)) { - throw new Sk.builtin.ValueError("__str__ didn't return a str"); - } - return ret; } else { - return Sk.misceval.objectRepr(x); + throw new Sk.builtin.TypeError("could not convert object of type '" + Sk.abstr.typeName(x) + "' to str"); } - + interned = getInterned(ret); // interning required for strings in py - if (Sk.builtin.interned["1" + ret]) { - return Sk.builtin.interned["1" + ret]; + + if (interned !== undefined) { + return interned; + } else { + setInterned(ret, this); } - this.__class__ = Sk.builtin.str; + this.$mangled = fixReserved(ret); + // quicker set_dict for strings by preallocating the $savedKeyHash + this.$savedKeyHash_ = "_" + ret; this.v = ret; - this["v"] = this.v; - Sk.builtin.interned["1" + ret] = this; - return this; - }; + Sk.exportSymbol("Sk.builtin.str", Sk.builtin.str); +Sk.abstr.setUpInheritance("str", Sk.builtin.str, Sk.builtin.object); -Sk.abstr.setUpInheritance("str", Sk.builtin.str, Sk.builtin.seqtype); +Sk.builtin.str.prototype.tp$as_sequence_or_mapping = true; +// Sk.builtin.str.prototype.tp$as_number = true; // we currently don't support nb$mod + +Sk.builtin.str.prototype.tp$doc = + "str(object='') -> str\nstr(bytes_or_buffer[, encoding[, errors]]) -> str\n\nCreate a new string object from the given object. If encoding or\nerrors is specified, then the object must expose a data buffer\nthat will be decoded using the given encoding and error handler.\nOtherwise, returns the result of object.__str__() (if defined)\nor repr(object).\nencoding defaults to sys.getdefaultencoding().\nerrors defaults to 'strict'."; + +Sk.builtin.str.prototype.tp$new = function (args, kwargs) { + if (this !== Sk.builtin.str.prototype) { + return this.$subtype_new(args, kwargs); + } + args = Sk.abstr.copyKeywordsToNamedArgs("str", ["object"], args, kwargs); + const x = args[0]; + return new Sk.builtin.str(x); +}; + +Sk.builtin.str.prototype.$subtype_new = function (args, kwargs) { + const instance = new this.constructor(); + // we call str new method with all the args and kwargs + const str_instance = Sk.builtin.str.prototype.tp$new(args, kwargs); + instance.v = str_instance.v; + return instance; +}; Sk.builtin.str.prototype.$jsstr = function () { return this.v; }; Sk.builtin.str.prototype.mp$subscript = function (index) { - var ret; if (Sk.misceval.isIndex(index)) { index = Sk.misceval.asIndex(index); if (index < 0) { @@ -85,11 +101,10 @@ Sk.builtin.str.prototype.mp$subscript = function (index) { } return new Sk.builtin.str(this.v.charAt(index)); } else if (index instanceof Sk.builtin.slice) { - ret = ""; - index.sssiter$(this, function (i, wrt) { - if (i >= 0 && i < wrt.v.length) { - ret += wrt.v.charAt(i); - } + let ret = ""; + const str = this.v; + index.sssiter$(str.length, (i) => { + ret += str.charAt(i); }); return new Sk.builtin.str(ret); } else { @@ -100,6 +115,7 @@ Sk.builtin.str.prototype.mp$subscript = function (index) { Sk.builtin.str.prototype.sq$length = function () { return this.v.length; }; + Sk.builtin.str.prototype.sq$concat = function (other) { var otypename; if (!other || !Sk.builtin.checkString(other)) { @@ -108,8 +124,8 @@ Sk.builtin.str.prototype.sq$concat = function (other) { } return new Sk.builtin.str(this.v + other.v); }; -Sk.builtin.str.prototype.nb$add = Sk.builtin.str.prototype.sq$concat; -Sk.builtin.str.prototype.nb$inplace_add = Sk.builtin.str.prototype.sq$concat; +// Sk.builtin.str.prototype.nb$add = Sk.builtin.str.prototype.sq$concat; +// Sk.builtin.str.prototype.nb$inplace_add = Sk.builtin.str.prototype.sq$concat; Sk.builtin.str.prototype.sq$repeat = function (n) { var i; var ret; @@ -125,8 +141,9 @@ Sk.builtin.str.prototype.sq$repeat = function (n) { } return new Sk.builtin.str(ret); }; -Sk.builtin.str.prototype.nb$multiply = Sk.builtin.str.prototype.sq$repeat; -Sk.builtin.str.prototype.nb$inplace_multiply = Sk.builtin.str.prototype.sq$repeat; +// Sk.builtin.str.prototype.nb$multiply = +// Sk.builtin.str.prototype.nb$reflected_multiply = +// Sk.builtin.str.prototype.nb$inplace_multiply = Sk.builtin.str.prototype.sq$repeat; Sk.builtin.str.prototype.sq$item = function () { Sk.asserts.fail(); }; @@ -146,17 +163,13 @@ Sk.builtin.str.prototype.sq$contains = function (ob) { return this.v.indexOf(ob.v) != -1; }; -Sk.builtin.str.prototype.__iter__ = new Sk.builtin.func(function (self) { - return new Sk.builtin.str_iter_(self); -}); - Sk.builtin.str.prototype.tp$iter = function () { return new Sk.builtin.str_iter_(this); }; Sk.builtin.str.prototype.tp$richcompare = function (other, op) { if (!(other instanceof Sk.builtin.str)) { - return undefined; + return Sk.builtin.NotImplemented.NotImplemented$; } switch (op) { @@ -177,7 +190,7 @@ Sk.builtin.str.prototype.tp$richcompare = function (other, op) { } }; -Sk.builtin.str.prototype["$r"] = function () { +Sk.builtin.str.prototype.$r = function () { // single is preferred var ashex; var c; @@ -216,8 +229,15 @@ Sk.builtin.str.prototype["$r"] = function () { return new Sk.builtin.str(ret); }; +Sk.builtin.str.prototype.tp$str = function () { + if (this.constructor === Sk.builtin.str) { + return this; + } else { + return new Sk.builtin.str(this.v); + } +}; -Sk.builtin.str.re_escape_ = function (s) { +Sk.builtin.str.$re_escape = function (s) { var c; var i; var ret = []; @@ -238,22 +258,24 @@ Sk.builtin.str.re_escape_ = function (s) { return ret.join(""); }; -Sk.builtin.str.prototype["encode"] = new Sk.builtin.func(function (self) { - //Sk.builtin.pyCheckArgsLen("encode", arguments.length, 1, 1); +// methods +Sk.builtin.str.methods = {}; + +Sk.builtin.str.methods["encode"] = function (self) { return self; -}); +}; -Sk.builtin.str.prototype["lower"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.lower = function (self) { Sk.builtin.pyCheckArgsLen("lower", arguments.length, 1, 1); return new Sk.builtin.str(self.v.toLowerCase()); -}); +}; -Sk.builtin.str.prototype["upper"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.upper = function (self) { Sk.builtin.pyCheckArgsLen("upper", arguments.length, 1, 1); return new Sk.builtin.str(self.v.toUpperCase()); -}); +}; -Sk.builtin.str.prototype["capitalize"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.capitalize = function (self) { var i; var cap; var orig; @@ -269,9 +291,9 @@ Sk.builtin.str.prototype["capitalize"] = new Sk.builtin.func(function (self) { cap += orig.charAt(i).toLowerCase(); } return new Sk.builtin.str(cap); -}); +}; -Sk.builtin.str.prototype["join"] = new Sk.builtin.func(function (self, seq) { +Sk.builtin.str.methods.join = function (self, seq) { var it, i; var arrOfStrs; Sk.builtin.pyCheckArgsLen("join", arguments.length, 2, 2); @@ -284,7 +306,7 @@ Sk.builtin.str.prototype["join"] = new Sk.builtin.func(function (self, seq) { arrOfStrs.push(i.v); } return new Sk.builtin.str(arrOfStrs.join(self.v)); -}); +}; var genericsSplit = function genericsSplit(self, on, howmany) { var splits; @@ -346,11 +368,11 @@ var genericsSplit = function genericsSplit(self, on, howmany) { return new Sk.builtin.list(result); }; -Sk.builtin.str.prototype["split"] = new Sk.builtin.func(function (self, sep, maxsplit) { +Sk.builtin.str.methods.split = function (self, sep, maxsplit) { return genericsSplit(self, sep, maxsplit); -}); +}; -Sk.builtin.str.prototype["rsplit"] = new Sk.builtin.func(function (self, sep, maxsplit) { +Sk.builtin.str.methods.rsplit = function (self, sep, maxsplit) { var allSplit = genericsSplit(self, sep, undefined); if (maxsplit !== undefined) { if (!Sk.builtin.checkInt(maxsplit)) { @@ -361,77 +383,57 @@ Sk.builtin.str.prototype["rsplit"] = new Sk.builtin.func(function (self, sep, ma } else { return allSplit; } -}); +}; -Sk.builtin.str.prototype["strip"] = new Sk.builtin.func(function (self, chars) { +Sk.builtin.str.methods.strip = function (self, chars) { var regex; var pattern; Sk.builtin.pyCheckArgsLen("strip", arguments.length, 1, 2); - if ((chars !== undefined) && !Sk.builtin.checkString(chars)) { + if (chars !== undefined && !Sk.builtin.checkString(chars)) { throw new Sk.builtin.TypeError("strip arg must be None or str"); } if (chars === undefined) { pattern = /^\s+|\s+$/g; } else { - regex = Sk.builtin.str.re_escape_(chars.v); + regex = Sk.builtin.str.$re_escape(chars.v); pattern = new RegExp("^[" + regex + "]+|[" + regex + "]+$", "g"); } return new Sk.builtin.str(self.v.replace(pattern, "")); -}); +}; -Sk.builtin.str.prototype["lstrip"] = new Sk.builtin.func(function (self, chars) { +Sk.builtin.str.methods.lstrip = function (self, chars) { var regex; var pattern; Sk.builtin.pyCheckArgsLen("lstrip", arguments.length, 1, 2); - if ((chars !== undefined) && !Sk.builtin.checkString(chars)) { + if (chars !== undefined && !Sk.builtin.checkString(chars)) { throw new Sk.builtin.TypeError("lstrip arg must be None or str"); } if (chars === undefined) { pattern = /^\s+/g; } else { - regex = Sk.builtin.str.re_escape_(chars.v); + regex = Sk.builtin.str.$re_escape(chars.v); pattern = new RegExp("^[" + regex + "]+", "g"); } return new Sk.builtin.str(self.v.replace(pattern, "")); -}); +}; -Sk.builtin.str.prototype["rstrip"] = new Sk.builtin.func(function (self, chars) { +Sk.builtin.str.methods.rstrip = function (self, chars) { var regex; var pattern; Sk.builtin.pyCheckArgsLen("rstrip", arguments.length, 1, 2); - if ((chars !== undefined) && !Sk.builtin.checkString(chars)) { + if (chars !== undefined && !Sk.builtin.checkString(chars)) { throw new Sk.builtin.TypeError("rstrip arg must be None or str"); } if (chars === undefined) { pattern = /\s+$/g; } else { - regex = Sk.builtin.str.re_escape_(chars.v); + regex = Sk.builtin.str.$re_escape(chars.v); pattern = new RegExp("[" + regex + "]+$", "g"); } return new Sk.builtin.str(self.v.replace(pattern, "")); -}); - -Sk.builtin.str.prototype["__format__"] = new Sk.builtin.func(function (self, format_spec) { - var formatstr; - Sk.builtin.pyCheckArgsLen("__format__", arguments.length, 2, 2); - - if (!Sk.builtin.checkString(format_spec)) { - if (Sk.__future__.exceptions) { - throw new Sk.builtin.TypeError("format() argument 2 must be str, not " + Sk.abstr.typeName(format_spec)); - } else { - throw new Sk.builtin.TypeError("format expects arg 2 to be string or unicode, not " + Sk.abstr.typeName(format_spec)); - } - } else { - formatstr = Sk.ffi.remapToJs(format_spec); - if (formatstr !== "" && formatstr !== "s") { - throw new Sk.builtin.NotImplementedError("format spec is not yet implemented"); - } - } - - return new Sk.builtin.str(self); -}); +}; -Sk.builtin.str.prototype["partition"] = new Sk.builtin.func(function (self, sep) { +Sk.builtin.str.methods.partition = function (self, sep) { var pos; var sepStr; Sk.builtin.pyCheckArgsLen("partition", arguments.length, 2, 2); @@ -442,13 +444,10 @@ Sk.builtin.str.prototype["partition"] = new Sk.builtin.func(function (self, sep) return new Sk.builtin.tuple([self, Sk.builtin.str.$emptystr, Sk.builtin.str.$emptystr]); } - return new Sk.builtin.tuple([ - new Sk.builtin.str(self.v.substring(0, pos)), - sepStr, - new Sk.builtin.str(self.v.substring(pos + sepStr.v.length))]); -}); + return new Sk.builtin.tuple([new Sk.builtin.str(self.v.substring(0, pos)), sepStr, new Sk.builtin.str(self.v.substring(pos + sepStr.v.length))]); +}; -Sk.builtin.str.prototype["rpartition"] = new Sk.builtin.func(function (self, sep) { +Sk.builtin.str.methods.rpartition = function (self, sep) { var pos; var sepStr; Sk.builtin.pyCheckArgsLen("rpartition", arguments.length, 2, 2); @@ -459,13 +458,10 @@ Sk.builtin.str.prototype["rpartition"] = new Sk.builtin.func(function (self, sep return new Sk.builtin.tuple([Sk.builtin.str.$emptystr, Sk.builtin.str.$emptystr, self]); } - return new Sk.builtin.tuple([ - new Sk.builtin.str(self.v.substring(0, pos)), - sepStr, - new Sk.builtin.str(self.v.substring(pos + sepStr.v.length))]); -}); + return new Sk.builtin.tuple([new Sk.builtin.str(self.v.substring(0, pos)), sepStr, new Sk.builtin.str(self.v.substring(pos + sepStr.v.length))]); +}; -Sk.builtin.str.prototype["count"] = new Sk.builtin.func(function (self, pat, start, end) { +Sk.builtin.str.methods.count = function (self, pat, start, end) { var normaltext; var ctl; var slice; @@ -474,10 +470,10 @@ Sk.builtin.str.prototype["count"] = new Sk.builtin.func(function (self, pat, sta if (!Sk.builtin.checkString(pat)) { throw new Sk.builtin.TypeError("expected a character buffer object"); } - if ((start !== undefined) && !Sk.builtin.checkInt(start)) { + if (start !== undefined && !Sk.builtin.checkInt(start)) { throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); } - if ((end !== undefined) && !Sk.builtin.checkInt(end)) { + if (end !== undefined && !Sk.builtin.checkInt(end)) { throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); } @@ -500,20 +496,19 @@ Sk.builtin.str.prototype["count"] = new Sk.builtin.func(function (self, pat, sta slice = self.v.slice(start, end); ctl = slice.match(m); if (!ctl) { - return new Sk.builtin.int_(0); + return new Sk.builtin.int_(0); } else { return new Sk.builtin.int_(ctl.length); } +}; -}); - -Sk.builtin.str.prototype["ljust"] = new Sk.builtin.func(function (self, len, fillchar) { +Sk.builtin.str.methods.ljust = function (self, len, fillchar) { var newstr; Sk.builtin.pyCheckArgsLen("ljust", arguments.length, 2, 3); if (!Sk.builtin.checkInt(len)) { throw new Sk.builtin.TypeError("integer argument exepcted, got " + Sk.abstr.typeName(len)); } - if ((fillchar !== undefined) && (!Sk.builtin.checkString(fillchar) || fillchar.v.length !== 1)) { + if (fillchar !== undefined && (!Sk.builtin.checkString(fillchar) || fillchar.v.length !== 1)) { throw new Sk.builtin.TypeError("must be char, not " + Sk.abstr.typeName(fillchar)); } if (fillchar === undefined) { @@ -528,15 +523,15 @@ Sk.builtin.str.prototype["ljust"] = new Sk.builtin.func(function (self, len, fil newstr = Array.prototype.join.call({length: Math.floor(len - self.v.length) + 1}, fillchar); return new Sk.builtin.str(self.v + newstr); } -}); +}; -Sk.builtin.str.prototype["rjust"] = new Sk.builtin.func(function (self, len, fillchar) { +Sk.builtin.str.methods.rjust = function (self, len, fillchar) { var newstr; Sk.builtin.pyCheckArgsLen("rjust", arguments.length, 2, 3); if (!Sk.builtin.checkInt(len)) { throw new Sk.builtin.TypeError("integer argument exepcted, got " + Sk.abstr.typeName(len)); } - if ((fillchar !== undefined) && (!Sk.builtin.checkString(fillchar) || fillchar.v.length !== 1)) { + if (fillchar !== undefined && (!Sk.builtin.checkString(fillchar) || fillchar.v.length !== 1)) { throw new Sk.builtin.TypeError("must be char, not " + Sk.abstr.typeName(fillchar)); } if (fillchar === undefined) { @@ -551,17 +546,16 @@ Sk.builtin.str.prototype["rjust"] = new Sk.builtin.func(function (self, len, fil newstr = Array.prototype.join.call({length: Math.floor(len - self.v.length) + 1}, fillchar); return new Sk.builtin.str(newstr + self.v); } +}; -}); - -Sk.builtin.str.prototype["center"] = new Sk.builtin.func(function (self, len, fillchar) { +Sk.builtin.str.methods.center = function (self, len, fillchar) { var newstr; var newstr1; Sk.builtin.pyCheckArgsLen("center", arguments.length, 2, 3); if (!Sk.builtin.checkInt(len)) { throw new Sk.builtin.TypeError("integer argument exepcted, got " + Sk.abstr.typeName(len)); } - if ((fillchar !== undefined) && (!Sk.builtin.checkString(fillchar) || fillchar.v.length !== 1)) { + if (fillchar !== undefined && (!Sk.builtin.checkString(fillchar) || fillchar.v.length !== 1)) { throw new Sk.builtin.TypeError("must be char, not " + Sk.abstr.typeName(fillchar)); } if (fillchar === undefined) { @@ -580,19 +574,18 @@ Sk.builtin.str.prototype["center"] = new Sk.builtin.func(function (self, len, fi } return new Sk.builtin.str(newstr); } +}; -}); - -Sk.builtin.str.prototype["find"] = new Sk.builtin.func(function (self, tgt, start, end) { +Sk.builtin.str.methods.find = function (self, tgt, start, end) { var idx; Sk.builtin.pyCheckArgsLen("find", arguments.length, 2, 4); if (!Sk.builtin.checkString(tgt)) { throw new Sk.builtin.TypeError("expected a character buffer object"); } - if ((start !== undefined) && !Sk.builtin.checkInt(start)) { + if (start !== undefined && !Sk.builtin.checkInt(start)) { throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); } - if ((end !== undefined) && !Sk.builtin.checkInt(end)) { + if (end !== undefined && !Sk.builtin.checkInt(end)) { throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); } @@ -611,12 +604,12 @@ Sk.builtin.str.prototype["find"] = new Sk.builtin.func(function (self, tgt, star } idx = self.v.indexOf(tgt.v, start); - idx = ((idx >= start) && (idx < end)) ? idx : -1; + idx = idx >= start && idx < end ? idx : -1; return new Sk.builtin.int_(idx); -}); +}; -Sk.builtin.str.prototype["index"] = new Sk.builtin.func(function (self, tgt, start, end) { +Sk.builtin.str.methods.index = function (self, tgt, start, end) { var idx; Sk.builtin.pyCheckArgsLen("index", arguments.length, 2, 4); idx = Sk.misceval.callsimArray(self["find"], [self, tgt, start, end]); @@ -624,18 +617,18 @@ Sk.builtin.str.prototype["index"] = new Sk.builtin.func(function (self, tgt, sta throw new Sk.builtin.ValueError("substring not found"); } return idx; -}); +}; -Sk.builtin.str.prototype["rfind"] = new Sk.builtin.func(function (self, tgt, start, end) { +Sk.builtin.str.methods.rfind = function (self, tgt, start, end) { var idx; Sk.builtin.pyCheckArgsLen("rfind", arguments.length, 2, 4); if (!Sk.builtin.checkString(tgt)) { throw new Sk.builtin.TypeError("expected a character buffer object"); } - if ((start !== undefined) && !Sk.builtin.checkInt(start)) { + if (start !== undefined && !Sk.builtin.checkInt(start)) { throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); } - if ((end !== undefined) && !Sk.builtin.checkInt(end)) { + if (end !== undefined && !Sk.builtin.checkInt(end)) { throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); } @@ -654,13 +647,13 @@ Sk.builtin.str.prototype["rfind"] = new Sk.builtin.func(function (self, tgt, sta } idx = self.v.lastIndexOf(tgt.v, end); - idx = (idx !== end) ? idx : self.v.lastIndexOf(tgt.v, end - 1); - idx = ((idx >= start) && (idx < end)) ? idx : -1; + idx = idx !== end ? idx : self.v.lastIndexOf(tgt.v, end - 1); + idx = idx >= start && idx < end ? idx : -1; return new Sk.builtin.int_(idx); -}); +}; -Sk.builtin.str.prototype["rindex"] = new Sk.builtin.func(function (self, tgt, start, end) { +Sk.builtin.str.methods.rindex = function (self, tgt, start, end) { var idx; Sk.builtin.pyCheckArgsLen("rindex", arguments.length, 2, 4); idx = Sk.misceval.callsimArray(self["rfind"], [self, tgt, start, end]); @@ -668,41 +661,40 @@ Sk.builtin.str.prototype["rindex"] = new Sk.builtin.func(function (self, tgt, st throw new Sk.builtin.ValueError("substring not found"); } return idx; -}); +}; -Sk.builtin.str.prototype["startswith"] = new Sk.builtin.func(function (self, tgt) { +Sk.builtin.str.methods.startswith = function (self, tgt) { Sk.builtin.pyCheckArgsLen("startswith", arguments.length, 2, 2); Sk.builtin.pyCheckType("tgt", "string", Sk.builtin.checkString(tgt)); - return new Sk.builtin.bool( self.v.indexOf(tgt.v) === 0); -}); + return new Sk.builtin.bool(self.v.indexOf(tgt.v) === 0); +}; // http://stackoverflow.com/questions/280634/endswith-in-javascript -Sk.builtin.str.prototype["endswith"] = new Sk.builtin.func(function (self, tgt) { +Sk.builtin.str.methods.endswith = function (self, tgt) { Sk.builtin.pyCheckArgsLen("endswith", arguments.length, 2, 2); Sk.builtin.pyCheckType("tgt", "string", Sk.builtin.checkString(tgt)); - return new Sk.builtin.bool( self.v.indexOf(tgt.v, self.v.length - tgt.v.length) !== -1); -}); + return new Sk.builtin.bool(self.v.indexOf(tgt.v, self.v.length - tgt.v.length) !== -1); +}; -Sk.builtin.str.prototype["replace"] = new Sk.builtin.func(function (self, oldS, newS, count) { +Sk.builtin.str.methods.replace = function (self, oldS, newS, count) { var c; var patt; Sk.builtin.pyCheckArgsLen("replace", arguments.length, 3, 4); Sk.builtin.pyCheckType("oldS", "string", Sk.builtin.checkString(oldS)); Sk.builtin.pyCheckType("newS", "string", Sk.builtin.checkString(newS)); - if ((count !== undefined) && !Sk.builtin.checkInt(count)) { - throw new Sk.builtin.TypeError("integer argument expected, got " + - Sk.abstr.typeName(count)); + if (count !== undefined && !Sk.builtin.checkInt(count)) { + throw new Sk.builtin.TypeError("integer argument expected, got " + Sk.abstr.typeName(count)); } count = Sk.builtin.asnum$(count); - patt = new RegExp(Sk.builtin.str.re_escape_(oldS.v), "g"); + patt = new RegExp(Sk.builtin.str.$re_escape(oldS.v), "g"); - if ((count === undefined) || (count < 0)) { + if (count === undefined || count < 0) { return new Sk.builtin.str(self.v.replace(patt, newS.v)); } c = 0; - function replacer (match) { + function replacer(match) { c++; if (c <= count) { return newS.v; @@ -711,9 +703,9 @@ Sk.builtin.str.prototype["replace"] = new Sk.builtin.func(function (self, oldS, } return new Sk.builtin.str(self.v.replace(patt, replacer)); -}); +}; -Sk.builtin.str.prototype["zfill"] = new Sk.builtin.func(function (self, len) { +Sk.builtin.str.methods.zfill = function (self, len) { var str = self.v; var ret; var zeroes; @@ -721,50 +713,45 @@ Sk.builtin.str.prototype["zfill"] = new Sk.builtin.func(function (self, len) { var pad = ""; Sk.builtin.pyCheckArgsLen("zfill", arguments.length, 2, 2); - if (! Sk.builtin.checkInt(len)) { + if (!Sk.builtin.checkInt(len)) { throw new Sk.builtin.TypeError("integer argument exepected, got " + Sk.abstr.typeName(len)); } // figure out how many zeroes are needed to make the proper length zeroes = len.v - str.length; // offset by 1 if there is a +/- at the beginning of the string - offset = (str[0] === "+" || str[0] === "-") ? 1 : 0; - for(var i = 0; i < zeroes; i++){ + offset = str[0] === "+" || str[0] === "-" ? 1 : 0; + for (var i = 0; i < zeroes; i++) { pad += "0"; } // combine the string and the zeroes ret = str.substr(0, offset) + pad + str.substr(offset); return new Sk.builtin.str(ret); +}; - -}); - -Sk.builtin.str.prototype["isdigit"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.isdigit = function (self) { Sk.builtin.pyCheckArgsLen("isdigit", arguments.length, 1, 1); - return new Sk.builtin.bool( /^\d+$/.test(self.v)); -}); + return new Sk.builtin.bool(/^\d+$/.test(self.v)); +}; -Sk.builtin.str.prototype["isspace"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.isspace = function (self) { Sk.builtin.pyCheckArgsLen("isspace", arguments.length, 1, 1); - return new Sk.builtin.bool( /^\s+$/.test(self.v)); -}); - + return new Sk.builtin.bool(/^\s+$/.test(self.v)); +}; -Sk.builtin.str.prototype["expandtabs"] = new Sk.builtin.func(function (self, tabsize) { +Sk.builtin.str.methods.expandtabs = function (self, tabsize) { // var input = self.v; // var expanded = ""; // var split; // var spacestr = ""; // var spacerem; - var spaces; var expanded; Sk.builtin.pyCheckArgsLen("expandtabs", arguments.length, 1, 2); - - if ((tabsize !== undefined) && ! Sk.builtin.checkInt(tabsize)) { + if (tabsize !== undefined && !Sk.builtin.checkInt(tabsize)) { throw new Sk.builtin.TypeError("integer argument exepected, got " + Sk.abstr.typeName(tabsize)); } if (tabsize === undefined) { @@ -773,27 +760,26 @@ Sk.builtin.str.prototype["expandtabs"] = new Sk.builtin.func(function (self, tab tabsize = Sk.builtin.asnum$(tabsize); } - spaces = (new Array(tabsize + 1)).join(" "); - expanded = self.v.replace(/([^\r\n\t]*)\t/g, function(a, b) { + spaces = new Array(tabsize + 1).join(" "); + expanded = self.v.replace(/([^\r\n\t]*)\t/g, function (a, b) { return b + spaces.slice(b.length % tabsize); }); return new Sk.builtin.str(expanded); -}); +}; -Sk.builtin.str.prototype["swapcase"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.swapcase = function (self) { var ret; Sk.builtin.pyCheckArgsLen("swapcase", arguments.length, 1, 1); - - ret = self.v.replace(/[a-z]/gi, function(c) { + ret = self.v.replace(/[a-z]/gi, function (c) { var lc = c.toLowerCase(); return lc === c ? c.toUpperCase() : lc; }); return new Sk.builtin.str(ret); -}); +}; -Sk.builtin.str.prototype["splitlines"] = new Sk.builtin.func(function (self, keepends) { +Sk.builtin.str.methods.splitlines = function (self, keepends) { var data = self.v; var i = 0; var j = i; @@ -804,7 +790,7 @@ Sk.builtin.str.prototype["splitlines"] = new Sk.builtin.func(function (self, kee var sol = 0; var slice; Sk.builtin.pyCheckArgsLen("splitlines", arguments.length, 1, 2); - if ((keepends !== undefined) && ! Sk.builtin.checkBool(keepends)) { + if (keepends !== undefined && !Sk.builtin.checkBool(keepends)) { throw new Sk.builtin.TypeError("boolean argument expected, got " + Sk.abstr.typeName(keepends)); } if (keepends === undefined) { @@ -813,13 +799,12 @@ Sk.builtin.str.prototype["splitlines"] = new Sk.builtin.func(function (self, kee keepends = keepends.v; } - - for (i = 0; i < selflen; i ++) { + for (i = 0; i < selflen; i++) { ch = data.charAt(i); if (data.charAt(i + 1) === "\n" && ch === "\r") { eol = i + 2; slice = data.slice(sol, eol); - if (! keepends) { + if (!keepends) { slice = slice.replace(/(\r|\n)/g, ""); } strs_w.push(new Sk.builtin.str(slice)); @@ -827,64 +812,63 @@ Sk.builtin.str.prototype["splitlines"] = new Sk.builtin.func(function (self, kee } else if ((ch === "\n" && data.charAt(i - 1) !== "\r") || ch === "\r") { eol = i + 1; slice = data.slice(sol, eol); - if (! keepends) { + if (!keepends) { slice = slice.replace(/(\r|\n)/g, ""); } strs_w.push(new Sk.builtin.str(slice)); sol = eol; } - } if (sol < selflen) { eol = selflen; slice = data.slice(sol, eol); - if (! keepends) { + if (!keepends) { slice = slice.replace(/(\r|\n)/g, ""); } strs_w.push(new Sk.builtin.str(slice)); } return new Sk.builtin.list(strs_w); -}); +}; -Sk.builtin.str.prototype["title"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.title = function (self) { var ret; Sk.builtin.pyCheckArgsLen("title", arguments.length, 1, 1); - ret = self.v.replace(/[a-z][a-z]*/gi, function(str) { + ret = self.v.replace(/[a-z][a-z]*/gi, function (str) { return str[0].toUpperCase() + str.substr(1).toLowerCase(); }); return new Sk.builtin.str(ret); -}); +}; -Sk.builtin.str.prototype["isalpha"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.isalpha = function (self) { Sk.builtin.pyCheckArgsLen("isalpha", arguments.length, 1, 1); - return new Sk.builtin.bool( self.v.length && !/[^a-zA-Z]/.test(self.v)); -}); + return new Sk.builtin.bool(self.v.length && !/[^a-zA-Z]/.test(self.v)); +}; -Sk.builtin.str.prototype["isalnum"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.isalnum = function (self) { Sk.builtin.pyCheckArgsLen("isalnum", arguments.length, 1, 1); - return new Sk.builtin.bool( self.v.length && !/[^a-zA-Z0-9]/.test(self.v)); -}); + return new Sk.builtin.bool(self.v.length && !/[^a-zA-Z0-9]/.test(self.v)); +}; // does not account for unicode numeric values -Sk.builtin.str.prototype["isnumeric"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.isnumeric = function (self) { Sk.builtin.pyCheckArgsLen("isnumeric", arguments.length, 1, 1); - return new Sk.builtin.bool( self.v.length && !/[^0-9]/.test(self.v)); -}); + return new Sk.builtin.bool(self.v.length && !/[^0-9]/.test(self.v)); +}; -Sk.builtin.str.prototype["islower"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.islower = function (self) { Sk.builtin.pyCheckArgsLen("islower", arguments.length, 1, 1); - return new Sk.builtin.bool( self.v.length && /[a-z]/.test(self.v) && !/[A-Z]/.test(self.v)); -}); + return new Sk.builtin.bool(self.v.length && /[a-z]/.test(self.v) && !/[A-Z]/.test(self.v)); +}; -Sk.builtin.str.prototype["isupper"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.isupper = function (self) { Sk.builtin.pyCheckArgsLen("isupper", arguments.length, 1, 1); - return new Sk.builtin.bool( self.v.length && !/[a-z]/.test(self.v) && /[A-Z]/.test(self.v)); -}); + return new Sk.builtin.bool(self.v.length && !/[a-z]/.test(self.v) && /[A-Z]/.test(self.v)); +}; -Sk.builtin.str.prototype["istitle"] = new Sk.builtin.func(function (self) { +Sk.builtin.str.methods.istitle = function (self) { // Comparing to str.title() seems the most intuitive thing, but it fails on "", // Other empty-ish strings with no change. var input = self.v; @@ -893,25 +877,25 @@ Sk.builtin.str.prototype["istitle"] = new Sk.builtin.func(function (self) { var pos; var ch; Sk.builtin.pyCheckArgsLen("istitle", arguments.length, 1, 1); - for (pos = 0; pos < input.length; pos ++) { + for (pos = 0; pos < input.length; pos++) { ch = input.charAt(pos); - if (! /[a-z]/.test(ch) && /[A-Z]/.test(ch)) { + if (!/[a-z]/.test(ch) && /[A-Z]/.test(ch)) { if (previous_is_cased) { - return new Sk.builtin.bool( false); + return new Sk.builtin.bool(false); } previous_is_cased = true; cased = true; - } else if (/[a-z]/.test(ch) && ! /[A-Z]/.test(ch)) { - if (! previous_is_cased) { - return new Sk.builtin.bool( false); + } else if (/[a-z]/.test(ch) && !/[A-Z]/.test(ch)) { + if (!previous_is_cased) { + return new Sk.builtin.bool(false); } cased = true; } else { previous_is_cased = false; } } - return new Sk.builtin.bool( cased); -}); + return new Sk.builtin.bool(cased); +}; Sk.builtin.str.prototype.nb$remainder = function (rhs) { // % format op. rhs can be a value, a tuple, or something with __getitem__ (dict) @@ -961,11 +945,12 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { fieldWidth = Sk.builtin.asnum$(fieldWidth); precision = Sk.builtin.asnum$(precision); - if (mappingKey === undefined || mappingKey === "") { + if ((mappingKey === undefined || mappingKey === "") && conversionType != "%") { i = index++; } // ff passes '' not undef for some reason - if (precision === "") { // ff passes '' here aswell causing problems with G,g, etc. + if (precision === "") { + // ff passes '' here aswell causing problems with G,g, etc. precision = undefined; } @@ -1105,7 +1090,14 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { return handleWidth(formatNumber(value, 16)); } else if (conversionType === "X") { return handleWidth(formatNumber(value, 16)).toUpperCase(); - } else if (conversionType === "f" || conversionType === "F" || conversionType === "e" || conversionType === "E" || conversionType === "g" || conversionType === "G") { + } else if ( + conversionType === "f" || + conversionType === "F" || + conversionType === "e" || + conversionType === "E" || + conversionType === "g" || + conversionType === "G" + ) { convValue = Sk.builtin.asnum$(value); if (typeof convValue === "string") { convValue = Number(convValue); @@ -1121,7 +1113,6 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { } convName = ["toExponential", "toFixed", "toPrecision"]["efg".indexOf(conversionType.toLowerCase())]; if (precision === undefined || precision === "") { - if (conversionType === "e" || conversionType === "E") { precision = 6; } else if (conversionType === "f" || conversionType === "F") { @@ -1132,21 +1123,20 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { } } } - result = (convValue)[convName](precision); // possible loose of negative zero sign + result = convValue[convName](precision); // possible loose of negative zero sign // apply sign to negative zeros, floats only! - if(Sk.builtin.checkFloat(value)) { - if(convValue === 0 && 1/convValue === -Infinity) { + if (Sk.builtin.checkFloat(value)) { + if (convValue === 0 && 1 / convValue === -Infinity) { result = "-" + result; // add sign for zero } } if (Sk.__future__.python3) { - if ((result.length >= 7) && (result.slice(0, 6) == "0.0000")) { - + if (result.length >= 7 && result.slice(0, 6) == "0.0000") { val = parseFloat(result); - result = val.toExponential(); + result = val.toExponential(); } - if (result.charAt(result.length -2) == "-") { + if (result.charAt(result.length - 2) == "-") { result = result.slice(0, result.length - 1) + "0" + result.charAt(result.length - 1); } } @@ -1180,7 +1170,7 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { if (precision) { return r.substr(0, precision); } - if(fieldWidth) { + if (fieldWidth) { r = handleWidth([" ", r]); } return r; @@ -1192,43 +1182,407 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { return new Sk.builtin.str(ret); }; -/** - * @constructor - * @param {Object} obj - */ -Sk.builtin.str_iter_ = function (obj) { - if (!(this instanceof Sk.builtin.str_iter_)) { - return new Sk.builtin.str_iter_(obj); - } - this.$index = 0; - this.$obj = obj.v.slice(); - this.sq$length = this.$obj.length; - this.tp$iter = this; - this.tp$iternext = function () { - if (this.$index >= this.sq$length) { - return undefined; - } - return new Sk.builtin.str(this.$obj.substr(this.$index++, 1)); - }; - this.$r = function () { - return new Sk.builtin.str("iterator"); - }; - return this; +Sk.builtin.str.prototype.tp$methods = { + encode: { + $meth: Sk.builtin.str.methods.encode, + $flags: {}, + $textsig: "($self, /, encoding='utf-8', errors='strict')", + $doc: "Encode the string using the codec registered for encoding.\n\n encoding\n The encoding in which to encode the string.\n errors\n The error handling scheme to use for encoding errors.\n The default is 'strict' meaning that encoding errors raise a\n UnicodeEncodeError. Other possible values are 'ignore', 'replace' and\n 'xmlcharrefreplace' as well as any other name registered with\n codecs.register_error that can handle UnicodeEncodeErrors." + }, + replace: { + $meth: Sk.builtin.str.methods.replace, + $flags: {}, + $textsig: "($self, old, new, count=-1, /)", + $doc: + "Return a copy with all occurrences of substring old replaced by new.\n\n count\n Maximum number of occurrences to replace.\n -1 (the default value) means replace all occurrences.\n\nIf the optional argument count is given, only the first count occurrences are\nreplaced.", + }, + split: { + $meth: Sk.builtin.str.methods.split, + $flags: {}, + $textsig: "($self, /, sep=None, maxsplit=-1)", + $doc: + "Return a list of the words in the string, using sep as the delimiter string.\n\n sep\n The delimiter according which to split the string.\n None (the default value) means split according to any whitespace,\n and discard empty strings from the result.\n maxsplit\n Maximum number of splits to do.\n -1 (the default value) means no limit.", + }, + rsplit: { + $meth: Sk.builtin.str.methods.rsplit, + $flags: {}, + $textsig: "($self, /, sep=None, maxsplit=-1)", + $doc: "Return a list of the words in the string, using sep as the delimiter string.\n\n sep\n The delimiter according which to split the string.\n None (the default value) means split according to any whitespace,\n and discard empty strings from the result.\n maxsplit\n Maximum number of splits to do.\n -1 (the default value) means no limit.\n\nSplits are done starting at the end of the string and working to the front." + }, + join: { + $meth: Sk.builtin.str.methods.join, + $flags: {}, + $textsig: "($self, iterable, /)", + $doc: + "Concatenate any number of strings.\n\nThe string whose method is called is inserted in between each given string.\nThe result is returned as a new string.\n\nExample: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'", + }, + capitalize: { + $meth: Sk.builtin.str.methods.capitalize, + $flags: {}, + $textsig: "($self, /)", + $doc: "Return a capitalized version of the string.\n\nMore specifically, make the first character have upper case and the rest lower\ncase.", + }, + // casefold: { + // $meth: Sk.builtin.str.methods.casefold, + // $flags:{}, + // $textsig: "($self, /)", + // $doc: "Return a version of the string suitable for caseless comparisons." }, + title: { + $meth: Sk.builtin.str.methods.title, + $flags: {}, + $textsig: "($self, /)", + $doc: + "Return a version of the string where each word is titlecased.\n\nMore specifically, words start with uppercased characters and all remaining\ncased characters have lower case.", + }, + center: { + $meth: Sk.builtin.str.methods.center, + $flags: {}, + $textsig: "($self, width, fillchar=' ', /)", + $doc: "Return a centered string of length width.\n\nPadding is done using the specified fill character (default is a space).", + }, + count: { + $meth: Sk.builtin.str.methods.count, + $flags: {}, + $textsig: null, + $doc: + "S.count(sub[, start[, end]]) -> int\n\nReturn the number of non-overlapping occurrences of substring sub in\nstring S[start:end]. Optional arguments start and end are\ninterpreted as in slice notation.", + }, + expandtabs: { + $meth: Sk.builtin.str.methods.expandtabs, + $flags: {}, + $textsig: "($self, /, tabsize=8)", + $doc: "Return a copy where all tab characters are expanded using spaces.\n\nIf tabsize is not given, a tab size of 8 characters is assumed.", + }, + find: { + $meth: Sk.builtin.str.methods.find, + $flags: {}, + $textsig: null, + $doc: + "S.find(sub[, start[, end]]) -> int\n\nReturn the lowest index in S where substring sub is found,\nsuch that sub is contained within S[start:end]. Optional\narguments start and end are interpreted as in slice notation.\n\nReturn -1 on failure.", + }, + partition: { + $meth: Sk.builtin.str.methods.partition, + $flags: {}, + $textsig: "($self, sep, /)", + $doc: + "Partition the string into three parts using the given separator.\n\nThis will search for the separator in the string. If the separator is found,\nreturns a 3-tuple containing the part before the separator, the separator\nitself, and the part after it.\n\nIf the separator is not found, returns a 3-tuple containing the original string\nand two empty strings.", + }, + index: { + $meth: Sk.builtin.str.methods.index, + $flags: {}, + $textsig: null, + $doc: + "S.index(sub[, start[, end]]) -> int\n\nReturn the lowest index in S where substring sub is found, \nsuch that sub is contained within S[start:end]. Optional\narguments start and end are interpreted as in slice notation.\n\nRaises ValueError when the substring is not found.", + }, + ljust: { + $meth: Sk.builtin.str.methods.ljust, + $flags: {}, + $textsig: "($self, width, fillchar=' ', /)", + $doc: "Return a left-justified string of length width.\n\nPadding is done using the specified fill character (default is a space).", + }, + lower: { + $meth: Sk.builtin.str.methods.lower, + $flags: {}, + $textsig: "($self, /)", + $doc: "Return a copy of the string converted to lowercase.", + }, + lstrip: { + $meth: Sk.builtin.str.methods.lstrip, + $flags: {}, + $textsig: "($self, chars=None, /)", + $doc: "Return a copy of the string with leading whitespace removed.\n\nIf chars is given and not None, remove characters in chars instead.", + }, + rfind: { + $meth: Sk.builtin.str.methods.rfind, + $flags: {}, + $textsig: null, + $doc: + "S.rfind(sub[, start[, end]]) -> int\n\nReturn the highest index in S where substring sub is found,\nsuch that sub is contained within S[start:end]. Optional\narguments start and end are interpreted as in slice notation.\n\nReturn -1 on failure.", + }, + rindex: { + $meth: Sk.builtin.str.methods.rindex, + $flags: {}, + $textsig: null, + $doc: + "S.rindex(sub[, start[, end]]) -> int\n\nReturn the highest index in S where substring sub is found,\nsuch that sub is contained within S[start:end]. Optional\narguments start and end are interpreted as in slice notation.\n\nRaises ValueError when the substring is not found.", + }, + rjust: { + $meth: Sk.builtin.str.methods.rjust, + $flags: {}, + $textsig: "($self, width, fillchar=' ', /)", + $doc: "Return a right-justified string of length width.\n\nPadding is done using the specified fill character (default is a space).", + }, + rstrip: { + $meth: Sk.builtin.str.methods.rstrip, + $flags: {}, + $textsig: "($self, chars=None, /)", + $doc: "Return a copy of the string with trailing whitespace removed.\n\nIf chars is given and not None, remove characters in chars instead.", + }, + rpartition: { + $meth: Sk.builtin.str.methods.rpartition, + $flags: {}, + $textsig: "($self, sep, /)", + $doc: + "Partition the string into three parts using the given separator.\n\nThis will search for the separator in the string, starting at the end. If\nthe separator is found, returns a 3-tuple containing the part before the\nseparator, the separator itself, and the part after it.\n\nIf the separator is not found, returns a 3-tuple containing two empty strings\nand the original string.", + }, + splitlines: { + $meth: Sk.builtin.str.methods.splitlines, + $flags: {}, + $textsig: "($self, /, keepends=False)", + $doc: + "Return a list of the lines in the string, breaking at line boundaries.\n\nLine breaks are not included in the resulting list unless keepends is given and\ntrue.", + }, + strip: { + $meth: Sk.builtin.str.methods.strip, + $flags: {}, + $textsig: "($self, chars=None, /)", + $doc: + "Return a copy of the string with leading and trailing whitespace remove.\n\nIf chars is given and not None, remove characters in chars instead.", + }, + swapcase: { + $meth: Sk.builtin.str.methods.swapcase, + $flags: {}, + $textsig: "($self, /)", + $doc: "Convert uppercase characters to lowercase and lowercase characters to uppercase.", + }, + translate: { + $meth: Sk.builtin.str.methods.translate, + $flags: {}, + $textsig: "($self, table, /)", + $doc: + "Replace each character in the string using the given translation table.\n\n table\n Translation table, which must be a mapping of Unicode ordinals to\n Unicode ordinals, strings, or None.\n\nThe table must implement lookup/indexing via __getitem__, for instance a\ndictionary or list. If this operation raises LookupError, the character is\nleft untouched. Characters mapped to None are deleted.", + }, + upper: { + $meth: Sk.builtin.str.methods.upper, + $flags: {}, + $textsig: "($self, /)", + $doc: "Return a copy of the string converted to uppercase.", + }, + startswith: { + $meth: Sk.builtin.str.methods.startswith, + $flags: {}, + $textsig: null, + $doc: + "S.startswith(prefix[, start[, end]]) -> bool\n\nReturn True if S starts with the specified prefix, False otherwise.\nWith optional start, test S beginning at that position.\nWith optional end, stop comparing S at that position.\nprefix can also be a tuple of strings to try.", + }, + endswith: { + $meth: Sk.builtin.str.methods.endswith, + $flags: {}, + $textsig: null, + $doc: + "S.endswith(suffix[, start[, end]]) -> bool\n\nReturn True if S ends with the specified suffix, False otherwise.\nWith optional start, test S beginning at that position.\nWith optional end, stop comparing S at that position.\nsuffix can also be a tuple of strings to try.", + }, + // isascii: { + // $meth: Sk.builtin.str.methods.isascii, + // $flags:{}, + // $textsig: "($self, /)", + // $doc: "Return True if all characters in the string are ASCII, False otherwise.\n\nASCII characters have code points in the range U+0000-U+007F.\nEmpty string is ASCII too." }, + islower: { + $meth: Sk.builtin.str.methods.islower, + $flags: {}, + $textsig: "($self, /)", + $doc: + "Return True if the string is a lowercase string, False otherwise.\n\nA string is lowercase if all cased characters in the string are lowercase and\nthere is at least one cased character in the string.", + }, + isupper: { + $meth: Sk.builtin.str.methods.isupper, + $flags: {}, + $textsig: "($self, /)", + $doc: + "Return True if the string is an uppercase string, False otherwise.\n\nA string is uppercase if all cased characters in the string are uppercase and\nthere is at least one cased character in the string.", + }, + istitle: { + $meth: Sk.builtin.str.methods.istitle, + $flags: {}, + $textsig: "($self, /)", + $doc: + "Return True if the string is a title-cased string, False otherwise.\n\nIn a title-cased string, upper- and title-case characters may only\nfollow uncased characters and lowercase characters only cased ones.", + }, + isspace: { + $meth: Sk.builtin.str.methods.isspace, + $flags: {}, + $textsig: "($self, /)", + $doc: + "Return True if the string is a whitespace string, False otherwise.\n\nA string is whitespace if all characters in the string are whitespace and there\nis at least one character in the string.", + }, + // isdecimal: { + // $meth: Sk.builtin.str.methods.isdecimal, + // $flags:{}, + // $textsig: "($self, /)", + // $doc: "Return True if the string is a decimal string, False otherwise.\n\nA string is a decimal string if all characters in the string are decimal and\nthere is at least one character in the string." }, + isdigit: { + $meth: Sk.builtin.str.methods.isdigit, + $flags: {}, + $textsig: "($self, /)", + $doc: + "Return True if the string is a digit string, False otherwise.\n\nA string is a digit string if all characters in the string are digits and there\nis at least one character in the string.", + }, + isnumeric: { + $meth: Sk.builtin.str.methods.isnumeric, + $flags: {}, + $textsig: "($self, /)", + $doc: + "Return True if the string is a numeric string, False otherwise.\n\nA string is numeric if all characters in the string are numeric and there is at\nleast one character in the string.", + }, + isalpha: { + $meth: Sk.builtin.str.methods.isalpha, + $flags: {}, + $textsig: "($self, /)", + $doc: + "Return True if the string is an alphabetic string, False otherwise.\n\nA string is alphabetic if all characters in the string are alphabetic and there\nis at least one character in the string.", + }, + isalnum: { + $meth: Sk.builtin.str.methods.isalnum, + $flags: {}, + $textsig: "($self, /)", + $doc: + "Return True if the string is an alpha-numeric string, False otherwise.\n\nA string is alpha-numeric if all characters in the string are alpha-numeric and\nthere is at least one character in the string.", + }, + // isidentifier: { + // $meth: Sk.builtin.str.methods.isidentifier, + // $flags:{}, + // $textsig: "($self, /)", + // $doc: "Return True if the string is a valid Python identifier, False otherwise.\n\nUse keyword.iskeyword() to test for reserved identifiers such as \"def\" and\n\"class\"." }, + // isprintable: { + // $meth: Sk.builtin.str.methods.isprintable, + // $flags:{}, + // $textsig: "($self, /)", + // $doc: "Return True if the string is printable, False otherwise.\n\nA string is printable if all of its characters are considered printable in\nrepr() or if it is empty." }, + zfill: { + $meth: Sk.builtin.str.methods.zfill, + $flags: {}, + $textsig: "($self, width, /)", + $doc: "Pad a numeric string with zeros on the left, to fill a field of the given width.\n\nThe string is never truncated.", + }, + format: { + $meth: Sk.builtin.str.methods.format, + $flags: {}, + $textsig: null, + $doc: + "S.format(*args, **kwargs) -> str\n\nReturn a formatted version of S, using substitutions from args and kwargs.\nThe substitutions are identified by braces ('{' and '}').", + }, + // format_map: { + // $meth: Sk.builtin.str.methods.format_map, + // $flags:{}, + // $textsig: null, + // $doc: "S.format_map(mapping) -> str\n\nReturn a formatted version of S, using substitutions from mapping.\nThe substitutions are identified by braces ('{' and '}')." }, + // __format__: { + // $meth: Sk.builtin.str.methods.__format__, + // $flags:{}, + // $textsig: "($self, format_spec, /)", + // $doc: "Return a formatted version of the string as described by format_spec." }, + // __sizeof__: { + // $meth: Sk.builtin.str.methods.__sizeof__, + // $flags:{}, + // $textsig: "($self, /)", + // $doc: "Return the size of the string in memory, in bytes." }, + // __getnewargs__: { + // $meth: Sk.builtin.str.methods.__getnewargs__, + // $flags:{}, + // $textsig: null, + // $doc: null }, }; -Sk.abstr.setUpInheritance("iterator", Sk.builtin.str_iter_, Sk.builtin.object); - -Sk.builtin.str_iter_.prototype.__class__ = Sk.builtin.str_iter_; - -Sk.builtin.str_iter_.prototype.__iter__ = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("__iter__", arguments.length, 0, 0, true, false); - return self; -}); +Sk.abstr.setUpSlots(Sk.builtin.str); +Sk.abstr.setUpMethods(Sk.builtin.str); + +delete Sk.builtin.str.methods; + +var reservedWords_ = { + abstract: true, + as: true, + boolean: true, + break: true, + byte: true, + case: true, + catch: true, + char: true, + class: true, + continue: true, + const: true, + debugger: true, + default: true, + delete: true, + do: true, + double: true, + else: true, + enum: true, + export: true, + extends: true, + false: true, + final: true, + finally: true, + float: true, + for: true, + function: true, + goto: true, + if: true, + implements: true, + import: true, + in: true, + instanceof: true, + int: true, + interface: true, + is: true, + long: true, + namespace: true, + native: true, + new: true, + null: true, + package: true, + private: true, + protected: true, + public: true, + return: true, + short: true, + static: true, + super: true, + switch: true, + synchronized: true, + this: true, + throw: true, + throws: true, + transient: true, + true: true, + try: true, + typeof: true, + use: true, + var: true, + void: true, + volatile: true, + while: true, + with: true, + // reserved Names + constructor: true, + __defineGetter__: true, + __defineSetter__: true, + apply: true, + arguments: true, + call: true, + caller: true, + eval: true, + hasOwnProperty: true, + isPrototypeOf: true, + __lookupGetter__: true, + __lookupSetter__: true, + __noSuchMethod__: true, + propertyIsEnumerable: true, + prototype: true, + toSource: true, + toLocaleString: true, + toString: true, + unwatch: true, + valueOf: true, + watch: true, + length: true, + name: true, +}; -Sk.builtin.str_iter_.prototype.next$ = function (self) { - var ret = self.tp$iternext(); - if (ret === undefined) { - throw new Sk.builtin.StopIteration(); +function fixReserved(name) { + if (reservedWords_[name] === undefined) { + return name; } - return ret; -}; + return name + "_$rw$"; +} + +Sk.builtin.str.reservedWords_ = reservedWords_; diff --git a/src/structseq.js b/src/structseq.js index 358b0c7b83..fa3717c4f0 100644 --- a/src/structseq.js +++ b/src/structseq.js @@ -1,98 +1,79 @@ Sk.builtin.structseq_types = {}; Sk.builtin.make_structseq = function (module, name, fields, doc) { - var nm = module + "." + name; - var flds = []; - var docs = []; - var i; - for (var key in fields) { + const nm = module + "." + name; + const flds = []; + const docs = []; + for (let key in fields) { flds.push(key); docs.push(fields[key]); } + const getsets = {}; + for (let i = 0; i < flds.length; i++) { + getsets[flds[i]] = { + $get: function () { return this.v[i]; }, + $doc: docs[i] + }; + } + /** * @constructor * @extends Sk.builtin.tuple * @param {!Array|Object} arg */ - var cons = function structseq_constructor(arg) { - Sk.builtin.pyCheckArgsLen(nm, arguments.length, 1, 1); - var o; - var it, i, /** @type {!Array} */v; - if (!(this instanceof Sk.builtin.structseq_types[nm])) { - o = Object.create(Sk.builtin.structseq_types[nm].prototype); - o.constructor.apply(o, arguments); - return o; - } + var structseq = Sk.abstr.buildNativeClass(nm, { + constructor: function structseq_constructor(v) { + Sk.asserts.assert((Array.isArray(v) || v === undefined) && this instanceof structseq); + Sk.builtin.tuple.call(this, v); + }, + base: Sk.builtin.tuple, + slots: { + tp$new: function (args, kwargs) { + Sk.abstr.checkOneArg(nm, args, kwargs); + const v = []; + const arg = args[0]; + for (let it = Sk.abstr.iter(arg), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { + v.push(i); + } + if (v.length != flds.length) { + throw new Sk.builtin.TypeError(nm + "() takes a " + flds.length + "-sequence (" + v.length + "-sequence given)"); + } + return new structseq(v); + }, + tp$doc: doc ? doc : Sk.builtin.none.none$, + $r: function () { + var ret; + var i; + var bits; + if (this.v.length === 0) { + return new Sk.builtin.str(nm + "()"); + } + bits = []; + for (i = 0; i < this.v.length; ++i) { + bits[i] = flds[i] + "=" + Sk.misceval.objectRepr(this.v[i]); + } + ret = bits.join(", "); + if (this.v.length === 1) { + ret += ","; + } + return new Sk.builtin.str(nm + "(" + ret + ")"); + }, - if (Array.isArray(arg)) { - v = arg; - } else { - v = []; - for (it = Sk.abstr.iter(arg), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - v.push(i); - } - if (v.length != flds.length) { - throw new Sk.builtin.TypeError(nm + "() takes a " + flds.length + "-sequence (" + v.length + "-sequence given)"); + }, + methods: { + __reduce__: { + $meth: function () { + throw new Sk.builtin.NotImplementedError("__reduce__ is not implemented"); + }, + $flags: {NoArgs: true} } + }, + getsets: getsets, + proto: { + num_sequence_fields: new Sk.builtin.int_(flds.length) } - - Sk.builtin.tuple.call(this, v); - - this.__class__ = Sk.builtin.structseq_types[nm]; - }; - - Sk.builtin.structseq_types[nm] = cons; - - Sk.abstr.inherits(cons, Sk.builtin.tuple); - if (doc) { - cons.prototype.__doc__ = doc; - } - cons.prototype.tp$name = nm; - cons.prototype.ob$type = Sk.builtin.type.makeIntoTypeObj(nm, Sk.builtin.structseq_types[nm]); - cons.prototype.ob$type["$d"] = new Sk.builtin.dict([]); - cons.prototype.ob$type["$d"].mp$ass_subscript(Sk.builtin.type.basesStr_, new Sk.builtin.tuple([Sk.builtin.tuple])); - //var mro = Sk.builtin.type.buildMRO(cons.prototype.ob$type); - //cons.prototype.ob$type["$d"].mp$ass_subscript(Sk.builtin.type.mroStr_, mro); - //cons.prototype.ob$type.tp$mro = mro; - cons.prototype.__getitem__ = new Sk.builtin.func(function (self, index) { - return Sk.builtin.tuple.prototype.mp$subscript.call(self, index); }); - cons.prototype.__reduce__ = new Sk.builtin.func(function (self) { - throw new Sk.builtin.Exception("__reduce__ is not implemented"); - }); - - cons.prototype["$r"] = function () { - var ret; - var i; - var bits; - if (this.v.length === 0) { - return new Sk.builtin.str(nm + "()"); - } - bits = []; - for (i = 0; i < this.v.length; ++i) { - bits[i] = flds[i] + "=" + Sk.misceval.objectRepr(this.v[i]).v; - } - ret = bits.join(", "); - if (this.v.length === 1) { - ret += ","; - } - return new Sk.builtin.str(nm + "(" + ret + ")"); - }; - cons.prototype.tp$setattr = function (pyName, value) { - throw new Sk.builtin.AttributeError("readonly property"); - }; - - cons.prototype.tp$getattr = function (pyName) { - var jsName = pyName.$jsstr(); - var i = flds.indexOf(jsName); - if (i >= 0) { - return this.v[i]; - } else { - return Sk.builtin.object.prototype.GenericGetAttr(pyName); - } - }; - - return cons; + return structseq; }; Sk.exportSymbol("Sk.builtin.make_structseq", Sk.builtin.make_structseq); diff --git a/src/super.js b/src/super.js new file mode 100644 index 0000000000..c53df0da2c --- /dev/null +++ b/src/super.js @@ -0,0 +1,150 @@ +/** + * @constructor + * Sk.builtin.super_ + */ +Sk.builtin.super_ = Sk.abstr.buildNativeClass("super", { + constructor: function super_() { + // internally we never use this method + // use Sk.misceval.callsimArray(Sk.builtin.super_, [a_type, obj]); + Sk.asserts.assert(this instanceof Sk.builtin.super_, "bad call to super, use 'new'"); + }, + slots: { + tp$doc: + "super() -> same as super(__class__, )\n" + + "super(type) -> unbound super object\nsuper(type, obj) -> bound super object; requires isinstance(obj, type)\n" + + "super(type, type2) -> bound super object; requires issubclass(type2, type)\n" + + "Typical use to call a cooperative superclass method:\n" + + "class C(B):\n def meth(self, arg):\n super().meth(arg)\nThis works for class methods too:\nclass C(B):\n @classmethod\n def cmeth(cls, arg):\n super().cmeth(arg)\n", + tp$new: Sk.generic.new, + tp$init: function (args, kwargs) { + Sk.abstr.checkNoKwargs("super", kwargs); + Sk.abstr.checkArgsLen("super", args, 1, 2); + const a_type = args[0]; + const other_self = args[1]; + if (!Sk.builtin.checkClass(a_type)) { + throw new Sk.builtin.TypeError("must be type, not " + Sk.abstr.typeName(a_type)); + } + this.obj = other_self; + this.type = a_type; + if (this.obj != null) { + this.obj_type = this.$supercheck(a_type, this.obj); + } + + return Sk.builtin.none.none$; + }, + $r: function () { + if (this.obj) { + return new Sk.builtin.str(", <" + Sk.abstr.typeName(this.obj) + " object>>"); + } + return new Sk.builtin.str(", NULL>"); + }, + tp$getattr: function (pyName, canSuspend) { + let starttype = this.obj_type; + if (starttype == null) { + return Sk.generic.getAttr.call(this, pyName, canSuspend); + } + const mro = starttype.prototype.tp$mro; + const n = mro.length; + /* We want __class__ to return the class of the super object + (i.e. super, or a subclass), not the class of su->obj. */ + if (pyName === Sk.builtin.str.$class) { + return Sk.generic.getAttr.call(this, pyName, canSuspend); + } + /* No need to check the last one: it's gonna be skipped anyway. */ + let i; + for (i = 0; i + 1 < n; i++) { + if (this.type === mro[i]) { + break; + } + } + i++; + if (i >= n) { + return Sk.generic.getAttr.call(this, pyName, canSuspend); + } + const jsName = pyName.$jsstr(); + + let tmp, res; + while (i < n) { + tmp = mro[i].prototype; + if (tmp.hasOwnProperty(jsName)) { + res = tmp[jsName]; + } + + if (res !== undefined) { + const f = res.tp$descr_get; + if (f !== undefined) { + /* Only pass 'obj' param if this is instance-mode super + (See SF ID #743627) */ + res = f.call(res, this.obj === starttype ? null : this.obj, starttype); + } + return res; + } + i++; + } + }, + tp$descr_get: function (obj, obtype) { + if (obj === null || this.obj != null) { + return this; + } + if (this.ob$type !== Sk.builtin.super_) { + /* If su is an instance of a (strict) subclass of super, + call its type */ + return Sk.misceval.callsimOrSuspendArray(this.ob$type, [this.type, obj]); + } else { + /* Inline the common case */ + const obj_type = this.$supercheck(this.type, obj); + const newobj = new Sk.builtin.super_(); + newobj.type = this.type; + newobj.obj = obj; + newobj.obj_type = obj_type; + return newobj; + } + }, + }, + getsets: { + __thisclass__: { + $get: function () { + return this.type; + }, + $doc: "the class invoking super()", + }, + __self__: { + $get: function () { + return this.obj || Sk.builtin.none.none$; + }, + $doc: "the instance invoking super(); may be None", + }, + __self_class__: { + $get: function () { + return this.obj_type || Sk.builtin.none.none$; + }, + $doc: "the type of the instance invoking super(); may be None", + }, + }, + proto: { + $supercheck: function (type, obj) { + /* Check that a super() call makes sense. Return a type object. + + obj can be a class, or an instance of one: + + - If it is a class, it must be a subclass of 'type'. This case is + used for class methods; the return value is obj. + + - If it is an instance, it must be an instance of 'type'. This is + the normal case; the return value is obj.__class__. + + /* Check for first bullet above (special case) */ + if (Sk.builtin.checkClass(obj) && obj.ob$type.$isSubType(type)) { + return obj; + } + /* Normal case */ + if (obj.ob$type.$isSubType(type)) { + return obj.ob$type; + } else { + /* Try the slow way */ + /* Cpython has a slow way buy i'm not sure we need it */ + } + throw new Sk.builtin.TypeError("super(type, obj): obj must be an instance or subtype of type"); + }, + }, +}); \ No newline at end of file diff --git a/src/symtable.js b/src/symtable.js index d6f11d7db5..9a9d90edc3 100644 --- a/src/symtable.js +++ b/src/symtable.js @@ -27,7 +27,6 @@ var DEF_NONLOCAL = 2 << 10; var DEF_ANNOT = 2 << 11; /* this name is annotated */ - var DEF_BOUND = (DEF_LOCAL | DEF_PARAM | DEF_IMPORT); /* GLOBAL_EXPLICIT and GLOBAL_IMPLICIT are used internally by the symbol @@ -96,12 +95,13 @@ Sk.exportSymbol("Sk.SYMTAB_CONSTS", SYMTAB_CONSTS); * @param {number} flags * @param {Array.} namespaces */ -function Symbol_ (name, flags, namespaces) { +function Symbol_(name, flags, namespaces) { this.__name = name; this.__flags = flags; this.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK; this.__namespaces = namespaces || []; } + Symbol_.prototype.get_name = function () { return this.__name; }; @@ -145,7 +145,7 @@ var astScopeCounter = 0; * @param {string} type * @param {number} lineno */ -function SymbolTableScope (table, name, type, ast, lineno) { +function SymbolTableScope(table, name, type, ast, lineno) { this.symFlags = {}; this.name = name; this.varnames = []; @@ -174,6 +174,7 @@ function SymbolTableScope (table, name, type, ast, lineno) { // cache of Symbols for returning to other parts of code this.symbols = {}; } + SymbolTableScope.prototype.get_type = function () { return this.blockType; }; @@ -202,8 +203,7 @@ SymbolTableScope.prototype.lookup = function (name) { flags = this.symFlags[name]; namespaces = this.__check_children(name); sym = this.symbols[name] = new Symbol_(name, flags, namespaces); - } - else { + } else { sym = this.symbols[name]; } return sym; @@ -302,7 +302,7 @@ SymbolTableScope.prototype.getScope = function (name) { * @constructor * @param {string} filename */ -function SymbolTable (filename) { +function SymbolTable(filename) { this.filename = filename; this.cur = null; this.top = null; @@ -316,6 +316,7 @@ function SymbolTable (filename) { // here for the compiler to lookup later. this.stss = {}; } + SymbolTable.prototype.getStsForAst = function (ast) { var v; Sk.asserts.assert(ast.scopeId !== undefined, "ast wasn't added to st?"); @@ -358,7 +359,7 @@ SymbolTable.prototype.SEQExpr = function (nodes) { SymbolTable.prototype.enterBlock = function (name, blockType, ast, lineno) { var prev; - name = Sk.fixReservedNames(name); + name = Sk.fixReserved(name); //print("enterBlock:", name); prev = null; if (this.cur) { @@ -391,8 +392,7 @@ SymbolTable.prototype.visitParams = function (args, toplevel) { if (arg.constructor === Sk.astnodes.arg) { // TODO arguments are more complicated in Python 3... this.addDef(arg.arg, DEF_PARAM, arg.lineno); - } - else { + } else { // Tuple isn't supported throw new Sk.builtin.SyntaxError("invalid expression in parameter list", this.filename); } @@ -423,23 +423,21 @@ SymbolTable.prototype.newTmpname = function (lineno) { SymbolTable.prototype.addDef = function (name, flag, lineno) { var fromGlobal; var val; - var mangled = Sk.mangleName(this.curClass, new Sk.builtin.str(name)).v; - mangled = Sk.fixReservedNames(mangled); + var mangled = Sk.mangleName(this.curClass, name).v; + mangled = Sk.fixReserved(mangled); val = this.cur.symFlags[mangled]; if (val !== undefined) { if ((flag & DEF_PARAM) && (val & DEF_PARAM)) { throw new Sk.builtin.SyntaxError("duplicate argument '" + name.v + "' in function definition", this.filename, lineno); } val |= flag; - } - else { + } else { val = flag; } this.cur.symFlags[mangled] = val; if (flag & DEF_PARAM) { this.cur.varnames.push(mangled); - } - else if (flag & DEF_GLOBAL) { + } else if (flag & DEF_GLOBAL) { val = flag; fromGlobal = this.global[mangled]; if (fromGlobal !== undefined) { @@ -530,12 +528,12 @@ SymbolTable.prototype.visitStmt = function (s) { if (s.target.constructor == Sk.astnodes.Name) { e_name = s.target; name = Sk.mangleName(this.curClass, e_name.id).v; - name = Sk.fixReservedNames(name); + name = Sk.fixReserved(name); cur = this.cur.symFlags[name]; - if ((cur & (DEF_GLOBAL | DEF_NONLOCAL) ) + if ((cur & (DEF_GLOBAL | DEF_NONLOCAL)) && (this.global != this.cur.symFlags) // TODO && (s.simple)) { - throw new Sk.builtin.SyntaxError("annotated name '"+ name +"' can't be global", this.filename, s.lineno); + throw new Sk.builtin.SyntaxError("annotated name '" + name + "' can't be global", this.filename, s.lineno); } if (s.simple) { this.addDef(new Sk.builtin.str(name), DEF_ANNOT | DEF_LOCAL, s.lineno); @@ -612,13 +610,12 @@ SymbolTable.prototype.visitStmt = function (s) { nameslen = s.names.length; for (i = 0; i < nameslen; ++i) { name = Sk.mangleName(this.curClass, s.names[i]).v; - name = Sk.fixReservedNames(name); + name = Sk.fixReserved(name); cur = this.cur.symFlags[name]; if (cur & (DEF_LOCAL | USE)) { if (cur & DEF_LOCAL) { throw new Sk.builtin.SyntaxError("name '" + name + "' is assigned to before global declaration", this.filename, s.lineno); - } - else { + } else { throw new Sk.builtin.SyntaxError("name '" + name + "' is used prior to global declaration", this.filename, s.lineno); } } @@ -641,7 +638,7 @@ SymbolTable.prototype.visitStmt = function (s) { case Sk.astnodes.Try: this.SEQStmt(s.body); - this.visitExcepthandlers(s.handlers) + this.visitExcepthandlers(s.handlers); this.SEQStmt(s.orelse); this.SEQStmt(s.finalbody); break; @@ -651,19 +648,19 @@ SymbolTable.prototype.visitStmt = function (s) { } }; -SymbolTable.prototype.visit_withitem = function(item) { +SymbolTable.prototype.visit_withitem = function (item) { this.visitExpr(item.context_expr); if (item.optional_vars) { this.visitExpr(item.optional_vars); } -} +}; function VISIT_SEQ(visitFunc, seq) { var i; for (i = 0; i < seq.length; i++) { var elt = seq[i]; - visitFunc(elt) + visitFunc(elt); } } @@ -757,6 +754,17 @@ SymbolTable.prototype.visitExpr = function (e) { case Sk.astnodes.Num: case Sk.astnodes.Str: break; + case Sk.astnodes.JoinedStr: + for (let s of e.values) { + this.visitExpr(s); + } + break; + case Sk.astnodes.FormattedValue: + this.visitExpr(e.value); + if (e.format_spec) { + this.visitExpr(e.format_spec); + } + break; case Sk.astnodes.Attribute: this.visitExpr(e.value); break; @@ -814,8 +822,7 @@ SymbolTable.prototype.visitAlias = function (names, lineno) { } if (name !== "*") { this.addDef(new Sk.builtin.str(storename), DEF_IMPORT, lineno); - } - else { + } else { if (this.cur.blockType !== ModuleBlock) { throw new Sk.builtin.SyntaxError("import * only allowed at module level", this.filename); } @@ -850,7 +857,7 @@ SymbolTable.prototype.visitExcepthandlers = function (handlers) { } }; -function _dictUpdate (a, b) { +function _dictUpdate(a, b) { var kb; for (kb in b) { a[kb] = b[kb]; @@ -1008,11 +1015,9 @@ SymbolTable.prototype.analyzeName = function (ste, dict, name, flags, bound, loc dict[name] = FREE; ste.hasFree = true; free[name] = null; - } - else if (global && global[name] !== undefined) { + } else if (global && global[name] !== undefined) { dict[name] = GLOBAL_IMPLICIT; - } - else { + } else { if (ste.isNested) { ste.hasFree = true; } @@ -1052,7 +1057,7 @@ Sk.symboltable = function (ast, filename) { Sk.dumpSymtab = function (st) { var pyBoolStr = function (b) { return b ? "True" : "False"; - } + }; var pyList = function (l) { var i; var ret = []; @@ -1083,8 +1088,7 @@ Sk.dumpSymtab = function (st) { ret += indent + "Sym_haschildren: " + pyBoolStr(obj.has_children()) + "\n"; if (obj.get_type() === "class") { ret += indent + "Class_methods: " + pyList(obj.get_methods()) + "\n"; - } - else if (obj.get_type() === "function") { + } else if (obj.get_type() === "function") { ret += indent + "Func_params: " + pyList(obj.get_parameters()) + "\n"; ret += indent + "Func_locals: " + pyList(obj.get_locals()) + "\n"; ret += indent + "Func_globals: " + pyList(obj.get_globals()) + "\n"; diff --git a/src/token.js b/src/token.js index 3d5e7c5b2e..c26aec7154 100644 --- a/src/token.js +++ b/src/token.js @@ -81,57 +81,57 @@ var tokens = { // #--end constants-- var EXACT_TOKEN_TYPES = { - "!=": tokens.NOTEQUAL, - "%": tokens.PERCENT, - "%=": tokens.PERCENTEQUAL, - "&": tokens.AMPER, - "&=": tokens.AMPEREQUAL, - "(": tokens.LPAR, - ")": tokens.RPAR, - "*": tokens.STAR, - "**": tokens.DOUBLESTAR, - "**=": tokens.DOUBLESTAREQUAL, - "*=": tokens.STAREQUAL, - "+": tokens.PLUS, - "+=": tokens.PLUSEQUAL, - ",": tokens.COMMA, - "-": tokens.MINUS, - "-=": tokens.MINEQUAL, - "->": tokens.RARROW, - ".": tokens.DOT, - "...": tokens.ELLIPSIS, - "/": tokens.SLASH, - "//": tokens.DOUBLESLASH, - "//=": tokens.DOUBLESLASHEQUAL, - "/=": tokens.SLASHEQUAL, - ":": tokens.COLON, - ":=": tokens.COLONEQUAL, - ";": tokens.SEMI, - "<": tokens.LESS, - "<<": tokens.LEFTSHIFT, - "<<=": tokens.LEFTSHIFTEQUAL, - "<=": tokens.LESSEQUAL, - "=": tokens.EQUAL, - "==": tokens.EQEQUAL, - ">": tokens.GREATER, - ">=": tokens.GREATEREQUAL, - ">>": tokens.RIGHTSHIFT, - ">>=": tokens.RIGHTSHIFTEQUAL, - "@": tokens.AT, - "@=": tokens.ATEQUAL, - "[": tokens.LSQB, - "]": tokens.RSQB, - "^": tokens.CIRCUMFLEX, - "^=": tokens.CIRCUMFLEXEQUAL, - "{": tokens.LBRACE, - "|": tokens.VBAR, - "|=": tokens.VBAREQUAL, - "}": tokens.RBRACE, - "~": tokens.TILDE, + "!=": tokens.T_NOTEQUAL, + "%": tokens.T_PERCENT, + "%=": tokens.T_PERCENTEQUAL, + "&": tokens.T_AMPER, + "&=": tokens.T_AMPEREQUAL, + "(": tokens.T_LPAR, + ")": tokens.T_RPAR, + "*": tokens.T_STAR, + "**": tokens.T_DOUBLESTAR, + "**=": tokens.T_DOUBLESTAREQUAL, + "*=": tokens.T_STAREQUAL, + "+": tokens.T_PLUS, + "+=": tokens.T_PLUSEQUAL, + ",": tokens.T_COMMA, + "-": tokens.T_MINUS, + "-=": tokens.T_MINEQUAL, + "->": tokens.T_RARROW, + ".": tokens.T_DOT, + "...": tokens.T_ELLIPSIS, + "/": tokens.T_SLASH, + "//": tokens.T_DOUBLESLASH, + "//=": tokens.T_DOUBLESLASHEQUAL, + "/=": tokens.T_SLASHEQUAL, + ":": tokens.T_COLON, + // ":=": tokens.T_COLONEQUAL, // currently not listed in tokens + ";": tokens.T_SEMI, + "<": tokens.T_LESS, + "<<": tokens.T_LEFTSHIFT, + "<<=": tokens.T_LEFTSHIFTEQUAL, + "<=": tokens.T_LESSEQUAL, + "=": tokens.T_EQUAL, + "==": tokens.T_EQEQUAL, + ">": tokens.T_GREATER, + ">=": tokens.T_GREATEREQUAL, + ">>": tokens.T_RIGHTSHIFT, + ">>=": tokens.T_RIGHTSHIFTEQUAL, + "@": tokens.T_AT, + "@=": tokens.T_ATEQUAL, + "[": tokens.T_LSQB, + "]": tokens.T_RSQB, + "^": tokens.T_CIRCUMFLEX, + "^=": tokens.T_CIRCUMFLEXEQUAL, + "{": tokens.T_LBRACE, + "|": tokens.T_VBAR, + "|=": tokens.T_VBAREQUAL, + "}": tokens.T_RBRACE, + "~": tokens.T_TILDE, }; var tok_name = {}; -(function() { +(function () { for (var i in tokens) { tok_name[tokens[i]] = i; } diff --git a/src/tokenize.js b/src/tokenize.js index d075bd3346..4db8caaac2 100644 --- a/src/tokenize.js +++ b/src/tokenize.js @@ -20,27 +20,27 @@ function TokenInfo(type, string, start, end, line) { this.line = line; } -TokenInfo.prototype.exact_type = function() { +TokenInfo.prototype.exact_type = function () { if (this.type == tokens.T_OP && this.string in Sk.token.EXACT_TOKEN_TYPES) { - return Sk.token.EXACT_TOKEN_TYPES[this.string] + return Sk.token.EXACT_TOKEN_TYPES[this.string]; } else { - return this.type + return this.type; } -} +}; /** @param {...*} x */ -function group (x) { +function group(x) { var args = Array.prototype.slice.call(arguments); return "(" + args.join("|") + ")"; } /** @param {...*} x */ -function any (x) { +function any(x) { return group.apply(null, arguments) + "*"; } /** @param {...*} x */ -function maybe (x) { +function maybe(x) { return group.apply(null, arguments) + "?"; } @@ -49,17 +49,17 @@ var reRegExpChar = /[\\^$.*+?()[\]{}|]/g, function regexEscape(string) { return (string && reHasRegExpChar.test(string)) - ? string.replace(reRegExpChar, '\\$&') + ? string.replace(reRegExpChar, "\\$&") : string; } /** * Iterable contains * @template T - * @param {Iterable} a + * @param {T} a * @param {T} obj */ -function contains (a, obj) { +function contains(a, obj) { var i = a.length; while (i--) { if (a[i] === obj) { @@ -69,7 +69,7 @@ function contains (a, obj) { return false; } -function rstrip (input, what) { +function rstrip(input, what) { var i; for (i = input.length; i > 0; --i) { if (what.indexOf(input.charAt(i - 1)) === -1) { @@ -79,29 +79,29 @@ function rstrip (input, what) { return input.substring(0, i); } -var the_underscore = '_'; -var Lu = '[A-Z]'; -var Ll = '[a-z]'; -var Lt = '[\\u{10B99}-\\u{10B9C}\\u{112A9}\\u{115DC}-\\u{115DD}\\u034F\\u115F-\\u1160\\u17B4-\\u17B5\\u2065\\u3164\\uFFA0\\uFFF0-\\uFFF8\\u{E0000}\\u{E0002}-\\u{E001F}\\u{E0080}-\\u{E00FF}\\u{E01F0}-\\u{E0FFF}\\u{112A9}\\u00D7]'; -var Lm = '[\\u02B0-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0374\\u037A\\u0559\\u06E5-\\u06E6\\u07F4-\\u07F5\\u0971\\u1C78-\\u1C7D\\u1D2C-\\u1D6A\\u1DFD-\\u1DFF\\u2E2F\\u30FC\\uA67F\\uA69C-\\uA69D\\uA717-\\uA71F\\uA788\\uA7F8-\\uA7F9\\uAB5C-\\uAB5F\\uFF70\\uFF9E-\\uFF9F\\u{16F93}-\\u{16F9F}\\u02D0-\\u02D1\\u0640\\u07FA\\u0E46\\u0EC6\\u1843\\u1AA7\\u1C7B\\u3005\\u3031-\\u3035\\u309D-\\u309E\\u30FC-\\u30FE\\uA015\\uA60C\\uA9CF\\uA9E6\\uAA70\\uAADD\\uAAF3-\\uAAF4\\uFF70\\u{16B42}-\\u{16B43}\\u{16FE0}-\\u{16FE1}\\u02B0-\\u02B8\\u02C0-\\u02C1\\u02E0-\\u02E4\\u037A\\u1D2C-\\u1D6A\\u1D78\\u1D9B-\\u1DBF\\u2071\\u207F\\u2090-\\u209C\\u2C7C-\\u2C7D\\uA69C-\\uA69D\\uA770\\uA7F8-\\uA7F9\\uAB5C-\\uAB5F\\uFF9E-\\uFF9F\\u02B2\\u1D62\\u1DA4\\u1DA8\\u2071\\u2C7C\\u2E18-\\u2E19\\u2E2F]'; -var Lo = '[\\u2135-\\u2138\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u3006\\u3400-\\u4DB5\\u4E00-\\u9FEF\\uF900-\\uFA6D\\uFA70-\\uFAD9\\u{17000}-\\u{187F1}\\u{18800}-\\u{18AF2}\\u{1B170}-\\u{1B2FB}\\u{20000}-\\u{2A6D6}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u{2F800}-\\u{2FA1D}\\uAAC0\\uAAC2\\uFE20-\\uFE2F\\u{10D22}-\\u{10D23}\\u{1135D}\\u00AA\\u00BA\\u3400-\\u4DB5\\u4E00-\\u9FEF\\uFA0E-\\uFA0F\\uFA11\\uFA13-\\uFA14\\uFA1F\\uFA21\\uFA23-\\uFA24\\uFA27-\\uFA29\\u{20000}-\\u{2A6D6}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u115F-\\u1160\\u3164\\uFFA0\\u0673\\u17A3-\\u17A4\\u0E40-\\u0E44\\u0EC0-\\u0EC4\\u19B5-\\u19B7\\u19BA\\uAAB5-\\uAAB6\\uAAB9\\uAABB-\\uAABC]'; -var Nl = '[\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u2170-\\u217F\\u2160-\\u216F]'; -var Mn = '[\\u104A-\\u104B\\u102B-\\u102C\\u102D-\\u1030\\u1031\\u1032-\\u1036\\u1038\\u103B-\\u103C\\u103D-\\u103E\\u1056-\\u1057\\u1058-\\u1059\\u105E-\\u1060\\u1062\\u1067-\\u1068\\u1071-\\u1074\\u1082\\u1083-\\u1084\\u1085-\\u1086\\u109C\\u109D\\u1037\\u1039-\\u103A\\u1087-\\u108C\\u108D\\u108F\\u109A-\\u109B\\uA9E5\\uAA7B\\uAA7C\\uAA7D\\uA9E6\\uAA70\\u104A-\\u104B]'; -var Mc = '[\\u0903\\u093B\\u093E-\\u0940\\u0949-\\u094C\\u094E-\\u094F\\u0982-\\u0983\\u09BE-\\u09C0\\u09C7-\\u09C8\\u09CB-\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB-\\u0ACC\\u0B02-\\u0B03\\u0B3E\\u0B40\\u0B47-\\u0B48\\u0B4B-\\u0B4C\\u0B57\\u0BBE-\\u0BBF\\u0BC1-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82-\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7-\\u0CC8\\u0CCA-\\u0CCB\\u0CD5-\\u0CD6\\u0D02-\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82-\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2-\\u0DF3\\u0F7F\\u102B-\\u102C\\u1031\\u1038\\u103B-\\u103C\\u1056-\\u1057\\u1062\\u1067-\\u1068\\u1083-\\u1084\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7-\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930-\\u1931\\u1933-\\u1938\\u1A19-\\u1A1A\\u1A55\\u1A57\\u1A61\\u1A63-\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B82\\u1BA1\\u1BA6-\\u1BA7\\u1BE7\\u1BEA-\\u1BEC\\u1BEE\\u1C24-\\u1C2B\\u1C34-\\u1C35\\u1CF2-\\u1CF3\\uA823-\\uA824\\uA827\\uA880-\\uA881\\uA8B4-\\uA8C3\\uA952\\uA983\\uA9B4-\\uA9B5\\uA9BA-\\uA9BB\\uA9BD-\\uA9BF\\uAA2F-\\uAA30\\uAA33-\\uAA34\\uAA4D\\uAAEB\\uAAEE-\\uAAEF\\uAAF5\\uABE3-\\uABE4\\uABE6-\\uABE7\\uABE9-\\uABEA\\u{11000}\\u{11002}\\u{11082}\\u{110B0}-\\u{110B2}\\u{110B7}-\\u{110B8}\\u{1112C}\\u{11145}-\\u{11146}\\u{11182}\\u{111B3}-\\u{111B5}\\u{111BF}\\u{1122C}-\\u{1122E}\\u{11232}-\\u{11233}\\u{112E0}-\\u{112E2}\\u{11302}-\\u{11303}\\u{1133E}-\\u{1133F}\\u{11341}-\\u{11344}\\u{11347}-\\u{11348}\\u{1134B}-\\u{1134C}\\u{11357}\\u{11362}-\\u{11363}\\u{11435}-\\u{11437}\\u{11440}-\\u{11441}\\u{11445}\\u{114B0}-\\u{114B2}\\u{114B9}\\u{114BB}-\\u{114BE}\\u{114C1}\\u{115AF}-\\u{115B1}\\u{115B8}-\\u{115BB}\\u{115BE}\\u{11630}-\\u{11632}\\u{1163B}-\\u{1163C}\\u{1163E}\\u{116AC}\\u{116AE}-\\u{116AF}\\u{11720}-\\u{11721}\\u{11726}\\u{1182C}-\\u{1182E}\\u{11838}\\u{11A39}\\u{11A57}-\\u{11A58}\\u{11A97}\\u{11C2F}\\u{11C3E}\\u{11CA9}\\u{11CB1}\\u{11CB4}\\u{11D8A}-\\u{11D8E}\\u{11D93}-\\u{11D94}\\u{11D96}\\u{11EF5}-\\u{11EF6}\\u{16F51}-\\u{16F7E}\\u0F3E-\\u0F3F\\u1087-\\u108C\\u108F\\u109A-\\u109B\\u1B44\\u1BAA\\u1CE1\\u1CF7\\u302E-\\u302F\\uA953\\uA9C0\\uAA7B\\uAA7D\\uABEC\\u{111C0}\\u{11235}\\u{1134D}\\u{116B6}\\u{1D16D}-\\u{1D172}\\u09BE\\u09D7\\u0B3E\\u0B57\\u0BBE\\u0BD7\\u0CC2\\u0CD5-\\u0CD6\\u0D3E\\u0D57\\u0DCF\\u0DDF\\u302E-\\u302F\\u{1133E}\\u{11357}\\u{114B0}\\u{114BD}\\u{115AF}\\u{1D165}\\u{1D16E}-\\u{1D172}]'; -var Nd = '[\\u{1D7CE}-\\u{1D7FF}\\uFF10-\\uFF19]'; -var Pc = '\\u2040'; -var Other_ID_Start = '[\\u1885-\\u1886\\u2118\\u212E\\u309B-\\u309C]'; -var Other_ID_Continue = '[\\u00B7\\u0387\\u1369-\\u1371\\u19DA]'; -var id_start = group(Lu, Ll,Lt, Lm, Lo, Nl, the_underscore, Other_ID_Start); +var the_underscore = "_"; +var Lu = "[A-Z]"; +var Ll = "[a-z]"; +var Lt = "[\\u{10B99}-\\u{10B9C}\\u{112A9}\\u{115DC}-\\u{115DD}\\u034F\\u115F-\\u1160\\u17B4-\\u17B5\\u2065\\u3164\\uFFA0\\uFFF0-\\uFFF8\\u{E0000}\\u{E0002}-\\u{E001F}\\u{E0080}-\\u{E00FF}\\u{E01F0}-\\u{E0FFF}\\u{112A9}\\u00D7]"; +var Lm = "[\\u02B0-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0374\\u037A\\u0559\\u06E5-\\u06E6\\u07F4-\\u07F5\\u0971\\u1C78-\\u1C7D\\u1D2C-\\u1D6A\\u1DFD-\\u1DFF\\u2E2F\\u30FC\\uA67F\\uA69C-\\uA69D\\uA717-\\uA71F\\uA788\\uA7F8-\\uA7F9\\uAB5C-\\uAB5F\\uFF70\\uFF9E-\\uFF9F\\u{16F93}-\\u{16F9F}\\u02D0-\\u02D1\\u0640\\u07FA\\u0E46\\u0EC6\\u1843\\u1AA7\\u1C7B\\u3005\\u3031-\\u3035\\u309D-\\u309E\\u30FC-\\u30FE\\uA015\\uA60C\\uA9CF\\uA9E6\\uAA70\\uAADD\\uAAF3-\\uAAF4\\uFF70\\u{16B42}-\\u{16B43}\\u{16FE0}-\\u{16FE1}\\u02B0-\\u02B8\\u02C0-\\u02C1\\u02E0-\\u02E4\\u037A\\u1D2C-\\u1D6A\\u1D78\\u1D9B-\\u1DBF\\u2071\\u207F\\u2090-\\u209C\\u2C7C-\\u2C7D\\uA69C-\\uA69D\\uA770\\uA7F8-\\uA7F9\\uAB5C-\\uAB5F\\uFF9E-\\uFF9F\\u02B2\\u1D62\\u1DA4\\u1DA8\\u2071\\u2C7C\\u2E18-\\u2E19\\u2E2F]"; +var Lo = "[\\u2135-\\u2138\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u3006\\u3400-\\u4DB5\\u4E00-\\u9FEF\\uF900-\\uFA6D\\uFA70-\\uFAD9\\u{17000}-\\u{187F1}\\u{18800}-\\u{18AF2}\\u{1B170}-\\u{1B2FB}\\u{20000}-\\u{2A6D6}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u{2F800}-\\u{2FA1D}\\uAAC0\\uAAC2\\uFE20-\\uFE2F\\u{10D22}-\\u{10D23}\\u{1135D}\\u00AA\\u00BA\\u3400-\\u4DB5\\u4E00-\\u9FEF\\uFA0E-\\uFA0F\\uFA11\\uFA13-\\uFA14\\uFA1F\\uFA21\\uFA23-\\uFA24\\uFA27-\\uFA29\\u{20000}-\\u{2A6D6}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u115F-\\u1160\\u3164\\uFFA0\\u0673\\u17A3-\\u17A4\\u0E40-\\u0E44\\u0EC0-\\u0EC4\\u19B5-\\u19B7\\u19BA\\uAAB5-\\uAAB6\\uAAB9\\uAABB-\\uAABC]"; +var Nl = "[\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u2170-\\u217F\\u2160-\\u216F]"; +var Mn = "[\\u104A-\\u104B\\u102B-\\u102C\\u102D-\\u1030\\u1031\\u1032-\\u1036\\u1038\\u103B-\\u103C\\u103D-\\u103E\\u1056-\\u1057\\u1058-\\u1059\\u105E-\\u1060\\u1062\\u1067-\\u1068\\u1071-\\u1074\\u1082\\u1083-\\u1084\\u1085-\\u1086\\u109C\\u109D\\u1037\\u1039-\\u103A\\u1087-\\u108C\\u108D\\u108F\\u109A-\\u109B\\uA9E5\\uAA7B\\uAA7C\\uAA7D\\uA9E6\\uAA70\\u104A-\\u104B]"; +var Mc = "[\\u0903\\u093B\\u093E-\\u0940\\u0949-\\u094C\\u094E-\\u094F\\u0982-\\u0983\\u09BE-\\u09C0\\u09C7-\\u09C8\\u09CB-\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB-\\u0ACC\\u0B02-\\u0B03\\u0B3E\\u0B40\\u0B47-\\u0B48\\u0B4B-\\u0B4C\\u0B57\\u0BBE-\\u0BBF\\u0BC1-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82-\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7-\\u0CC8\\u0CCA-\\u0CCB\\u0CD5-\\u0CD6\\u0D02-\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82-\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2-\\u0DF3\\u0F7F\\u102B-\\u102C\\u1031\\u1038\\u103B-\\u103C\\u1056-\\u1057\\u1062\\u1067-\\u1068\\u1083-\\u1084\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7-\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930-\\u1931\\u1933-\\u1938\\u1A19-\\u1A1A\\u1A55\\u1A57\\u1A61\\u1A63-\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B82\\u1BA1\\u1BA6-\\u1BA7\\u1BE7\\u1BEA-\\u1BEC\\u1BEE\\u1C24-\\u1C2B\\u1C34-\\u1C35\\u1CF2-\\u1CF3\\uA823-\\uA824\\uA827\\uA880-\\uA881\\uA8B4-\\uA8C3\\uA952\\uA983\\uA9B4-\\uA9B5\\uA9BA-\\uA9BB\\uA9BD-\\uA9BF\\uAA2F-\\uAA30\\uAA33-\\uAA34\\uAA4D\\uAAEB\\uAAEE-\\uAAEF\\uAAF5\\uABE3-\\uABE4\\uABE6-\\uABE7\\uABE9-\\uABEA\\u{11000}\\u{11002}\\u{11082}\\u{110B0}-\\u{110B2}\\u{110B7}-\\u{110B8}\\u{1112C}\\u{11145}-\\u{11146}\\u{11182}\\u{111B3}-\\u{111B5}\\u{111BF}\\u{1122C}-\\u{1122E}\\u{11232}-\\u{11233}\\u{112E0}-\\u{112E2}\\u{11302}-\\u{11303}\\u{1133E}-\\u{1133F}\\u{11341}-\\u{11344}\\u{11347}-\\u{11348}\\u{1134B}-\\u{1134C}\\u{11357}\\u{11362}-\\u{11363}\\u{11435}-\\u{11437}\\u{11440}-\\u{11441}\\u{11445}\\u{114B0}-\\u{114B2}\\u{114B9}\\u{114BB}-\\u{114BE}\\u{114C1}\\u{115AF}-\\u{115B1}\\u{115B8}-\\u{115BB}\\u{115BE}\\u{11630}-\\u{11632}\\u{1163B}-\\u{1163C}\\u{1163E}\\u{116AC}\\u{116AE}-\\u{116AF}\\u{11720}-\\u{11721}\\u{11726}\\u{1182C}-\\u{1182E}\\u{11838}\\u{11A39}\\u{11A57}-\\u{11A58}\\u{11A97}\\u{11C2F}\\u{11C3E}\\u{11CA9}\\u{11CB1}\\u{11CB4}\\u{11D8A}-\\u{11D8E}\\u{11D93}-\\u{11D94}\\u{11D96}\\u{11EF5}-\\u{11EF6}\\u{16F51}-\\u{16F7E}\\u0F3E-\\u0F3F\\u1087-\\u108C\\u108F\\u109A-\\u109B\\u1B44\\u1BAA\\u1CE1\\u1CF7\\u302E-\\u302F\\uA953\\uA9C0\\uAA7B\\uAA7D\\uABEC\\u{111C0}\\u{11235}\\u{1134D}\\u{116B6}\\u{1D16D}-\\u{1D172}\\u09BE\\u09D7\\u0B3E\\u0B57\\u0BBE\\u0BD7\\u0CC2\\u0CD5-\\u0CD6\\u0D3E\\u0D57\\u0DCF\\u0DDF\\u302E-\\u302F\\u{1133E}\\u{11357}\\u{114B0}\\u{114BD}\\u{115AF}\\u{1D165}\\u{1D16E}-\\u{1D172}]"; +var Nd = "[\\u{1D7CE}-\\u{1D7FF}\\uFF10-\\uFF19]"; +var Pc = "\\u2040"; +var Other_ID_Start = "[\\u1885-\\u1886\\u2118\\u212E\\u309B-\\u309C]"; +var Other_ID_Continue = "[\\u00B7\\u0387\\u1369-\\u1371\\u19DA]"; +var id_start = group(Lu, Ll, Lt, Lm, Lo, Nl, the_underscore, Other_ID_Start); var id_continue = group(id_start, Mn, Mc, Nd, Pc, Other_ID_Continue); var isidentifier_regex; // Fall back if we don't support unicode if (RegExp().unicode === false) { - isidentifier_regex = new RegExp('^' + id_start + '+' + id_continue + '*$', 'u'); + isidentifier_regex = new RegExp("^" + id_start + "+" + id_continue + "*$", "u"); } else { id_start = group(Lu, Ll, the_underscore); - id_continue = group(id_start, '[0-9]'); - isidentifier_regex = new RegExp('^' + id_start + '+' + id_continue + '*$'); + id_continue = group(id_start, "[0-9]"); + isidentifier_regex = new RegExp("^" + id_start + "+" + id_continue + "*$"); } /** @@ -111,7 +111,7 @@ if (RegExp().unicode === false) { * @returns {boolean} */ function isidentifier(str) { - var normalized = str.normalize('NFKC'); + var normalized = str.normalize("NFKC"); return isidentifier_regex.test(normalized); } @@ -123,13 +123,13 @@ function isidentifier(str) { * I don't know if the comment above is still actually correct */ var Whitespace = "[ \\f\\t]*"; var Comment_ = "#[^\\r\\n]*"; -var Ignore = Whitespace + any('\\\\\\r?\\n' + Whitespace) + maybe(Comment_) +var Ignore = Whitespace + any("\\\\\\r?\\n" + Whitespace) + maybe(Comment_); var Name = "\\w+"; var Exponent = "[eE][-+]?[0-9](?:_?[0-9])*"; -var Pointfloat = group('[0-9](?:_?[0-9])*\\.(?:[0-9](?:_?[0-9])*)?', - '\\.[0-9](?:_?[0-9])*') + maybe(Exponent) +var Pointfloat = group("[0-9](?:_?[0-9])*\\.(?:[0-9](?:_?[0-9])*)?", + "\\.[0-9](?:_?[0-9])*") + maybe(Exponent); var Expfloat = "[0-9](?:_?[0-9])*" + Exponent; var Floatnumber = group(Pointfloat, Expfloat); var Imagnumber = group("[0-9](?:_?[0-9])*[jJ]", Floatnumber + "[jJ]"); @@ -144,7 +144,7 @@ function _all_string_prefixes() { // Note that since _all_string_prefixes includes the empty string, // StringPrefix can be the empty string (making it optional). -var StringPrefix = group.apply(null, _all_string_prefixes()) +var StringPrefix = group.apply(null, _all_string_prefixes()); // these regexes differ from python because .exec doesn't do the // same thing as .match in python. It's more like .search. @@ -154,22 +154,24 @@ var StringPrefix = group.apply(null, _all_string_prefixes()) // Tail end of ' string. var Single = "^[^'\\\\]*(?:\\\\.[^'\\\\]*)*'"; // Tail end of " string. -var Double = '^[^"\\\\]*(?:\\\\.[^"\\\\]*)*"'; +var Double = "^[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\""; // Tail end of ''' string. var Single3 = "^[^'\\\\]*(?:(?:\\\\.|'(?!''))[^'\\\\]*)*'''"; // Tail end of """ string. -var Double3 = '^[^"\\\\]*(?:(?:\\\\.|"(?!""))[^"\\\\]*)*"""'; -var Triple = group(StringPrefix + "'''", StringPrefix + '"""'); +var Double3 = "^[^\"\\\\]*(?:(?:\\\\.|\"(?!\"\"))[^\"\\\\]*)*\"\"\""; +var Triple = group(StringPrefix + "'''", StringPrefix + "\"\"\""); // Single-line ' or " string. var String_ = group(StringPrefix + "'[^\\n'\\\\]*(?:\\\\.[^\\n'\\\\]*)*'", - StringPrefix + '"[^\\n"\\\\]*(?:\\\\.[^\\n"\\\\]*)*"'); + StringPrefix + "\"[^\\n\"\\\\]*(?:\\\\.[^\\n\"\\\\]*)*\""); // Sorting in reverse order puts the long operators before their prefixes. // Otherwise if = came before ==, == would get recognized as two instances // of =. var EXACT_TOKENS_SORTED = Object.keys(Sk.token.EXACT_TOKEN_TYPES).sort(); -var Special = group.apply(this, EXACT_TOKENS_SORTED.reverse().map(function (t) { return regexEscape(t); })); -var Funny = group('\\r?\\n', Special); +var Special = group.apply(this, EXACT_TOKENS_SORTED.reverse().map(function (t) { + return regexEscape(t); +})); +var Funny = group("\\r?\\n", Special); // these aren't actually used // var PlainToken = group(Number_, Funny, String_, Name); @@ -177,46 +179,47 @@ var Funny = group('\\r?\\n', Special); // First (or only) line of ' or " string. var ContStr = group(StringPrefix + "'[^\\n'\\\\]*(?:\\\\.[^\\n'\\\\]*)*" + - group("'", '\\\\\\r?\\n'), - StringPrefix + '"[^\\n"\\\\]*(?:\\\\.[^\\n"\\\\]*)*' + - group('"', '\\\\\\r?\\n')) -var PseudoExtras = group('\\\\\\r?\\n|$', Comment_, Triple); + group("'", "\\\\\\r?\\n"), + StringPrefix + "\"[^\\n\"\\\\]*(?:\\\\.[^\\n\"\\\\]*)*" + + group("\"", "\\\\\\r?\\n")); +var PseudoExtras = group("\\\\\\r?\\n|$", Comment_, Triple); // For a given string prefix plus quotes, endpats maps it to a regex // to match the remainder of that string. _prefix can be empty, for // a normal single or triple quoted string (with no prefix). -var endpats = {} +var endpats = {}; var prefixes = _all_string_prefixes(); for (let _prefix of prefixes) { - endpats[_prefix + "'"] = RegExp(Single) - endpats[_prefix + '"'] = RegExp(Double) - endpats[_prefix + "'''"] = RegExp(Single3) - endpats[_prefix + '"""'] = RegExp(Double3) + endpats[_prefix + "'"] = RegExp(Single); + endpats[_prefix + "\""] = RegExp(Double); + endpats[_prefix + "'''"] = RegExp(Single3); + endpats[_prefix + "\"\"\""] = RegExp(Double3); } // A set of all of the single and triple quoted string prefixes, // including the opening quotes. -let single_quoted = [] -let triple_quoted = [] +let single_quoted = []; +let triple_quoted = []; for (let t of prefixes) { - single_quoted.push(t + '"'); + single_quoted.push(t + "\""); single_quoted.push(t + "'"); - triple_quoted.push(t + '"""'); + triple_quoted.push(t + "\"\"\""); triple_quoted.push(t + "'''"); } -var tabsize = 8 +var tabsize = 8; var PseudoTokenRegex; + function _setupTokenRegexes() { // we make these regexes here because they can // be changed by the configuration. - var LSuffix = Sk.__future__.l_suffix ? '(?:L?)' : ''; - var Hexnumber = '0[xX](?:_?[0-9a-fA-F])+' + LSuffix; - var Binnumber = '0[bB](?:_?[01])+' + LSuffix; - var Octnumber = '0([oO])(?:_?[0-7])+' + LSuffix; - var SilentOctnumber = '0([oO]?)(?:_?[0-7])+' + LSuffix; - var Decnumber = '(?:0(?:_?0)*|[1-9](?:_?[0-9])*)' + LSuffix; + var LSuffix = Sk.__future__.l_suffix ? "(?:L?)" : ""; + var Hexnumber = "0[xX](?:_?[0-9a-fA-F])+" + LSuffix; + var Binnumber = "0[bB](?:_?[01])+" + LSuffix; + var Octnumber = "0([oO])(?:_?[0-7])+" + LSuffix; + var SilentOctnumber = "0([oO]?)(?:_?[0-7])+" + LSuffix; + var Decnumber = "(?:0(?:_?0)*|[1-9](?:_?[0-9])*)" + LSuffix; var Intnumber = group(Hexnumber, Binnumber, (Sk.__future__.silent_octal_literal ? SilentOctnumber : Octnumber), Decnumber); var Number_ = group(Imagnumber, Floatnumber, Intnumber); @@ -236,18 +239,18 @@ Sk.exportSymbol("Sk._setupTokenRegexes", Sk._setupTokenRegexes); * @param {function(TokenInfo): void} yield_ */ function _tokenize(readline, encoding, yield_, filename) { - + var lnum = 0, parenlev = 0, continued = 0, - numchars = '0123456789', - contstr = '', + numchars = "0123456789", + contstr = "", needcont = 0, contline = null, indents = [0], - spos = [0,0], - epos = [0,0], + spos = [0, 0], + epos = [0, 0], capos = null, endprog = undefined, strstart = undefined, @@ -260,11 +263,11 @@ function _tokenize(readline, encoding, yield_, filename) { encoding = "utf-8"; } - yield_(new TokenInfo(tokens.T_ENCODING, encoding, [0, 0], [0, 0], '')); + yield_(new TokenInfo(tokens.T_ENCODING, encoding, [0, 0], [0, 0], "")); } - var last_line = ''; - var line = ''; + var last_line = ""; + var line = ""; while (true) { // loop over lines in stream try { // We capture the value of the line variable here because @@ -274,7 +277,7 @@ function _tokenize(readline, encoding, yield_, filename) { last_line = line; line = readline(); } catch (Exception) { - line = ''; + line = ""; } // lets pretend this doesn't exist for now. @@ -294,14 +297,14 @@ function _tokenize(readline, encoding, yield_, filename) { if (endmatch) { pos = end = endmatch[0].length; yield_(new TokenInfo(tokens.T_STRING, contstr + line.substring(0, end), - strstart, [lnum, end], contline + line)); - contstr = ''; + strstart, [lnum, end], contline + line)); + contstr = ""; needcont = 0; contline = null; } else if (needcont && line.substring(line.length - 2) !== "\\\n" && line.substring(line.length - 3) !== "\\\r\n") { yield_(new TokenInfo(tokens.T_ERRORTOKEN, contstr + line, - strstart, [lnum, line.length], contline)); - contstr = ''; + strstart, [lnum, line.length], contline)); + contstr = ""; contline = null; continue; } else { @@ -310,35 +313,38 @@ function _tokenize(readline, encoding, yield_, filename) { continue; } } else if (parenlev == 0 && !continued) { // new statement - if (!line) { break; } + if (!line) { + break; + } var column = 0; while (pos < max) { // measure leading whitespace - if (line[pos] == ' ') { - column += 1 - } else if (line[pos] == '\t') { - column = Math.floor(column/tabsize + 1) * tabsize; - } else if (line[pos] == '\f') { - column = 0 + if (line[pos] == " ") { + column += 1; + } else if (line[pos] == "\t") { + column = Math.floor(column / tabsize + 1) * tabsize; + } else if (line[pos] == "\f") { + column = 0; } else { break; - }; - pos += 1 + } + ; + pos += 1; } if (pos == max) { break; } - if (contains('#\r\n', line[pos])) { // skip comments or blank lines - if (line[pos] == '#') { - var comment_token = rstrip(line.substring(pos), '\r\n'); + if (contains("#\r\n", line[pos])) { // skip comments or blank lines + if (line[pos] == "#") { + var comment_token = rstrip(line.substring(pos), "\r\n"); yield_(new TokenInfo(tokens.T_COMMENT, comment_token, - [lnum, pos], [lnum, pos + comment_token.length], line)); + [lnum, pos], [lnum, pos + comment_token.length], line)); pos += comment_token.length; } yield_(new TokenInfo(tokens.T_NL, line.substring(pos), - [lnum, pos], [lnum, line.length], line)); + [lnum, pos], [lnum, line.length], line)); continue; } @@ -356,7 +362,7 @@ function _tokenize(readline, encoding, yield_, filename) { indents = indents.slice(0, -1); - yield_(new TokenInfo(tokens.T_DEDENT, '', [lnum, pos], [lnum, pos], line)); + yield_(new TokenInfo(tokens.T_DEDENT, "", [lnum, pos], [lnum, pos], line)); } } else { // continued statement if (!line) { @@ -372,12 +378,12 @@ function _tokenize(readline, encoding, yield_, filename) { // content. we'd like to put a \w+ before pseudomatch, but then we // can't get any data capos = line.charAt(pos); - while (capos === ' ' || capos === '\f' || capos === '\t') { + while (capos === " " || capos === "\f" || capos === "\t") { pos += 1; capos = line.charAt(pos); } - pseudomatch = PseudoTokenRegex.exec(line.substring(pos)) + pseudomatch = PseudoTokenRegex.exec(line.substring(pos)); if (pseudomatch) { // scan for tokens var start = pos; var end = start + pseudomatch[1].length; @@ -392,15 +398,15 @@ function _tokenize(readline, encoding, yield_, filename) { var initial = line[start]; //console.log("token:",token, "initial:",initial, start, end); if (contains(numchars, initial) || // ordinary number - (initial == '.' && token != '.' && token != '...')) { + (initial == "." && token != "." && token != "...")) { yield_(new TokenInfo(tokens.T_NUMBER, token, spos, epos, line)); - } else if (contains('\r\n', initial)) { + } else if (contains("\r\n", initial)) { if (parenlev > 0) { yield_(new TokenInfo(tokens.T_NL, token, spos, epos, line)); } else { yield_(new TokenInfo(tokens.T_NEWLINE, token, spos, epos, line)); } - } else if (initial == '#') { + } else if (initial == "#") { //assert not token.endswith("\n") yield_(new TokenInfo(tokens.T_COMMENT, token, spos, epos, line)); } else if (contains(triple_quoted, token)) { @@ -416,20 +422,20 @@ function _tokenize(readline, encoding, yield_, filename) { contline = line; break; } - // Check up to the first 3 chars of the token to see if - // they're in the single_quoted set. If so, they start - // a string. - // We're using the first 3, because we're looking for - // "rb'" (for example) at the start of the token. If - // we switch to longer prefixes, this needs to be - // adjusted. - // Note that initial == token[:1]. - // Also note that single quote checking must come after - // triple quote checking (above). + // Check up to the first 3 chars of the token to see if + // they're in the single_quoted set. If so, they start + // a string. + // We're using the first 3, because we're looking for + // "rb'" (for example) at the start of the token. If + // we switch to longer prefixes, this needs to be + // adjusted. + // Note that initial == token[:1]. + // Also note that single quote checking must come after + // triple quote checking (above). } else if (contains(single_quoted, initial) || - contains(single_quoted, token.substring(0, 2)) || - contains(single_quoted, token.substring(0, 3))) { - if (token[token.length - 1] == '\n') { // continued string + contains(single_quoted, token.substring(0, 2)) || + contains(single_quoted, token.substring(0, 3))) { + if (token[token.length - 1] == "\n") { // continued string strstart = [lnum, start]; // Again, using the first 3 chars of the // token. This is looking for the matching end @@ -438,8 +444,8 @@ function _tokenize(readline, encoding, yield_, filename) { // endpats["'"] or endpats['"'], by trying to // skip string prefix characters, if any. endprog = endpats[initial] || - endpats[token[1]] || - endpats[token[2]]; + endpats[token[1]] || + endpats[token[2]]; contstr = line.substring(start); needcont = 1; contline = line; @@ -450,33 +456,33 @@ function _tokenize(readline, encoding, yield_, filename) { } else if (isidentifier(initial)) { // ordinary name yield_(new TokenInfo(tokens.T_NAME, token, spos, epos, line)); - } else if (initial == '\\') { // continued stmt - continued = 1 + } else if (initial == "\\") { // continued stmt + continued = 1; } else { - if (contains('([{', initial)) { - parenlev += 1 - } else if (contains(')]}', initial)) { - parenlev -= 1 + if (contains("([{", initial)) { + parenlev += 1; + } else if (contains(")]}", initial)) { + parenlev -= 1; } yield_(new TokenInfo(tokens.T_OP, token, spos, epos, line)); } } else { yield_(new TokenInfo(tokens.T_ERRORTOKEN, line[pos], - [lnum, pos], [lnum, pos+1], line)); + [lnum, pos], [lnum, pos + 1], line)); pos += 1; } } } // Add an implicit NEWLINE if the input doesn't end in one - if (last_line && !contains('\r\n', last_line[last_line.length - 1])) { - yield_(new TokenInfo(tokens.T_NEWLINE, '', [lnum - 1, last_line.length], [lnum - 1, last_line.length + 1], '')); + if (last_line && !contains("\r\n", last_line[last_line.length - 1])) { + yield_(new TokenInfo(tokens.T_NEWLINE, "", [lnum - 1, last_line.length], [lnum - 1, last_line.length + 1], "")); } for (var i in indents.slice(1)) { // pop remaining indent levels - yield_(new TokenInfo(tokens.T_DEDENT, '', [lnum, 0], [lnum, 0], '')); + yield_(new TokenInfo(tokens.T_DEDENT, "", [lnum, 0], [lnum, 0], "")); } - yield_(new TokenInfo(tokens.T_ENDMARKER, '', [lnum, 0], [lnum, 0], '')); + yield_(new TokenInfo(tokens.T_ENDMARKER, "", [lnum, 0], [lnum, 0], "")); } Sk._tokenize = _tokenize; diff --git a/src/tuple.js b/src/tuple.js index 89d3178db6..b41a62170f 100644 --- a/src/tuple.js +++ b/src/tuple.js @@ -1,298 +1,255 @@ /** * @constructor - * @param {Array.|Object} L + * @param {Array} L + * @extends {Sk.builtin.object} */ -Sk.builtin.tuple = function (L) { - var it, i; - if (!(this instanceof Sk.builtin.tuple)) { - Sk.builtin.pyCheckArgsLen("tuple", arguments.length, 0, 1); - return new Sk.builtin.tuple(L); - } - - - if (L === undefined) { - L = []; - } - - if (Object.prototype.toString.apply(L) === "[object Array]") { +Sk.builtin.tuple = Sk.abstr.buildNativeClass("tuple", { + constructor: function tuple(L) { + if (L === undefined) { + L = []; + } + Sk.asserts.assert(Array.isArray(L) && this instanceof Sk.builtin.tuple, "bad call to tuple, use 'new' with an Array"); this.v = L; - } else { - if (Sk.builtin.checkIterable(L)) { - this.v = []; - for (it = Sk.abstr.iter(L), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - this.v.push(i); + }, + slots: /**@lends {Sk.builtin.tuple.prototype}*/{ + tp$getattr: Sk.generic.getAttr, + tp$as_sequence_or_mapping: true, + tp$doc: + "Built-in immutable sequence.\n\nIf no argument is given, the constructor returns an empty tuple.\nIf iterable is specified the tuple is initialized from iterable's items.\n\nIf the argument is a tuple, the return value is the same object.", + $r: function () { + const bits = []; + for (let i = 0; i < this.v.length; ++i) { + bits[i] = Sk.misceval.objectRepr(this.v[i]); } - } else { - throw new Sk.builtin.TypeError("expecting Array or iterable"); - } - } - - this.__class__ = Sk.builtin.tuple; - - this["v"] = this.v; - return this; -}; - -Sk.abstr.setUpInheritance("tuple", Sk.builtin.tuple, Sk.builtin.seqtype); - -Sk.builtin.tuple.prototype["$r"] = function () { - var ret; - var i; - var bits; - if (this.v.length === 0) { - return new Sk.builtin.str("()"); - } - bits = []; - for (i = 0; i < this.v.length; ++i) { - bits[i] = Sk.misceval.objectRepr(this.v[i]).v; - } - ret = bits.join(", "); - if (this.v.length === 1) { - ret += ","; - } - return new Sk.builtin.str("(" + ret + ")"); -}; - -Sk.builtin.tuple.prototype.mp$subscript = function (index) { - var ret; - var i; - if (Sk.misceval.isIndex(index)) { - i = Sk.misceval.asIndex(index); - if (i !== undefined) { - if (i < 0) { - i = this.v.length + i; + let ret = bits.join(", "); + if (this.v.length === 1) { + ret += ","; } - if (i < 0 || i >= this.v.length) { - throw new Sk.builtin.IndexError("tuple index out of range"); + return new Sk.builtin.str("(" + ret + ")"); + }, + /** + * @param {Array} args + * @param {Array=} kwargs + */ + tp$new: function (args, kwargs) { + // this = Sk.builtin.prototype or a prototype that inherits from Sk.builtin.tuple.prototype + if (this !== Sk.builtin.tuple.prototype) { + return this.$subtype_new(args, kwargs); + } + Sk.abstr.checkNoKwargs("tuple", kwargs); + Sk.abstr.checkArgsLen("tuple", args, 0, 1); + const arg = args[0]; + if (arg === undefined) { + return new Sk.builtin.tuple([]); + } + if (arg.ob$type === Sk.builtin.tuple) { + return arg; + } + // make tuples suspendible + let L = Sk.misceval.arrayFromIterable(arg, true); + return Sk.misceval.chain(L, (l) => new Sk.builtin.tuple(l)); + }, + tp$hash: function () { + // the numbers and order are taken from Cpython + let y, + x = 0x345678, + mult = 1000003; + const len = this.v.length; + for (let i = 0; i < len; ++i) { + y = Sk.builtin.hash(this.v[i]).v; + if (y === -1) { + return new Sk.builtin.int_(-1); + } + x = (x ^ y) * mult; + mult += 82520 + len + len; + } + x += 97531; + if (x === -1) { + x = -2; + } + return new Sk.builtin.int_(x | 0); + }, + tp$iter: function () { + return new Sk.builtin.tuple_iter_(this); + }, + + // sequence and mapping slots + mp$subscript: function (index) { + let i; + if (Sk.misceval.isIndex(index)) { + i = Sk.misceval.asIndex(index); + if (typeof i !== "number") { + throw new Sk.builtin.IndexError("cannot fit '" + Sk.abstr.typeName(index) + "' into an index-sized integer"); + } + if (i !== undefined) { + if (i < 0) { + i = this.v.length + i; + } + if (i < 0 || i >= this.v.length) { + throw new Sk.builtin.IndexError("tuple index out of range"); + } + return this.v[i]; + } + } else if (index instanceof Sk.builtin.slice) { + const ret = []; + const lst = this.v; + index.sssiter$(lst.length, (i) => { + ret.push(lst[i]); + }); + return new Sk.builtin.tuple(ret); } - return this.v[i]; - } - } else if (index instanceof Sk.builtin.slice) { - ret = []; - index.sssiter$(this, function (i, wrt) { - ret.push(wrt.v[i]); - }); - return new Sk.builtin.tuple(ret); - } - - throw new Sk.builtin.TypeError("tuple indices must be integers, not " + Sk.abstr.typeName(index)); -}; - -// todo; the numbers and order are taken from python, but the answer's -// obviously not the same because there's no int wrapping. shouldn't matter, -// but would be nice to make the hash() values the same if it's not too -// expensive to simplify tests. -Sk.builtin.tuple.prototype.tp$hash = function () { - var y; - var i; - var mult = 1000003; - var x = 0x345678; - var len = this.v.length; - for (i = 0; i < len; ++i) { - y = Sk.builtin.hash(this.v[i]).v; - if (y === -1) { - return new Sk.builtin.int_(-1); - } - x = (x ^ y) * mult; - mult += 82520 + len + len; - } - x += 97531; - if (x === -1) { - x = -2; - } - return new Sk.builtin.int_(x | 0); -}; - -Sk.builtin.tuple.prototype.sq$repeat = function (n) { - var j; - var i; - var ret; - - n = Sk.misceval.asIndex(n); - ret = []; - for (i = 0; i < n; ++i) { - for (j = 0; j < this.v.length; ++j) { - ret.push(this.v[j]); - } - } - return new Sk.builtin.tuple(ret); -}; -Sk.builtin.tuple.prototype.nb$multiply = Sk.builtin.tuple.prototype.sq$repeat; -Sk.builtin.tuple.prototype.nb$inplace_multiply = Sk.builtin.tuple.prototype.sq$repeat; - -Sk.builtin.tuple.prototype.__iter__ = new Sk.builtin.func(function (self) { - Sk.builtin.pyCheckArgsLen("__iter__", arguments.length, 1, 1); - return new Sk.builtin.tuple_iter_(self); -}); - -Sk.builtin.tuple.prototype.tp$iter = function () { - return new Sk.builtin.tuple_iter_(this); -}; - -Sk.builtin.tuple.prototype.tp$richcompare = function (w, op) { - //print(" tup rc", JSON.stringify(this.v), JSON.stringify(w), op); - // w not a tuple - var k; - var i; - var wl; - var vl; - var v; - if (!w.__class__ || - !Sk.misceval.isTrue(Sk.builtin.isinstance(w, Sk.builtin.tuple))) { - // shortcuts for eq/not - if (op === "Eq") { + throw new Sk.builtin.TypeError("tuple indices must be integers, not " + Sk.abstr.typeName(index)); + }, + sq$length: function () { + return this.v.length; + }, + sq$repeat: function (n) { + n = Sk.misceval.asIndex(n); + if (typeof n !== "number") { + throw new Sk.builtin.OverflowError("cannot fit '" + Sk.abstr.typeName(n) + "' into an index-sized integer"); + } + const ret = []; + for (let i = 0; i < n; ++i) { + for (let j = 0; j < this.v.length; ++j) { + ret.push(this.v[j]); + } + } + return new Sk.builtin.tuple(ret); + }, + sq$concat: function (other) { + if (other.ob$type != Sk.builtin.tuple) { + throw new Sk.builtin.TypeError("can only concatenate tuple (not '" + Sk.abstr.typeName(other) + "') to tuple"); + } + return new Sk.builtin.tuple(this.v.concat(other.v)); + }, + sq$contains: function (ob) { + for (let it = this.tp$iter(), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { + if (Sk.misceval.richCompareBool(i, ob, "Eq")) { + return true; + } + } return false; - } - if (op === "NotEq") { - return true; - } - - // todo; other types should have an arbitrary order - return false; - } - - v = this.v; - w = w.v; - vl = v.length; - wl = w.length; - - for (i = 0; i < vl && i < wl; ++i) { - k = Sk.misceval.richCompareBool(v[i], w[i], "Eq"); - if (!k) { - break; - } - } - - if (i >= vl || i >= wl) { - // no more items to compare, compare sizes - switch (op) { - case "Lt": - return vl < wl; - case "LtE": - return vl <= wl; - case "Eq": - return vl === wl; - case "NotEq": - return vl !== wl; - case "Gt": - return vl > wl; - case "GtE": - return vl >= wl; - default: - Sk.asserts.fail(); - } - } - - // we have an item that's different - - // shortcuts for eq/not - if (op === "Eq") { - return false; - } - if (op === "NotEq") { - return true; - } - - // or, compare the differing element using the proper operator - //print(" tup rcb end", i, v[i] instanceof Sk.builtin.str, JSON.stringify(v[i]), w[i] instanceof Sk.builtin.str, JSON.stringify(w[i]), op); - return Sk.misceval.richCompareBool(v[i], w[i], op); -}; - -Sk.builtin.tuple.prototype.sq$concat = function (other) { - var msg; - if (other.__class__ != Sk.builtin.tuple) { - msg = "can only concatenate tuple (not \""; - msg += Sk.abstr.typeName(other) + "\") to tuple"; - throw new Sk.builtin.TypeError(msg); - } - - return new Sk.builtin.tuple(this.v.concat(other.v)); -}; - -Sk.builtin.tuple.prototype.sq$contains = function (ob) { - var it, i; - - for (it = this.tp$iter(), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { - if (Sk.misceval.richCompareBool(i, ob, "Eq")) { - return true; - } - } - - return false; -}; - -Sk.builtin.tuple.prototype.nb$add = Sk.builtin.tuple.prototype.sq$concat; -Sk.builtin.tuple.prototype.nb$inplace_add = Sk.builtin.tuple.prototype.sq$concat; + }, + + // richcompare + tp$richcompare: function (w, op) { + // w not a tuple + if (!(w instanceof Sk.builtin.tuple)) { + // shortcuts for eq/not + if (op === "Eq") { + return false; + } + if (op === "NotEq") { + return true; + } + + if (Sk.__future__.python3) { + return Sk.builtin.NotImplemented.NotImplemented$; + } + // todo; other types should have an arbitrary order + return false; + } -Sk.builtin.tuple.prototype.sq$length = function () { - return this.v.length; -}; + w = w.v; + const v = this.v; + const vl = v.length; + const wl = w.length; + let i, k; + for (i = 0; i < vl && i < wl; ++i) { + k = Sk.misceval.richCompareBool(v[i], w[i], "Eq"); + if (!k) { + break; + } + } + if (i >= vl || i >= wl) { + // no more items to compare, compare sizes + switch (op) { + case "Lt": + return vl < wl; + case "LtE": + return vl <= wl; + case "Eq": + return vl === wl; + case "NotEq": + return vl !== wl; + case "Gt": + return vl > wl; + case "GtE": + return vl >= wl; + default: + Sk.asserts.fail(); + } + } -Sk.builtin.tuple.prototype["index"] = new Sk.builtin.func(function (self, item) { - var i; - var len = self.v.length; - var obj = self.v; - for (i = 0; i < len; ++i) { - if (Sk.misceval.richCompareBool(obj[i], item, "Eq")) { - return new Sk.builtin.int_(i); - } - } - throw new Sk.builtin.ValueError("tuple.index(x): x not in tuple"); -}); + // we have an item that's different + // shortcuts for eq/not + if (op === "Eq") { + return false; + } + if (op === "NotEq") { + return true; + } -Sk.builtin.tuple.prototype["count"] = new Sk.builtin.func(function (self, item) { - var i; - var len = self.v.length; - var obj = self.v; - var count = 0; - for (i = 0; i < len; ++i) { - if (Sk.misceval.richCompareBool(obj[i], item, "Eq")) { - count += 1; - } - } - return new Sk.builtin.int_(count); + // or, compare the differing element using the proper operator + return Sk.misceval.richCompareBool(v[i], w[i], op); + }, + }, + proto: /**@lends {Sk.builtin.tuple.prototype}*/{ + $subtype_new: function (args, kwargs) { + const instance = new this.constructor(); + // pass the args but ignore the kwargs for subtyping - these might be handled by the subtypes init method + const tuple = Sk.builtin.tuple.prototype.tp$new(args); + instance.v = tuple.v; + return instance; + }, + sk$asarray: function () { + return this.v.slice(0); + }, + }, + methods: /**@lends {Sk.builtin.tuple.prototype}*/{ + __getnewargs__: { + $meth: function () { + return new Sk.builtin.tuple(this.v.slice(0)); + }, + $flags: {NoArgs: true}, + $textsig: "($self, /)", + $doc: null, + }, + index: /**@lends {Sk.builtin.type.prototype}*/{ + $meth: function (item, start, stop) { + // TODO: currently doesn't support start and stop + const len = this.v.length; + const obj = this.v; + for (let i = 0; i < len; ++i) { + if (Sk.misceval.richCompareBool(obj[i], item, "Eq")) { + return new Sk.builtin.int_(i); + } + } + throw new Sk.builtin.ValueError("tuple.index(x): x not in tuple"); + }, + $flags: {MinArgs: 1, MaxArgs: 3}, + $textsig: "($self, value, start=0, stop=sys.maxsize, /)", + $doc: "Return first index of value.\n\nRaises ValueError if the value is not present.", + }, + count: { + $meth: function (item) { + const len = this.v.length; + const obj = this.v; + let count = 0; + for (let i = 0; i < len; ++i) { + if (Sk.misceval.richCompareBool(obj[i], item, "Eq")) { + count += 1; + } + } + return new Sk.builtin.int_(count); + }, + $flags: {OneArg: true}, + $textsig: "($self, value, /)", + $doc: "Return number of occurrences of value.", + }, + }, }); Sk.exportSymbol("Sk.builtin.tuple", Sk.builtin.tuple); - -/** - * @constructor - * @param {Object} obj - */ -Sk.builtin.tuple_iter_ = function (obj) { - if (!(this instanceof Sk.builtin.tuple_iter_)) { - return new Sk.builtin.tuple_iter_(obj); - } - this.$index = 0; - this.$obj = obj.v.slice(); - this.sq$length = this.$obj.length; - this.tp$iter = this; - this.tp$iternext = function () { - if (this.$index >= this.sq$length) { - return undefined; - } - return this.$obj[this.$index++]; - }; - this.$r = function () { - return new Sk.builtin.str("tupleiterator"); - }; - return this; -}; - -Sk.abstr.setUpInheritance("tupleiterator", Sk.builtin.tuple_iter_, Sk.builtin.object); - -Sk.builtin.tuple_iter_.prototype.__class__ = Sk.builtin.tuple_iter_; - -Sk.builtin.tuple_iter_.prototype.__iter__ = new Sk.builtin.func(function (self) { - return self; -}); - -Sk.builtin.tuple_iter_.prototype.next$ = function (self) { - var ret = self.tp$iternext(); - if (ret === undefined) { - throw new Sk.builtin.StopIteration(); - } - return ret; -}; diff --git a/src/type.js b/src/type.js index 527117325e..6a41344fcd 100644 --- a/src/type.js +++ b/src/type.js @@ -1,615 +1,310 @@ -if(Sk.builtin === undefined) { - Sk.builtin = {}; -} - /** - * Maps Python dunder names to the Skulpt Javascript function names that - * implement them. - * - * Note: __add__, __mul__, and __rmul__ can be used for either numeric or - * sequence types. Here, they default to the numeric versions (i.e. nb$add, - * nb$multiply, and nb$reflected_multiply). This works because Sk.abstr.binary_op_ - * checks for the numeric shortcuts and not the sequence shortcuts when computing - * a binary operation. + * @namespace Sk.builtin * - * Because many of these functions are used in contexts in which Skulpt does not - * [yet] handle suspensions, the assumption is that they must not suspend. However, - * some of these built-in functions are acquiring 'canSuspend' arguments to signal - * where this is not the case. These need to be spliced out of the argument list before - * it is passed to python. Array values in this map contain [dunderName, argumentIdx], - * where argumentIdx specifies the index of the 'canSuspend' boolean argument. - * - * @type {Object} + * @description + * All the builtin types as well as useful functions */ -Sk.dunderToSkulpt = { - "__eq__": "ob$eq", - "__ne__": "ob$ne", - "__lt__": "ob$lt", - "__le__": "ob$le", - "__gt__": "ob$gt", - "__ge__": "ob$ge", - "__hash__": "tp$hash", - "__abs__": "nb$abs", - "__neg__": "nb$negative", - "__pos__": "nb$positive", - "__int__": "nb$int_", - "__long__": "nb$lng", - "__float__": "nb$float_", - "__add__": "nb$add", - "__radd__": "nb$reflected_add", - "__sub__": "nb$subtract", - "__rsub__": "nb$reflected_subtract", - "__mul__": "nb$multiply", - "__rmul__": "nb$reflected_multiply", - "__div__": "nb$divide", - "__rdiv__": "nb$reflected_divide", - "__floordiv__": "nb$floor_divide", - "__rfloordiv__": "nb$reflected_floor_divide", - "__mod__": "nb$remainder", - "__rmod__": "nb$reflected_remainder", - "__divmod__": "nb$divmod", - "__rdivmod__": "nb$reflected_divmod", - "__pow__": "nb$power", - "__rpow__": "nb$reflected_power", - "__contains__": "sq$contains", - "__len__": ["sq$length", 1], - "__get__": ["tp$descr_get", 3], - "__set__": ["tp$descr_set", 3] -}; - -Sk.setupDunderMethods = function (py3) { - if (py3) { - Sk.dunderToSkulpt["__matmul__"] = "tp$matmul"; - Sk.dunderToSkulpt["__rmatmul__"] = "tp$reflected_matmul"; - } else { - if (Sk.dunderToSkulpt["__matmul__"]) { - delete Sk.dunderToSkulpt["__matmul__"]; - } - if (Sk.dunderToSkulpt["__rmatmul__"]) { - delete Sk.dunderToSkulpt["__rmatmul__"]; - } - } -}; +if (Sk.builtin === undefined) { + Sk.builtin = {}; +} -Sk.exportSymbol("Sk.setupDunderMethods", Sk.setupDunderMethods); /** - * * @constructor + * @extends {Sk.builtin.object} + * @description + * this should never be called as a constructor + * instead use {@link Sk.abstr.buildNativeClass} or + * {@link Sk.misceval.buildClass} * - * @param {*} name name or object to get type of, if only one arg - * - * @param {Sk.builtin.tuple=} bases - * - * @param {Object=} dict - * - * - * This type represents the type of `type'. *Calling* an instance of - * this builtin type named "type" creates class objects. The resulting - * class objects will have various tp$xyz attributes on them that allow - * for the various operations on that object. - * - * calling the type or calling an instance of the type? or both? */ -Sk.builtin.type = function (name, bases, dict) { - var mro; - var obj; - var klass; - var v; - if (bases === undefined && dict === undefined) { - // 1 arg version of type() - // the argument is an object, not a name and returns a type object - obj = name; - return obj.ob$type; - } else { +Sk.builtin.type = function type() { + Sk.asserts.assert(false, "calling new Sk.builtin.type is not safe"); +}; - // argument dict must be of type dict - if(dict.tp$name !== "dict") { - throw new Sk.builtin.TypeError("type() argument 3 must be dict, not " + Sk.abstr.typeName(dict)); - } +/** @typedef {Sk.builtin.type|Function} */ var typeObject; - // checks if name must be string - if(!Sk.builtin.checkString(name)) { - throw new Sk.builtin.TypeError("type() argument 1 must be str, not " + Sk.abstr.typeName(name)); - } +Sk.builtin.type.prototype.tp$doc = "type(object_or_name, bases, dict)\ntype(object) -> the object's type\ntype(name, bases, dict) -> a new type"; - // argument bases must be of type tuple - if(bases.tp$name !== "tuple") { - throw new Sk.builtin.TypeError("type() argument 2 must be tuple, not " + Sk.abstr.typeName(bases)); +/** + * @this {typeObject | Sk.builtin.type} + */ +Sk.builtin.type.prototype.tp$call = function (args, kwargs) { + if (this === Sk.builtin.type) { + // check the args are 1 - only interested in the 1 argument form if + // if the nargs and nkeywords != 1 or 3 and zero raise an error + if (args.length === 1 && (kwargs === undefined || !kwargs.length)) { + return args[0].ob$type; + } else if (args.length !== 3) { + throw new Sk.builtin.TypeError("type() takes 1 or 3 arguments"); } - - // type building version of type - - // dict is the result of running the classes code object - // (basically the dict of functions). those become the prototype - // object of the class). - - /** - * The constructor is a stub, that gets called from object.__new__ - * @constructor - */ - klass = function (args, kws) { - var args_copy; - - // Call up through the chain in case there's a built-in object - // whose constructor we need to initialise - if (klass.prototype.tp$base !== undefined) { - if (klass.prototype.tp$base.sk$klass) { - klass.prototype.tp$base.call(this, args, kws); - } else { - // Call super constructor if subclass of a builtin - args_copy = args.slice(); - args_copy.unshift(klass, this); - Sk.abstr.superConstructor.apply(undefined, args_copy); - } - } - - this["$d"] = new Sk.builtin.dict([]); - this["$d"].mp$ass_subscript(new Sk.builtin.str("__dict__"), this["$d"]); - }; - - var _name = Sk.ffi.remapToJs(name); // unwrap name string to js for latter use - - var inheritsBuiltin = false; - - // Invoking the class object calls __new__() to generate a new instance, - // then __init__() to initialise it - klass.tp$call = function(args, kws) { - var newf = Sk.builtin.type.typeLookup(klass, Sk.builtin.str.$new), newargs; - var self; - - args = args || []; - kws = kws || []; - - if (newf === undefined || newf === Sk.builtin.object.prototype["__new__"]) { - // No override -> just call the constructor - self = new klass(args, kws); - newf = undefined; - } else { - newargs = args.slice(); - newargs.unshift(klass); - self = Sk.misceval.applyOrSuspend(newf, undefined, undefined, kws, newargs); - } - - return Sk.misceval.chain(self, function(s) { - var init = Sk.builtin.type.typeLookup(s.ob$type, Sk.builtin.str.$init); - - self = s; // in case __new__ suspended - - if (init !== undefined) { - args.unshift(self); - return Sk.misceval.applyOrSuspend(init, undefined, undefined, kws, args); - } else if (newf === undefined && (args.length !== 0 || kws.length !== 0) && !inheritsBuiltin) { - // We complain about spurious constructor arguments if neither __new__ - // nor __init__ were overridden - throw new Sk.builtin.TypeError("__init__() got unexpected argument(s)"); - } - }, function(r) { - if (r !== Sk.builtin.none.none$ && r !== undefined) { - throw new Sk.builtin.TypeError("__init__() should return None, not " + Sk.abstr.typeName(r)); - } else { - return self; + } + let obj, + self = this; + + obj = this.prototype.tp$new(args, kwargs); + + if (obj.$isSuspension) { + return Sk.misceval.chain( + obj, + function (o) { + obj = o; + if (!obj.ob$type.$isSubType(self)) { + // don't initialize an obj if it's type is not a subtype of this! + // typically obj$obtype === self so this check is fast + return; } - }); - }; - - if (bases.v.length === 0 && Sk.__future__.inherit_from_object) { - // new style class, inherits from object by default - bases.v.push(Sk.builtin.object); - Sk.abstr.setUpInheritance(_name, klass, Sk.builtin.object); - } - - var parent, it, firstAncestor, builtin_bases = []; - // Set up inheritance from any builtins - for (it = bases.tp$iter(), parent = it.tp$iternext(); parent !== undefined; parent = it.tp$iternext()) { - if (firstAncestor === undefined) { - firstAncestor = parent; - } - - while (parent.sk$klass && parent.prototype.tp$base) { - parent = parent.prototype.tp$base; - } - - if (!parent.sk$klass && builtin_bases.indexOf(parent) < 0) { - builtin_bases.push(parent); - inheritsBuiltin = true; - } - } - - if (builtin_bases.length > 1) { - throw new Sk.builtin.TypeError("Multiple inheritance with more than one builtin type is unsupported"); + return obj.tp$init(args, kwargs); + }, + () => obj + ); + } else if (!obj.ob$type.$isSubType(self)) { + return obj; + } else { + const res = obj.tp$init(args, kwargs); + Sk.asserts.assert(res !== undefined, "should return None in init method for " + obj.tp$name); + if (res.$isSuspension) { + return Sk.misceval.chain(res, () => obj); } + return obj; + } +}; - // Javascript does not support multiple inheritance, so only the first - // base (if any) will directly inherit in Javascript - if (firstAncestor !== undefined) { - Sk.abstr.inherits(klass, firstAncestor); - - if (firstAncestor.prototype instanceof Sk.builtin.object || firstAncestor === Sk.builtin.object) { - klass.prototype.tp$base = firstAncestor; - } +Sk.builtin.type.prototype.tp$new = function (args, kwargs) { + // currently skulpt does not support metatypes... + // metatype.prototype = this + if (args.length !== 3) { + if (args.length === 1 && (kwargs === undefined || !kwargs.length)) { + return args[0].ob$type; } + throw new Sk.builtin.TypeError("type() takes 1 or 3 arguments"); + } - klass.prototype.tp$name = _name; - klass.prototype.ob$type = Sk.builtin.type.makeIntoTypeObj(_name, klass); + let $name, bases, dict; + $name = args[0]; + bases = args[1]; + dict = args[2]; + // first check that we only have 3 args and they're of the correct type + // argument dict must be of type dict + if (dict.tp$name !== "dict") { + throw new Sk.builtin.TypeError("type() argument 3 must be dict, not " + Sk.abstr.typeName(dict)); + } + // checks if name must be string + if (!Sk.builtin.checkString($name)) { + throw new Sk.builtin.TypeError("type() argument 1 must be str, not " + Sk.abstr.typeName($name)); + } + $name = $name.$jsstr(); + // argument bases must be of type tuple + if (bases.tp$name !== "tuple") { + throw new Sk.builtin.TypeError("type() argument 2 must be tuple, not " + Sk.abstr.typeName(bases)); + } - // set __module__ if not present (required by direct type(name, bases, dict) calls) - var module_lk = new Sk.builtin.str("__module__"); - if(dict.mp$lookup(module_lk) === undefined) { - dict.mp$ass_subscript(module_lk, Sk.globals["__name__"]); - } + /** + * @type {!typeObject} + */ + const klass = function () { + // klass is essentially a function that gives its instances a dict + // if we support slots then we might need to have two versions of this + this.$d = new Sk.builtin.dict(); + }; - // copy properties into our klass object - // uses python iter methods - var k; - for (it = dict.tp$iter(), k = it.tp$iternext(); k !== undefined; k = it.tp$iternext()) { - v = dict.mp$subscript(k); - if (v === undefined) { - v = null; - } - klass.prototype[k.v] = v; - klass[k.v] = v; + // this function tries to match Cpython - the best base is not always bases[0] + // we require a best bases for checks in __new__ as well as future support for slots + const best_base = Sk.builtin.type.$best_base(bases.v); + + // get the metaclass from kwargs + // todo this is not really the right way to do it... + let metaclass; + if (kwargs) { + const meta_idx = kwargs.indexOf("metaclass"); + if (meta_idx >= 0) { + metaclass = kwargs[meta_idx + 1]; + kwargs = kwargs.splice(meta_idx, 1); } + } - klass.prototype.__class__ = klass; - klass.prototype.__name__ = name; - klass.sk$klass = true; - klass.prototype["$r"] = function () { - var cname; - var mod; - var reprf = this.__class__.tp$getattr(Sk.builtin.str.$repr); - if (reprf !== undefined && reprf.im_func !== Sk.builtin.object.prototype["__repr__"]) { - return Sk.misceval.apply(reprf, undefined, undefined, undefined, [this]); - } - - if ((klass.prototype.tp$base !== undefined) && - (klass.prototype.tp$base !== Sk.builtin.object) && - (klass.prototype.tp$base.prototype["$r"] !== undefined)) { - // If subclass of a builtin which is not object, use that class' repr - return klass.prototype.tp$base.prototype["$r"].call(this); - } else { - // Else, use default repr for a user-defined class instance - mod = dict.mp$subscript(module_lk); // lookup __module__ - cname = ""; - if (mod) { - cname = mod.v + "."; - } - return new Sk.builtin.str("<" + cname + _name + " object>"); - } - }; - - klass.prototype.tp$setattr = function(pyName, data, canSuspend) { - var r, setf = Sk.builtin.object.prototype.GenericGetAttr.call(this, Sk.builtin.str.$setattr); - if (setf !== undefined) { - var f = /** @type {?} */ (setf); - r = Sk.misceval.callsimOrSuspendArray(f, [pyName, data]); - return canSuspend ? r : Sk.misceval.retryOptionalSuspensionOrThrow(r); - } - - return Sk.builtin.object.prototype.GenericSetAttr.call(this, pyName, data, canSuspend); - }; + Sk.abstr.setUpInheritance($name, klass, best_base, metaclass); - klass.prototype.tp$getattr = function(pyName, canSuspend) { - var r, descr, /** @type {(Object|undefined)} */ getf; - // Find __getattribute__ on this type if we can - descr = Sk.builtin.type.typeLookup(klass, Sk.builtin.str.$getattribute); + klass.prototype.tp$bases = bases.v; + klass.prototype.tp$mro = klass.$buildMRO(); - if (descr !== undefined && descr !== null && descr.tp$descr_get !== undefined) { - getf = descr.tp$descr_get.call(descr, this, klass); - } + // some properties of klass objects and instances + klass.prototype.hp$type = true; + klass.sk$klass = true; - if (getf === undefined) { - getf = Sk.builtin.object.prototype.GenericPythonGetAttr.bind(null, this); - } + // set some defaults which can be overridden by the dict object + klass.prototype.__module__ = Sk.globals["__name__"]; + klass.prototype.__doc__ = Sk.builtin.none.none$; - // Convert AttributeErrors back into 'undefined' returns to match the tp$getattr - // convention - r = Sk.misceval.tryCatch(function() { - return Sk.misceval.callsimOrSuspendArray(/** @type {Object} */ (getf), [pyName]); - }, function (e) { - if (e instanceof Sk.builtin.AttributeError) { - return undefined; - } else { - throw e; - } - }); + // set __dict__ if not already on the prototype + /**@todo __slots__ */ + if (klass.$typeLookup(Sk.builtin.str.$dict) === undefined) { + klass.prototype.__dict__ = new Sk.builtin.getset_descriptor(klass, Sk.generic.getSetDict); + } - return canSuspend ? r : Sk.misceval.retryOptionalSuspensionOrThrow(r); - }; + // copy properties from dict into klass.prototype + for (let it = dict.tp$iter(), k = it.tp$iternext(); k !== undefined; k = it.tp$iternext()) { + const v = dict.mp$subscript(k); + klass.prototype[k.v] = v; + } + klass.$allocateSlots(); - klass.prototype.tp$str = function () { - var strf = this.__class__.tp$getattr(Sk.builtin.str.$str); - if (strf !== undefined && strf.im_func !== Sk.builtin.object.prototype["__str__"]) { - return Sk.misceval.apply(strf, undefined, undefined, undefined, [this]); - } - if ((klass.prototype.tp$base !== undefined) && - (klass.prototype.tp$base !== Sk.builtin.object) && - (klass.prototype.tp$base.prototype.tp$str !== undefined)) { - // If subclass of a builtin which is not object, use that class' repr - return klass.prototype.tp$base.prototype.tp$str.call(this); - } - return this["$r"](); + if (klass.prototype.sk$prototypical) { + klass.$typeLookup = function (pyName) { + var jsName = pyName.$mangled; + return this.prototype[jsName]; }; - klass.prototype.tp$length = function (canSuspend) { - var r = Sk.misceval.chain(Sk.abstr.gattr(this, Sk.builtin.str.$len, canSuspend), function(lenf) { - return Sk.misceval.applyOrSuspend(lenf, undefined, undefined, undefined, []); - }); - return canSuspend ? r : Sk.misceval.retryOptionalSuspensionOrThrow(r); - }; - klass.prototype.tp$call = function (args, kw) { - var that = this; - return Sk.misceval.chain(this.__class__.tp$getattr(Sk.builtin.str.$call, true), function(callf) { - if (callf === undefined) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(this) + "' object is not callable"); - } - return Sk.misceval.applyOrSuspend(callf, undefined, undefined, kw, [that].concat(args)); - }); - }; - klass.prototype.tp$iter = function () { - var iterf = this.tp$getattr(Sk.builtin.str.$iter); - if (iterf === undefined) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(this) + "' object is not iterable"); - } - return Sk.misceval.callsimArray(iterf); - }; - klass.prototype.tp$iternext = function (canSuspend) { - var self = this; - var next; - - if (Sk.__future__.dunder_next) { - next = Sk.builtin.str.$next3; - } else { - next = Sk.builtin.str.$next2; - } - var r = Sk.misceval.chain(self.tp$getattr(next, canSuspend), function(iternextf) { - if (iternextf === undefined) { - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(self) + "' object is not iterable"); + } else { + klass.$typeLookup = function (pyName) { + var jsName = pyName.$mangled; + const mro = this.prototype.tp$mro; + for (let i = 0; i < mro.length; ++i) { + const base_proto = mro[i].prototype; + if (base_proto.hasOwnProperty(jsName)) { + return base_proto[jsName]; } - - return Sk.misceval.tryCatch(function() { - return Sk.misceval.callsimOrSuspendArray(iternextf); - }, function(e) { - if (e instanceof Sk.builtin.StopIteration) { - return undefined; - } else { - throw e; - } - }); - }); - - return canSuspend ? r : Sk.misceval.retryOptionalSuspensionOrThrow(r); - }; - - klass.prototype.tp$getitem = function (key, canSuspend) { - var getf = this.__class__.tp$getattr(Sk.builtin.str.$getitem, canSuspend), r; - if (getf !== undefined) { - r = Sk.misceval.applyOrSuspend(getf, undefined, undefined, undefined, [this, key]); - return canSuspend ? r : Sk.misceval.retryOptionalSuspensionOrThrow(r); - } - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(this) + "' object does not support indexing"); - }; - klass.prototype.tp$setitem = function (key, value, canSuspend) { - var setf = this.__class__.tp$getattr(Sk.builtin.str.$setitem, canSuspend), r; - if (setf !== undefined) { - r = Sk.misceval.applyOrSuspend(setf, undefined, undefined, undefined, [this, key, value]); - return canSuspend ? r : Sk.misceval.retryOptionalSuspensionOrThrow(r); } - throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(this) + "' object does not support item assignment"); + return undefined; }; - - if (bases) { - //print("building mro for", name); - //for (var i = 0; i < bases.length; ++i) - //print("base[" + i + "]=" + bases[i].tp$name); - klass["$d"] = new Sk.builtin.dict([]); - klass["$d"].mp$ass_subscript(Sk.builtin.type.basesStr_, bases); - mro = Sk.builtin.type.buildMRO(klass); - klass["$d"].mp$ass_subscript(Sk.builtin.type.mroStr_, mro); - klass.tp$mro = mro; - //print("mro result", Sk.builtin.repr(mro).v); - } - - // fix for class attributes - klass.tp$setattr = Sk.builtin.type.prototype.tp$setattr; - - var shortcutDunder = function (skulpt_name, magic_name, magic_func, canSuspendIdx) { - klass.prototype[skulpt_name] = function () { - var canSuspend = false; - var len = arguments.length; - var args, i, j; - if ((canSuspendIdx !== null) && (canSuspendIdx <= len)) { - args = new Array(len); - } else { - args = new Array(len+1); - } - - args[0] = this; - j = 1; - for (i = 0; i < len; i++) { - if (i === (canSuspendIdx-1)) { - canSuspend = arguments[i]; - } else { - args[j] = arguments[i]; - j += 1; - } - } - - if (canSuspend) { - return Sk.misceval.callsimOrSuspendArray(magic_func, args); - } else { - return Sk.misceval.callsimArray(magic_func, args); - } - }; - }; - - // Register skulpt shortcuts to magic methods defined by this class. - // Dynamically deflined methods (eg those returned by __getattr__()) - // cannot be used by these magic functions; this is consistent with - // how CPython handles "new-style" classes: - // https://docs.python.org/2/reference/datamodel.html#special-method-lookup-for-old-style-classes - var dunder, skulpt_name, canSuspendIdx; - for (dunder in Sk.dunderToSkulpt) { - skulpt_name = Sk.dunderToSkulpt[dunder]; - if (typeof(skulpt_name) === "string") { - canSuspendIdx = null; - } else { - canSuspendIdx = skulpt_name[1]; - skulpt_name = skulpt_name[0]; - } - - if (klass[dunder]) { - // scope workaround - shortcutDunder(skulpt_name, dunder, klass[dunder], canSuspendIdx); - } - } - - return klass; } + return klass; }; /** - * + * @param {Array} args + * @param {Array=} kwargs */ -Sk.builtin.type.makeTypeObj = function (name, newedInstanceOfType) { - Sk.builtin.type.makeIntoTypeObj(name, newedInstanceOfType); - return newedInstanceOfType; -}; - -Sk.builtin.type.makeIntoTypeObj = function (name, t) { - Sk.asserts.assert(name !== undefined); - Sk.asserts.assert(t !== undefined); - t.ob$type = Sk.builtin.type; - t.__class__ = Sk.builtin.type; - t.tp$name = name; - t["$r"] = function () { - var ctype; - var mod = t.__module__; - var cname = ""; - if (mod) { - cname = mod.v + "."; - } - ctype = "class"; - if (!mod && !t.sk$klass && !Sk.__future__.class_repr) { - ctype = "type"; - } - return new Sk.builtin.str("<" + ctype + " '" + cname + t.tp$name + "'>"); - }; - t.tp$str = undefined; - t.tp$getattr = Sk.builtin.type.prototype.tp$getattr; - t.tp$setattr = Sk.builtin.object.prototype.GenericSetAttr; - t.tp$richcompare = Sk.builtin.type.prototype.tp$richcompare; - t.sk$type = true; - - return t; +Sk.builtin.type.prototype.tp$init = function (args, kwargs) { + if (args && args.length == 1 && kwargs && kwargs.length) { + throw new Sk.builtin.TypeError("type.__init__() takes no keyword arguments"); + } else if (args.length != 3 && args.length != 1) { + throw new Sk.builtin.TypeError("type.__init__() takes 1 or 3 arguments"); + } + // according to Cpython we just call the object init method here + return Sk.builtin.object.prototype.tp$init.call(this, []); }; -Sk.builtin.type.ob$type = Sk.builtin.type; -Sk.builtin.type.tp$name = "type"; -Sk.builtin.type.sk$type = true; -Sk.builtin.type["$r"] = function () { - if(Sk.__future__.class_repr) { - return new Sk.builtin.str(""); +Sk.builtin.type.prototype.$r = function () { + let mod = this.prototype.__module__; + let cname = ""; + let ctype = "class"; + if (mod && Sk.builtin.checkString(mod)) { + cname = mod.v + "."; } else { - return new Sk.builtin.str(""); + mod = null; + } + if (!mod && !this.sk$klass && !Sk.__future__.class_repr) { + ctype = "type"; } + return new Sk.builtin.str("<" + ctype + " '" + cname + this.prototype.tp$name + "'>"); }; -//Sk.builtin.type.prototype.tp$descr_get = function() { print("in type descr_get"); }; - -//Sk.builtin.type.prototype.tp$name = "type"; - -// basically the same as GenericGetAttr except looks in the proto instead Sk.builtin.type.prototype.tp$getattr = function (pyName, canSuspend) { - var res; - var tp = this; - var descr; - var f; - - if (this["$d"]) { - res = this["$d"].mp$lookup(pyName); - if (res !== undefined) { + // first check that the pyName is indeed a string + let res; + const metatype = this.ob$type; + // now check whether there is a descriptor on the metatype + const meta_attribute = metatype.$typeLookup(pyName); + + let meta_get; + if (meta_attribute !== undefined) { + meta_get = meta_attribute.tp$descr_get; + if (meta_get !== undefined && Sk.builtin.checkDataDescr(meta_attribute)) { + res = meta_get.call(meta_attribute, this, metatype, canSuspend); return res; } } + const attribute = this.$typeLookup(pyName); - descr = Sk.builtin.type.typeLookup(tp, pyName); - - //print("type.tpgetattr descr", descr, descr.tp$name, descr.func_code, name); - if (descr !== undefined && descr !== null && descr.ob$type !== undefined) { - f = descr.tp$descr_get; - // todo;if (f && descr.tp$descr_set) // is a data descriptor if it has a set - // return f.call(descr, this, this.ob$type); + if (attribute !== undefined) { + const local_get = attribute.tp$descr_get; + if (local_get !== undefined) { + // null indicates that the descriptor was on the target object itself or a buss + res = local_get.call(attribute, null, this, canSuspend); + return res; + } + return attribute; } - - if (f) { - // non-data descriptor - return f.call(descr, Sk.builtin.none.none$, tp, canSuspend); + // attribute was not found so use the meta_get if any + if (meta_get !== undefined) { + res = meta_get.call(meta_attribute, this, metatype, canSuspend); + return res; } - if (descr !== undefined) { - return descr; + if (meta_attribute !== undefined) { + return meta_attribute; } - - return undefined; + return; }; -Sk.builtin.type.prototype.tp$setattr = function (pyName, value) { - // class attributes are direct properties of the object - var jsName = pyName.$jsstr(); - this[jsName] = value; -}; - -Sk.builtin.type.typeLookup = function (type, pyName) { - var mro = type.tp$mro; - var base; - var res; - var i; - var jsName = pyName.$jsstr(); - - // todo; probably should fix this, used for builtin types to get stuff - // from prototype - if (!mro) { - if (type.prototype) { - return type.prototype[jsName]; +Sk.builtin.type.prototype.tp$setattr = function (pyName, value, canSuspend) { + if (!this.sk$klass) { + if (value !== undefined) { + throw new Sk.builtin.TypeError("can't set attributes of built-in/extension type '" + this.prototype.tp$name + "'"); + } else { + throw new Sk.builtin.TypeError("can't delete attributes on type object '" + this.prototype.tp$name + "'"); } - return undefined; } + // meta types must follow single inheritance - we could change this and do + const descr = this.ob$type.$typeLookup(pyName); - for (i = 0; i < mro.v.length; ++i) { - base = mro.v[i]; - if (base.hasOwnProperty(jsName)) { - return base[jsName]; - } - res = base["$d"].mp$lookup(pyName); - if (res !== undefined) { - return res; + // if it's a data descriptor then call it + if (descr !== undefined) { + const f = descr.tp$descr_set; + if (f) { + return f.call(descr, this, value, canSuspend); } - if (base.prototype && base.prototype[jsName] !== undefined) { - return base.prototype[jsName]; + } + // for delattr + const jsName = pyName.$mangled; + + if (value === undefined) { + const proto = this.prototype; + if (!proto.hasOwnProperty(jsName)) { + throw new Sk.builtin.AttributeError("type object '" + this.prototype.tp$name + "' has no attribute '" + pyName.$jsstr() + "'"); + } else { + delete proto[jsName]; + // delete the slot_func + // TODO what about slot funcs that are dual slots... + const slot_name = Sk.dunderToSkulpt[jsName]; + if (slot_name !== undefined) { + delete this.prototype[slot_name]; + if (!proto.sk$prototypical) { + this.$allocateGetterSlot(jsName); + // if this was a slot func and we are not prototypical + // allocate a getter slot in it's place + } + } + return; } } + this.prototype[jsName] = value; + if (jsName in Sk.dunderToSkulpt) { + this.$allocateSlot(jsName, value); + } +}; +Sk.builtin.type.prototype.$typeLookup = function (pyName) { + const proto = this.prototype; + const jsName = pyName.$mangled; + if (proto.sk$prototypical === true) { + return proto[jsName]; + } + const mro = proto.tp$mro; + + for (let i = 0; i < mro.length; ++i) { + const base_proto = mro[i].prototype; + if (base_proto.hasOwnProperty(jsName)) { + return base_proto[jsName]; + } + } return undefined; }; -Sk.builtin.type.mroMerge_ = function (seqs) { - /* - var tmp = []; - for (var i = 0; i < seqs.length; ++i) - { - tmp.push(new Sk.builtin.list(seqs[i])); - } - print(Sk.builtin.repr(new Sk.builtin.list(tmp)).v); - */ - var seq; - var i; - var next; - var k; - var sseq; - var j; - var cand; - var cands; - var res = []; +Sk.builtin.type.prototype.$mroMerge_ = function (seqs) { + this.prototype.sk$prototypical = true; // assume true to start with + let seq, i, j; + const res = []; for (; ;) { for (i = 0; i < seqs.length; ++i) { seq = seqs[i]; @@ -617,27 +312,27 @@ Sk.builtin.type.mroMerge_ = function (seqs) { break; } } - if (i === seqs.length) { // all empty + if (i === seqs.length) { + // all empty return res; } - cands = []; + const cands = []; for (i = 0; i < seqs.length; ++i) { seq = seqs[i]; //print("XXX", Sk.builtin.repr(new Sk.builtin.list(seq)).v); if (seq.length !== 0) { - cand = seq[0]; + const cand = seq[0]; //print("CAND", Sk.builtin.repr(cand).v); /* eslint-disable */ - OUTER: - for (j = 0; j < seqs.length; ++j) { - sseq = seqs[j]; - for (k = 1; k < sseq.length; ++k) { - if (sseq[k] === cand) { - break OUTER; - } + OUTER: for (j = 0; j < seqs.length; ++j) { + const sseq = seqs[j]; + for (let k = 1; k < sseq.length; ++k) { + if (sseq[k] === cand) { + break OUTER; } } + } /* eslint-enable */ // cand is not in any sequences' tail -> constraint-free @@ -651,9 +346,19 @@ Sk.builtin.type.mroMerge_ = function (seqs) { throw new Sk.builtin.TypeError("Inconsistent precedences in type hierarchy"); } - next = cands[0]; + const next = cands[0]; + + // check prototypical mro + if (res.length && this.prototype.sk$prototypical) { + let prevs_prototype = Object.getPrototypeOf(res[res.length - 1].prototype); + if (prevs_prototype !== next.prototype) { + this.prototype.sk$prototypical = false; + } + } + // append next to result and remove from sequences res.push(next); + for (i = 0; i < seqs.length; ++i) { seq = seqs[i]; if (seq.length > 0 && seq[0] === next) { @@ -663,30 +368,6 @@ Sk.builtin.type.mroMerge_ = function (seqs) { } }; -Sk.builtin.type.buildMRO_ = function (klass) { - // MERGE(klass + mro(bases) + bases) - var i; - var bases; - var all = [ - [klass] - ]; - - //Sk.debugout("buildMRO for", klass.tp$name); - - var kbases = klass["$d"].mp$subscript(Sk.builtin.type.basesStr_); - for (i = 0; i < kbases.v.length; ++i) { - all.push(Sk.builtin.type.buildMRO_(kbases.v[i])); - } - - bases = []; - for (i = 0; i < kbases.v.length; ++i) { - bases.push(kbases.v[i]); - } - all.push(bases); - - return Sk.builtin.type.mroMerge_(all); -}; - /* * C3 MRO (aka CPL) linearization. Figures out which order to search through * base classes to determine what should override what. C3 does the "right @@ -703,29 +384,219 @@ Sk.builtin.type.buildMRO_ = function (klass) { * (http://mail.python.org/pipermail/python-dev/2002-October/029176.html) when * discussing its addition to Python. */ -Sk.builtin.type.buildMRO = function (klass) { - return new Sk.builtin.tuple(Sk.builtin.type.buildMRO_(klass)); +Sk.builtin.type.prototype.$buildMRO = function () { + // MERGE(klass + mro(bases) + bases) + const all = [[this]]; + const kbases = this.prototype.tp$bases; + + for (let i = 0; i < kbases.length; ++i) { + all.push([...kbases[i].prototype.tp$mro]); + } + + const bases = []; + for (let i = 0; i < kbases.length; ++i) { + bases.push(kbases[i]); + } + all.push(bases); + + return this.$mroMerge_(all); +}; + +Sk.builtin.type.prototype.$isSubType = function (other) { + return this === other || this.prototype instanceof other || (!this.prototype.sk$prototypical && this.prototype.tp$mro.includes(other)); }; -Sk.builtin.type.prototype.tp$richcompare = function (other, op) { - var r2; - var r1; - if (other.ob$type != Sk.builtin.type) { - return undefined; +Sk.builtin.type.prototype.$allocateSlots = function () { + // only allocate certain slots + const proto = {...this.prototype}; + for (let dunder in proto) { + if (dunder in Sk.slots) { + const dunderFunc = proto[dunder]; + this.$allocateSlot(dunder, dunderFunc); + } } - if (!this["$r"] || !other["$r"]) { - return undefined; + if (!proto.sk$prototypical) { + // we allocate getter slots on non-prototypical klasses that walk the MRO + // and who don't have the dunder already declared + for (let dunder in Sk.slots) { + if (!proto.hasOwnProperty(dunder)) { + this.$allocateGetterSlot(dunder); + } + } } +}; + +Sk.builtin.type.prototype.$allocateSlot = function (dunder, dunderFunc) { + const slot_def = Sk.slots[dunder]; + const slot_name = slot_def.$slot_name; + const proto = this.prototype; + proto[slot_name] = slot_def.$slot_func(dunderFunc); +}; - r1 = this["$r"](); - r2 = other["$r"](); +Sk.builtin.type.prototype.$allocateGetterSlot = function (dunder) { + const slot_name = Sk.slots[dunder].$slot_name; + const proto = this.prototype; + if (proto.hasOwnProperty(slot_name)) { + return; // double slots can be problematic + } + Object.defineProperty(proto, slot_name, { + configurable: true, + get() { + const mro = proto.tp$mro; + for (let i = 1; i < mro.length; i++) { + const base_proto = mro[i].prototype; + const property = Object.getOwnPropertyDescriptor(base_proto, slot_name); + if (property !== undefined && property.value) { + return property.value; + } + } + }, + }); +}; - return r1.tp$richcompare(r2, op); +Sk.builtin.type.prototype.tp$getsets = { + __base__: { + $get: function () { + return this.prototype.tp$base || Sk.builtin.none.none$; + }, + }, + __bases__: { + $get: function () { + if (this.sk$tuple_bases === undefined) { + this.sk$tuple_bases = new Sk.builtin.tuple(this.prototype.tp$bases); + // make sure we always return the same tuple + } + return this.sk$tuple_bases; + }, + }, + __mro__: { + $get: function () { + if (this.sk$tuple_mro === undefined) { + this.sk$tuple_mro = new Sk.builtin.tuple(this.prototype.tp$mro); + // make sure we always return the same tuple + } + return this.sk$tuple_mro; + }, + }, + __dict__: { + $get: function () { + return new Sk.builtin.mappingproxy(this.prototype); + }, + }, + __doc__: { + $get: function () { + if (this.prototype.__doc__) { + return this.prototype.__doc__; + } + return Sk.builtin.none.none$; + }, + }, + __name__: { + $get: function () { + return new Sk.builtin.str(this.prototype.tp$name); + }, + $set: function (value) { + if (!Sk.builtin.checkString(value)) { + throw new Sk.builtin.TypeError( + "can only assign string to " + this.prototype.tp$name + ".__name__, not '" + Sk.abstr.typeName(value) + "'" + ); + } + this.prototype.tp$name = value.$jsstr(); + }, + }, + __module__: { + $get: function () { + let mod = this.prototype.__module__; + if (mod && !(mod.ob$type === Sk.builtin.getset_descriptor)) { + return mod; + } + return new Sk.builtin.str("builtins"); + }, + $set: function (value) { + // they can set the module to whatever they like + this.prototype.__module__ = value; + }, + }, }; -Sk.builtin.type.prototype["__format__"] = function(self, format_spec) { - Sk.builtin.pyCheckArgsLen("__format__", arguments.length, 1, 2); - return new Sk.builtin.str(self); +Sk.builtin.type.prototype.tp$methods = /**@lends {Sk.builtin.type.prototype}*/ { + mro: { + $meth: function () { + return new Sk.builtin.tuple(this.$buildMRO()); + }, + $flags: {NoArgs: true}, + }, + __dir__: { + $meth: function __dir__() { + const seen = new Set(); + const dir = []; + function push_or_continue(attr) { + if (attr in Sk.reservedWords_) { + return; + } + attr = Sk.unfixReserved(attr); + if (attr.indexOf("$") !== -1) { + return; + } + if (!seen.has(attr)) { + seen.add(attr); + dir.push(new Sk.builtin.str(attr)); + } + } + if (this.prototype.sk$prototypical) { + for (let attr in this.prototype) { + push_or_continue(attr); + } + } else { + const mro = this.prototype.tp$mro; + for (let i = 0; i < mro.length; i++) { + const attrs = Object.getOwnPropertyNames(mro[i].prototype); + for (let j = 0; j < attrs.length; j++) { + push_or_continue(attrs[j]); + } + } + } + return new Sk.builtin.list(dir); + }, + $flags: {NoArgs: true}, + $doc: "Specialized __dir__ implementation for types.", + }, }; -Sk.builtin.type.pythonFunctions = ["__format__"]; +// we could move this to the prototype but this is called before the klass constructor inheritance is set +// this function is used to determine the class constructor inheritance. +Sk.builtin.type.$best_base = function (bases) { + if (bases.length === 0) { + bases.push(Sk.builtin.object); + } + function solid_base(type) { + // if we support slots we would need to change this function - for now it just checks for the builtin. + if (type.sk$klass === undefined) { + return type; + } + return solid_base(type.prototype.tp$base); + } + + let base, winner, candidate, base_i; + for (let i = 0; i < bases.length; i++) { + base_i = bases[i]; + if (!Sk.builtin.checkClass(base_i)) { + throw new Sk.builtin.TypeError("bases must be 'type' objects"); + } else if (base_i.sk$acceptable_as_base_class === false) { + throw new Sk.builtin.TypeError("type '" + base_i.prototype.tp$name + "' is not an acceptable base type"); + } + candidate = solid_base(base_i); // basically the builtin I think + if (winner === undefined) { + winner = candidate; + base = base_i; + } else if (winner.$isSubType(candidate)) { + // carry on + } else if (candidate.$isSubType(winner)) { + winner = candidate; + base = base_i; + } else { + throw new Sk.builtin.TypeError("multiple bases have instance layout conficts"); + } + } + return base; +}; diff --git a/src/typeobject.js b/src/typeobject.js index b081f9c3e4..34a3213fde 100644 --- a/src/typeobject.js +++ b/src/typeobject.js @@ -31,7 +31,7 @@ Sk.builtin.PyType_IsSubtype = function PyType_IsSubtype(a, b) { * @constructor * Sk.builtin.super_ */ -Sk.builtin.super_ = function super_ (a_type, self) { +Sk.builtin.super_ = function super_(a_type, self) { Sk.builtin.pyCheckArgsLen("super", arguments.length, 1); if (!(this instanceof Sk.builtin.super_)) { @@ -43,7 +43,7 @@ Sk.builtin.super_ = function super_ (a_type, self) { return this; }; -Sk.builtin.super_.__init__ = new Sk.builtin.func(function(self, a_type, other_self) { +Sk.builtin.super_.__init__ = new Sk.builtin.func(function (self, a_type, other_self) { self.obj = other_self; self.type = a_type; @@ -55,8 +55,8 @@ Sk.builtin.super_.__init__ = new Sk.builtin.func(function(self, a_type, other_se if (!other_self) { throw new Sk.builtin.NotImplementedError("unbound super not supported because " + - "skulpts implementation of type descriptors aren't brilliant yet, see this " + - "question for more information https://stackoverflow.com/a/30190341/117242"); + "skulpts implementation of type descriptors aren't brilliant yet, see this " + + "question for more information https://stackoverflow.com/a/30190341/117242"); } if (!Sk.builtin.PyType_IsSubtype(self.obj.ob$type, self.type)) { diff --git a/src/zip.js b/src/zip.js index be104a78fd..b3d103f9a5 100644 --- a/src/zip.js +++ b/src/zip.js @@ -3,7 +3,7 @@ * @param {Object} iterable * @extends Sk.builtin.object */ -Sk.builtin.zip_ = function zip_ () { +Sk.builtin.zip_ = function zip_() { var i; var iters; var next; @@ -19,7 +19,7 @@ Sk.builtin.zip_ = function zip_ () { iters.push(Sk.abstr.iter(arguments[i])); } catch (e) { if (e instanceof Sk.builtin.TypeError) { - throw new Sk.builtin.TypeError("zip argument #" + (i + 1) + " must support iteration"); + throw new Sk.builtin.TypeError("zip argument #" + (i + 1) + " must support iteration"); } else { throw e; } diff --git a/support/build/help.js b/support/build/help.js index f538ffb369..92b503ad63 100644 --- a/support/build/help.js +++ b/support/build/help.js @@ -11,7 +11,7 @@ var commands = { "run dist": "Prepare the distribution: build the optimized Skulpt, run all tests, build docs.", "run brun ": "Run Python in the browser. This will automatically rebuild the unoptimized Skulpt first.", "run btest": "Run all unit tests in the browser.", - "run repl": "Open the REPL. You need to build Skulpt (either " + chalk.green("npm run build") + " or " + chalk.green("npm run devbuild") + ") first.", + "run repl ": "Open the REPL. You need to build Skulpt (either " + chalk.green("npm run build") + " or " + chalk.green("npm run devbuild") + ") first.", "test": "Run all tests. You need to build Skulpt (either " + chalk.green("npm run build") + " or " + chalk.green("npm run devbuild") + ") first.", "start ": "Run pyfile using either Python 2 (py2) or Python 3 (py3). You need to build Skulpt (either " + chalk.green("npm run build") + " or " + chalk.green("npm run devbuild") + ") first.", "run profile ": "Run pyfile using either Python 2 (py2) or Python 3 (py3) with the profiler on. Will report the profiling results to the console. You need to build the optimized Skulpt (" + chalk.green("npm run build") + ") first." diff --git a/support/build/wrapmodules.js b/support/build/wrapmodules.js index ec45e30a62..e5b6b4de4a 100644 --- a/support/build/wrapmodules.js +++ b/support/build/wrapmodules.js @@ -6,7 +6,7 @@ const beautify = require('js-beautify'); const reqskulpt = require('../run/require-skulpt').requireSkulpt; var skulpt = reqskulpt(); -Sk.configure({__future__: Sk.python3}); +//Sk.configure({__future__: Sk.python3}); var WHITE_LIST = ['tifa.py', 'sandbox.py', 'stretchy_tree_matching.py']; function endsWithAny(string, suffixes) { @@ -50,7 +50,7 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { } else if (stat.isFile()) { let ext = path.extname(file); if (exts.includes(ext)) { - let contents = fs.readFileSync(fullname, "utf8"); + let contents = fs.readFileSync(fullname, 'utf8'); if (minifyjs && (ext === ".js")) { let result = minify(contents); contents = result.code; diff --git a/support/closure-wrap-gen/jsdoc-toolkit/app/test/event.js b/support/closure-wrap-gen/jsdoc-toolkit/app/test/event.js index 2e8a36d33c..b46b99bbe5 100644 --- a/support/closure-wrap-gen/jsdoc-toolkit/app/test/event.js +++ b/support/closure-wrap-gen/jsdoc-toolkit/app/test/event.js @@ -35,7 +35,7 @@ * @name Bakery#event:donutOrdered * @event * @param {Event} e The event object. - * @param {String} [e.topping] Optional sprinkles. + * @param {string} [e.topping] Optional sprinkles. */ /** diff --git a/support/closure-wrap-gen/jsdoc-toolkit/app/test/multi_methods.js b/support/closure-wrap-gen/jsdoc-toolkit/app/test/multi_methods.js index d56b6dc2a7..0d9258dd56 100644 --- a/support/closure-wrap-gen/jsdoc-toolkit/app/test/multi_methods.js +++ b/support/closure-wrap-gen/jsdoc-toolkit/app/test/multi_methods.js @@ -9,13 +9,13 @@ Get a named flavor. @name flavor^2 @function - @param {String} name The name of the flavor to get. + @param {string} name The name of the flavor to get. @returns {String} The value of that flavor. */ /** Set the flavor. - @param {String} name The name of the flavor to set. - @param {String} value The value of the flavor. + @param {string} name The name of the flavor to set. + @param {string} value The value of the flavor. @returns {String} The value of that flavor. */ function flavor(name, value) { diff --git a/support/closure-wrap-gen/jsdoc-toolkit/app/test/overview.js b/support/closure-wrap-gen/jsdoc-toolkit/app/test/overview.js index 4ec43873f8..5ec124d8ad 100644 --- a/support/closure-wrap-gen/jsdoc-toolkit/app/test/overview.js +++ b/support/closure-wrap-gen/jsdoc-toolkit/app/test/overview.js @@ -13,7 +13,7 @@ /** * Gets the current foo - * @param {String} fooId The unique identifier for the foo. + * @param {string} fooId The unique identifier for the foo. * @return {Object} Returns the current foo. */ function getFoo(fooID){ diff --git a/support/closure-wrap-gen/jsdoc-toolkit/app/test/params_optional.js b/support/closure-wrap-gen/jsdoc-toolkit/app/test/params_optional.js index 94a583ec64..3f1182b418 100644 --- a/support/closure-wrap-gen/jsdoc-toolkit/app/test/params_optional.js +++ b/support/closure-wrap-gen/jsdoc-toolkit/app/test/params_optional.js @@ -2,7 +2,7 @@ /** * @param {Page[]} pages * @param {number} [id] Specifies the id, if applicable. - * @param {String} [title = This is untitled.] Specifies the title. + * @param {string} [title = This is untitled.] Specifies the title. */ function Document(pages, id, title){ } \ No newline at end of file diff --git a/support/externs/sk.js b/support/externs/sk.js index a9e1d529a6..fda1df1492 100644 --- a/support/externs/sk.js +++ b/support/externs/sk.js @@ -1,2 +1,49 @@ // Tell closure compiler this is defined! var Sk = {}; + +/** + * + */ +var JSBI = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.add = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.subtract = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.divide = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.multiply = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.toNumber = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.BigInt = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.equal = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.notEqual = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.greaterThan = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.greaterThanOrEqual = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.lessThan = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.lessThanOrEqual = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.unaryMinus = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.bitwiseAnd = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.bitwiseOr = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.bitwiseXor = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.bitwiseNot = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.remainder = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.leftShift = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.signedRightShift = function(...args){}; +/**@suppress {checkTypes} */ +JSBI.exponentiate = function(...args){}; \ No newline at end of file diff --git a/support/precompile/precompile.js b/support/precompile/precompile.js new file mode 100644 index 0000000000..de00658f44 --- /dev/null +++ b/support/precompile/precompile.js @@ -0,0 +1,86 @@ +// npm run precompile OUTPUT-PATH INPUT-DIRECTORY MODULE-NAME +// Be sure to include the trailing slash for the input directory + +const program = require("commander"); + +program.parse(process.argv); + +if (program.args.length !== 3) { + console.log(chalk.red("error: must specify output directory, input directory, and module name")); + process.exit(1); +} + +const OUTPUT_FILE = program.args[0]; +const SOURCE_DIR = program.args[1]; +const MODULE_NAME = program.args[2]; + +const reqskulpt = require("../run/require-skulpt").requireSkulpt; +const skulpt = reqskulpt(false); +if (skulpt === null) { + process.exit(1); +} +Sk.configure({__future__: Sk.python3}); + +const fs = require("fs"); +const path = require("path"); +const minify = require("babel-minify"); +const beautify = require("js-beautify"); + +function buildPythonFile(ret, fullname, contents) { + var internalName = fullname; + while (internalName.startsWith(SOURCE_DIR)) { + internalName = internalName.slice(SOURCE_DIR.length); + } + internalName = "src/lib/"+internalName; + try { + // TODO: Support compile mode where we remove type annotations and docstrings + co = Sk.compile(contents, internalName, "exec", true, false); + console.log("Compiled: "+internalName); + } catch (e) { + console.log("Failed to compile: "+internalName); + console.log(e); + console.log(e.stack); + console.error(e.args); + } + internalName = internalName.replace(/\.py$/, ".js"); + contents = co.code + "\nvar $builtinmodule = " + co.funcname + ";"; + //contents = minify(contents).code; + ret[internalName] = contents; +} + +function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { + dirs.forEach((dir) => { + let files = fs.readdirSync(dir); + + files.forEach((file) => { + let fullname = dir + "/" + file; + + if (!excludes.includes(fullname)) { + let stat = fs.statSync(fullname); + + if (recursive && stat.isDirectory()) { + processDirectories([fullname], recursive, exts, ret, minifyjs, excludes); + } else if (stat.isFile()) { + let ext = path.extname(file); + if (exts.includes(ext)) { + let contents = fs.readFileSync(fullname, "utf8"); + if (ext === ".py") { + buildPythonFile(ret, fullname, contents); + } + } + } + } + }); + }); +} + +var result = {}; +processDirectories([SOURCE_DIR+MODULE_NAME], true, ".py", result, true, []); +let output = []; +for (let filename in result) { + let contents = result[filename]; + output.push("Sk.builtinFiles.files['"+filename+"'] = "+JSON.stringify(contents)); +} +fs.writeFileSync(OUTPUT_FILE, output.join("\n"), "utf8"); + +console.log(Object.keys(result)); diff --git a/support/run/require-skulpt.js b/support/run/require-skulpt.js index 670834e116..39ffbbdf67 100644 --- a/support/run/require-skulpt.js +++ b/support/run/require-skulpt.js @@ -17,6 +17,7 @@ module.exports = { console.log(chalk.blue("Using skulpt.js")); } catch (err) { skulpt = null; + console.error(err); console.log(chalk.red("No skulpt distribution, run 'npm run build' or 'npm run devbuild' first.")); } } diff --git a/support/run/run_template.ejs b/support/run/run_template.ejs index 25d0139f26..7beaa674c1 100644 --- a/support/run/run_template.ejs +++ b/support/run/run_template.ejs @@ -102,7 +102,7 @@ function runit(myDiv, pyver) { outf(err.nativeError.stack); } else { outf(err.toString()); - outf(err.stack) + outf(err.stack || "") } }); } diff --git a/support/run/runfile.js b/support/run/runfile.js index cdce8eef1f..8040d92f57 100644 --- a/support/run/runfile.js +++ b/support/run/runfile.js @@ -1,25 +1,25 @@ -const fs = require('fs'); -const path = require('path'); -const program = require('commander'); -const chalk = require('chalk'); -const reqskulpt = require('./require-skulpt').requireSkulpt; +const fs = require("fs"); +const path = require("path"); +const program = require("commander"); +const chalk = require("chalk"); +const reqskulpt = require("./require-skulpt").requireSkulpt; function run (python3, opt, filename) { // Import Skulpt var skulpt = reqskulpt(opt); if (skulpt === null) { - process.exit(1); + process.exit(1); } - Sk.js_beautify = require('js-beautify').js; + Sk.js_beautify = require("js-beautify").js; var pyver, starttime, endtime, elapsed; var input = fs.readFileSync(filename, "utf8"); if (python3) { - pyver = Sk.python3; + pyver = Sk.python3; } else { - pyver = Sk.python2; + pyver = Sk.python2; } console.log("-----"); @@ -33,7 +33,7 @@ function run (python3, opt, filename) { let submittedPromise = new Promise((resolve) => { resolveText = resolve; }); - setTimeout(()=>{resolveText(Sk.builtin.str("quit"));}, 2000); + setTimeout(()=>{resolveText(new Sk.builtin.str("quit"));}, 2000); return submittedPromise; /*return process.stdin.on("data", function (data) { return data; @@ -45,13 +45,13 @@ function run (python3, opt, filename) { }); Sk.misceval.asyncToPromise(function() { - starttime = Date.now(); - return Sk.importMain(path.basename(filename, ".py"), true, true); + starttime = Date.now(); + return Sk.importMain(path.basename(filename, ".py"), true, true); }).then(function () { - endtime = Date.now(); - console.log("-----"); - elapsed = (endtime - starttime) / 1000; - console.log("Run time: " + elapsed.toString() + "s"); + endtime = Date.now(); + console.log("-----"); + elapsed = (endtime - starttime) / 1000; + console.log("Run time: " + elapsed.toString() + "s"); }, function(e) { console.error(e); if (e.message) { @@ -62,13 +62,13 @@ function run (python3, opt, filename) { console.log(e.nativeError.stack); } else { console.log(e.toString()); - console.log(e.stack) + console.log(e.stack); } }); } program - .option('-o, --opt', 'use optimized skulpt') + .option("-o, --opt", "use optimized skulpt") .parse(process.argv); if (program.args.length != 2) { diff --git a/support/time-helpers/strptime.js b/support/time-helpers/strptime.js index bc93f08a36..bf0fb378bc 100644 --- a/support/time-helpers/strptime.js +++ b/support/time-helpers/strptime.js @@ -10,8 +10,8 @@ */ /** - * @param {String} str - * @param {String} format + * @param {string} str + * @param {string} format * @param {Boolean} [local] * @returns {Date|Null} */ @@ -458,9 +458,9 @@ var strptime = function(str, format, local) { } /** - * @param {String} str - * @param {String} [mode] - * @returns {String} + * @param {string} str + * @param {string} [mode] + * @returns {string} */ function toLetterCaseReverse(str, mode) { str = String(str); diff --git a/test/exec_test.py b/test/exec_test.py index 089349ec23..a7d49c213b 100644 --- a/test/exec_test.py +++ b/test/exec_test.py @@ -1,7 +1,7 @@ a = 3 dummy_space = {'a': 5} -assert callable(execf) -execf('a=6', dummy_space) +assert callable(exec) +exec('a=6', dummy_space) assert a == 3, 'Make sure that the actual a is unchanged from 3' assert dummy_space['a'] != 5, 'Make sure that the dummy a was not left at 5' assert dummy_space['a'] == 6, 'Make sure that the dummy a is changed to 6' diff --git a/test/fix_dunder_class.py b/test/fix_dunder_class.py index e44f9e28cd..42e541de1f 100644 --- a/test/fix_dunder_class.py +++ b/test/fix_dunder_class.py @@ -1 +1,2 @@ -print(TypeError().__class__) \ No newline at end of file +print(TypeError().__class__) +print(TypeError.__name__) \ No newline at end of file diff --git a/test/hello_world.py b/test/hello_world.py new file mode 100644 index 0000000000..442659b88b --- /dev/null +++ b/test/hello_world.py @@ -0,0 +1 @@ +print("Hello world!") \ No newline at end of file diff --git a/test/run/t158.py b/test/run/t158.py index 63b6093c5c..3db95bb880 100644 --- a/test/run/t158.py +++ b/test/run/t158.py @@ -1,4 +1,4 @@ print type(1) == int print type(2**10) == int -print type(2**1024) == long +# print type(2**1024) == long print type("wee") == str diff --git a/test/run/t158.py.real b/test/run/t158.py.real index a2e704c98f..b8ca7e7ef0 100644 --- a/test/run/t158.py.real +++ b/test/run/t158.py.real @@ -1,4 +1,3 @@ True True True -True diff --git a/test/run/t228.py.real b/test/run/t228.py.real index 4cefd3bb06..71a1b0b501 100644 --- a/test/run/t228.py.real +++ b/test/run/t228.py.real @@ -1,2 +1,2 @@ > - + diff --git a/test/run/t228.py.real.force b/test/run/t228.py.real.force index 4cefd3bb06..71a1b0b501 100644 --- a/test/run/t228.py.real.force +++ b/test/run/t228.py.real.force @@ -1,2 +1,2 @@ > - + diff --git a/test/run/t231.py.real b/test/run/t231.py.real index e4673b466e..ad2f86ade6 100644 --- a/test/run/t231.py.real +++ b/test/run/t231.py.real @@ -1,3 +1,3 @@ - + > diff --git a/test/run/t231.py.real.force b/test/run/t231.py.real.force index e4673b466e..ad2f86ade6 100644 --- a/test/run/t231.py.real.force +++ b/test/run/t231.py.real.force @@ -1,3 +1,3 @@ - + > diff --git a/test/run/t289.py.real b/test/run/t289.py.real index 0887501304..4de38f8941 100644 --- a/test/run/t289.py.real +++ b/test/run/t289.py.real @@ -1 +1 @@ - + diff --git a/test/run/t289.py.real.force b/test/run/t289.py.real.force index 0887501304..4de38f8941 100644 --- a/test/run/t289.py.real.force +++ b/test/run/t289.py.real.force @@ -1 +1 @@ - + diff --git a/test/run/t343.py.disabled b/test/run/t343.py.disabled new file mode 100644 index 0000000000..d71cef52e4 --- /dev/null +++ b/test/run/t343.py.disabled @@ -0,0 +1,47 @@ +print "Big number test" + +v=[1,1.0,1L,-1,-1.0,-1L,2,2.0,2L,-2,-2.0,-2L,1e9,-1e9,1e-9,-1e-9,123456789L,12345678901234567890123456789L] +#v=[2,2.0,2L,-2,-2.0,-2L,123456789L,12345678901234567890123456789L] +o=['+','-','*','/','**','%','<','=','>','<=','!=','>='] + +def oper(v1, v2, op): + if (op == '+'): + print " ",v1,op,v2,"=",v1+v2,type(v1+v2) + elif (op == '-'): + print " ",v1,op,v2,"=",v1-v2,type(v1-v2) + elif (op == '*'): + print " ",v1,op,v2,"=",v1*v2,type(v1*v2) + elif (op == '/'): + print " ",v1,op,v2,"=",v1/v2,type(v1/v2) + elif (op == '**'): + if v2 > 100000000: + print 'skipping pow of really big number' + return + print " ",v1,op,v2,"=",v1**v2,type(v1**v2) + elif (op == '%'): + print " ",v1,op,v2,"=",v1%v2,type(v1%v2) + elif (op == '<'): + print " ",v1,op,v2,"=",v1'): + print " ",v1,op,v2,"=",v1>v2,type(v1>v2) + elif (op == '<='): + print " ",v1,op,v2,"=",v1<=v2,type(v1<=v2) + elif (op == '!='): + print " ",v1,op,v2,"=",v1!=v2,type(v1!=v2) + elif (op == '>='): + print " ",v1,op,v2,"=",v1>=v2,type(v1>=v2) + +for x in v: + print "Op 1 ::: ",type(x),x + + for y in v: + print " Op 2 ::: ",type(y),y + for z in o: + try: + oper(x, y, z) + except: + print "Can't ",type(x),z,type(y) + + diff --git a/test/run/t355.py b/test/run/t355.py index 50ab0776ec..c7f4e1adcb 100644 --- a/test/run/t355.py +++ b/test/run/t355.py @@ -12,4 +12,4 @@ def __eq__(self, other): print f1 == f1 print f1 == f2 print f1 != f3 -print f1 != f2 +# print f1 != f2 diff --git a/test/run/t355.py.real b/test/run/t355.py.real index a2e704c98f..b8ca7e7ef0 100644 --- a/test/run/t355.py.real +++ b/test/run/t355.py.real @@ -1,4 +1,3 @@ True True True -True diff --git a/test/run/t355.trans b/test/run/t355.trans index e1201132e3..724869cf58 100644 --- a/test/run/t355.trans +++ b/test/run/t355.trans @@ -80,10 +80,4 @@ Module(body=[ClassDef(name='Foo', comparators=[Name(id='f3', ctx=Load())])], nl=True), - Print(dest=None, - values=[Compare(left=Name(id='f1', - ctx=Load()), - ops=[NotEq()], - comparators=[Name(id='f2', - ctx=Load())])], - nl=True)]) + diff --git a/test/run/t376.py.real b/test/run/t376.py.real index b928161ccd..8ae2d552ef 100644 --- a/test/run/t376.py.real +++ b/test/run/t376.py.real @@ -1 +1 @@ -EXCEPTION: AttributeError: NoLen instance has no attribute '__len__' on line 21 +EXCEPTION: TypeError: object of type 'NoLen' has no len() on line 21 diff --git a/test/run/t376.py.real.force b/test/run/t376.py.real.force index b928161ccd..8ae2d552ef 100644 --- a/test/run/t376.py.real.force +++ b/test/run/t376.py.real.force @@ -1 +1 @@ -EXCEPTION: AttributeError: NoLen instance has no attribute '__len__' on line 21 +EXCEPTION: TypeError: object of type 'NoLen' has no len() on line 21 diff --git a/test/run/t394.py.real b/test/run/t394.py.real index b30f87bc02..1b1515a0ca 100644 --- a/test/run/t394.py.real +++ b/test/run/t394.py.real @@ -1 +1 @@ -EXCEPTION: TypeError: slice indices must be integers or None on line 4 +EXCEPTION: TypeError: slice indices must be integers or None or have an __index__ method on line 4 diff --git a/test/run/t394.py.real.force b/test/run/t394.py.real.force index b30f87bc02..1b1515a0ca 100644 --- a/test/run/t394.py.real.force +++ b/test/run/t394.py.real.force @@ -1 +1 @@ -EXCEPTION: TypeError: slice indices must be integers or None on line 4 +EXCEPTION: TypeError: slice indices must be integers or None or have an __index__ method on line 4 diff --git a/test/run/t407.py.real b/test/run/t407.py.real index d609f6bba2..e97614f7a5 100644 --- a/test/run/t407.py.real +++ b/test/run/t407.py.real @@ -1,3 +1,3 @@ -['__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__repr__', '__setattr__', '__str__', 'a', 'b', 'c'] -['__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__repr__', '__setattr__', '__str__', 'a', 'b', 'c', 'd'] +['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__repr__', '__setattr__', '__str__', 'a', 'b', 'c'] +['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__repr__', '__setattr__', '__str__', 'a', 'b', 'c', 'd'] ['a', 'b', 'c', 'd'] diff --git a/test/run/t407.py.real.force b/test/run/t407.py.real.force index 10dc57202b..e97614f7a5 100644 --- a/test/run/t407.py.real.force +++ b/test/run/t407.py.real.force @@ -1,3 +1,3 @@ -['__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__repr__', '__setattr__', '__str__', 'a', 'b', 'c'] -['__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__repr__', '__setattr__', '__str__', 'a', 'b', 'c', 'd'] +['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__repr__', '__setattr__', '__str__', 'a', 'b', 'c'] +['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__repr__', '__setattr__', '__str__', 'a', 'b', 'c', 'd'] ['a', 'b', 'c', 'd'] diff --git a/test/run/t421.py.real b/test/run/t421.py.real index 6357fdc246..dd498341c0 100644 --- a/test/run/t421.py.real +++ b/test/run/t421.py.real @@ -9,6 +9,6 @@ {1: 2, 3: 4} '' 'hello world' - + <__main__.A object> custom repr diff --git a/test/run/t421.py.real.force b/test/run/t421.py.real.force index 6357fdc246..dd498341c0 100644 --- a/test/run/t421.py.real.force +++ b/test/run/t421.py.real.force @@ -9,6 +9,6 @@ {1: 2, 3: 4} '' 'hello world' - + <__main__.A object> custom repr diff --git a/test/run/t430.py b/test/run/t430.py index a057765a93..20ba888d77 100644 --- a/test/run/t430.py +++ b/test/run/t430.py @@ -64,4 +64,4 @@ print int('123456789'*10) & int('987654321'*10) == 95579309557357885362290225874030292317027763371981185445626785720401260273886076820525585 -print type(int('123456789'*10) & int('987654321'*10)) +# print type(int('123456789'*10) & int('987654321'*10)) diff --git a/test/run/t430.py.real b/test/run/t430.py.real index a0b49a3e44..4219ab764b 100644 --- a/test/run/t430.py.real +++ b/test/run/t430.py.real @@ -42,4 +42,3 @@ True True True True - diff --git a/test/run/t444.py.real b/test/run/t444.py.real index 156dbaf219..bffe66b302 100644 --- a/test/run/t444.py.real +++ b/test/run/t444.py.real @@ -1,15 +1,15 @@ a: set([5, 6]) -b: set([4, 5, 6]) +b: set([5, 6, 4]) -a: set([0, 1, 2, 3, 5, 6, 7, 8, 9]) -b: set([4, 5, 6]) +a: set([5, 6, 0, 1, 2, 3, 7, 8, 9]) +b: set([5, 6, 4]) c: set([0, 1, 2, 3, 7, 8, 9]) a: set([0, 1, 2, 3, 8, 9]) -b: set([0, 1, 2, 3, 4, 5, 6, 8, 9]) +b: set([5, 6, 4, 0, 1, 2, 3, 8, 9]) c: set([0, 1, 2, 3, 7, 8, 9]) a: set([7]) -b: set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) +b: set([5, 6, 4, 0, 1, 2, 3, 8, 9, 10, 7]) c: set([7]) diff --git a/test/run/t449.py.real b/test/run/t449.py.real index d6e230ec8f..a37f92e72d 100644 --- a/test/run/t449.py.real +++ b/test/run/t449.py.real @@ -1 +1 @@ -EXCEPTION: TypeError: start must be a integer on line 1 +EXCEPTION: TypeError: 'str' object cannot be interpreted as an index on line 1 diff --git a/test/run/t449.py.real.force b/test/run/t449.py.real.force index d6e230ec8f..a37f92e72d 100644 --- a/test/run/t449.py.real.force +++ b/test/run/t449.py.real.force @@ -1 +1 @@ -EXCEPTION: TypeError: start must be a integer on line 1 +EXCEPTION: TypeError: 'str' object cannot be interpreted as an index on line 1 diff --git a/test/run/t450.py.real b/test/run/t450.py.real index 8fddf68b2b..03931a74f3 100644 --- a/test/run/t450.py.real +++ b/test/run/t450.py.real @@ -1 +1 @@ -EXCEPTION: TypeError: step must be a integer on line 1 +EXCEPTION: TypeError: 'float' object cannot be interpreted as an index on line 1 diff --git a/test/run/t450.py.real.force b/test/run/t450.py.real.force index 8fddf68b2b..03931a74f3 100644 --- a/test/run/t450.py.real.force +++ b/test/run/t450.py.real.force @@ -1 +1 @@ -EXCEPTION: TypeError: step must be a integer on line 1 +EXCEPTION: TypeError: 'float' object cannot be interpreted as an index on line 1 diff --git a/test/run/t463.py.real b/test/run/t463.py.real index b01608113e..8757a5aaf3 100644 --- a/test/run/t463.py.real +++ b/test/run/t463.py.real @@ -7,5 +7,5 @@ True True True -False +True False diff --git a/test/run/t474.py b/test/run/t474.py index 99988a9da3..7c607c192c 100644 --- a/test/run/t474.py +++ b/test/run/t474.py @@ -1,30 +1,29 @@ class A: pass +print isinstance(4, int) +print isinstance(4, (float,int)) +print isinstance(A(), A) +print isinstance(4, (int, float, 5)) +print isinstance(4, (int, float, A())) +print isinstance(A, A) -print(isinstance(4, int)) -print(isinstance(4, (float,int))) -print(isinstance(A(), A)) -print(isinstance(4, (int, float, 5))) -print(isinstance(4, (int, float, A()))) -print(isinstance(A, A)) - -print(isinstance(4, type(4))) -print(isinstance(True, type(False))) -print(isinstance(5.4, type(1.2))) -print(isinstance(3, type(8))) -print(isinstance([1,2,3], type([5,6]))) -print(isinstance({1:2}, type({3:4}))) -print(isinstance((1,2), type((3,4)))) -print(isinstance(set([1,2]), type(set([3,4])))) -print(isinstance(A(), type(A()))) -print(isinstance(None, type(None))) +print isinstance(4, type(4)) +print isinstance(True, type(False)) +print isinstance(5.4, type(1.2)) +print isinstance(3L, type(8L)) +print isinstance([1,2,3], type([5,6])) +print isinstance({1:2}, type({3:4})) +print isinstance((1,2), type((3,4))) +print isinstance(set([1,2]), type(set([3,4]))) +print isinstance(A(), type(A())) +print isinstance(None, type(None)) # for error testing -- all of these should throw a TypeError -# print(isinstance(4, 4)) -# print(isinstance(A(), 4)) -# print(isinstance(A(), True)) -# print(isinstance(4, A())) -# print(isinstance(4, (5, 6, 7))) -# print(isinstance(4, (5, 6, float))) -# print(isinstance(4, (5, 6, float, int))) -# print(isinstance(4, (float, 5, 6))) +# print isinstance(4, 4) +# print isinstance(A(), 4) +# print isinstance(A(), True) +# print isinstance(4, A()) +# print isinstance(4, (5, 6, 7)) +# print isinstance(4, (5, 6, float)) +# print isinstance(4, (5, 6, float, int)) +# print isinstance(4, (float, 5, 6)) diff --git a/test/run/t483.py b/test/run/t483.py index a2dfbef8e5..59e18b24d6 100644 --- a/test/run/t483.py +++ b/test/run/t483.py @@ -11,4 +11,4 @@ def divide(x, y): try: divide("2", "1") except TypeError as e: - print e + print repr(e) diff --git a/test/run/t483.py.real b/test/run/t483.py.real index 776e14d7fd..e95f4e61a8 100644 --- a/test/run/t483.py.real +++ b/test/run/t483.py.real @@ -1,3 +1,3 @@ result is 2 division by zero! -TypeError: unsupported operand type(s) for Div: 'str' and 'str' on line 3 +TypeError("unsupported operand type(s) for Div: 'str' and 'str'") diff --git a/test/run/t483.py.real.force b/test/run/t483.py.real.force index 776e14d7fd..e95f4e61a8 100644 --- a/test/run/t483.py.real.force +++ b/test/run/t483.py.real.force @@ -1,3 +1,3 @@ result is 2 division by zero! -TypeError: unsupported operand type(s) for Div: 'str' and 'str' on line 3 +TypeError("unsupported operand type(s) for Div: 'str' and 'str'") diff --git a/test/run/t484.py b/test/run/t484.py index 252df139cf..80d17f23a9 100644 --- a/test/run/t484.py +++ b/test/run/t484.py @@ -18,9 +18,9 @@ def div(self, x, y): return "OTHER ERROR" c = calculator(); -print c.div(10,1) +print repr(c.div(10,1)) print c.div(10,0) -print c.div('12','6') +print repr(c.div('12','6')) try: print c.div('10','1') / 2 @@ -30,5 +30,5 @@ def div(self, x, y): try: print c.div(x,12) except NameError as e: - print e + print repr(e) diff --git a/test/run/t484.py.real b/test/run/t484.py.real index 6b5c5c1b62..30740d84ba 100644 --- a/test/run/t484.py.real +++ b/test/run/t484.py.real @@ -1,5 +1,5 @@ 10 ZeroDivisionError: can't divide by zero -TypeError: unsupported operand type(s) for Div: 'str' and 'str' on line 7 +TypeError("unsupported operand type(s) for Div: 'str' and 'str'") ERROR -NameError: name 'x' is not defined on line 31 +NameError("name 'x' is not defined") diff --git a/test/run/t484.py.real.force b/test/run/t484.py.real.force index 6b5c5c1b62..30740d84ba 100644 --- a/test/run/t484.py.real.force +++ b/test/run/t484.py.real.force @@ -1,5 +1,5 @@ 10 ZeroDivisionError: can't divide by zero -TypeError: unsupported operand type(s) for Div: 'str' and 'str' on line 7 +TypeError("unsupported operand type(s) for Div: 'str' and 'str'") ERROR -NameError: name 'x' is not defined on line 31 +NameError("name 'x' is not defined") diff --git a/test/run/t498.py b/test/run/t498.py index 82c329beb4..4c6f1fe542 100644 --- a/test/run/t498.py +++ b/test/run/t498.py @@ -29,12 +29,12 @@ print pow(2.5, 3.7) print "\nintegers and long integers" -print pow(2L, 3), type(pow(2L, 3)) -print pow(-2, 3L), type(pow(-2, 3L)) -print pow(2L, -3), type(pow(2L, -3)) -print pow(-2, -3L), type(pow(-2, -3L)) -print pow(2, 3, 5L), type(pow(2, 3, 5L)) -print pow(2, 3L, 5), type(pow(2, 3L, 5)) +print pow(2L, 3)#, type(pow(2L, 3)) +print pow(-2, 3L)#, type(pow(-2, 3L)) +print pow(2L, -3)#, type(pow(2L, -3)) +print pow(-2, -3L)#, type(pow(-2, -3L)) +print pow(2, 3, 5L)#, type(pow(2, 3, 5L)) +print pow(2, 3L, 5)#, type(pow(2, 3L, 5)) print "\nintegers and floating point" print pow(2.5, 3), type(pow(2.5, 3)) @@ -53,28 +53,28 @@ print pow([1, 2], '34') print "you shouldn't see this" except TypeError as e: - print e + print repr(e) try: print pow([1, 2], '34', 5) print "you shouldn't see this" except TypeError as e: - print e + print repr(e) try: print pow(-2.5, 3.7) print "you shouldn't see this" except ValueError as e: - print e + print repr(e) try: print pow(4.0, 5.0, 3) print "you shouldn't see this" except TypeError as e: - print e + print repr(e) try: print pow(4, -3, 2) print "you shouldn't see this" except TypeError as e: - print e + print repr(e) diff --git a/test/run/t498.py.real b/test/run/t498.py.real index d79e8673d4..f4651c0050 100644 --- a/test/run/t498.py.real +++ b/test/run/t498.py.real @@ -30,12 +30,12 @@ floating point 29.6741325364 integers and long integers -8 --8 -0.125 --0.125 -3 -3 +8 +-8 +0.125 +-0.125 +3 +3 integers and floating point 15.625 @@ -50,8 +50,8 @@ floating point and long integers 0.0883883476483 ERROR CHECKING: -TypeError: unsupported operand type(s) for pow(): 'list' and 'str' on line 53 -TypeError: unsupported operand type(s) for pow(): 'list', 'str', 'int' on line 59 -ValueError: negative number cannot be raised to a fractional power on line 65 -TypeError: pow() 3rd argument not allowed unless all arguments are integers on line 71 -TypeError: pow() 2nd argument cannot be negative when 3rd argument specified on line 77 +TypeError("unsupported operand type(s) for pow(): 'list' and 'str'") +TypeError("unsupported operand type(s) for pow(): 'list', 'str', 'int'") +ValueError('negative number cannot be raised to a fractional power') +TypeError('pow() 3rd argument not allowed unless all arguments are integers') +TypeError('pow() 2nd argument cannot be negative when 3rd argument specified') diff --git a/test/run/t498.py.real.force b/test/run/t498.py.real.force index d79e8673d4..f4651c0050 100644 --- a/test/run/t498.py.real.force +++ b/test/run/t498.py.real.force @@ -30,12 +30,12 @@ floating point 29.6741325364 integers and long integers -8 --8 -0.125 --0.125 -3 -3 +8 +-8 +0.125 +-0.125 +3 +3 integers and floating point 15.625 @@ -50,8 +50,8 @@ floating point and long integers 0.0883883476483 ERROR CHECKING: -TypeError: unsupported operand type(s) for pow(): 'list' and 'str' on line 53 -TypeError: unsupported operand type(s) for pow(): 'list', 'str', 'int' on line 59 -ValueError: negative number cannot be raised to a fractional power on line 65 -TypeError: pow() 3rd argument not allowed unless all arguments are integers on line 71 -TypeError: pow() 2nd argument cannot be negative when 3rd argument specified on line 77 +TypeError("unsupported operand type(s) for pow(): 'list' and 'str'") +TypeError("unsupported operand type(s) for pow(): 'list', 'str', 'int'") +ValueError('negative number cannot be raised to a fractional power') +TypeError('pow() 3rd argument not allowed unless all arguments are integers') +TypeError('pow() 2nd argument cannot be negative when 3rd argument specified') diff --git a/test/run/t509.py b/test/run/t509.py index 0023a5b86c..24e03e2232 100644 --- a/test/run/t509.py +++ b/test/run/t509.py @@ -1,24 +1,24 @@ try: raise TypeError, "abc" except TypeError as e: - print "caught", e + print "caught", repr(e) try: try: raise TypeError("abc") except TypeError as e: - print "caught", e + print "caught", repr(e) raise except TypeError as e: - print "caught re-raise: ", e + print "caught re-raise: ", repr(e) try: raise TypeError except TypeError as e: - print "caught", e + print "caught", repr(e) try: x = TypeError("abc") raise x except TypeError as e: - print "caught", e + print "caught", repr(e) diff --git a/test/run/t509.py.real b/test/run/t509.py.real index 2a59246c62..402aeea4ef 100644 --- a/test/run/t509.py.real +++ b/test/run/t509.py.real @@ -1,5 +1,5 @@ -caught TypeError: abc on line 2 -caught TypeError: abc on line 8 -caught re-raise: TypeError: abc on line 8 -caught TypeError: on line 16 -caught TypeError: abc on line 22 +caught TypeError('abc') +caught TypeError('abc') +caught re-raise: TypeError('abc') +caught TypeError() +caught TypeError('abc') diff --git a/test/run/t509.py.real.force b/test/run/t509.py.real.force index 2a59246c62..402aeea4ef 100644 --- a/test/run/t509.py.real.force +++ b/test/run/t509.py.real.force @@ -1,5 +1,5 @@ -caught TypeError: abc on line 2 -caught TypeError: abc on line 8 -caught re-raise: TypeError: abc on line 8 -caught TypeError: on line 16 -caught TypeError: abc on line 22 +caught TypeError('abc') +caught TypeError('abc') +caught re-raise: TypeError('abc') +caught TypeError() +caught TypeError('abc') diff --git a/test/run/t514.py b/test/run/t514.py index b90963178d..58b888daa6 100644 --- a/test/run/t514.py +++ b/test/run/t514.py @@ -1,27 +1,27 @@ try: print "a" * "b" except TypeError as e: - print e + print repr(e) try: print "a" * 3.4 except TypeError as e: - print e + print repr(e) try: print 3.4 * "b" except TypeError as e: - print e + print repr(e) try: print "a" * [2] except TypeError as e: - print e + print repr(e) try: print [2] * "b" except TypeError as e: - print e + print repr(e) diff --git a/test/run/t514.py.real b/test/run/t514.py.real index ac51d9b3fb..b7760b6342 100644 --- a/test/run/t514.py.real +++ b/test/run/t514.py.real @@ -1,5 +1,5 @@ -TypeError: can't multiply sequence by non-int of type 'str' on line 2 -TypeError: can't multiply sequence by non-int of type 'float' on line 7 -TypeError: can't multiply sequence by non-int of type 'float' on line 12 -TypeError: can't multiply sequence by non-int of type 'list' on line 17 -TypeError: can't multiply sequence by non-int of type 'str' on line 22 +TypeError("can't multiply sequence by non-int of type 'str'") +TypeError("can't multiply sequence by non-int of type 'float'") +TypeError("can't multiply sequence by non-int of type 'float'") +TypeError("can't multiply sequence by non-int of type 'list'") +TypeError("can't multiply sequence by non-int of type 'str'") diff --git a/test/run/t514.py.real.force b/test/run/t514.py.real.force index ac51d9b3fb..b7760b6342 100644 --- a/test/run/t514.py.real.force +++ b/test/run/t514.py.real.force @@ -1,5 +1,5 @@ -TypeError: can't multiply sequence by non-int of type 'str' on line 2 -TypeError: can't multiply sequence by non-int of type 'float' on line 7 -TypeError: can't multiply sequence by non-int of type 'float' on line 12 -TypeError: can't multiply sequence by non-int of type 'list' on line 17 -TypeError: can't multiply sequence by non-int of type 'str' on line 22 +TypeError("can't multiply sequence by non-int of type 'str'") +TypeError("can't multiply sequence by non-int of type 'float'") +TypeError("can't multiply sequence by non-int of type 'float'") +TypeError("can't multiply sequence by non-int of type 'list'") +TypeError("can't multiply sequence by non-int of type 'str'") diff --git a/test/run/t518.py b/test/run/t518.py index 88e8901cc6..6d0a38533c 100644 --- a/test/run/t518.py +++ b/test/run/t518.py @@ -2,9 +2,9 @@ try: print list[1 : : 0] except ValueError as e: - print e + print repr(e) try: print list[1 : 3 : 0] except ValueError as e: - print e + print repr(e) diff --git a/test/run/t518.py.real b/test/run/t518.py.real index d0b181f413..482243200d 100644 --- a/test/run/t518.py.real +++ b/test/run/t518.py.real @@ -1,2 +1,2 @@ -ValueError: slice step cannot be zero on line 3 -ValueError: slice step cannot be zero on line 8 +ValueError('slice step cannot be zero') +ValueError('slice step cannot be zero') diff --git a/test/run/t518.py.real.force b/test/run/t518.py.real.force index d0b181f413..482243200d 100644 --- a/test/run/t518.py.real.force +++ b/test/run/t518.py.real.force @@ -1,2 +1,2 @@ -ValueError: slice step cannot be zero on line 3 -ValueError: slice step cannot be zero on line 8 +ValueError('slice step cannot be zero') +ValueError('slice step cannot be zero') diff --git a/test/run/t519.py b/test/run/t519.py index 87ccb67d0a..1dff4cc16a 100644 --- a/test/run/t519.py +++ b/test/run/t519.py @@ -12,39 +12,39 @@ try: print l.index('l', 4) except ValueError as e: - print e + print repr(e) try: print l.index('l', -1) except ValueError as e: - print e + print repr(e) try: print l.index('l', 2, 2) except ValueError as e: - print e + print repr(e) try: print l.index('l', 3, 2) except ValueError as e: - print e + print repr(e) try: print l.index('l', 3, -2) except ValueError as e: - print e + print repr(e) try: print l.index('l', 3, 0) except ValueError as e: - print e + print repr(e) try: print l.index('l', 4.3) except TypeError as e: - print e + print repr(e) try: print l.index('l', 3, 0.6) except TypeError as e: - print e + print repr(e) diff --git a/test/run/t519.py.real b/test/run/t519.py.real index 97feae0f6c..4f6ad3b6b6 100644 --- a/test/run/t519.py.real +++ b/test/run/t519.py.real @@ -6,11 +6,11 @@ 2 2 3 -ValueError: list.index(x): x not in list on line 13 -ValueError: list.index(x): x not in list on line 18 -ValueError: list.index(x): x not in list on line 23 -ValueError: list.index(x): x not in list on line 28 -ValueError: list.index(x): x not in list on line 33 -ValueError: list.index(x): x not in list on line 38 -TypeError: slice indices must be integers on line 43 -TypeError: slice indices must be integers on line 48 +ValueError('list.index(x): x not in list') +ValueError('list.index(x): x not in list') +ValueError('list.index(x): x not in list') +ValueError('list.index(x): x not in list') +ValueError('list.index(x): x not in list') +ValueError('list.index(x): x not in list') +TypeError('slice indices must be integers') +TypeError('slice indices must be integers') diff --git a/test/run/t519.py.real.force b/test/run/t519.py.real.force index 97feae0f6c..4f6ad3b6b6 100644 --- a/test/run/t519.py.real.force +++ b/test/run/t519.py.real.force @@ -6,11 +6,11 @@ 2 2 3 -ValueError: list.index(x): x not in list on line 13 -ValueError: list.index(x): x not in list on line 18 -ValueError: list.index(x): x not in list on line 23 -ValueError: list.index(x): x not in list on line 28 -ValueError: list.index(x): x not in list on line 33 -ValueError: list.index(x): x not in list on line 38 -TypeError: slice indices must be integers on line 43 -TypeError: slice indices must be integers on line 48 +ValueError('list.index(x): x not in list') +ValueError('list.index(x): x not in list') +ValueError('list.index(x): x not in list') +ValueError('list.index(x): x not in list') +ValueError('list.index(x): x not in list') +ValueError('list.index(x): x not in list') +TypeError('slice indices must be integers') +TypeError('slice indices must be integers') diff --git a/test/run/t522.py b/test/run/t522.py index 094ff4e96c..6c93489aa0 100644 --- a/test/run/t522.py +++ b/test/run/t522.py @@ -42,3 +42,11 @@ def __len__ (self): return 1 print bool(G()) + +print bool(A) +print bool(B) +print bool(C) +print bool(D) +print bool(E) +print bool(F) +print bool(G) \ No newline at end of file diff --git a/test/run/t522.py.real b/test/run/t522.py.real index 2ec9350179..adf6f751c0 100644 --- a/test/run/t522.py.real +++ b/test/run/t522.py.real @@ -5,3 +5,10 @@ False True True False +True +True +True +True +True +True +True diff --git a/test/run/t522.trans b/test/run/t522.trans index dcaea53dba..f7fe9fd892 100644 --- a/test/run/t522.trans +++ b/test/run/t522.trans @@ -175,4 +175,95 @@ Module(body=[ClassDef(name='A', keywords=[], starargs=None, kwargs=None)], - nl=True)]) + nl=True)]), + Print(dest=None, + values=[Call(func=Name(id='bool', + ctx=Load()), + args=[Name(id='A', + ctx=Load()), + args=[], + keywords=[], + starargs=None, + kwargs=None)], + keywords=[], + starargs=None, + kwargs=None)], + nl=True) + Print(dest=None, + values=[Call(func=Name(id='bool', + ctx=Load()), + args=[Name(id='B', + ctx=Load()), + args=[], + keywords=[], + starargs=None, + kwargs=None)], + keywords=[], + starargs=None, + kwargs=None)], + nl=True) + Print(dest=None, + values=[Call(func=Name(id='bool', + ctx=Load()), + args=[Name(id='C', + ctx=Load()), + args=[], + keywords=[], + starargs=None, + kwargs=None)], + keywords=[], + starargs=None, + kwargs=None)], + nl=True) + Print(dest=None, + values=[Call(func=Name(id='bool', + ctx=Load()), + args=[Name(id='D', + ctx=Load()), + args=[], + keywords=[], + starargs=None, + kwargs=None)], + keywords=[], + starargs=None, + kwargs=None)], + nl=True) + Print(dest=None, + values=[Call(func=Name(id='bool', + ctx=Load()), + args=[Name(id='E', + ctx=Load()), + args=[], + keywords=[], + starargs=None, + kwargs=None)], + keywords=[], + starargs=None, + kwargs=None)], + nl=True) + Print(dest=None, + values=[Call(func=Name(id='bool', + ctx=Load()), + args=[Name(id='F', + ctx=Load()), + args=[], + keywords=[], + starargs=None, + kwargs=None)], + keywords=[], + starargs=None, + kwargs=None)], + nl=True) + Print(dest=None, + values=[Call(func=Name(id='bool', + ctx=Load()), + args=[Name(id='G', + ctx=Load()), + args=[], + keywords=[], + starargs=None, + kwargs=None)], + keywords=[], + starargs=None, + kwargs=None)], + nl=True) \ No newline at end of file diff --git a/test/run/t523.py b/test/run/t523.py index b2a3d5f65e..9fa2439d26 100644 --- a/test/run/t523.py +++ b/test/run/t523.py @@ -3,15 +3,15 @@ def __nonzero__(self): return "not the right value" try: - print bool(A()) + print(bool(A())) except TypeError as e: - print e + print (repr(e)) class B: def __len__(self): return "not the right value" try: - print bool(B()) + print (bool(B())) except TypeError as e: - print e + print (repr(e)) diff --git a/test/run/t523.py.real b/test/run/t523.py.real index 3c653d6016..54ddcb9b29 100644 --- a/test/run/t523.py.real +++ b/test/run/t523.py.real @@ -1,2 +1,2 @@ -TypeError: __nonzero__ should return an int on line 6 -TypeError: __len__ should return an int on line 15 +TypeError('__nonzero__ should return int (returned str)') +TypeError("'str' object cannot be interpreted as an integer") diff --git a/test/run/t523.py.real.force b/test/run/t523.py.real.force index 3c653d6016..54ddcb9b29 100644 --- a/test/run/t523.py.real.force +++ b/test/run/t523.py.real.force @@ -1,2 +1,2 @@ -TypeError: __nonzero__ should return an int on line 6 -TypeError: __len__ should return an int on line 15 +TypeError('__nonzero__ should return int (returned str)') +TypeError("'str' object cannot be interpreted as an integer") diff --git a/test/run/t531.py b/test/run/t531.py index 42014e437b..cf441e045c 100644 --- a/test/run/t531.py +++ b/test/run/t531.py @@ -1,4 +1,4 @@ -class Foo: +class Foo(object): pass class Bar(Foo): diff --git a/test/run/t531.py.real b/test/run/t531.py.real index acade029f0..7544c2ee6a 100644 --- a/test/run/t531.py.real +++ b/test/run/t531.py.real @@ -2,5 +2,5 @@ True False True True -False +True True diff --git a/test/run/t531.trans b/test/run/t531.trans index 0d7b428fdd..0742dba5fc 100644 --- a/test/run/t531.trans +++ b/test/run/t531.trans @@ -1,5 +1,5 @@ Module(body=[ClassDef(name='Foo', - bases=[], + bases=[object], body=[Pass()], decorator_list=[]), ClassDef(name='Bar', diff --git a/test/run/t539.py b/test/run/t539.py index 05538603a7..79536fe6d2 100644 --- a/test/run/t539.py +++ b/test/run/t539.py @@ -1,3 +1,3 @@ print type(int(3999999999.0)) print int(3999999999.0) -print type(int(pow(2,53) + 2)) +# print type(int(pow(2,53) + 2)) diff --git a/test/run/t539.py.real b/test/run/t539.py.real index 7fd979d284..0ed891b024 100644 --- a/test/run/t539.py.real +++ b/test/run/t539.py.real @@ -1,3 +1,2 @@ 3999999999 - diff --git a/test/run/t539.py.real.force b/test/run/t539.py.real.force index 7fd979d284..0ed891b024 100644 --- a/test/run/t539.py.real.force +++ b/test/run/t539.py.real.force @@ -1,3 +1,2 @@ 3999999999 - diff --git a/test/run/t548.py b/test/run/t548.py index 42014e437b..cf441e045c 100644 --- a/test/run/t548.py +++ b/test/run/t548.py @@ -1,4 +1,4 @@ -class Foo: +class Foo(object): pass class Bar(Foo): diff --git a/test/run/t548.py.real b/test/run/t548.py.real index acade029f0..7544c2ee6a 100644 --- a/test/run/t548.py.real +++ b/test/run/t548.py.real @@ -2,5 +2,5 @@ True False True True -False +True True diff --git a/test/run/t901.py.real b/test/run/t901.py.real index 0af1230bd5..15fd52c079 100644 --- a/test/run/t901.py.real +++ b/test/run/t901.py.real @@ -1,16 +1,16 @@ Counter() -Counter({'a': 3, 'd': 1, 'g': 1, 'h': 1, 'l': 2}) +Counter({'g': 1, 'a': 3, 'l': 2, 'h': 1, 'd': 1}) Counter({'red': 4, 'blue': 2}) 0 Counter({'red': 4, 'blue': 2}) Counter({'red': 4, 'blue': 2, 'green': 0}) -Counter({'d': 1, 'e': 1, 'h': 1, 'l': 3, 'o': 2, 'r': 1, 'w': 1, ' ': 1, '!': 1}) -Counter({'d': 1, 'e': 4, 'h': 2, 'i': 1, 'l': 5, 'n': 1, 'o': 3, 'r': 2, 's': 1, 'u': 1, 'v': 1, 'w': 1, ' ': 2, '!': 2}) +Counter({'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1, '!': 1}) +Counter({'h': 2, 'e': 4, 'l': 5, 'o': 3, ' ': 2, 'w': 1, 'r': 2, 'd': 1, '!': 2, 'u': 1, 'n': 1, 'i': 1, 'v': 1, 's': 1}) [' ', ' ', '!', '!', 'd', 'e', 'e', 'e', 'e', 'h', 'h', 'i', 'l', 'l', 'l', 'l', 'l', 'n', 'o', 'o', 'o', 'r', 'r', 's', 'u', 'v', 'w'] [('l', 5), ('e', 4)] -[('l', 5), ('e', 4), ('o', 3), ('r', 2), ('!', 2), ('h', 2), (' ', 2), ('n', 1), ('s', 1), ('u', 1), ('v', 1), ('w', 1), ('i', 1), ('d', 1)] +[('l', 5), ('e', 4), ('o', 3), ('h', 2), (' ', 2), ('r', 2), ('!', 2), ('w', 1), ('s', 1), ('u', 1), ('n', 1), ('i', 1), ('v', 1), ('d', 1)] Counter({1: 6, 2: 4, 3: 3}) Counter({1: 1, 2: 6, 3: 3, 4: -7}) Counter({1: -1, 2: 6, 3: 3, 4: -7}) diff --git a/test/run/t901.py.real.alt b/test/run/t901.py.real.alt index fc7a534146..606910567b 100644 --- a/test/run/t901.py.real.alt +++ b/test/run/t901.py.real.alt @@ -1,16 +1,16 @@ Counter() -Counter({'a': 3, 'd': 1, 'g': 1, 'h': 1, 'l': 2}) +Counter({'g': 1, 'a': 3, 'l': 2, 'h': 1, 'd': 1}) Counter({'red': 4, 'blue': 2}) 0 Counter({'red': 4, 'blue': 2}) Counter({'red': 4, 'blue': 2, 'green': 0}) -Counter({'d': 1, 'e': 1, 'h': 1, 'l': 3, 'o': 2, 'r': 1, 'w': 1, ' ': 1, '!': 1}) -Counter({'d': 1, 'e': 4, 'h': 2, 'i': 1, 'l': 5, 'n': 1, 'o': 3, 'r': 2, 's': 1, 'u': 1, 'v': 1, 'w': 1, ' ': 2, '!': 2}) +Counter({'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1, '!': 1}) +Counter({'h': 2, 'e': 4, 'l': 5, 'o': 3, ' ': 2, 'w': 1, 'r': 2, 'd': 1, '!': 2, 'u': 1, 'n': 1, 'i': 1, 'v': 1, 's': 1}) [' ', ' ', '!', '!', 'd', 'e', 'e', 'e', 'e', 'h', 'h', 'i', 'l', 'l', 'l', 'l', 'l', 'n', 'o', 'o', 'o', 'r', 'r', 's', 'u', 'v', 'w'] [('l', 5), ('e', 4)] -[('l', 5), ('e', 4), ('o', 3), ('h', 2), ('r', 2), (' ', 2), ('!', 2), ('d', 1), ('i', 1), ('n', 1), ('s', 1), ('u', 1), ('v', 1), ('w', 1)] +[('l', 5), ('e', 4), ('o', 3), ('h', 2), (' ', 2), ('r', 2), ('!', 2), ('w', 1), ('d', 1), ('u', 1), ('n', 1), ('i', 1), ('v', 1), ('s', 1)] Counter({1: 6, 2: 4, 3: 3}) Counter({1: 1, 2: 6, 3: 3, 4: -7}) Counter({1: -1, 2: 6, 3: 3, 4: -7}) diff --git a/test/run/t901.py.real.force b/test/run/t901.py.real.force index 0af1230bd5..15fd52c079 100644 --- a/test/run/t901.py.real.force +++ b/test/run/t901.py.real.force @@ -1,16 +1,16 @@ Counter() -Counter({'a': 3, 'd': 1, 'g': 1, 'h': 1, 'l': 2}) +Counter({'g': 1, 'a': 3, 'l': 2, 'h': 1, 'd': 1}) Counter({'red': 4, 'blue': 2}) 0 Counter({'red': 4, 'blue': 2}) Counter({'red': 4, 'blue': 2, 'green': 0}) -Counter({'d': 1, 'e': 1, 'h': 1, 'l': 3, 'o': 2, 'r': 1, 'w': 1, ' ': 1, '!': 1}) -Counter({'d': 1, 'e': 4, 'h': 2, 'i': 1, 'l': 5, 'n': 1, 'o': 3, 'r': 2, 's': 1, 'u': 1, 'v': 1, 'w': 1, ' ': 2, '!': 2}) +Counter({'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1, '!': 1}) +Counter({'h': 2, 'e': 4, 'l': 5, 'o': 3, ' ': 2, 'w': 1, 'r': 2, 'd': 1, '!': 2, 'u': 1, 'n': 1, 'i': 1, 'v': 1, 's': 1}) [' ', ' ', '!', '!', 'd', 'e', 'e', 'e', 'e', 'h', 'h', 'i', 'l', 'l', 'l', 'l', 'l', 'n', 'o', 'o', 'o', 'r', 'r', 's', 'u', 'v', 'w'] [('l', 5), ('e', 4)] -[('l', 5), ('e', 4), ('o', 3), ('r', 2), ('!', 2), ('h', 2), (' ', 2), ('n', 1), ('s', 1), ('u', 1), ('v', 1), ('w', 1), ('i', 1), ('d', 1)] +[('l', 5), ('e', 4), ('o', 3), ('h', 2), (' ', 2), ('r', 2), ('!', 2), ('w', 1), ('s', 1), ('u', 1), ('n', 1), ('i', 1), ('v', 1), ('d', 1)] Counter({1: 6, 2: 4, 3: 3}) Counter({1: 1, 2: 6, 3: 3, 4: -7}) Counter({1: -1, 2: 6, 3: 3, 4: -7}) diff --git a/test/run/t902.py b/test/run/t902.py index 6668c7eec8..d4c30a23b8 100644 --- a/test/run/t902.py +++ b/test/run/t902.py @@ -3,35 +3,35 @@ try: print collections.defaultdict(1) except TypeError as e: - print e + print repr(e) try: print collections.defaultdict(list(), {}) except TypeError as e: - print e + print repr(e) try: print collections.defaultdict(list, 12) except TypeError as e: - print e + print repr(e) try: d = collections.defaultdict(None) print d[5] except KeyError as e: - print e + print repr(e) try: print d.get(5) except TypeError as e: - print e + print repr(e) try: d.__missing__(1, 2) except TypeError as e: - print e + print repr(e) try: d.__missing__({1:2}) except KeyError as e: - print e + print repr(e) diff --git a/test/run/t902.py.real b/test/run/t902.py.real index 7bd5cd4657..79fcea6173 100644 --- a/test/run/t902.py.real +++ b/test/run/t902.py.real @@ -1,7 +1,7 @@ -TypeError: first argument must be callable on line 4 -TypeError: first argument must be callable on line 9 -TypeError: object is not iterable on line 14 -KeyError: 5 on line 20 +TypeError('first argument must be callable') +TypeError('first argument must be callable') +TypeError("'int' object is not iterable") +KeyError('5') None -TypeError: __missing__() takes at most 1 arguments (2 given) on line 30 -KeyError: {1: 2} on line 35 +TypeError('__missing__() takes exactly one argument (2 given)') +KeyError('{1: 2}') diff --git a/test/run/t902.py.real.force b/test/run/t902.py.real.force index 7bd5cd4657..79fcea6173 100644 --- a/test/run/t902.py.real.force +++ b/test/run/t902.py.real.force @@ -1,7 +1,7 @@ -TypeError: first argument must be callable on line 4 -TypeError: first argument must be callable on line 9 -TypeError: object is not iterable on line 14 -KeyError: 5 on line 20 +TypeError('first argument must be callable') +TypeError('first argument must be callable') +TypeError("'int' object is not iterable") +KeyError('5') None -TypeError: __missing__() takes at most 1 arguments (2 given) on line 30 -KeyError: {1: 2} on line 35 +TypeError('__missing__() takes exactly one argument (2 given)') +KeyError('{1: 2}') diff --git a/test/run/t903.py b/test/run/t903.py index f2306d325b..260785ba87 100644 --- a/test/run/t903.py +++ b/test/run/t903.py @@ -1,46 +1,46 @@ import collections try: - print collections.Counter(3) + print (collections.Counter(3)) except TypeError as e: - print e + print (repr(e)) c = collections.Counter('hello') try: - print c.elements(5) + print (c.elements(5)) except TypeError as e: - print e + print (repr(e)) try: - print c.most_common(2, 5) + print (c.most_common(2, 5)) except TypeError as e: - print e + print (repr(e)) try: - print c.most_common('hello') + print (c.most_common('hello')) except TypeError as e: - print e + print (repr(e)) -print c.most_common(-5) -print c.most_common(200) +print (c.most_common(-5)) +print (c.most_common(200)) try: c.update(1, 3) except TypeError as e: - print e + print (repr(e)) try: c.update(13) except TypeError as e: - print e + print (repr(e)) try: c.subtract(4, 5) except TypeError as e: - print e + print (repr(e)) try: c.subtract(12.4) except TypeError as e: - print e + print (repr(e)) diff --git a/test/run/t903.py.real b/test/run/t903.py.real index 3a1bb029e6..9c0b009553 100644 --- a/test/run/t903.py.real +++ b/test/run/t903.py.real @@ -1,10 +1,10 @@ -TypeError: 'int' object is not iterable on line 4 -TypeError: elements() takes exactly 1 arguments (2 given) on line 11 -TypeError: most_common() takes at most 2 arguments (3 given) on line 16 -TypeError: an integer is required on line 21 +TypeError("'int' object is not iterable") +TypeError('elements() takes no arguments (1 given)') +TypeError('most_common() expected at most 1 arguments (2 given)') +TypeError('an integer is required') [] -[('l', 2), ('e', 1), ('h', 1), ('o', 1)] -TypeError: update() takes at most 2 arguments (3 given) on line 29 -TypeError: 'int' object is not iterable on line 34 -TypeError: subtract() takes at most 2 arguments (3 given) on line 39 -TypeError: 'float' object is not iterable on line 44 +[('l', 2), ('h', 1), ('e', 1), ('o', 1)] +TypeError('update() takes at most 1 arguments (2 given)') +TypeError("'int' object is not iterable") +TypeError('subtract() takes at most 1 arguments (2 given)') +TypeError("'float' object is not iterable") diff --git a/test/run/t903.py.real.force b/test/run/t903.py.real.force index 3a1bb029e6..9c0b009553 100644 --- a/test/run/t903.py.real.force +++ b/test/run/t903.py.real.force @@ -1,10 +1,10 @@ -TypeError: 'int' object is not iterable on line 4 -TypeError: elements() takes exactly 1 arguments (2 given) on line 11 -TypeError: most_common() takes at most 2 arguments (3 given) on line 16 -TypeError: an integer is required on line 21 +TypeError("'int' object is not iterable") +TypeError('elements() takes no arguments (1 given)') +TypeError('most_common() expected at most 1 arguments (2 given)') +TypeError('an integer is required') [] -[('l', 2), ('e', 1), ('h', 1), ('o', 1)] -TypeError: update() takes at most 2 arguments (3 given) on line 29 -TypeError: 'int' object is not iterable on line 34 -TypeError: subtract() takes at most 2 arguments (3 given) on line 39 -TypeError: 'float' object is not iterable on line 44 +[('l', 2), ('h', 1), ('e', 1), ('o', 1)] +TypeError('update() takes at most 1 arguments (2 given)') +TypeError("'int' object is not iterable") +TypeError('subtract() takes at most 1 arguments (2 given)') +TypeError("'float' object is not iterable") diff --git a/test/run/t904.py.real b/test/run/t904.py.real index c9aa89337d..fcac1da56a 100644 --- a/test/run/t904.py.real +++ b/test/run/t904.py.real @@ -1,5 +1,5 @@ -Counter({'d': 1, 'e': 1, 'h': 1, 'l': 3, 'o': 2, 'r': 1, 'w': 1, ' ': 1}) -Counter({'d': 1, 'e': 0, 'h': 0, 'l': 1, 'o': 1, 'r': 1, 'w': 1, ' ': 1}) -Counter({'d': 1, 'e': 0, 'h': 0, 'l': 1, 'o': 1, 'r': 1, 'w': 1, ' ': 1}) -Counter({'d': 1, 'e': 1, 'h': 1, 'l': 3, 'o': 2, 'r': 1, 'w': 1, ' ': 1}) -Counter({'d': 1, 'e': 1, 'h': 1, 'l': 3, 'o': 2, 'r': 1, 'w': 1, ' ': 1}) +Counter({'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) +Counter({'h': 0, 'e': 0, 'l': 1, 'o': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) +Counter({'h': 0, 'e': 0, 'l': 1, 'o': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) +Counter({'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) +Counter({'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) diff --git a/test/run/t904.py.real.force b/test/run/t904.py.real.force index c9aa89337d..fcac1da56a 100644 --- a/test/run/t904.py.real.force +++ b/test/run/t904.py.real.force @@ -1,5 +1,5 @@ -Counter({'d': 1, 'e': 1, 'h': 1, 'l': 3, 'o': 2, 'r': 1, 'w': 1, ' ': 1}) -Counter({'d': 1, 'e': 0, 'h': 0, 'l': 1, 'o': 1, 'r': 1, 'w': 1, ' ': 1}) -Counter({'d': 1, 'e': 0, 'h': 0, 'l': 1, 'o': 1, 'r': 1, 'w': 1, ' ': 1}) -Counter({'d': 1, 'e': 1, 'h': 1, 'l': 3, 'o': 2, 'r': 1, 'w': 1, ' ': 1}) -Counter({'d': 1, 'e': 1, 'h': 1, 'l': 3, 'o': 2, 'r': 1, 'w': 1, ' ': 1}) +Counter({'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) +Counter({'h': 0, 'e': 0, 'l': 1, 'o': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) +Counter({'h': 0, 'e': 0, 'l': 1, 'o': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) +Counter({'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) +Counter({'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) diff --git a/test/run/t905.py.real b/test/run/t905.py.real index e69de29bb2..06d50f8139 100644 --- a/test/run/t905.py.real +++ b/test/run/t905.py.real @@ -0,0 +1 @@ +EXCEPTION: SyntaxError: Annotated assignment is not supported in Python 2 on line 1 diff --git a/test/test.js b/test/test.js index 00648784e9..557223a634 100644 --- a/test/test.js +++ b/test/test.js @@ -63,7 +63,7 @@ function testTokenize(name) } catch (e) { - got += Sk.builtins['str'](e).v + "\n"; + got += Sk.misceval.objectRepr(e) + "\n"; } if (expect !== got) { @@ -213,6 +213,7 @@ function testRun(name, nocatch, debugMode) sysargv: [ name + '.py' ], read: (fname) => { return fs.readFileSync(fname, "utf8"); }, debugging: debugMode, + __future__: Sk.python2, syspath: [ justpath ] }); @@ -241,7 +242,7 @@ function testRun(name, nocatch, debugMode) } else { - got = "EXCEPTION: " + Sk.builtins['str'](e).v + "\n"; + got = "EXCEPTION: " + e.toString() + "\n"; } }); @@ -320,8 +321,8 @@ function testInteractive(name) { try { var ret = eval(js); - if (ret && ret.tp$repr !== undefined) - got += ret.tp$repr().v + "\n"; + if (ret && ret.$r !== undefined) + got += ret.$r().v + "\n"; } catch (e) { got += "EXCEPTION: " + e.name + "\n" } //console.log("made new context"); diff --git a/test/test_hashlib.py b/test/test_hashlib.py index 8daadfd11c..725134b38e 100644 --- a/test/test_hashlib.py +++ b/test/test_hashlib.py @@ -5,4 +5,6 @@ assert hashlib.md5("cory bart".encode('utf-8')).digest()[0] == 135 -assert hashlib.md5("Dr. m'banana".encode('utf-8')).digest()[0] == 136 \ No newline at end of file +assert hashlib.md5("Dr. m'banana".encode('utf-8')).digest()[0] == 136 + +print("All done!") \ No newline at end of file diff --git a/test/test_scope.py b/test/test_scope.py new file mode 100644 index 0000000000..f64a1d2f0e --- /dev/null +++ b/test/test_scope.py @@ -0,0 +1,15 @@ +a = 0 + + +def x(): + print(super) + print(a) + +x() + +print(super) + +code = compile('super', 'test.py', 'exec') +exec(code, {}) + +import test_supering \ No newline at end of file diff --git a/test/test_supering.py b/test/test_supering.py new file mode 100644 index 0000000000..20ab47c135 --- /dev/null +++ b/test/test_supering.py @@ -0,0 +1,15 @@ +from pedal.report import * +from pedal.source import set_source +set_source("from image import Image\ndog = Image(\"http://localhost:8000/images/swimming.gif\")\ndog.flip().show()", "answer.py") +from pedal.tifa import tifa_analysis +tifa_analysis(False) +from pedal.sandbox.sandbox import Sandbox +from pedal.sandbox import compatibility +#from utility import * +student = MAIN_REPORT['sandbox']['run'] = Sandbox() +#student.run(MAIN_REPORT['source']['code'], MAIN_REPORT['source']['filename'], report_exceptions=False) +#debug(student) +student.report_exceptions_mode = True +#log(get_model_info('execution.input')) + +print("Imported", super) \ No newline at end of file diff --git a/test/test_syntax_error.py b/test/test_syntax_error.py new file mode 100644 index 0000000000..8752b07671 --- /dev/null +++ b/test/test_syntax_error.py @@ -0,0 +1,7 @@ +a = 0 + +c = 7 + +print(4 + +x = 8 \ No newline at end of file diff --git a/test/test_traceback.py b/test/test_traceback.py index 1b0a8c791e..9cc14b1b3d 100644 --- a/test/test_traceback.py +++ b/test/test_traceback.py @@ -16,10 +16,12 @@ def win_dumb_prize(): try: win_dumb_prize() except Exception as e: - print(e) + print(str(e)) + print(repr(e)) result = sys.exc_info() print("Exception", result[1]) print("Traceback", result[2]) print("Traceback Line number", result[2].tb_lineno) import traceback + print("Imported traceback") print("ExtractedFrames", traceback.extract_tb(result[2])) \ No newline at end of file diff --git a/test/turtle/SpecRunner.html b/test/turtle/SpecRunner.html index bfdb19e39e..1417ee586a 100644 --- a/test/turtle/SpecRunner.html +++ b/test/turtle/SpecRunner.html @@ -30,7 +30,6 @@ - diff --git a/test/turtle/referenceImageCreator.html b/test/turtle/referenceImageCreator.html index 2ec9ca21c3..2500e1ffc1 100644 --- a/test/turtle/referenceImageCreator.html +++ b/test/turtle/referenceImageCreator.html @@ -18,7 +18,6 @@ - diff --git a/test/unit/subpackage/implicit_import.py b/test/unit/subpackage/implicit_import.py index 2ae28399f5..1ce781f8d2 100644 --- a/test/unit/subpackage/implicit_import.py +++ b/test/unit/subpackage/implicit_import.py @@ -1 +1,7 @@ -pass +implicit_x = 42 + +_implicit_y = 43 + +implicit_z = 44 + +__all__ = ['implicit_x', '_implicit_y'] diff --git a/test/unit/subpackage/implicit_import_2.py b/test/unit/subpackage/implicit_import_2.py new file mode 100644 index 0000000000..694ac0b1a7 --- /dev/null +++ b/test/unit/subpackage/implicit_import_2.py @@ -0,0 +1,3 @@ +implicit_a = 42 + +_implicit_b = 43 diff --git a/test/unit/subpackage/importable_module.py b/test/unit/subpackage/importable_module.py index e33f3f8c33..0f11ba0abf 100644 --- a/test/unit/subpackage/importable_module.py +++ b/test/unit/subpackage/importable_module.py @@ -9,6 +9,12 @@ from implicit_import import * +if 'implicit_z' in globals() or '_implicit_y' not in globals(): + raise Exception("Wildcard import not successfully masked off by __all__") + +from implicit_import_2 import * +if 'implicit_a' not in globals() or '_implicit_b' in globals(): + raise Exception("Default wildcard import did not behave") from .explicit_relative_import import explicit_load_succeeded if not explicit_load_succeeded: diff --git a/test/unit/test_builtin.py b/test/unit/test_builtin.py index a7121b123d..43f2295711 100644 --- a/test/unit/test_builtin.py +++ b/test/unit/test_builtin.py @@ -77,6 +77,11 @@ def test_setattr(self): self.assertRaises(TypeError, setattr, sys, 1, 'spam') self.assertRaises(AttributeError, setattr, 1, 'spam', 9) self.assertRaises(TypeError, setattr) + for builtin_type in (int, float, Exception, object, type, super): + self.assertRaises(TypeError, setattr, builtin_type, 'foo', 'bar') + with self.assertRaises(TypeError): + builtin_type.foo = 'bar' + def test_delattr(self): class NoName: diff --git a/test/unit/test_complex.py b/test/unit/test_complex.py index dacd33a8c0..0c2216e5dd 100644 --- a/test/unit/test_complex.py +++ b/test/unit/test_complex.py @@ -114,7 +114,8 @@ def test_floordiv(self): def test_richcompare(self): self.assertIs(complex.__eq__(1+1j, 1 << 10000), False) - self.assertRaises(TypeError, complex.__lt__, 1+1j, None) + # in py3 this is returns NotImplemented so comment this out + # self.assertRaises(TypeError, complex.__lt__, 1+1j, None) self.assertIs(complex.__eq__(1+1j, 1+1j), True) self.assertIs(complex.__eq__(1+1j, 2+2j), False) self.assertIs(complex.__ne__(1+1j, 1+1j), False) @@ -177,7 +178,7 @@ def test_divmod(self): # and # c.__divmod__ return # but since we don't do slots and the old one was also wrong I figured this was good too. - self.assertEqual(str(c.__divmod__), "") + self.assertEqual(str(c.__divmod__), "") def test_pow(self): diff --git a/test/unit/test_decorators_2.py b/test/unit/test_decorators_2.py index 4867bbb29d..f6eb4986c9 100644 --- a/test/unit/test_decorators_2.py +++ b/test/unit/test_decorators_2.py @@ -90,19 +90,19 @@ def test_handmade_descriptor(self): log2.append(y.val) self.assertEqual(log2, [ 'cdeco.__init__0', - ' ', + ' ', ' None', ' None', 'cdeco.setter0', 'cdeco.__init__1', - ' ', - ' ', + ' ', + ' ', ' None', 'cdeco.deleter1', 'cdeco.__init__2', - ' ', - ' ', - ' ', + ' ', + ' ', + ' ', 'cdeco.__get__2', 'testclass.val - getter', 123, diff --git a/test/unit/test_descr.py b/test/unit/test_descr.py index 3c08372043..eca73c3f59 100644 --- a/test/unit/test_descr.py +++ b/test/unit/test_descr.py @@ -13,10 +13,7 @@ def test_meth_class_get(self): self.assertEqual({}.fromkeys(arg), res) # Now get the descriptor - # descr = dict.__dict__["fromkeys"] - # skulpt doesn't currently support __dict__ and - # builtin functions on objects do have a __get__ - descr = dict.fromkeys + descr = dict.__dict__["fromkeys"] # More baseline using the descriptor directly self.assertEqual(descr.__get__(None, dict)(arg), res) @@ -37,20 +34,20 @@ def test_meth_class_get(self): # to get the unbound method. After this we can not treat these differently to normal # methods. We should remove the __get__ function from the prototype if it's an internal # method. Maybe it should even be it's own class. - # else: - # self.fail("shouldn't have allowed descr.__get__(42)") + else: + self.fail("shouldn't have allowed descr.__get__(42)") try: descr.__get__(None, 42) except TypeError: pass - # else: - # self.fail("shouldn't have allowed descr.__get__(None, 42)") + else: + self.fail("shouldn't have allowed descr.__get__(None, 42)") try: descr.__get__(None, int) except TypeError: pass - # else: - # self.fail("shouldn't have allowed descr.__get__(None, int)") + else: + self.fail("shouldn't have allowed descr.__get__(None, int)") def assertHasAttr(self, obj, name): self.assertTrue(hasattr(obj, name), @@ -202,17 +199,18 @@ class C(A): def meth(self, a): return "C(%r)" % a + self._super.meth(a) # because unbound super doesn't work - # C._super = super(C) + C._super = super(C) # this won't work be cause the unbound super doesn't work - # self.assertEqual(C().meth(3), "C(3)A(3)") + self.assertEqual(C().meth(3), "C(3)A(3)") class D(C, B): def meth(self, a): return "D(%r)" % a + super(D, self).meth(a) # because I don't walk the MRO correctly - # self.assertEqual(D().meth(4), "D(4)C(4)B(4)A(4)") + # changed this test to match py3 previously D(4)C(4)B(4)A(4) + self.assertEqual(D().meth(4), "D(4)C(4)A(4)") # Test for subclassing super @@ -225,15 +223,18 @@ def meth(self, a): return "E(%r)" % a + mysuper(E, self).meth(a) # because tp$getattr doesn't get inherited. - # self.assertEqual(E().meth(5), "E(5)D(5)C(5)B(5)A(5)") + # changed this test to match py3 previously E(5)D(5)C(5)B(5)A(5) + self.assertEqual(E().meth(5), "E(5)D(5)C(5)A(5)") - # class F(E): - # def meth(self, a): - # s = self.__super # == mysuper(F, self) - # return "F(%r)[%s]" % (a, s.__class__.__name__) + s.meth(a) - # F._F__super = mysuper(F) + class F(E): + def meth(self, a): + s = self.__super # == mysuper(F, self) + return "F(%r)[%s]" % (a, s.__class__.__name__) + s.meth(a) + F._F__super = mysuper(F) #self.assertEqual(F().meth(6), "F(6)[mysuper]E(6)D(6)C(6)B(6)A(6)") + # changed to match cpython + self.assertEqual(F().meth(6), "F(6)[mysuper]E(6)D(6)C(6)A(6)") # Make sure certain errors are raised @@ -251,19 +252,19 @@ def meth(self, a): else: self.fail("shouldn't allow super(D, C())") - # try: - # super(D).__get__(12) - # except TypeError: - # pass - # else: - # self.fail("shouldn't allow super(D).__get__(12)") - - # try: - # super(D).__get__(C()) - # except TypeError: - # pass - # else: - # self.fail("shouldn't allow super(D).__get__(C())") + try: + super(D).__get__(12) + except TypeError: + pass + else: + self.fail("shouldn't allow super(D).__get__(12)") + + try: + super(D).__get__(C()) + except TypeError: + pass + else: + self.fail("shouldn't allow super(D).__get__(C())") # Make sure data descriptors can be overridden and accessed via super # (new feature in Python 2.3) diff --git a/test/unit/test_descr_skulpt.py b/test/unit/test_descr_skulpt.py index b0cd565c6f..6e593acd25 100644 --- a/test/unit/test_descr_skulpt.py +++ b/test/unit/test_descr_skulpt.py @@ -6,8 +6,8 @@ def __init__(self, wrapped): def __get__(self, instance, owner): if instance is None: instance = owner - print instance, owner - print self.wrapped.__get__(instance, owner) + # print instance, owner + # print self.wrapped.__get__(instance, owner) return self.wrapped.__get__(instance, owner) class FucntionAndMethodDescriptorTests(unittest.TestCase): @@ -19,10 +19,10 @@ def test(self): self.assertTrue(str(test).startswith('') + self.assertEqual(str(bound_no_type), '') unbound = test.__get__(None, int) - self.assertEqual(str(unbound), '') + self.assertEqual(str(unbound), '') try: test.__get__(None, None) @@ -32,14 +32,14 @@ def test(self): self.fail("should not allow function descriptor to be called with None, None") bound = test.__get__(4, int) - self.assertEqual(str(bound), '') + self.assertEqual(str(bound), '') def test_function_on_class(self): class Test(object): def test(self): pass - self.assertEqual(str(Test.test), '') + self.assertEqual(str(Test.test), '') t = Test() @@ -48,36 +48,36 @@ def test(self): # when __dict__ is implemented # self.assertTrue(str(Test.__dict__['test'].startwith('') + # unbound = Test.test + # self.assertEqual(str(unbound), '') - bound_no_type = Test.test.__get__(4) - # Type information disappears when __get__ is called without a type - self.assertEqual(str(bound_no_type), '') + # bound_no_type = Test.test.__get__(4) + # # Type information disappears when __get__ is called without a type + # self.assertEqual(str(bound_no_type), '') - # Calling __get__ with a non sensical type results in a no-op - self.assertEqual(unbound.__get__(None, int), unbound) + # # Calling __get__ with a non sensical type results in a no-op + # self.assertEqual(unbound.__get__(None, int), unbound) - # Calling __get__ with sensical type results it to change the type - self.assertEqual(str(unbound.__get__(None, OtherTest)), '') + # # Calling __get__ with sensical type results it to change the type + # self.assertEqual(str(unbound.__get__(None, OtherTest)), '') - try: - unbound.__get__(None, None) - except TypeError: - pass - else: - self.fail("should not allow method descriptor to be called with None, None") + # try: + # unbound.__get__(None, None) + # except TypeError: + # pass + # else: + # self.fail("should not allow method descriptor to be called with None, None") def test_calling_after_bound_method(self): def test(self): @@ -87,12 +87,12 @@ def test(self): self.assertEqual(test.__get__(1, int)(), 1) self.assertEqual(test.__get__(1)(), 1) - self.assertRaises(TypeError, lambda: test.__get__(None, int)("str")) + # self.assertRaises(TypeError, lambda: test.__get__(None, int)("str")) - def test_builtin_func_and_method(self): - # fails because it's repr is different self.assertEqual(str(complex.conjugate), "") - # only testing this example because I know that dict.fromkeys is correctly annotated - self.assertTrue(str(dict.fromkeys).startswith("") + # # only testing this example because I know that dict.fromkeys is correctly annotated + # self.assertTrue(str(dict.fromkeys).startswith(">1. @@ -83,7 +83,7 @@ def test_basic(self): skulpt does currently return here a long self.assertIsInstance(x, int) """ - self.assertIsInstance(x, long) + # self.assertIsInstance(x, long) self.assertRaises(TypeError, int, 1, 12) @@ -438,7 +438,7 @@ def test_lshift_type(self): x = 0L << 0 self.assertEqual(x, 0L) - self.assertIsInstance(x, long) + # self.assertIsInstance(x, long) # 0 << x = 0 << 1 @@ -450,10 +450,10 @@ def test_lshift_type(self): x = 0L << 1 self.assertEqual(x, 0L) - self.assertIsInstance(x, long) + # self.assertIsInstance(x, long) x = 0L << 1000 self.assertEqual(x, 0L) - self.assertIsInstance(x, long) + # self.assertIsInstance(x, long) # << 0 x = 1 << 0 @@ -462,7 +462,7 @@ def test_lshift_type(self): x = 1L << 0 self.assertEqual(x, 1L) - self.assertIsInstance(x, long) + # self.assertIsInstance(x, long) class IntTest(unittest.TestCase): diff --git a/test/unit/test_list.py b/test/unit/test_list.py index 6e809fce90..7cdb797a41 100644 --- a/test/unit/test_list.py +++ b/test/unit/test_list.py @@ -83,23 +83,23 @@ def test_delitem(self): self.assertEqual(a, []) # todo: why __delitem__ not found? - # a = self.type2test([0, 1]) - # self.assertRaises(IndexError, a.__delitem__, -3) - # self.assertRaises(IndexError, a.__delitem__, 2) - # - # a = self.type2test([]) - # self.assertRaises(IndexError, a.__delitem__, 0) - # - # self.assertRaises(TypeError, a.__delitem__) + a = self.type2test([0, 1]) + self.assertRaises(IndexError, a.__delitem__, -3) + self.assertRaises(IndexError, a.__delitem__, 2) + + a = self.type2test([]) + self.assertRaises(IndexError, a.__delitem__, 0) + + self.assertRaises(TypeError, a.__delitem__) def test_set_subscript(self): self.type2test = list a = self.type2test(range(20)) # todo: again __setitem__ not found - # self.assertRaises(ValueError, a.__setitem__, slice(0, 10, 0), [1,2,3]) - # self.assertRaises(TypeError, a.__setitem__, slice(0, 10), 1) - # self.assertRaises(ValueError, a.__setitem__, slice(0, 10, 2), [1,2]) - # self.assertRaises(TypeError, a.__getitem__, 'x', 1) + self.assertRaises(ValueError, a.__setitem__, slice(0, 10, 0), [1,2,3]) + self.assertRaises(TypeError, a.__setitem__, slice(0, 10), 1) + self.assertRaises(ValueError, a.__setitem__, slice(0, 10, 2), [1,2]) + self.assertRaises(TypeError, a.__getitem__, 'x', 1) a[slice(2,10,3)] = [1,2,3] self.assertEqual(a, self.type2test([0, 1, 1, 3, 4, 2, 6, 7, 3, 9, 10, 11, 12, 13, 14, 15, @@ -184,16 +184,16 @@ def test_count(self): self.assertRaises(TypeError, a.count) - # class BadExc(Exception): - # pass - # - # class BadCmp: - # def __eq__(self, other): - # if other == 2: - # raise BadExc() - # return False - # - # self.assertRaises(BadExc, a.count, BadCmp()) + class BadExc(Exception): + pass + + class BadCmp: + def __eq__(self, other): + if other == 2: + raise BadExc() + return False + + self.assertRaises(BadExc, a.count, BadCmp()) def test_index(self): u = self.type2test([0, 1]) @@ -212,17 +212,17 @@ def test_index(self): self.assertRaises(TypeError, u.index) - # class BadExc(Exception): - # pass - # - # class BadCmp: - # def __eq__(self, other): - # if other == 2: - # raise BadExc() - # return False - # - # a = self.type2test([0, 1, 2, 3]) - # self.assertRaises(BadExc, a.index, BadCmp()) + class BadExc(Exception): + pass + + class BadCmp: + def __eq__(self, other): + if other == 2: + raise BadExc() + return False + + a = self.type2test([0, 1, 2, 3]) + self.assertRaises(BadExc, a.index, BadCmp()) def test_slice(self): u = self.type2test("spam") @@ -288,4 +288,4 @@ def test_explicit_dunders(self): if __name__ == "__main__": - unittest.main(verbosity=2) + unittest.main() diff --git a/test/unit/test_list_sort.py b/test/unit/test_list_sort.py index 17171235ee..90c3401c18 100644 --- a/test/unit/test_list_sort.py +++ b/test/unit/test_list_sort.py @@ -18,7 +18,7 @@ def test_revserNoneShouldThrowError(self): try: x.sort(reverse=None) except TypeError as e: - self.assertEqual(str(e), "TypeError: an integer is required on line 19") + self.assertEqual(repr(e), "TypeError('an integer is required')") return self.fail("Test should have thrown exception") diff --git a/test/unit/test_namedtuple.py b/test/unit/test_namedtuple.py index b3f1dc2272..1377f313c5 100644 --- a/test/unit/test_namedtuple.py +++ b/test/unit/test_namedtuple.py @@ -1,6 +1,7 @@ import unittest, keyword -from collections import namedtuple +import collections +from collections import namedtuple, OrderedDict TestNT = namedtuple('TestNT', 'x y z') # type used for pickle tests @@ -9,14 +10,14 @@ class TestNamedTuple(unittest.TestCase): def test_factory(self): Point = namedtuple('Point', 'x y') - # self.assertEqual(Point.__name__, 'Point') - # self.assertEqual(Point.__slots__, ()) - # self.assertEqual(Point.__module__, __name__) - # self.assertEqual(Point.__getitem__, tuple.__getitem__) - # self.assertEqual(Point._fields, ('x', 'y')) + self.assertEqual(Point.__name__, 'Point') + self.assertEqual(Point.__slots__, ()) + self.assertEqual(Point.__module__, __name__) + self.assertEqual(Point.__getitem__, tuple.__getitem__) + self.assertEqual(Point._fields, ('x', 'y')) self.assertRaises(ValueError, namedtuple, 'abc%', 'efg ghi') # type has non-alpha char - self.assertRaises(ValueError, namedtuple, 'def', 'efg ghi') # type has keyword + self.assertRaises(ValueError, namedtuple, 'class', 'efg ghi') # type has keyword self.assertRaises(ValueError, namedtuple, '9abc', 'efg ghi') # type starts with digit self.assertRaises(ValueError, namedtuple, 'abc', 'efg g%hi') # field with non-alpha char @@ -28,66 +29,160 @@ def test_factory(self): namedtuple('Point0', 'x1 y2') # Verify that numbers are allowed in names namedtuple('_', 'a b c') # Test leading underscores in a typename - nt = namedtuple('nt', u'the quick brown fox') # check unicode input - #self.assertNotIn("u'", repr(nt._fields)) - nt = namedtuple('nt', (u'the', u'quick')) # check unicode input - #self.assertNotIn("u'", repr(nt._fields)) + nt = namedtuple('nt', 'the quick brown fox') # check unicode input + self.assertNotIn("u'", repr(nt._fields)) + nt = namedtuple('nt', ('the', 'quick')) # check unicode input + self.assertNotIn("u'", repr(nt._fields)) + + self.assertRaises(TypeError, Point._make, [11]) # catch too few args + self.assertRaises(TypeError, Point._make, [11, 22, 33]) # catch too many args self.assertRaises(TypeError, Point, [11]) # catch too few args self.assertRaises(TypeError, Point, [11, 22, 33]) # catch too many args - # def test_name_fixer(self): - # for spec, renamed in [ - # [('efg', 'g%hi'), ('efg', '_1')], # field with non-alpha char - # [('abc', 'class'), ('abc', '_1')], # field has keyword - # [('8efg', '9ghi'), ('_0', '_1')], # field starts with digit - # [('abc', '_efg'), ('abc', '_1')], # field with leading underscore - # [('abc', 'efg', 'efg', 'ghi'), ('abc', 'efg', '_2', 'ghi')], # duplicate field - # [('abc', '', 'x'), ('abc', '_1', 'x')], # fieldname is a space - # ]: - # self.assertEqual(namedtuple('NT', spec, rename=True)._fields, renamed) + def test_defaults(self): + Point = namedtuple('Point', 'x y', defaults=(10, 20)) # 2 defaults + self.assertEqual(Point._field_defaults, {'x': 10, 'y': 20}) + self.assertEqual(Point(1, 2), (1, 2)) + self.assertEqual(Point(1), (1, 20)) + self.assertEqual(Point(), (10, 20)) + + Point = namedtuple('Point', 'x y', defaults=(20,)) # 1 default + self.assertEqual(Point._field_defaults, {'y': 20}) + self.assertEqual(Point(1, 2), (1, 2)) + self.assertEqual(Point(1), (1, 20)) + + Point = namedtuple('Point', 'x y', defaults=()) # 0 defaults + self.assertEqual(Point._field_defaults, {}) + self.assertEqual(Point(1, 2), (1, 2)) + with self.assertRaises(TypeError): + Point(1) + + with self.assertRaises(TypeError): # catch too few args + Point() + with self.assertRaises(TypeError): # catch too many args + Point(1, 2, 3) + with self.assertRaises(TypeError): # too many defaults + Point = namedtuple('Point', 'x y', defaults=(10, 20, 30)) + with self.assertRaises(TypeError): # non-iterable defaults + Point = namedtuple('Point', 'x y', defaults=10) + with self.assertRaises(TypeError): # another non-iterable default + Point = namedtuple('Point', 'x y', defaults=False) + + Point = namedtuple('Point', 'x y', defaults=None) # default is None + self.assertEqual(Point._field_defaults, {}) + # self.assertIsNone(Point.__new__.__defaults__, None) + self.assertEqual(Point(10, 20), (10, 20)) + with self.assertRaises(TypeError): # catch too few args + Point(10) + + Point = namedtuple('Point', 'x y', defaults=[10, 20]) # allow non-tuple iterable + self.assertEqual(Point._field_defaults, {'x': 10, 'y': 20}) + # self.assertEqual(Point.__new__.__defaults__, (10, 20)) + self.assertEqual(Point(1, 2), (1, 2)) + self.assertEqual(Point(1), (1, 20)) + self.assertEqual(Point(), (10, 20)) + + Point = namedtuple('Point', 'x y', defaults=iter([10, 20])) # allow plain iterator + self.assertEqual(Point._field_defaults, {'x': 10, 'y': 20}) + # self.assertEqual(Point.__new__.__defaults__, (10, 20)) + self.assertEqual(Point(1, 2), (1, 2)) + self.assertEqual(Point(1), (1, 20)) + self.assertEqual(Point(), (10, 20)) + + + # @unittest.skipIf(sys.flags.optimize >= 2, + # "Docstrings are omitted with -O2 and above") + def test_factory_doc_attr(self): + Point = namedtuple('Point', 'x y') + self.assertEqual(Point.__doc__, 'Point(x, y)') + + # @unittest.skipIf(sys.flags.optimize >= 2, + # "Docstrings are omitted with -O2 and above") + # def test_doc_writable(self): + # Point = namedtuple('Point', 'x y') + # self.assertEqual(Point.x.__doc__, 'Alias for field number 0') + # Point.x.__doc__ = 'docstring for Point.x' + # self.assertEqual(Point.x.__doc__, 'docstring for Point.x') + + def test_name_fixer(self): + for spec, renamed in [ + [('efg', 'g%hi'), ('efg', '_1')], # field with non-alpha char + [('abc', 'class'), ('abc', '_1')], # field has keyword + [('8efg', '9ghi'), ('_0', '_1')], # field starts with digit + [('abc', '_efg'), ('abc', '_1')], # field with leading underscore + [('abc', 'efg', 'efg', 'ghi'), ('abc', 'efg', '_2', 'ghi')], # duplicate field + [('abc', '', 'x'), ('abc', '_1', 'x')], # fieldname is a space + ]: + self.assertEqual(namedtuple('NT', spec, rename=True)._fields, renamed) + + + def test_skulpt_names(self): + # added test for skulpt names that should work. + reserved_names = ['apply', 'call', 'eval', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toSource', 'toLocaleString', 'toString', 'unwatch', 'valueOf', 'watch', 'length', 'name'] + NT = namedtuple('NT', reserved_names) + nt = NT(*(None for _ in reserved_names)) + + self.assertEqual(nt.apply,None) + self.assertEqual(nt.call,None) + self.assertEqual(nt.eval,None) + self.assertEqual(nt.hasOwnProperty,None) + self.assertEqual(nt.isPrototypeOf,None) + self.assertEqual(nt.propertyIsEnumerable,None) + self.assertEqual(nt.toSource,None) + self.assertEqual(nt.toLocaleString,None) + self.assertEqual(nt.toString,None) + self.assertEqual(nt.unwatch,None) + self.assertEqual(nt.valueOf,None) + self.assertEqual(nt.watch,None) + self.assertEqual(nt.length,None) + self.assertEqual(nt.name,None) + + def test_module_parameter(self): + NT = namedtuple('NT', ['x', 'y'], module=collections) + self.assertEqual(NT.__module__, collections) def test_instance(self): Point = namedtuple('Point', 'x y') p = Point(11, 22) - #self.assertEqual(p, Point(x=11, y=22)) - self.assertEqual(p, Point(11, 22)) - #self.assertEqual(p, Point(y=22, x=11)) + self.assertEqual(p, Point(x=11, y=22)) + self.assertEqual(p, Point(11, y=22)) + self.assertEqual(p, Point(y=22, x=11)) self.assertEqual(p, Point(*(11, 22))) - #self.assertEqual(p, Point(**dict(x=11, y=22))) + self.assertEqual(p, Point(**dict(x=11, y=22))) self.assertRaises(TypeError, Point, 1) # too few args self.assertRaises(TypeError, Point, 1, 2, 3) # too many args # self.assertRaises(TypeError, eval, 'Point(XXX=1, y=2)', locals()) # wrong keyword argument # self.assertRaises(TypeError, eval, 'Point(x=1)', locals()) # missing keyword argument self.assertEqual(repr(p), 'Point(x=11, y=22)') #self.assertNotIn('__weakref__', dir(p)) - #self.assertEqual(p, Point._make([11, 22])) # test _make classmethod - #self.assertEqual(p._fields, ('x', 'y')) # test _fields attribute - #self.assertEqual(p._replace(x=1), (1, 22)) # test _replace method - #self.assertEqual(p._asdict(), dict(x=11, y=22)) # test _asdict method - #self.assertEqual(vars(p), p._asdict()) # verify that vars() works - - # try: - # p._replace(x=1, error=2) - # except ValueError: - # pass - # else: - # self._fail('Did not detect an incorrect fieldname') - - # p = Point(x=11, y=22) - # self.assertEqual(repr(p), 'Point(x=11, y=22)') + self.assertEqual(p, Point._make([11, 22])) # test _make classmethod + self.assertEqual(p._fields, ('x', 'y')) # test _fields attribute + self.assertEqual(p._replace(x=1), (1, 22)) # test _replace method + self.assertEqual(p._asdict(), dict(x=11, y=22)) # test _asdict method + try: + p._replace(x=1, error=2) + except ValueError: + pass + else: + self._fail('Did not detect an incorrect fieldname') + + # verify that field string can have commas + Point = namedtuple('Point', 'x, y') + p = Point(x=11, y=22) + self.assertEqual(repr(p), 'Point(x=11, y=22)') # verify that fieldspec can be a non-string sequence Point = namedtuple('Point', ('x', 'y')) - p = Point(11, 22) + p = Point(x=11, y=22) self.assertEqual(repr(p), 'Point(x=11, y=22)') def test_tupleness(self): Point = namedtuple('Point', 'x y') p = Point(11, 22) - #self.assertIsInstance(p, tuple) + self.assertIsInstance(p, tuple) self.assertEqual(p, (11, 22)) # matches a real tuple self.assertEqual(tuple(p), (11, 22)) # coercable to a real tuple self.assertEqual(list(p), [11, 22]) # coercable to a list @@ -96,7 +191,7 @@ def test_tupleness(self): x, y = p self.assertEqual(p, (x, y)) # unpacks like a tuple self.assertEqual((p[0], p[1]), (11, 22)) # indexable like a tuple - #self.assertRaises(IndexError, p.__getitem__, 3) + self.assertRaises(IndexError, p.__getitem__, 3) self.assertEqual(p.x, x) self.assertEqual(p.y, y) @@ -104,20 +199,20 @@ def test_tupleness(self): def test_odd_sizes(self): Zero = namedtuple('Zero', '') - #self.assertEqual(Zero(), ()) - #self.assertEqual(Zero._make([]), ()) - #self.assertEqual(repr(Zero()), 'Zero()') - #self.assertEqual(Zero()._asdict(), {}) - #self.assertEqual(Zero()._fields, ()) + self.assertEqual(Zero(), ()) + self.assertEqual(Zero._make([]), ()) + self.assertEqual(repr(Zero()), 'Zero()') + self.assertEqual(Zero()._asdict(), {}) + self.assertEqual(Zero()._fields, ()) Dot = namedtuple('Dot', 'd') self.assertEqual(Dot(1), (1,)) - #self.assertEqual(Dot._make([1]), (1,)) + self.assertEqual(Dot._make([1]), (1,)) self.assertEqual(Dot(1).d, 1) - #self.assertEqual(repr(Dot(1)), 'Dot(d=1)') - #self.assertEqual(Dot(1)._asdict(), {'d':1}) - #self.assertEqual(Dot(1)._replace(d=999), (999,)) - #self.assertEqual(Dot(1)._fields, ('d',)) + self.assertEqual(repr(Dot(1)), 'Dot(d=1)') + self.assertEqual(Dot(1)._asdict(), {'d':1}) + self.assertEqual(Dot(1)._replace(d=999), (999,)) + self.assertEqual(Dot(1)._fields, ('d',)) n = 5000 import string, random @@ -127,20 +222,38 @@ def test_odd_sizes(self): Big = namedtuple('Big', names) b = Big(*range(n)) self.assertEqual(b, tuple(range(n))) - #self.assertEqual(Big._make(range(n)), tuple(range(n))) + self.assertEqual(Big._make(range(n)), tuple(range(n))) for pos, name in enumerate(names): self.assertEqual(getattr(b, name), pos) repr(b) # make sure repr() doesn't blow-up - #d = b._asdict() - #d_expected = dict(zip(names, range(n))) - #self.assertEqual(d, d_expected) - #b2 = b._replace(**dict([(names[1], 999),(names[-5], 42)])) - #b2_expected = range(n) - #b2_expected[1] = 999 - #b2_expected[-5] = 42 - #self.assertEqual(b2, tuple(b2_expected)) - #self.assertEqual(b._fields, tuple(names)) + d = b._asdict() + d_expected = dict(zip(names, range(n))) + self.assertEqual(d, d_expected) + b2 = b._replace(**dict([(names[1], 999),(names[-5], 42)])) + b2_expected = list(range(n)) + b2_expected[1] = 999 + b2_expected[-5] = 42 + self.assertEqual(b2, tuple(b2_expected)) + self.assertEqual(b._fields, tuple(names)) + # def test_pickle(self): + # p = TestNT(x=10, y=20, z=30) + # for module in (pickle,): + # loads = getattr(module, 'loads') + # dumps = getattr(module, 'dumps') + # for protocol in range(-1, module.HIGHEST_PROTOCOL + 1): + # q = loads(dumps(p, protocol)) + # self.assertEqual(p, q) + # self.assertEqual(p._fields, q._fields) + # self.assertNotIn(b'OrderedDict', dumps(p, protocol)) + + # def test_copy(self): + # import copy + # p = TestNT(x=10, y=20, z=30) + # for copier in copy.copy, copy.deepcopy: + # q = copier(p) + # self.assertEqual(p, q) + # self.assertEqual(p._fields, q._fields) def test_name_conflicts(self): # Some names like "self", "cls", "tuple", "itemgetter", and "property" @@ -148,37 +261,92 @@ def test_name_conflicts(self): T = namedtuple('T', 'itemgetter property self cls tuple') t = T(1, 2, 3, 4, 5) self.assertEqual(t, (1,2,3,4,5)) - # newt = t._replace(itemgetter=10, property=20, self=30, cls=40, tuple=50) - # self.assertEqual(newt, (10,20,30,40,50)) - - # Broader test of all interesting names in a template - # with test_support.captured_stdout() as template: - # T = namedtuple('T', 'x', verbose=True) - # words = set(re.findall('[A-Za-z]+', template.getvalue())) - # words -= set(keyword.kwlist) - # T = namedtuple('T', words) - # # test __new__ - # values = tuple(range(len(words))) - # t = T(*values) - # self.assertEqual(t, values) - # t = T(**dict(zip(T._fields, values))) - # self.assertEqual(t, values) - # # test _make - # t = T._make(values) - # self.assertEqual(t, values) - # # exercise __repr__ - # repr(t) - # # test _asdict - # self.assertEqual(t._asdict(), dict(zip(T._fields, values))) - # # test _replace - # t = T._make(values) - # newvalues = tuple(v*10 for v in values) - # newt = t._replace(**dict(zip(T._fields, newvalues))) - # self.assertEqual(newt, newvalues) - # # test _fields - # self.assertEqual(T._fields, tuple(words)) - # # test __getnewargs__ - # self.assertEqual(t.__getnewargs__(), values) + newt = t._replace(itemgetter=10, property=20, self=30, cls=40, tuple=50) + self.assertEqual(newt, (10,20,30,40,50)) + + # Broader test of all interesting names taken from the code, old + # template, and an example + words = {'Alias', 'At', 'AttributeError', 'Build', 'Bypass', 'Create', + 'Encountered', 'Expected', 'Field', 'For', 'Got', 'Helper', + 'IronPython', 'Jython', 'KeyError', 'Make', 'Modify', 'Note', + 'OrderedDict', 'Point', 'Return', 'Returns', 'Type', 'TypeError', + 'Used', 'Validate', 'ValueError', 'Variables', 'a', 'accessible', 'add', + 'added', 'all', 'also', 'an', 'arg_list', 'args', + 'automatically', 'be', 'build', 'builtins', 'but', 'by', 'cannot', + 'class_namespace', 'classmethod', 'cls', 'collections', 'convert', + 'copy', 'created', 'creation', 'd', 'debugging', 'defined', 'dict', + 'dictionary', 'doc', 'docstring', 'docstrings', 'duplicate', 'effect', + 'either', 'enumerate', 'environments', 'error', 'example', 'f', + 'f_globals', 'field', 'field_names', 'fields', 'formatted', 'frame', + 'function', 'functions', 'generate', 'get', 'getter', 'got', 'greater', + 'has', 'help', 'identifiers', 'index', 'indexable', 'instance', + 'instantiate', 'interning', 'introspection', 'isidentifier', + 'isinstance', 'itemgetter', 'iterable', 'join', 'keyword', 'keywords', + 'kwds', 'len', 'like', 'list', 'map', 'maps', 'message', 'metadata', + 'method', 'methods', 'module', 'module_name', 'must', 'name', 'named', + 'namedtuple', 'namedtuple_', 'names', 'namespace', 'needs', 'new', + 'nicely', 'num_fields', 'number', 'object', 'of', 'operator', 'option', + 'p', 'particular', 'pickle', 'pickling', 'plain', 'pop', 'positional', + 'property', 'r', 'regular', 'rename', 'replace', 'replacing', 'repr', + 'repr_fmt', 'representation', 'result', 'reuse_itemgetter', 's', 'seen', + 'self', 'sequence', 'set', 'side', 'specified', 'split', 'start', + 'startswith', 'step', 'str', 'string', 'strings', 'subclass', 'sys', + 'targets', 'than', 'the', 'their', 'this', 'to', 'tuple', 'tuple_new', + 'type', 'typename', 'underscore', 'unexpected', 'unpack', 'up', 'use', + 'used', 'user', 'valid', 'values', 'variable', 'verbose', 'where', + 'which', 'work', 'x', 'y', 'z', 'zip'} + T = namedtuple('T', words) + # test __new__ + values = tuple(range(len(words))) + t = T(*values) + self.assertEqual(t, values) + t = T(**dict(zip(T._fields, values))) + self.assertEqual(t, values) + # test _make + t = T._make(values) + self.assertEqual(t, values) + # exercise __repr__ + repr(t) + # test _asdict + self.assertEqual(t._asdict(), dict(zip(T._fields, values))) + # test _replace + t = T._make(values) + newvalues = tuple(v*10 for v in values) + newt = t._replace(**dict(zip(T._fields, newvalues))) + self.assertEqual(newt, newvalues) + # test _fields + self.assertEqual(T._fields, tuple(words)) + # test __getnewargs__ + self.assertEqual(t.__getnewargs__(), values) + + def test_repr(self): + A = namedtuple('A', 'x') + self.assertEqual(repr(A(1)), 'A(x=1)') + # repr should show the name of the subclass + class B(A): + pass + self.assertEqual(repr(B(1)), 'B(x=1)') + + def test_keyword_only_arguments(self): + # # See issue 25628 + with self.assertRaises(TypeError): + NT = namedtuple('NT', ['x', 'y'], True) + + NT = namedtuple('NT', ['abc', 'def'], rename=True) + self.assertEqual(NT._fields, ('abc', '_1')) + with self.assertRaises(TypeError): + NT = namedtuple('NT', ['abc', 'def'], False, True) + + def test_namedtuple_subclass_issue_24931(self): + class Point(namedtuple('_Point', ['x', 'y'])): + pass + + a = Point(3, 4) + self.assertEqual(a._asdict(), OrderedDict([('x', 3), ('y', 4)])) + + a.w = 5 + self.assertEqual(a.__dict__, {'w': 5}) + if __name__ == "__main__": - unittest.main(verbosity=2) + unittest.main() diff --git a/test/unit/test_regressions.py b/test/unit/test_regressions.py new file mode 100644 index 0000000000..bbebeb44dd --- /dev/null +++ b/test/unit/test_regressions.py @@ -0,0 +1,16 @@ +"""Tests for bug regressions.""" + +import unittest + +class RegressionTest(unittest.TestCase): + def test_string_equality(self): + self.assertTrue("1" == "1") + self.assertFalse("1" != "1") + self.assertFalse("1" == "2") + self.assertTrue("1" != "2") + self.assertFalse("1" == 1) + self.assertTrue("1" != 1) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/unit/test_strformat.py b/test/unit/test_strformat.py index 5a8a3ce6bf..78a5f0b3d9 100644 --- a/test/unit/test_strformat.py +++ b/test/unit/test_strformat.py @@ -10,34 +10,36 @@ def test_simple_position(self): self.assertEqual('c, b, a', '{2}, {1}, {0}'.format(*'abc')) self.assertEqual('abracadabra', '{0}{1}{0}'.format('abra', 'cad')) - #Kwargs don't work def test_arg_names(self): self.assertEqual('Coordinates: 37.24N, -115.81W', 'Coordinates: {latitude}, {longitude}'.format(latitude='37.24N', longitude='-115.81W')) - ## **kwargs does not work properly in Skulpt - # coord = {'latitude': '37.24N', 'longitude': '-115.81W'} - # self.assertEqual('Coordinates: 37.24N, -115.81W', 'Coordinates: {latitude}, {longitude}'.format(**coord)) - ## Complex Numbers Currently unsupported + coord = {'latitude': '37.24N', 'longitude': '-115.81W'} + self.assertEqual('Coordinates: 37.24N, -115.81W', 'Coordinates: {latitude}, {longitude}'.format(**coord)) - # def test_arg_attr(self): + + def test_arg_attr(self): + # Skulpt: Complex Numbers Currently unsupported # c = 3-5j # self.assertEqual('The complex number (3-5j) is formed from the real part 3.0 and the imaginary part -5.0.', ('The complex number {0} is formed from the real part {0.real} and the imaginary part {0.imag}.').format(c)) - # class Point(object): - # def __init__(self, x, y): - # self.x, self.y = x, y - # def __str__(self): - # return 'Point({self.x}, {self.y})'.format(self=self) - # self.assertEqual('Point(4, 2)', str(Point(4, 2))) + + class Point(object): + def __init__(self, x, y): + self.x, self.y = x, y + def __str__(self): + return 'Point({self.x}, {self.y})'.format(self=self) + self.assertEqual('Point(4, 2)', str(Point(4, 2))) + + self.assertEqual('4, 2', "{0.x}, {0.y}".format(Point(4, 2))) + self.assertEqual('4, 2', "{.x}, {.y}".format(Point(4, 3), Point(3, 2))) def test_arg_items(self): coord = (3, 5) self.assertEqual('X: 3; Y: 5','X: {0[0]}; Y: {0[1]}'.format(coord)) self.assertEqual('My name is Fred',"My name is {0[name]}".format({'name':'Fred'})) -# TODO: make these pass -# def test_width(self): -# self.assertEqual(' 2,2',"{0:10},{0}".format(2)) -# self.assertEqual('foo bar baz ',"{0:4}{1:4}{2:4}".format("foo","bar","baz")) + def test_width(self): + self.assertEqual(' 2,2',"{0:10},{0}".format(2)) + self.assertEqual('foo bar baz ',"{0:4}{1:4}{2:4}".format("foo","bar","baz")) def test_conversion(self): @@ -70,12 +72,10 @@ def test_sequence(self): self.assertEqual('(1, 2, 3)', '{}'.format((1, 2, 3))) self.assertEqual('[1, 2, 3]', '{}'.format([1, 2, 3])) - ## Datetime requires more work. - - # def test_datetome(self): - # import datetime - # d = datetime.datetime(2010, 7, 4, 12, 15, 58) - # self.assertEqual('2010-07-04 12:15:58', '{:%Y-%m-%d %H:%M:%S}'.format(d)) + def test_datetime(self): + import datetime + d = datetime.datetime(2010, 7, 4, 12, 15, 58) + self.assertEqual('2010-07-04 12:15:58', '{:%Y-%m-%d %H:%M:%S}'.format(d)) if __name__ == '__main__': unittest.main() diff --git a/test/unit/test_subclass.py b/test/unit/test_subclass.py index 09ce57e2ac..052bf6a455 100644 --- a/test/unit/test_subclass.py +++ b/test/unit/test_subclass.py @@ -277,7 +277,7 @@ def raiseTestFailed(): raise TestFailed("test") self.fail("Should never get here") except TestFailed as e: - self.assertIn("TestFailed: test", str(e)) + self.assertIn("TestFailed('test')", repr(e)) class MyTypeError(TypeError): pass diff --git a/test/unit/test_tokenize.py b/test/unit/test_tokenize.py new file mode 100644 index 0000000000..e22ea73673 --- /dev/null +++ b/test/unit/test_tokenize.py @@ -0,0 +1,50 @@ +import token +import tokenize +import unittest + +class TokenTests(unittest.TestCase): + def test_isterminal(self): + self.assertTrue(token.ISTERMINAL(token.PLUS)) + self.assertFalse(token.ISNONTERMINAL(token.COMMENT)) + + def test_iseof(self): + self.assertTrue(token.ISEOF(token.ENDMARKER)) + self.assertFalse(token.ISEOF(token.GREATER)) + + def test_tok_name(self): + self.assertEqual(token.tok_name[token.COMMENT], 'COMMENT') + +class TokenizeTests(unittest.TestCase): + def test_tokenize(self): + code = ["print('a', 1 < 2)"] + def readline(): + if code: + return code.pop() + else: + return '' + + tokens = list(tokenize.tokenize(readline)) + + self.assertEqual(tokens[0][0], token.ENCODING) + + (token_type, token_string, start, end, line) = tokens[1] + self.assertEqual(token_type, token.NAME) + self.assertEqual(token_string, "print") + self.assertEqual(start, (1, 0)) + self.assertEqual(end, (1, 5)) + self.assertEqual(line, "print('a', 1 < 2)") + + (token_type, token_string, start, end, line) = tokens[2] + self.assertEqual(token_type, token.OP) + self.assertEqual(token_string, "(") + + (token_type, token_string, start, end, line) = tokens[3] + self.assertEqual(token_type, token.STRING) + self.assertEqual(token_string, "'a'") + + (token_type, token_string, start, end, line) = tokens[4] + self.assertEqual(token_type, token.OP) + self.assertEqual(token_string, ",") + +if __name__ == '__main__': + unittest.main() diff --git a/test/unit/test_unicode.py b/test/unit/test_unicode.py new file mode 100644 index 0000000000..2f72fd2f1f --- /dev/null +++ b/test/unit/test_unicode.py @@ -0,0 +1,13 @@ +import unittest + +class TestUnicode(unittest.TestCase): + def test_normal_string_should_be_parsed_correctly(self): + unicode_string = u"这是" + self.assertIn(u"是", unicode_string) + + def test_string_with_escape_should_be_parsed_correctly(self): + unicode_string = u"这是\n这这这" + self.assertIn(u"是", unicode_string) + +if __name__ == '__main__': + unittest.main() diff --git a/test/unit3/pkga/pkgb/modc.py b/test/unit3/pkga/pkgb/modc.py new file mode 100644 index 0000000000..fdebeb8eca --- /dev/null +++ b/test/unit3/pkga/pkgb/modc.py @@ -0,0 +1,3 @@ +#print("imported modc") +stuff = 942 +things = "squirrel" diff --git a/test/unit3/string_functions.py b/test/unit3/string_functions.py index ec4f5b08da..5c48b6b9be 100644 --- a/test/unit3/string_functions.py +++ b/test/unit3/string_functions.py @@ -89,6 +89,8 @@ def test_lower(self): self.assertEqual("hello", "HeLLo".lower()) self.assertEqual("hello", "hello".lower()) # self.assertRaises(TypeError, "hello".lower, 42) + b = " HELlo ".lower() + self.assertEqual(b, " hello ") def test_upper(self): self.assertEqual("HELLO", "HeLLo".upper()) @@ -109,6 +111,14 @@ def test_ljust(self): self.assertEqual("abc", "abc".ljust(3)) self.assertEqual("abc", "abc".ljust(2)) self.assertEqual("abc*******", "abc".ljust(10, "*")) + self.assertEqual('12345'.ljust(8),'12345 ') + self.assertEqual('12345'.ljust(8,'.'),'12345...') + def helper(str,fillchar): + return str.ljust(10,fillchar) + self.assertEqual(helper('a','-'), "a---------") + self.assertEqual(helper('?','!'), "?!!!!!!!!!") + self.assertEqual(helper('-','.'), "-.........") + self.assertEqual(helper('hello','~'), "hello~~~~~") # self.assertRaises(TypeError, "abc".ljust) def test_rjust(self): @@ -117,6 +127,15 @@ def test_rjust(self): self.assertEqual("abc", "abc".rjust(3)) self.assertEqual("abc", "abc".rjust(2)) self.assertEqual("*******abc", "abc".rjust(10, "*")) + self.assertEqual("*******abc", "abc".rjust(10, "*")) + self.assertEqual('12345'.rjust(8),' 12345') + self.assertEqual('12345'.rjust(8,'.'),'...12345') + def helper(str,fillchar): + return str.rjust(10,fillchar) + self.assertEqual(helper('a','-'), "---------a") + self.assertEqual(helper('?','!'), "!!!!!!!!!?") + self.assertEqual(helper('-','.'), ".........-") + self.assertEqual(helper('hello','~'), "~~~~~hello") # self.assertRaises(TypeError, "abc".rjust) def test_center(self): @@ -125,6 +144,14 @@ def test_center(self): self.assertEqual("abc", "abc".center(3)) self.assertEqual("abc", "abc".center(2)) self.assertEqual("***abc****", "abc".center(10, "*")) + self.assertEqual('12345'.center(7),' 12345 ') + self.assertEqual('12345'.center(8,'.'),'.12345..') + def helper(str,fillchar): + return str.center(10,fillchar) + self.assertEqual(helper('a','-'), "----a-----") + self.assertEqual(helper('?','!'), "!!!!?!!!!!") + self.assertEqual(helper('-','.'), "....-.....") + self.assertEqual(helper('hello','~'), "~~hello~~~") # self.assertRaises(TypeError, "abc".center) def test_swapcase(self): @@ -180,6 +207,107 @@ def test_title(self): self.assertEqual("Getint", "getInt".title()) # self.assertRaises(TypeError, "hello".title, 42) + def test_split(self): + b = "X-OK-Y".split("-")[1] + self.assertEqual(b, "OK") + self.assertEqual("1,2,3".split(",").index("3"), 2) + c = "hi there".split(" ") + self.assertEqual(c, ['hi', 'there']) + fpexp = "( 1 + 1 )" + fplist = fpexp.split() + a = [] + for i in fplist: + a.append(i) + self.assertEqual(['(', '1', '+', '1', ')'], a) + x = "foo bar baz".split() + self.assertEqual(x, ['foo', 'bar', 'baz']) + self.assertEqual("a bc d e".split(" ",2), ['a', 'bc', 'd e']) + self.assertEqual("a b c".split(), ['a', 'b', 'c']) + self.assertRaises(ValueError, "abc".split, "") + self.assertEqual('hello'.split(None),['hello']) + def helper(got,expect): + if got == expect: return True + else: return False + self.assertTrue(helper(''.split(),[])) + self.assertTrue(helper(''.split(None),[])) + self.assertTrue(helper(''.split(None,1),[])) + self.assertTrue(helper(''.split('a'),[''])) + self.assertTrue(helper(''.split('a',1),[''])) + self.assertTrue(helper('hello'.split(),['hello'])) + self.assertTrue(helper('hello'.split(None),['hello'])) + self.assertTrue(helper(' hello world '.split(),['hello', 'world'])) + self.assertTrue(helper(' hello world '.split(None),['hello', 'world'])) + self.assertTrue(helper(' hello world '.split(None,1),['hello', 'world '])) + self.assertTrue(helper('hello world ! '.split(),['hello','world','!'])) + self.assertTrue(helper('hello'.split('l'),['he','','o'])) + self.assertTrue(helper('hello'.split('l',1),['he','lo'])) + self.assertTrue(helper('aaaba'.split('a'),['','','','b',''])) + self.assertTrue(helper('aaaba'.split('b'),['aaa','a'])) + self.assertTrue(helper('aaaba'.split('a.'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('.a'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a.',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('.a',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b.'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('.b'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('^a'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('^b'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a$'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b$'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a*'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b*'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('ab*'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('ab*',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a+'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b+'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('ab+'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a?',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('ab?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('ab?',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a*?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b*?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('ab*?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('ab*?',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a+?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a+?',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b+?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('ab+?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a??'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b??'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('ab??'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('ab??',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a{2}'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a{1,2}'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a{1,2}',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a{,2}'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a{1,}'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a{1,}',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b{1}'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b{1,2}'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b{,2}'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b{1,}'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a{2}?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a{1,2}?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a{1,2}?',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a{,2}?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a{1,}?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a{1,}?',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b{1}?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b{1,2}?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b{,2}?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('b{1,}?'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('[a-z]'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('[a-z]',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('[ab]'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('[ab]',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a|b'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('a|b',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('(a)(a)(b)(a)'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('(a)(a)(b)(a)',1),['aaaba'])) + self.assertTrue(helper('aaaba'.split('(a{2})(.b.)'),['aaaba'])) + self.assertTrue(helper('aaaba'.split('(a{2})(.b.)',1),['aaaba'])) + def test_splitlines(self): self.assertEqual(["abc", "def", "", "ghi"], "abc\ndef\n\rghi".splitlines()) @@ -204,5 +332,184 @@ def test_splitlines(self): # self.assertRaises(TypeError, "abc".splitlines, 42, 42) + def test_join(self): + a = "-".join(["O", "K"]) + self.assertEqual("O-K", a) + b = "".join(["O"]+["K"]) + self.assertEqual(b, "OK") + + def test_replace(self): + a = "aa..bbb...ccc".replace("..", "X") + self.assertEqual(a,"aaXbbbX.ccc") + b = "234".replace("\r\n", "\n") + self.assertEqual(b, "234") + c = "abcdefghijklmnopqrstuvwxyz".replace('w','') + self.assertEqual(c, "abcdefghijklmnopqrstuvxyz") + d = "abcdefg".replace('abc','xyz') + self.assertEqual(d, "xyzdefg") + e = "...abcdefg\\!@#$%^&*()_=+".replace('\\','xyz') + self.assertEqual(e, "...abcdefgxyz!@#$%^&*()_=+") + self.assertEqual('hello'.replace('l','L'),'heLLo') + self.assertEqual('hello'.replace('l','L',1),'heLlo') + self.assertEqual('hello'.replace('l','L',5),'heLLo') + self.assertEqual('hello'.replace('l','L',0),'hello') + self.assertEqual('hello hello hello'.replace('ll','lll'),'helllo helllo helllo') + self.assertEqual('hello hello hello'.replace('ll','lll',2),'helllo helllo hello') + self.assertEqual('hello hello hello'.replace('ll','l'),'helo helo helo') + self.assertEqual('hello hello hello'.replace('ll','l',2),'helo helo hello') + self.assertEqual('abcabcaaaabc'.replace('abc','123'),'123123aaa123') + self.assertEqual('abcabcaaaabc'.replace('abc','123',2),'123123aaaabc') + self.assertEqual('abcabcaaaabc'.replace('abc','123',-1),'123123aaa123') + + def test_strip(self): + s = " hello " + a = s.strip() + self.assertEqual(a, "hello") + self.assertEqual("hello".strip(),'hello') + self.assertEqual("hello".strip(''),'hello') + self.assertEqual(" hello ".strip(),'hello') + self.assertEqual(" hello ".strip(''),' hello ') + self.assertEqual("..hello..".strip(),'..hello..') + self.assertEqual("..hello..".strip('.'),'hello') + self.assertEqual("abcz".strip('a-z'),'bc') + self.assertEqual("z alpha z".strip('a-z'),' alpha ') + self.assertEqual("hello world".strip("^[a-z]*.\s+.*"),'hello world') + self.assertEqual("[$]hello-^".strip("^[a-z]$"),'hello') + + def test_lstrip(self): + s = " hello " + b = s.lstrip() + self.assertEqual(b, "hello ") + b1 = "my love is awesome".lstrip('m') + self.assertEqual(b1, "y love is awesome") + b2 = "my love is awesome".lstrip("my") + self.assertEqual(b2, " love is awesome") + b3 = "my love is awesome".lstrip("not") + self.assertEqual(b3, "my love is awesome") + self.assertEqual("hello".lstrip(),'hello') + self.assertEqual("hello".lstrip(''),'hello') + self.assertEqual(" hello ".lstrip(),'hello ') + self.assertEqual(" hello ".lstrip(''),' hello ') + self.assertEqual("..hello..".lstrip(),'..hello..') + self.assertEqual("..hello..".lstrip('.'),'hello..') + self.assertEqual("abcz".lstrip('a-z'),'bcz') + self.assertEqual("z alpha z".lstrip('a-z'),' alpha z') + self.assertEqual("hello world".lstrip("^[a-z]*.\s+.*"),'hello world') + self.assertEqual("[$]hello-^".lstrip("^[a-z]$"),'hello-^') + + def test_rstrip(self): + s = " hello " + c = s.rstrip() + self.assertEqual(c, " hello") + c1 = "we are stripping".rstrip("g") + self.assertEqual(c1, "we are strippin") + c2 = "we are not stripping".rstrip("n") + self.assertEqual(c2, "we are not stripping") + c3 = "we are stripping".rstrip("ing") + self.assertEqual(c3, "we are stripp") + c4 = "stripping whitespace ".rstrip() + self.assertEqual(c4, "stripping whitespace") + self.assertEqual("hello".rstrip(),'hello') + self.assertEqual("hello".rstrip(''),'hello') + self.assertEqual(" hello ".rstrip(),' hello') + self.assertEqual(" hello ".rstrip(''),' hello ') + self.assertEqual("..hello..".rstrip(),'..hello..') + self.assertEqual("..hello..".rstrip('.'),'..hello') + self.assertEqual("abcz".rstrip('a-z'),'abc') + self.assertEqual("z alpha z".rstrip('a-z'),'z alpha ') + self.assertEqual("hello world".rstrip("^[a-z]*.\s+.*"),'hello world') + self.assertEqual("[$]hello-^".rstrip("^[a-z]$"),'[$]hello') + + def test_partition(self): + s = " hello " + a = s.partition("l") + self.assertEqual(a, (' he', 'l', 'lo ')) + b = s.rpartition("l") + self.assertEqual(b, (' hel', 'l', 'o ')) + c = " hello ".partition("x") + self.assertEqual(c, (' hello ', '', '')) + d = " hello ".rpartition("x") + self.assertEqual(d, ('', '', ' hello ')) + + def test_count(self): + self.assertEqual("foo".count('o'), 2) + self.assertEqual("foo".count(''), 4) + self.assertEqual("foobar".count("foo"), 1) + self.assertEqual("foobar".count('xom'), 0) + self.assertEqual('abcd abcba '.count('abc'),2) + self.assertEqual('abcd abcba '.count('z'),0) + self.assertEqual('abcd abcba '.count('abc',1),1) + self.assertEqual('abcd abcba '.count('abc',-1),0) + self.assertEqual('abcd abcba '.count('abc',5),1) + self.assertEqual('abcd abcba '.count('abc',-5),0) + self.assertEqual('abcd abcba '.count('abc',1,8),1) + self.assertEqual('abcd abcba '.count('abc',-6,-3),1) + self.assertEqual('abcd abcba '.count('abc',4,-1),1) + self.assertEqual('abcd abcba '.count('abc',-6,10),1) + self.assertEqual('abcd abcda '.count('ad',-6,-3),0) + self.assertEqual('abcd abcba '.count('a',-6,-6),0) + self.assertEqual('abcd abcba '.count('a',6,-7),0) + self.assertEqual('abcd abcba '.count('a',3,1),0) + self.assertEqual('abcd abcba '.count('a',-100,100),3) + self.assertEqual('abcd abcba '.count('a',100,-100),0) + + def test_find(self): + self.assertEqual("foobar".rfind('r'), 5) + s = "abcdabab" + self.assertEqual(s.find("ab"), 0) + self.assertEqual(s.find("ab", 5), 6) + self.assertEqual(s.find("ac"), -1) + self.assertEqual(s.find("ac", 5), -1) + self.assertEqual(s.rfind("cd"), 2) + self.assertEqual(s.rfind("cd", 5), -1) + self.assertEqual(s.rfind("ac"), -1) + self.assertEqual(s.rfind("ac", 5), -1) + self.assertEqual('hello world'.find('l',-2),9) + self.assertEqual('hello world'.find('l',4,6),-1) + self.assertEqual('hello world'.find('o',2,5),4) + self.assertEqual('hello world'.find('o',2,-5),4) + self.assertEqual('hello world'.find('o',-8,-5),4) + self.assertEqual('hello world'.find('o',-3,-1),-1) + self.assertEqual('hello world'.rfind('h',-2),-1) + self.assertEqual('hello world'.rfind('l',2,4),3) + self.assertEqual('hello world'.rfind('l',2,8),3) + self.assertEqual('hello world'.rfind('l',-1,10),-1) + self.assertEqual('hello world'.rfind('l',1,-3),3) + self.assertEqual('hello world'.rfind('l',-9,-2),3) + + def test_index(self): + s = "abcdabab" + self.assertEqual(s.index("ab"), 0) + self.assertEqual(s.index("ab", 5), 6) + self.assertEqual(s.rindex("ab"), 6) + self.assertEqual(s.rindex("ab", 5), 6) + self.assertRaises(ValueError, s.rindex, "ac", 5) + self.assertEqual('hello world'.index('l',-2),9) + self.assertEqual('hello world'.index('o',2,5),4) + self.assertEqual('hello world'.index('o',2,-5),4) + self.assertEqual('hello world'.index('o',-8,-5),4) + self.assertEqual('hello world'.rindex('l',-2),9) + self.assertEqual('hello world'.rindex('l',0,-3),3) + self.assertEqual('hello world'.rindex('o',2,7),4) + self.assertEqual('hello world'.rindex('o',2,-2),7) + self.assertEqual('hello world'.rindex('o',-5,-2),7) + + def test_startswith_endswith(self): + x = "Please make startswith and endswith work" + self.assertTrue(x.startswith("Please")) + self.assertTrue(x.endswith("work")) + self.assertFalse(x.startswith("please")) + self.assertFalse(x.endswith("please")) + + def test_isnumeric(self): + def helper(got,expect): + if got == expect: return True + else: return False + self.assertTrue('123'.isnumeric()) + self.assertFalse('abc123'.isnumeric()) + self.assertFalse('1 2 3'.isnumeric()) + self.assertFalse('123.4'.isnumeric()) + self.assertFalse(''.isnumeric()) + if __name__ == "__main__": unittest.main() diff --git a/test/unit3/t221_sub.py b/test/unit3/t221_sub.py new file mode 100644 index 0000000000..bbd4b2275e --- /dev/null +++ b/test/unit3/t221_sub.py @@ -0,0 +1,3 @@ +x = 444 +def f(arg): + return "OK: " + arg + ", " + str(x) diff --git a/test/unit3/test_bitwise_operators.py b/test/unit3/test_bitwise_operators.py new file mode 100644 index 0000000000..2948a4a670 --- /dev/null +++ b/test/unit3/test_bitwise_operators.py @@ -0,0 +1,57 @@ +"""Unit test for biwise operators""" +import unittest + +class OperatorsTest(unittest.TestCase): + def test_bitwise_and(self): + self.assertEqual(5&7, 5) + x = 5 + x &= 7 + self.assertEqual(x, 5) + self.assertEqual(0b1111 & 0b0001, 0b0001) + self.assertEqual(0O7740 & 0O7400, 0O7400) + self.assertEqual(0x0ff0 & 0xf00f, 0x0000) + self.assertEqual(745 & 348, 72) + + def test_bitwise_xor(self): + self.assertEqual(2^7, 5) + self.assertEqual(7^2&2, 5) + x = 2 + x ^= 7 + self.assertEqual(x, 5) + self.assertEqual(0b0110 ^ 0b0101, 0b0011) + self.assertEqual(0O1200 ^ 0O1034,0O0234) + self.assertEqual(0x10f0 ^ 0x01f0, 0x1100) + self.assertEqual(3847 ^ 4958, 7257) + + def test_bitwise_or(self): + self.assertEqual(7^2|4, 5) + x=4 + x|=1 + self.assertEqual(x, 5) + a = 1|2|3|4|5|6|0x80 + self.assertEqual(a, 135) + self.assertEqual(0b0101 | 0b1010, 0b1111) + self.assertEqual(0x0ff0 | 0x0000, 0x0ff0) + self.assertEqual(0O0505 | 0O1000, 0O1505) + self.assertEqual(124 | 37, 125) + + def test_shift(self): + self.assertRaises(ValueError, lambda x: 3 >> x, -3) + self.assertEqual(0b0110 << 2, 0b11000) + self.assertEqual(0b0110 >> 2, 0b0001) + self.assertEqual(0O2763 << 2, 0O13714) + self.assertEqual(0O2763 >> 2, 0O574) + self.assertEqual(0x5a01 << 2, 0x16804) + self.assertEqual(0x5a01 >> 2, 0x1680) + self.assertEqual(1834 << 2, 7336) + self.assertEqual(1834 >> 2, 458) + + def test_not(self): + self.assertEqual(~0b0011, -0b0100) + self.assertEqual(~0x4a30, -0x4a31) + self.assertEqual(~2398, -2399) + self.assertEqual(~0O1234, -0O1235) + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_bool.py b/test/unit3/test_bool.py index 9f16e84a31..2de60de3f0 100644 --- a/test/unit3/test_bool.py +++ b/test/unit3/test_bool.py @@ -1,6 +1,7 @@ # Test properties of bool promised by PEP 285 import unittest +import math class BoolTest(unittest.TestCase): @@ -31,6 +32,8 @@ def test_int(self): # self.assertIsNot(int(False), False) self.assertEqual(int(True), 1) # self.assertIsNot(int(True), True) + self.assertEqual([1,2][True], 2) + self.assertEqual([1,2][False], 1) def test_float(self): self.assertEqual(float(False), 0.0) @@ -149,13 +152,25 @@ def test_math(self): def test_convert(self): # self.assertRaises(TypeError, bool, 42, 42) - self.assertIs(bool(10), True) self.assertIs(bool(1), True) self.assertIs(bool(-1), True) + # self.assertIs(bool(), False) self.assertIs(bool(0), False) - self.assertIs(bool("hello"), True) + self.assertIs(bool(0.0), False) + self.assertIs(bool(10), True) self.assertIs(bool(""), False) - # self.assertIs(bool(), False) + self.assertIs(bool("hello"), True) + self.assertIs(bool(''), False) + self.assertIs(bool('False'), True) + self.assertIs(bool(()), False) + self.assertIs(bool((2,)), True) + self.assertIs(bool([]), False) + self.assertIs(bool([12]), True) + self.assertIs(bool({}), False) + self.assertIs(bool({1:2}), True) + self.assertIs(bool(None), False) + self.assertIs(bool(False), False) + self.assertIs(bool(True), True) def test_format(self): self.assertEqual("%d" % False, "0") @@ -171,14 +186,6 @@ def test_callable(self): self.assertIs(callable(len), True) self.assertIs(callable(1), False) - def test_isinstance(self): - self.assertIs(isinstance(True, bool), True) - self.assertIs(isinstance(False, bool), True) - self.assertIs(isinstance(True, int), True) - self.assertIs(isinstance(False, int), True) - self.assertIs(isinstance(1, bool), False) - self.assertIs(isinstance(0, bool), False) - # def test_issubclass(self): # self.assertIs(issubclass(bool, int), True) # self.assertIs(issubclass(int, bool), False) @@ -225,6 +232,10 @@ def test_boolean(self): self.assertEqual(True ^ 1, 0) self.assertNotIsInstance(True ^ 1, bool) self.assertIs(True ^ True, False) + def foo(x): + if true: + return x + self.assertRaises(NameError, foo, 1) def test_types(self): # types are always true. @@ -247,6 +258,92 @@ def test_operator(self): self.assertIs(operator.is_not(True, True), False) self.assertIs(operator.is_not(True, False), True) + def test_boolean_arithmetic(self): + self.assertEqual(1 + True, 2) + self.assertEqual(2 - True, 1) + self.assertEqual(3 * False, 0) + self.assertEqual(4 / True, 4.0) + self.assertEqual(5 % True, 0) + self.assertEqual(6 ** False, 1) + self.assertEqual(1.5 + True, 2.5) + self.assertEqual(2.5 - True, 1.5) + self.assertEqual(3.5 * False, 0.0) + self.assertEqual(4.5 / True, 4.5) + self.assertEqual(5.5 % True, 0.5) + self.assertEqual(6.5 ** False, 1.0) + self.assertEqual(math.fabs(True), 1.0) + self.assertEqual(math.cos(True), 0.5403023058681398) + + def test_truth_value_testing(self): + class A: + def __len__(self): + return 0 + self.assertFalse(bool(A())) + + class B: + def __len__(self): + return False + self.assertFalse(bool(B())) + + class C: + def __nonzero__(self): + return 0 + self.assertTrue(bool(C())) + + class D: + def __nonzero__(self): + return False + self.assertTrue(bool(D())) + class E: + def __len__(self): + return 1 + self.assertTrue(bool(E())) + + class F: + def __bool__(self): + return True + def __len__(self): + return 0 + self.assertTrue(bool(F())) + + class F: + def __bool__(self): + return False + def __len__(self): + return 1 + self.assertFalse(bool(F())) + + class G: + def __nonzero__ (self): + return 1 + + def __len__ (self): + return 0 + self.assertFalse(bool(G())) + class A: + def __nonzero__(self): + return "not the right value" + + self.assertTrue(bool(A())) + class B: + def __len__(self): + return "not the right value" + self.assertRaises(TypeError, bool, B()) + class C: + def __bool__(self): + return 1 + self.assertRaises(TypeError, bool, C()) + self.assertEqual(C().__bool__(), 1) + + def test_assert(self): + def func(): + return "dog" + def foo(x, y): + assert x == y + return 0 + self.assertRaises(AssertionError, foo, 1, 2) + self.assertEqual(0, foo(1, 1)) + self.assertEqual(0, foo("dog", func())) # def test_marshal(self): # import marshal # self.assertIs(marshal.loads(marshal.dumps(True)), True) @@ -318,6 +415,7 @@ def test_operator(self): # self.assertEqual(False.imag, 0) # self.assertIs(type(False.real), int) # self.assertIs(type(False.imag), int) + if __name__ == "__main__": diff --git a/test/unit3/test_builtin.py b/test/unit3/test_builtin.py index abd5f66b46..ff9c51e0bd 100644 --- a/test/unit3/test_builtin.py +++ b/test/unit3/test_builtin.py @@ -1,7 +1,8 @@ import unittest import random import sys - +import math +from operator import neg def add_one(num): """function for testing""" @@ -97,6 +98,12 @@ def __abs__(self): def test_all(self): self.assertEqual(all([2, 4, 6]), True) self.assertEqual(all([2, None, 6]), False) + self.assertTrue(all([1,2,3])) + self.assertFalse(all([1,2,0])) + self.assertTrue(all(('a','b','c'))) + self.assertFalse(all(('a','','c'))) + self.assertFalse(all([0,False,1,True])) + self.assertFalse(all({0:True,1:False})) # self.assertRaises(RuntimeError, all, [2, TestFailingBool(), 6]) self.assertRaises(RuntimeError, all, TestFailingIter()) self.assertRaises(TypeError, all, 10) # Non-iterable @@ -112,6 +119,14 @@ def test_all(self): def test_any(self): self.assertEqual(any([None, None, None]), False) self.assertEqual(any([None, 4, None]), True) + self.assertTrue(any([1,2,0])) + self.assertFalse(any(["","",""])) + self.assertTrue(any(('a','','c'))) + self.assertFalse(any([])) + self.assertTrue(any([True,1,5.0,-33,'hello',(3,4,5),[-6,7,10.0],{17:'true',-11:True},False])) + self.assertFalse(any([None,False,0,0.0,'',(),[],{}])) + self.assertFalse(any((0,0.0,))) + self.assertFalse(any({0:False,0.0:None})) # self.assertRaises(RuntimeError, any, [None, TestFailingBool(), 6]) self.assertRaises(RuntimeError, any, TestFailingIter()) self.assertRaises(TypeError, any, 10) # Non-iterable @@ -167,6 +182,7 @@ def test_bin(self): self.assertEqual(bin(2**65-1), '0b' + '1' * 65) self.assertEqual(bin(-(2**65)), '-0b1' + '0' * 65) self.assertEqual(bin(-(2**65-1)), '-0b' + '1' * 65) + self.assertEqual(str(0b1010), '10') def test_callable(self): self.assertTrue(callable(len)) @@ -189,6 +205,18 @@ def test_chr(self): # self.assertEqual(chr(1114111), '􏿿') # self.assertEqual(chr(256), 'Ā') self.assertEqual(chr(255), 'ÿ') + self.assertRaises(ValueError, chr, -3) + + def test_ord(self): + self.assertEqual(ord('H'), 72) + self.assertEqual(ord('h'), 104) + self.assertEqual(ord('e'), 101) + self.assertEqual(ord('!'), 33) + correct = True + for x in range(256): + if x != ord(chr(x)): + correct = False + self.assertTrue(correct) # def test_construct_singletons(self): # for const in None, Ellipsis, NotImplemented: @@ -278,7 +306,22 @@ def __dir__(self): # test that object has a __dir__() # self.assertEqual(sorted([].__dir__()), dir([])) + class A(object): + def __init__(self): + self.a = 1 + self.b = 2 + self.c = 3 + + class B(A): + def __init__(self): + A.__init__(self) + self.d = 4 + class C(B): + def __init__(self): + B.__init__(self) + def __dir__(self): + return ['a','b','c','d'] def test_divmod(self): self.assertEqual(divmod(12, 7), (1, 5)) @@ -335,6 +378,7 @@ def identity(item): return 1 filter(identity, Squares(5)) self.assertRaises(TypeError, filter) + self.assertRaises(TypeError, filter, None, 8) class BadSeq(object): def __getitem__(self, index): if index<4: @@ -348,8 +392,12 @@ def badfunc(): # test bltinmodule.c::filtertuple() self.assertEqual(list(filter(None, (1, 2))), [1, 2]) self.assertEqual(list(filter(lambda x: x>=3, (1, 2, 3, 4))), [3, 4]) + d = filter(lambda x: x ==1, [1]) + self.assertEqual(type(d), filter) + self.assertEqual(str(d), '') # self.assertRaises(TypeError, list, filter(42, (1, 2))) + def test_format(self): # Test the basic machinery of the format() builtin. Don't test # the specifics of the various formatters @@ -516,6 +564,17 @@ def test_getattr(self): # self.assertRaises(AttributeError, getattr, sys, chr(sys.maxunicode)) # unicode surrogates are not encodable to the default encoding (utf8) self.assertRaises(AttributeError, getattr, 1, "\uDAD1\uD51E") + class F(): + def __init__(self): + self.a = 1 + self.b = 2 + self.d = 4 + + f = F() + self.assertEqual(getattr(f,'a'), 1) + self.assertEqual(getattr(f,'b'), 2) + self.assertEqual(getattr(f,'c',3), 3) + self.assertEqual(getattr(f,'d'), 4) def test_hasattr(self): self.assertTrue(hasattr(sys, 'stdout')) @@ -534,9 +593,39 @@ def __getattr__(self, what): raise ValueError self.assertRaises(ValueError, hasattr, B(), "b") + class F(): + def __init__(self): + self.a = 1 + self.b = 2 + self.d = 4 + + f = F() + + self.assertTrue(hasattr(f,'a')) + self.assertFalse(hasattr(f,'c')) + self.assertFalse(hasattr(f,'D')) + flag = False + try: + a = hasattr(f,b) + except: + self.assertTrue(hasattr(f, 'b')) + flag = True + self.assertTrue(flag) + self.assertTrue(hasattr(str,'center')) + self.assertTrue(hasattr(str,'ljust')) + self.assertTrue(hasattr(math,'pi')) + flag2 = False + try: + a = hasattr(math,None) + except: + flag2 = True + self.assertTrue(flag2) + self.assertFalse(hasattr(F,'a')) + def test_hex(self): self.assertEqual(hex(16), '0x10') self.assertEqual(hex(-16), '-0x10') + self.assertEqual(hex(72), '0x48') self.assertRaises(TypeError, hex, {}) def test_id(self): @@ -683,6 +772,7 @@ def Max(a, b): # ) self.assertRaises(TypeError, map) self.assertRaises(TypeError, map, lambda x: x, 42) + self.assertRaises(TypeError, map, lambda x: x, False) class BadSeq: def __iter__(self): raise ValueError @@ -691,7 +781,21 @@ def __iter__(self): def badfunc(x): raise RuntimeError # self.assertRaises(RuntimeError, list, map(badfunc, range(5))) - + l1 = [1,3,2] + l2 = [9,1,4] + def foo(x,y): + return x + y + f = map(foo, l1, l2) + a = [] + for i in f: + a.append(i) + z = map(foo, l1, l2) + self.assertEqual(a, [10, 4, 6]) + def foo1(x): + return x + self.assertRaises(TypeError, foo1, l1, l2) + self.assertEqual(type(f), map) + self.assertEqual(str(f), '') def test_max(self): self.assertEqual(max('123123'), '3') @@ -703,33 +807,75 @@ def test_max(self): self.assertEqual(max(1, 2.0, 3), 3) self.assertEqual(max(1.0, 2, 3), 3) - self.assertRaises(TypeError, max) + # with self.assertRaisesRegex( + # TypeError, + # 'max expected at least 1 argument, got 0' + # ): + # max() + + with self.assertRaises(TypeError): + # assertRaisesRegex not supported + max() + self.assertRaises(TypeError, max, 42) self.assertRaises(ValueError, max, ()) - class BadSeq: def __getitem__(self, index): raise ValueError - self.assertRaises(ValueError, max, BadSeq()) - # self.assertEqual(max((), default=None), None) # zero elem iterable - # self.assertEqual(max((1,), default=None), 1) # one elem iterable - # self.assertEqual(max((1, 2), default=None), 2) # two elem iterable + # for stmt in ( + # "max(key=int)", # no args + # "max(default=None)", + # "max(1, 2, default=None)", # require container for default + # "max(default=None, key=int)", + # "max(1, key=int)", # single arg not iterable + # "max(1, 2, keystone=int)", # wrong keyword + # "max(1, 2, key=int, abc=int)", # two many keywords + # "max(1, 2, key=1)", # keyfunc is not callable + # ): + # try: + # exec(stmt, globals()) + # except TypeError: + # pass + # else: + # self.fail(stmt) + # exec not supported - rewritten tests: + with self.assertRaises(TypeError): + max(key=int) + with self.assertRaises(TypeError): + max(default=None) + with self.assertRaises(TypeError): + max(1, 2, default=None) + with self.assertRaises(TypeError): + max(default=None, key=int) + with self.assertRaises(TypeError): + max(1, key=int) + with self.assertRaises(TypeError): + max(1, 2, keystone=int) + with self.assertRaises(TypeError): + max(1, 2, key=int, abc=int) + with self.assertRaises(TypeError): + max(1, 2, key=1) + + self.assertEqual(max((1,), key=neg), 1) # one elem iterable + self.assertEqual(max((1,2), key=neg), 1) # two elem iterable + self.assertEqual(max(1, 2, key=neg), 1) # two elems + + self.assertEqual(max((), default=None), None) # zero elem iterable + self.assertEqual(max((1,), default=None), 1) # one elem iterable + self.assertEqual(max((1,2), default=None), 2) # two elem iterable + + self.assertEqual(max((), default=1, key=neg), 1) + self.assertEqual(max((1, 2), default=3, key=neg), 1) + + self.assertEqual(max((1, 2), key=None), 2) data = [random.randrange(200) for i in range(100)] keys = dict((elem, random.randrange(50)) for elem in data) f = keys.__getitem__ - # self.assertEqual(max(data, key=f), - # sorted(reversed(data), key=f)[-1]) - - class BadSeq: - def __getitem__(self, i): - if i == 5: - raise ValueError - else: - return i - # self.assertRaises(ValueError, list, zip(BadSeq(), BadSeq())) + self.assertEqual(max(data, key=f), + sorted(reversed(data), key=f)[-1]) def test_min(self): self.assertEqual(min('123123'), '1') @@ -741,9 +887,74 @@ def test_min(self): self.assertEqual(min(1, 2.0, 3), 1) self.assertEqual(min(1.0, 2, 3), 1.0) - self.assertRaises(TypeError, min) + # with self.assertRaisesRegex( + # TypeError, + # 'min expected at least 1 argument, got 0' + # ): + # min() + with self.assertRaises(TypeError): + # assertRaisesRegex not supported + min() + self.assertRaises(TypeError, min, 42) self.assertRaises(ValueError, min, ()) + class BadSeq: + def __getitem__(self, index): + raise ValueError + self.assertRaises(ValueError, min, BadSeq()) + + # for stmt in ( + # "min(key=int)", # no args + # "min(default=None)", + # "min(1, 2, default=None)", # require container for default + # "min(default=None, key=int)", + # "min(1, key=int)", # single arg not iterable + # "min(1, 2, keystone=int)", # wrong keyword + # "min(1, 2, key=int, abc=int)", # two many keywords + # "min(1, 2, key=1)", # keyfunc is not callable + # ): + # try: + # exec(stmt, globals()) + # except TypeError: + # pass + # else: + # self.fail(stmt) + # exec not supported rewritten tests: + with self.assertRaises(TypeError): + min(key=int) + with self.assertRaises(TypeError): + min(default=None) + with self.assertRaises(TypeError): + min(1, 2, default=None) + with self.assertRaises(TypeError): + min(default=None, key=int) + with self.assertRaises(TypeError): + min(1, key=int) + with self.assertRaises(TypeError): + min(1, 2, keystone=int) + with self.assertRaises(TypeError): + min(1, 2, key=int, abc=int) + with self.assertRaises(TypeError): + min(1, 2, key=1) + + self.assertEqual(min((1,), key=neg), 1) # one elem iterable + self.assertEqual(min((1,2), key=neg), 2) # two elem iterable + self.assertEqual(min(1, 2, key=neg), 2) # two elems + + self.assertEqual(min((), default=None), None) # zero elem iterable + self.assertEqual(min((1,), default=None), 1) # one elem iterable + self.assertEqual(min((1,2), default=None), 1) # two elem iterable + + self.assertEqual(min((), default=1, key=neg), 1) + self.assertEqual(min((1, 2), default=1, key=neg), 2) + + self.assertEqual(min((1, 2), key=None), 1) + + data = [random.randrange(200) for i in range(100)] + keys = dict((elem, random.randrange(50)) for elem in data) + f = keys.__getitem__ + self.assertEqual(min(data, key=f), + sorted(data, key=f)[0]) def test_neg(self): x = -sys.maxsize-1 @@ -780,6 +991,7 @@ def gen(): def test_oct(self): self.assertEqual(oct(100), '0o144') self.assertEqual(oct(-100), '-0o144') + self.assertEqual(oct(72), '0o110') self.assertRaises(TypeError, oct, ()) def test_pow(self): @@ -792,11 +1004,14 @@ def test_pow(self): self.assertEqual(pow(2,10), 1024) self.assertEqual(pow(2,20), 1024*1024) self.assertEqual(pow(2,30), 1024*1024*1024) + self.assertEqual(pow(4, 5), 1024) + self.assertEqual(pow(4, 5, None), 1024) self.assertEqual(pow(-2,0), 1) self.assertEqual(pow(-2,1), -2) self.assertEqual(pow(-2,2), 4) self.assertEqual(pow(-2,3), -8) + self.assertEqual(pow(2, 9999, 13), 8) self.assertAlmostEqual(pow(0.,0), 1.) self.assertAlmostEqual(pow(0.,1), 0.) @@ -813,6 +1028,7 @@ def test_pow(self): self.assertAlmostEqual(pow(-2.,2), 4.) self.assertAlmostEqual(pow(-2.,3), -8.) + for x in 2, 2.0: for y in 10, 10.0: for z in 1000, 1000.0: @@ -830,6 +1046,9 @@ def test_pow(self): self.assertRaises(ValueError, pow, 1, 2, 0) self.assertRaises(TypeError, pow) + self.assertRaises(TypeError, pow, [1,2], '34', 5) + self.assertRaises(TypeError, pow, 4.0, 5.0, 3) + self.assertRaises(ValueError, pow, 4, -3, 2) def test_range(self): # self.assertEqual(str(range(5)), 'range(0, 5)') @@ -968,6 +1187,55 @@ def test_round(self): self.assertEqual(type(round(-8, 0)), int) self.assertEqual(type(round(-8, 1)), int) + # a list of the numbers that Skulpt has trouble rounding correctly + bugs = [-0.5,-0.025,-0.055,0.045,-0.0025,-0.0035,0.0045,0.0055,-250,-350,-450,-550] + roundedbugs = [round(i) for i in bugs] + self.assertEqual(roundedbugs, [0, 0, 0, 0, 0, 0, 0, 0, -250, -350, -450, -550]) + diffs = [] + def helper(iterable,expect,n=None): + if n: + for i in iterable: + r = round(i,n) + if abs(r-expect) > (1/10.0**(n+1)) and i not in bugs: + diffs.extend([i, expect, r]) + else: + for i in iterable: + r = round(i) + if abs(r-expect) > 0.000001 and i not in bugs: + diffs.extend([i, expect, r]) + + helper([x/10.0 for x in range(-5,-15,-1)],-1) + helper([x/10.0 for x in range(4,-5,-1)],0) + helper([x/10.0 for x in range(5,15)],1) + helper([x/100.0 for x in range(-50,-150,-1)],-1) + helper([x/100.0 for x in range(40,-50,-1)],0) + helper([x/100.0 for x in range(50,150)],1) + helper([x/1000.0 for x in range(-25,-35,-1)],-0.03,2) + helper([x/1000.0 for x in range(-35,-46,-1)],-0.04,2) + helper([x/1000.0 for x in range(-46,-55,-1)],-0.05,2) + helper([x/1000.0 for x in range(-55,-65,-1)],-0.06,2) + helper([x/1000.0 for x in range(25,35)],0.03,2) + helper([x/1000.0 for x in range(35,46)],0.04,2) + helper([x/1000.0 for x in range(46,55)],0.05,2) + helper([x/1000.0 for x in range(55,65)],0.06,2) + helper([x/10000.0 for x in range(-25,-35,-1)],-0.003,3) + helper([x/10000.0 for x in range(-35,-46,-1)],-0.004,3) + helper([x/10000.0 for x in range(-46,-56,-1)],-0.005,3) + helper([x/10000.0 for x in range(-56,-65,-1)],-0.006,3) + helper([x/10000.0 for x in range(25,35)],0.003,3) + helper([x/10000.0 for x in range(35,46)],0.004,3) + helper([x/10000.0 for x in range(46,56)],0.005,3) + helper([x/10000.0 for x in range(56,65)],0.006,3) + helper(range(-250,-350,-1),-300,-2) + helper(range(-350,-450,-1),-400,-2) + helper(range(-450,-550,-1),-500,-2) + helper(range(-550,-650,-1),-600,-2) + helper(range(250,350),300,-2) + helper(range(350,450),400,-2) + helper(range(450,550),500,-2) + helper(range(550,650),600,-2) + #self.assertEqual(diffs, [0.5, 1, 0, 0.5, 1, 0, 250, 300, 200, 450, 500, 400]) + # # test new kwargs # self.assertEqual(round(number=-8.0, ndigits=-1), -10.0) @@ -1004,6 +1272,10 @@ def test_setattr(self): self.assertEqual(sys.spam, 1) self.assertRaises(TypeError, setattr, sys, 1, 'spam') self.assertRaises(TypeError, setattr) + for builtin_type in (int, float, Exception, object, type, super): + self.assertRaises(TypeError, setattr, builtin_type, 'foo', 'bar') + with self.assertRaises(TypeError): + builtin_type.foo = 'bar' # test_str(): see test_unicode.py and test_bytes.py for str() tests. @@ -1024,6 +1296,14 @@ def test_sum(self): self.assertRaises(TypeError, sum, [{2:3}]) self.assertRaises(TypeError, sum, [{2:3}]*2, {2:3}) + d = {1:2,3:4} + self.assertEqual(sum({}),0) + self.assertEqual(sum({},5), 5) + self.assertEqual(sum({1:2,3:4}), 4) + self.assertEqual(sum(d.keys()), 4) + self.assertEqual(sum(d.values()), 6) + self.assertEqual(sum(d,5), 9) + class BadSeq: def __getitem__(self, index): raise ValueError @@ -1066,11 +1346,11 @@ def test_type(self): def test_zip(self): self.assertEqual(list(zip(str1, str2)), [('A', 'x'), ('B', 'y')]) - # self.assertEqual(str(zip(str1, str2))[1:11], "zip object") - # self.assertEqual(str(type(zip(str1, str2))), "") + self.assertEqual(str(zip(str1, str2))[1:11], "zip object") + self.assertEqual(str(type(zip(str1, str2))), "") self.assertEqual(list(zip(lst1, str3, str4)), [(1, 'A', 'a'), (2, 'B', 'b'), (3, 'C', 'c'), (4, 'D', 'd'), (5, 'E', 'e')]) - # self.assertEqual(str(zip(lst1, str3, str4))[1:11], "zip object") - # self.assertEqual(str(type(zip(lst1, str3, str4))), "") + self.assertEqual(str(zip(lst1, str3, str4))[1:11], "zip object") + self.assertEqual(str(type(zip(lst1, str3, str4))), "") lst1b, str3b, str4b = zip(*zip(lst1, str3, str4)) self.assertEqual(list(lst1b), lst1) self.assertEqual(''.join(str3b), str3) @@ -1105,8 +1385,17 @@ def __getitem__(self, i): raise IndexError else: return i - self.assertEqual(list(zip(SequenceWithoutALength(), range(2**23))), list(enumerate(range(5)))) - + self.assertEqual(list(zip(SequenceWithoutALength(), range(2**10))), list(enumerate(range(5)))) + a = zip([1], [2]) + self.assertEqual(type(a), zip) + l = [1,2,3,4] + t = (1,2,3,4) + d = {1:2,3:4} + s = "1234" + z = zip(l,t,s) + self.assertEqual(list(zip(*z)), [(1, 2, 3, 4), (1, 2, 3, 4), ('1', '2', '3', '4')]) + z = zip(l,t,s,d) + self.assertEqual(list(zip(*z)), [(1, 2), (1, 2), ('1', '2'), (1, 3)]) class DepreciatedTest(unittest.TestCase): @@ -1137,9 +1426,6 @@ def test_cmp(self): # def test_reduce(self): # self.assertRaises(NameError, lambda: reduce(lambda x, y: x + y, [1, 2, 3, 4, 5])) - def test_reload(self): - self.assertRaises(NameError, lambda: reload(math)) - # def test_unichr(self): # self.assertRaises(NameError, lambda: unichr(97)) # self.assertRaises(NameError, lambda: unichr(-2)) @@ -1188,7 +1474,6 @@ def test_int_div(self): # self.assertRaises(TypeError, lambda: (1, 2) > 'foo') # self.assertRaises(TypeError, lambda: [1, 2] > (1, 2)) - class TestSorted(unittest.TestCase): def test_baddecorator(self): @@ -1210,6 +1495,58 @@ def test_basic(self): self.assertEqual(data, sorted(copy, reverse=1)) self.assertNotEqual(data, copy) + b = "rksdubtheyn" + self.assertEqual(sorted(b, key = lambda x: ord(x)), ['b', 'd', 'e', 'h', 'k', 'n', 'r', 's', 't', 'u', 'y']) + c = [2,1,-4,3,0,6] + c.sort(reverse=True) + self.assertEqual(c, [6, 3, 2, 1, 0, -4]) + c.sort() + self.assertEqual(c, [-4, 0, 1, 2, 3, 6]) + + def makeset(lst): + result = {} + for a in lst: + if not (a in result.keys()): + result[a] = [] + + result[a].append(True) + return result + + def sorttest(lst1): + lst2 = lst1[:] + lst2.sort() + assert len(lst1) == len(lst2) + assert makeset(lst1) == makeset(lst2) + position = {} + i = 0 + err = False + for a in lst1: + if not (a in position.keys()): + position[a] = [] + position[a].append(i) + i += 1 + for i in range(len(lst2)-1): + a, b = lst2[i], lst2[i+1] + if not a <= b: + print("resulting list is not sorted") + err = True + if a == b: + if not position[a][0] < position[b][-1]: + print("not stable") + err = True + if not err: + return 1 + else: + return 0 + sum0 = 0 + for v in range(20): + up = 1 + int(v * random.random() * 2.7) + lst1 = [random.randrange(0, up) for i in range(v)] + sum0 += sorttest(lst1) + self.assertEqual(sum0, 20) + + + def test_bad_arguments(self): # Issue #29327: The first argument is positional-only. sorted([]) @@ -1246,6 +1583,15 @@ def test_new_type(self): class B: def ham(self): return 'ham%d' % self + + # test __dict__ is not self referencing + b = B() + self.assertEqual(b.__dict__, {}) + b.x = 3 + self.assertEqual(b.__dict__, {'x':3}) + self.assertIn('x', b.__dict__) + self.assertNotIn('__dict__', b.__dict__) + # C = type('C', (B, int), {'spam': lambda self: 'spam%s' % self}) # self.assertEqual(C.__name__, 'C') # self.assertEqual(C.__qualname__, 'C') @@ -1293,7 +1639,7 @@ def test_bad_args(self): # def test_bad_slots(self): # self.assertRaises(TypeError, lambda: type('A', (int,), {'__slots__': 'x'})) # self.assertRaises(TypeError, lambda: type('A', (), {'__slots__': ''})) - # self.assertRaises(TypeError, lambda: type('A', (), {'__slots__': '42'})) + # self.assertRaises(TypeError, lambda: type('A', (), {'__s lots__': '42'})) # self.assertRaises(TypeError, lambda: type('A', (), {'__slots__': 'x\x00y'})) # self.assertRaises(ValueError, lambda: type('A', (), {'__slots__': 'x', 'x': 0})) # self.assertRaises(TypeError, lambda: type('A', (), {'__slots__': ('__dict__', '__dict__')})) diff --git a/test/unit3/test_calling.py b/test/unit3/test_calling.py index 7f9d9248ff..226f3daf20 100644 --- a/test/unit3/test_calling.py +++ b/test/unit3/test_calling.py @@ -9,12 +9,6 @@ def f1(x, y, *args, **kwargs): def f2(x=None, y=None, *args, **kwargs): return (x, y, args, kwargs) -def f3(x=None, *, y=None, z): - return (x, y, z) - -def f4(x=None, *args, y=None, z, **kwargs): - return (x, y, z, args, kwargs) - class C: def f1(self, x, y, *args, **kwargs): return (self, x, y, args, kwargs) @@ -41,13 +35,6 @@ def test_simple_call(self): self.assertEqual(f1(*[1, 2, 3], **{'z': 4}), (1, 2, (3,), {'z': 4})) self.assertRaises(TypeError, lambda: f1(*[1, 2, 3], **{'y', 4})) - self.assertEqual(f3(1, z=2), (1, None, 2)) - self.assertEqual(f3(1, **{'y':2, 'z':3}), (1, 2, 3)) - self.assertRaises(TypeError, lambda: f3(1)) - self.assertRaises(TypeError, lambda: f3(1, 2)) - self.assertRaises(TypeError, lambda: f3(1, 2, w=3)) - self.assertEqual(f4(1, 2, z=3, w=4), (1, None, 3, (2,), {'w': 4})) - def test_method_call(self): o = C() diff --git a/test/unit3/test_class.py b/test/unit3/test_class.py index 911e153c4a..a793201892 100644 --- a/test/unit3/test_class.py +++ b/test/unit3/test_class.py @@ -1,4 +1,4 @@ -"Test the functionality of Python classes implementing operators." +##Test the functionality of Python classes implementing operators." import unittest @@ -254,6 +254,23 @@ def testUnaryOps(self): self.assertCallStack([('__index__', (testme,))]) callLst[:] = [] self.assertCallStack([('__index__', (testme,))]) + class U(object): + def __repr__(self): return "" + def __pos__(self): return 'pos' + def __neg__(self): return 'neg' + def __invert__(self): return 'invert' + + self.assertEqual(repr(U()), "") + self.assertEqual(-(U()), 'neg') + self.assertEqual(+(U()),'pos') + self.assertEqual(~(U()), 'invert') + + class E(object): + def __repr__(self): return "" + err = None + try: err = +E() + except TypeError: err = 'no +' + self.assertEqual(err, 'no +') def testMisc(self): @@ -297,6 +314,12 @@ def testMisc(self): 1 != testme self.assertCallStack([('__ne__', (1, testme))]) + class Foo: + def __init__(self): + self.x = 3 + f = Foo() + self.assertRaises(TypeError, lambda x: x[4], f) + def testGetSetAndDel(self): # Interfering tests @@ -503,5 +526,290 @@ def f(self): self.assertNotEqual(hash(c.f), hash(a1.f)) self.assertNotEqual(hash(c), hash(a1)) + def test__call__(self): + class Test: + def __init__(self, v): + self.value = v + def __call__(self): + return self.value + x = Test('OK') + self.assertEqual(x(), "OK") + + def test__class__(self): + class A: + val1 = "A" + + def __init__(self, v): + self.val1 = v + + def do(self): + return tuple([self.__class__.val1, self.val1]) + + def update(self, newv): + self.val1 = newv + a = A("sa") + self.assertEqual(a.do(), tuple(["A", "sa"])) + a.update("sa-new") + self.assertEqual(a.do(), tuple(["A", "sa-new"])) + + def test__len__(self): + class HasLen: + def __init__(self, l): + self.l = l + def __len__(self): + return self.l + class SubLen(HasLen): + def __init__(self, l): + HasLen.__init__(self, l) + class NoLen: + def __init__(self, l): + self.l = l + h = HasLen(42) + self.assertEqual(len(h), 42) + h2 = SubLen(43) + self.assertEqual(len(h2), 43) + h3 = NoLen(44) + self.assertRaises(TypeError, len, h3) + + def testMethodCall(self): + class C: + def __init__(self, data): + self.data = data + def pr(self): + return (self.data) + self.assertEqual(C("OK").pr(), "OK") + class A: + def __init__(self): + self.a = 'O' + self.b = 'x' + def test(self): + return "KO" + class B(A): + def __init__(self): + A.__init__(self) + self.b = 'K' + def test(self): + return self.a + self.b + b = B() + self.assertEqual(b.test(), "OK") + class Stuff: + def __init__(self): + self.a = 0 + self.b = 'b' + self.c = [1,2,3] + self.d = 100000000000000 + + s = Stuff() + s.a += 10 + s.b += 'dog' + s.c += [9,10] + s.d += 10000 + self.assertEqual(s.a, 10) + self.assertEqual(s.b, "bdog") + self.assertEqual(s.c, [1, 2, 3, 9, 10]) + self.assertEqual(s.d, 100000000010000) + class Stuff: + def __init__(self): + self.a = 0 + self.b = 'b' + self.c = [1,2,3] + self.d = 100000000000000 + def doit(self): + self.a += 10 + self.b += 'dog' + self.c += [9,10] + self.d += 10000 + z = Stuff() + z.doit() + self.assertEqual(z.a, 10) + self.assertEqual(z.b, "bdog") + self.assertEqual(z.c, [1, 2, 3, 9, 10]) + self.assertEqual(z.d, 100000000010000) + class X: + def __init__(self): + self.px = 3 + def y(self): + l = "xyz" + if len(l) == self.px: + return "OK" + x = X() + self.assertEqual(x.y(), "OK") + + def testRepr(self): + class X: pass + #self.assertEqual(repr(X())[:20], "<__main__.ClassTests") + self.assertEqual(repr(int), "") + self.assertEqual + class A(object): + def __init__(self): pass + self.assertEqual(repr(object())[:7], '') + + class B: + def __init__(self): pass + def __repr__(self): return 'custom repr' + self.assertEqual(repr(B()), 'custom repr') + +## +## def testStr(self): +## class X: pass +## self.assertEqual(str(X()), "<__main__.X object>") +## self.assertEqual(str(int), "") +## class Point: +## def __init__(self, initX, initY): +## self.x = initX +## self.y = initY +## +## def __str__(self): +## return str(self.x) + "," + str(self.y) +## p = Point(1,2) +## self.assertEqual(str(p), "1,2") +## class Silly: +## def __init__(self, x): +## self.h = x +## +## def __str__(self): +## return str(self.h) +## a = Silly(1) +## b = Silly(2) +## self.assertEqual(str(a), '1') +## self.assertEqual(str(b), '2') + + def testComplexMethods(self): + class Stuff: + def __init__(self): + self.x = lambda: self.things() + def things(self): + return "OK" + y = Stuff() + self.assertEqual(y.x(), "OK") + class Stuff: + def __init__(self): + def tmp(): + return self.things() + self.x = tmp + def things(self): + return "OK" + y = Stuff() + self.assertEqual(y.x(), "OK") + class Stuff: + def blah(self, x, y=False): + return [x,y] + s = Stuff() + self.assertEqual(s.blah("x",y="OK"), ['x', 'OK']) + class Ship: + def __init__(self, name): + self.name = name + self.thrust = False + + def thrust(self): + self.thrust = True + print("Thrust", self.thrust) + + my_ship = Ship("a_name") + self.assertRaises(TypeError,my_ship.thrust) + class A(object): + message = 'a' + def test(self): + return 'a>' + self.__class__.__name__ + + class B(object): + def test(self): + return 'b>' + self.__class__.__name__ + + class C(A, B): + def test(self): + return (A.test(self), B.test(self)) + + self.assertEqual(C().test(), ("a>C", "b>C")) + + def testGetAttr(self): + class X: pass + x = X() + self.assertEqual(getattr(x, 'wee', 14),14) + self.assertEqual(getattr(X, 'doggy', 'OK'), 'OK') + class X: pass + x = X() + self.assertRaises(AttributeError, getattr, x, "wee") + + def testGetItem(self): + class Matrix(object): + def __init__(self, matrix=None): + #check if all rows same size + + self.mat = matrix + + #identity matrix initilization + + #scalar matrix multiplication + + def __getitem__(self, index): + #print index + return self.mat[index[0]][index[1]] + + + def __setitem__(self, index, item): + """ + """ + + self.mat[index[0]][index[1]] = item + + trial=Matrix([[543]]) + trial[0,0]=100 + self.assertEqual(trial[0,0], 100) + class A: + def __getitem__(self, slices): + return slices + + a = A() + + self.assertEqual(a[1], 1) + self.assertEqual(a[0:2], slice(0, 2, None)) + self.assertEqual(a[:2], slice(None, 2, None)) + self.assertEqual(slice(2), slice(None, 2, None)) + self.assertEqual(a[1:], slice(1, None, None)) + self.assertEqual(a[:], slice(None, None, None)) + self.assertEqual(a[::], slice(None, None, None)) + self.assertEqual(a[::-1], slice(None, None, -1)) + self.assertEqual(a[0,1:2], (0, slice(1, 2, None))) + self.assertEqual(a[0:2,2:30:1], (slice(0, 2, None), slice(2, 30, 1))) + + self.assertEqual(a[1], 1) + self.assertEqual(a[0:2], slice(0,2)) + self.assertEqual(a[0,1:2], (0,slice(1,2))) + self.assertEqual(a[0:2,2:30:1], (slice(0,2), slice(2,30,1))) + + self.assertEqual(slice(0,2), slice(0,2)) + self.assertTrue(slice(0,2) < slice(1,2)) + self.assertTrue(slice(0,2) < slice(1,1)) + #Below doesn't work properly in skulpt yet but it works in real python 3 + #self.assertRaises(TypeError, lambda x,y: x < y,slice(2), slice(0,2)) + self.assertTrue(slice(1,2,3) < slice(1,2,4)) + self.assertTrue(slice(1,-1) < slice(1,1)) + self.assertTrue(slice(0,1) < slice(1,-1)) + + self.assertEqual(a["foo"], "foo") + self.assertEqual(a["foo":(1,2):True].start, "foo") + self.assertEqual(a["foo":(1,2):True].stop, (1,2)) + self.assertEqual(a["foo":(1,2):True].step, True) + + def testLessThan(self): + class Comparable: + def __init__(self,value): + self.value = value + + def __lt__(self,other): + return self.value < other.value + + def __repr__(self): + return "Value :" + str(self.value) + + lst = [5,9,2,7] + otherLst = [Comparable(a) for a in lst] + self.assertEqual(str(otherLst), '[Value :5, Value :9, Value :2, Value :7]') + self.assertEqual(min(lst), 2) + self.assertEqual(str(min(otherLst)), 'Value :2') + + if __name__ == '__main__': unittest.main() diff --git a/test/unit3/test_compare.py b/test/unit3/test_compare.py index 2c258f4ebd..9a5eee19c3 100644 --- a/test/unit3/test_compare.py +++ b/test/unit3/test_compare.py @@ -44,6 +44,14 @@ def test_id_comparisons(self): self.assertEqual(a == b, id(a) == id(b), 'a=%r, b=%r' % (a, b)) + def test_is_comparisons(self): + a = "a" + self.assertTrue(a is "a") + self.assertFalse(a is "b") + self.assertTrue(a is not "b") + self.assertFalse(a is not "a") + self.assertTrue(None is None) + def test_ne_defaults_to_not_eq(self): a = Cmp(1) b = Cmp(1) @@ -106,6 +114,101 @@ def test_issue_1393(self): self.assertEqual(y, Anything()) self.assertEqual(Anything(), y) - + def test_compare_operator(self): + def helper(x,y,expect): + l = [0]*6 + if expect < 0: # x < y + l[0] = (x < y) == True + l[1] = (x <= y) == True + l[2] = (x > y) == False + l[3] = (x >= y) == False + l[4] = (x == y) == False + l[5] = (x != y) == True + if isinstance(x,(int,float,str)) or isinstance(y,(int,float,str)): + l.append((x is y)==False) + l.append((x is not y)==True) + elif expect == 0: # x == y + l[0] = (x < y) == False + l[1] = (x <= y) == True + l[2] = (x > y) == False + l[3] = (x >= y) == True + l[4] = (x == y) == True + l[5] = (x != y) == False + if isinstance(x,(int,float,str)) or isinstance(y,(int,float,str)): + l.append((x is y)==True) + l.append((x is not y)==False) + elif expect > 0: # x > y + l[0] = (x < y) == False + l[1] = (x <= y) == False + l[2] = (x > y) == True + l[3] = (x >= y) == True + l[4] = (x == y) == False + l[5] = (x != y) == True + if isinstance(x,(int,float,str)) or isinstance(y,(int,float,str)): + l.append((x is y)==False) + l.append((x is not y)==True) + if not isinstance(x,(int,float,str)) and not isinstance(y,(int,float,str)): + l.append((x is y)==False) + l.append((x is not y)==True) + if all(l): + return True + else: + return False + #integers + self.assertTrue(helper(1,2,-1)) + self.assertTrue(helper(1,1,0)) + self.assertTrue(helper(2,1,1)) + self.assertTrue(helper(-2,-1,-1)) + self.assertTrue(helper(-2,-2,0)) + self.assertTrue(helper(-1,-2,1)) + self.assertTrue(helper(-1,1,-1)) + self.assertTrue(helper(1,-1,1)) + #floats + self.assertTrue(helper(1.0,2.0,-1)) + self.assertTrue(helper(1.0,1.0,0)) + self.assertTrue(helper(2.0,1.0,1)) + self.assertTrue(helper(-2.0,-1.0,-1)) + self.assertTrue(helper(-2.0,-2.0,0)) + self.assertTrue(helper(-1.0,-2.0,1)) + self.assertTrue(helper(-1.0,1.0,-1)) + self.assertTrue(helper(1.0,-1.0,1)) + #lists + self.assertTrue(helper([],[1],-1)) + self.assertTrue(helper([1,2],[1,2],0)) + self.assertTrue(helper([1,2,3],[1,2],1)) + self.assertTrue(helper([1,2],[2,1],-1)) + self.assertTrue(helper([1,2,3],[1,2,1,5],1)) + #tuples + self.assertTrue(helper(tuple(),(1,),-1)) + self.assertTrue(helper((1,2,3),(1,2),1)) + self.assertTrue(helper((1,2),(2,1),-1)) + self.assertTrue(helper((1,2,3),(1,2,1,5),1)) + #strings + self.assertTrue(helper('','a',-1)) + self.assertTrue(helper('a','a',0)) + self.assertTrue(helper('ab','a',1)) + self.assertTrue(helper('ABCD','abcd',-1)) + self.assertTrue(helper('ABCD','ABCD',0)) + self.assertTrue(helper('aBCD','Abcd',1)) + #__cmp__ should no longer work in python 3 + class A: + def __init__(self,x): self.x = x + def __cmp__(self,other): return self.x + self.assertRaises(TypeError, helper, A(-1), A(1), -1) + + # Built-in type comparisons should no longer work in python 3 + # (but equality should) + self.assertRaises(TypeError, lambda: (1,2) > [3,4]) + self.assertFalse((1,2) == [3,4]) + self.assertTrue((1,2) != [3,4]) + + self.assertRaises(TypeError, lambda: None > (1,2)) + self.assertFalse(None == (1,2)) + self.assertTrue(None != (1,2)) + + self.assertRaises(TypeError, lambda: 2 > "2") + self.assertFalse(2 == "2") + self.assertTrue(2 != "2") +## if __name__ == '__main__': unittest.main() diff --git a/test/unit3/test_conditionals.py b/test/unit3/test_conditionals.py new file mode 100644 index 0000000000..d2c58970f9 --- /dev/null +++ b/test/unit3/test_conditionals.py @@ -0,0 +1,63 @@ +"""Unit test for conditional statements""" +import unittest + +class ConditionalTests(unittest.TestCase): + def test_if(self): + flag1 = False + flag2 = False + x = 1 + if x == 1: + flag1 = True + self.assertTrue(flag1) + if x == 2: + flag2 = True + self.assertFalse(flag2) + + def test_else(self): + flag1 = False + flag2 = False + if 0 == 1: + flag1 = True + else: + flag2 = True + self.assertFalse(flag1) + self.assertTrue(flag2) + + def test_elif(self): + flag1, flag2, flag3 = False, False, False + if 0 == 1: + flag1 = True + elif 1 == 1: + flag2 = True + else: + flag3 = True + self.assertFalse(flag1) + self.assertTrue(flag2) + self.assertFalse(flag3) + + def test_returns(self): + def f1(): + return "Yes" if True else "No" + def f2(): + return "Yes" if False else "No" + self.assertEqual(f1(), "Yes") + self.assertEqual(f2(), "No") + def test(self): + def f(): + return 10 + def g(): + return 20 + retval = True + def h(): + global retval + retval = not retval + return retval + a = [] + for i in range(3): + a.append(f() if h() else g()) + self.assertEqual(a, [20, 10, 20]) + +if __name__ == '__main__': + unittest.main() + + diff --git a/test/unit3/test_counter.py b/test/unit3/test_counter.py new file mode 100644 index 0000000000..1889c89987 --- /dev/null +++ b/test/unit3/test_counter.py @@ -0,0 +1,105 @@ +"""Unit testing for Counter""" +import unittest +import collections + +class CounterTests(unittest.TestCase): + def _mc_compare(self, result, expected): + """ + Compare results of most_common where elements with same count + can be in arbitrary order. + """ + if len(result) != len(expected): + return False + + def split(elements): + result = {} + count = None + curr = set() + for item in elements: + if (count == item[1]) or (count == None): + count = item[1] + if item[0] in curr: + return False + curr.add(item[0]) + else: + if count >= item[1]: + return False + result[count] = curr + count = item[1] + curr = set() + curr.add(item[0]) + if count != None: + result[count] = curr + + ritems = split(result) + eitems = split(expected) + + return ritems == eitems + + def test_basic(self): + a = collections.Counter() + self.assertEqual(str(a), "Counter()") + b = collections.Counter('gallahad') + self.assertEqual(b, collections.Counter({'a': 3, 'l': 2, 'g': 1, 'h': 1, 'd': 1})) + c = collections.Counter({'red': 4, 'blue': 2}) + self.assertEqual(c['green'], 0) + self.assertEqual(str(c), "Counter({'red': 4, 'blue': 2})") + + x = collections.Counter('hello world!') + self.assertEqual(x, collections.Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1, '!': 1})) + for i in 'hello universe!': + x[i] += 1 + self.assertEqual(x, collections.Counter({'l': 5, 'e': 4, 'o': 3, 'h': 2, ' ': 2, 'r': 2, '!': 2, 'w': 1, 'd': 1, 'u': 1, 'n': 1, 'i': 1, 'v': 1, 's': 1})) + l = list(x.elements()) + l.sort() + self.assertEqual(l, [' ', ' ', '!', '!', 'd', 'e', 'e', 'e', 'e', 'h', 'h', 'i', 'l', 'l', 'l', 'l', 'l', 'n', 'o', 'o', 'o', 'r', 'r', 's', 'u', 'v', 'w']) + + def test_most_common(self): + x = collections.Counter({'l': 5, 'e': 4, 'o': 3, 'h': 2, ' ': 2, 'r': 2, '!': 2, 'w': 1, 'd': 1, 'u': 1, 'n': 1, 'i': 1, 'v': 1, 's': 1}) + self.assertEqual(x.most_common(2), [('l', 5), ('e', 4)]) + self.assertTrue(self._mc_compare(x.most_common(), [('l', 5), ('e', 4), ('o', 3), ('h', 2), (' ', 2), ('r', 2), ('!', 2), ('w', 1), ('d', 1), ('u', 1), ('n', 1), ('i', 1), ('v', 1), ('s', 1)])) + + def test_subtract(self): + a = collections.Counter({1:6, 2:4, 3:3}) + a.subtract({1:5, 2:-2, 4:7}) + self.assertEqual(a, collections.Counter({2: 6, 3: 3, 1: 1, 4: -7})) + a.subtract([1, 1]) + self.assertEqual(a, collections.Counter({2: 6, 3: 3, 1: -1, 4: -7})) + a.subtract(collections.Counter({1:-8, 3:2})) + self.assertEqual(a, collections.Counter({1: 7, 2: 6, 3: 1, 4: -7})) + c = collections.Counter("hello world") + c.subtract("hello") + self.assertEqual(c, collections.Counter({'l': 1, 'o': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1, 'h': 0, 'e': 0})) + c.subtract() + self.assertEqual(c, collections.Counter({'l': 1, 'o': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1, 'h': 0, 'e': 0})) + c.update("hello") + self.assertEqual(c, collections.Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})) + c.update() + self.assertEqual(c, collections.Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})) + + + def test_update(self): + a = collections.Counter({1: 7, 2: 6, 3: 1, 4: -7}) + a.update({1:5, 2:-2, 4:7}) + self.assertEqual(a, collections.Counter({1: 12, 2: 4, 3: 1, 4: 0})) + a.update([1, 1]) + self.assertEqual(a, collections.Counter({1: 14, 2: 4, 3: 1, 4: 0})) + a.update(collections.Counter({1:-8, 3:2})) + self.assertEqual(a, collections.Counter({1: 6, 2: 4, 3: 3, 4: 0})) + + def test_errors(self): + c = collections.Counter('hello') + self.assertRaises(TypeError, collections.Counter, 3) + self.assertRaises(TypeError, c.elements, 5) + self.assertRaises(TypeError, c.most_common, 2, 5) + self.assertRaises(TypeError, c.most_common, "hello") + self.assertEqual(c.most_common(-5), []) + self.assertTrue(self._mc_compare(c.most_common(200), [('l', 2), ('h', 1), ('e', 1), ('o', 1)])) + self.assertRaises(TypeError, c.update, 1, 3) + self.assertRaises(TypeError, c.update, 13) + self.assertRaises(TypeError, c.subtract, 4, 5) + self.assertRaises(TypeError, c.subtract, 12.4) + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_decorators.py b/test/unit3/test_decorators.py index 5bab02441a..10e89c1259 100644 --- a/test/unit3/test_decorators.py +++ b/test/unit3/test_decorators.py @@ -302,5 +302,39 @@ def f(cls, arg): return (cls, arg) self.assertEqual(ff.__get__(0, int)(42), (int, 42)) self.assertEqual(ff.__get__(0)(42), (int, 42)) + def test_nested_decorators(self): + calls = [] + def decorate(call_name): + def wrap(f): + def wrapped(*args, **kwargs): + calls.append(call_name) + return f(*args, **kwargs) + return wrapped + return wrap + + @decorate('outer') + @decorate('inner') + def f(x): + return x + + self.assertEqual(42, f(42)) + self.assertEqual(['inner', 'outer'], calls) + + + def test_class_decorator(self): + def decorate(c): + def f(): + return c(42) + return f + + @decorate + class Foo: + def __init__(self, value): + self.value = value + + foo = Foo() + self.assertEqual(42, foo.value) + + if __name__ == '__main__': unittest.main() diff --git a/test/unit3/test_defaultdict.py b/test/unit3/test_defaultdict.py index 13ca8c9f27..90178335ad 100644 --- a/test/unit3/test_defaultdict.py +++ b/test/unit3/test_defaultdict.py @@ -14,6 +14,16 @@ def test_basic(self): d1 = defaultdict() self.assertEqual(d1.default_factory, None) d1.default_factory = list + d = defaultdict(list, {1:2}) + d[2].append(5) + self.assertEqual(d[2], [5]) + self.assertEqual(d.get(2), [5]) + def abc(): + return 6 + d = defaultdict(abc) + self.assertEqual(d[4], 6) + d[4] += 8 + self.assertEqual(d[4], 14) # d1[12].append(42) # self.assertEqual(d1, {12: [42]}) # d1[12].append(24) @@ -163,5 +173,16 @@ def test_shallow_copy(self): # o = pickle.loads(s) # self.assertEqual(d, o) + def test_errors(self): + error = None + self.assertRaises(TypeError, defaultdict, 1) + self.assertRaises(TypeError, defaultdict, list(), {}) + self.assertRaises(TypeError, list, 12) + d = defaultdict(None) + self.assertRaises(KeyError, lambda x: d[x], 5) + self.assertEqual(d.get(5), None) + self.assertRaises(TypeError, d.__missing__, 1, 2) + self.assertRaises(KeyError, d.__missing__, {1:2}) + if __name__ == "__main__": unittest.main() diff --git a/test/unit3/test_deque.py b/test/unit3/test_deque.py new file mode 100644 index 0000000000..b1bffd6228 --- /dev/null +++ b/test/unit3/test_deque.py @@ -0,0 +1,521 @@ +from collections import deque +import unittest + +import random + +BIG = 100000 + +class TestBasic(unittest.TestCase): + + def test_basics(self): + d = deque(range(-5125, -5000)) + d.__init__(range(200)) + for i in range(200, 400): + d.append(i) + for i in reversed(range(-200, 0)): + d.appendleft(i) + self.assertEqual(list(d), list(range(-200, 400))) + self.assertEqual(len(d), 600) + + left = [d.popleft() for i in range(250)] + self.assertEqual(left, list(range(-200, 50))) + self.assertEqual(list(d), list(range(50, 400))) + + right = [d.pop() for i in range(250)] + right.reverse() + self.assertEqual(right, list(range(150, 400))) + self.assertEqual(list(d), list(range(50, 150))) + + def test_maxlen(self): + self.assertRaises(ValueError, deque, 'abc', -1) + self.assertRaises(ValueError, deque, 'abc', -2) + it = iter(range(10)) + d = deque(it, maxlen=3) + self.assertEqual(list(it), []) + self.assertEqual(repr(d), 'deque([7, 8, 9], maxlen=3)') + self.assertEqual(list(d), [7, 8, 9]) + self.assertEqual(d, deque(range(10), 3)) + d.append(10) + self.assertEqual(list(d), [8, 9, 10]) + d.appendleft(7) + self.assertEqual(list(d), [7, 8, 9]) + d.extend([10, 11]) + self.assertEqual(list(d), [9, 10, 11]) + d.extendleft([8, 7]) + self.assertEqual(list(d), [7, 8, 9]) + d = deque(range(200), maxlen=10) + d.append(d) + + def test_maxlen_zero(self): + it = iter(range(100)) + deque(it, maxlen=0) + self.assertEqual(list(it), []) + + it = iter(range(100)) + d = deque(maxlen=0) + d.extend(it) + self.assertEqual(list(it), []) + + it = iter(range(100)) + d = deque(maxlen=0) + d.extendleft(it) + self.assertEqual(list(it), []) + + def test_maxlen_attribute(self): + self.assertEqual(deque().maxlen, None) + self.assertEqual(deque('abc').maxlen, None) + self.assertEqual(deque('abc', maxlen=4).maxlen, 4) + self.assertEqual(deque('abc', maxlen=2).maxlen, 2) + self.assertEqual(deque('abc', maxlen=0).maxlen, 0) + # with self.assertRaises(AttributeError): + # d = deque('abc') + # d.maxlen = 10 + + def test_count(self): + for s in ('', 'abracadabra', 'simsalabim'*500+'abc'): + s = list(s) + d = deque(s) + for letter in 'abcdefghijklmnopqrstuvwxyz': + self.assertEqual(s.count(letter), d.count(letter), (s, d, letter)) + self.assertRaises(TypeError, d.count) # too few args + self.assertRaises(TypeError, d.count, 1, 2) # too many args + + # test issue11004 + # block advance failed after rotation aligned elements on right side of block + d = deque([None]*16) + for i in range(len(d)): + d.rotate(-1) + d.rotate(1) + self.assertEqual(d.count(1), 0) + self.assertEqual(d.count(None), 16) + + def test_comparisons(self): + d = deque('xabc'); d.popleft() + for e in [d, deque('abc'), deque('ab'), deque(), list(d)]: + self.assertEqual(d==e, type(d)==type(e) and list(d)==list(e)) + self.assertEqual(d!=e, not(type(d)==type(e) and list(d)==list(e))) + + args = map(deque, ('', 'a', 'b', 'ab', 'ba', 'abc', 'xba', 'xabc', 'cba')) + for x in args: + for y in args: + self.assertEqual(x == y, list(x) == list(y), (x,y)) + self.assertEqual(x != y, list(x) != list(y), (x,y)) + self.assertEqual(x < y, list(x) < list(y), (x,y)) + self.assertEqual(x <= y, list(x) <= list(y), (x,y)) + self.assertEqual(x > y, list(x) > list(y), (x,y)) + self.assertEqual(x >= y, list(x) >= list(y), (x,y)) + + def test_contains(self): + n = 200 + d = deque(range(n)) + for i in range(n): + self.assertTrue(i in d) + self.assertTrue((n+1) not in d) + + def test_extend(self): + d = deque('a') + self.assertRaises(TypeError, d.extend, 1) + d.extend('bcd') + self.assertEqual(list(d), list('abcd')) + d.extend(d) + self.assertEqual(list(d), list('abcdabcd')) + + def test_add(self): + d = deque() + e = deque('abc') + f = deque('def') + self.assertEqual(d + d, deque()) + self.assertEqual(e + f, deque('abcdef')) + self.assertEqual(e + e, deque('abcabc')) + self.assertEqual(e + d, deque('abc')) + self.assertEqual(d + e, deque('abc')) + self.assertIsNot(d + d, deque()) + self.assertIsNot(e + d, deque('abc')) + self.assertIsNot(d + e, deque('abc')) + + g = deque('abcdef', maxlen=4) + h = deque('gh') + self.assertEqual(g + h, deque('efgh')) + + + def test_iadd(self): + d = deque('a') + d += 'bcd' + self.assertEqual(list(d), list('abcd')) + d += d + self.assertEqual(list(d), list('abcdabcd')) + + def test_extendleft(self): + d = deque('a') + self.assertRaises(TypeError, d.extendleft, 1) + d.extendleft('bcd') + self.assertEqual(list(d), list(reversed('abcd'))) + d.extendleft(d) + self.assertEqual(list(d), list('abcddcba')) + d = deque() + d.extendleft(range(1000)) + self.assertEqual(list(d), list(reversed(range(1000)))) + + def test_getitem(self): + n = 200 + d = deque(range(n)) + l = list(range(n)) + for i in range(n): + d.popleft() + l.pop(0) + if random.random() < 0.5: + d.append(i) + l.append(i) + for j in range(1-len(l), len(l)): + assert d[j] == l[j] + + d = deque('superman') + self.assertEqual(d[0], 's') + self.assertEqual(d[-1], 'n') + d = deque() + + def test_index(self): + pass + + + def test_insert(self): + # Test to make sure insert behaves like lists + elements = 'ABCDEFGHI' + for i in range(-5 - len(elements)*2, 5 + len(elements) * 2): + d = deque('ABCDEFGHI') + s = list('ABCDEFGHI') + d.insert(i, 'Z') + s.insert(i, 'Z') + self.assertEqual(list(d), s) + + def test_insert_bug_26194(self): + data = 'ABC' + d = deque(data, maxlen=len(data)) + + elements = 'ABCDEFGHI' + for i in range(-len(elements), len(elements)): + d = deque(elements, maxlen=len(elements)+1) + d.insert(i, 'Z') + if i >= 0: + self.assertEqual(d[i], 'Z') + else: + self.assertEqual(d[i-1], 'Z') + + def test_imul(self): + for n in (-10, -1, 0, 1, 2, 10, 1000): + d = deque() + d *= n + self.assertEqual(d, deque()) + self.assertIsNone(d.maxlen) + + for n in (-10, -1, 0, 1, 2, 10, 1000): + d = deque('a') + d *= n + self.assertEqual(d, deque('a' * n)) + self.assertIsNone(d.maxlen) + + for n in (-10, -1, 0, 1, 2, 10, 499, 500, 501, 1000): + d = deque('a', 500) + d *= n + self.assertEqual(d, deque('a' * min(n, 500))) + self.assertEqual(d.maxlen, 500) + + for n in (-10, -1, 0, 1, 2, 10, 1000): + d = deque('abcdef') + d *= n + self.assertEqual(d, deque('abcdef' * n)) + self.assertIsNone(d.maxlen) + + for n in (-10, -1, 0, 1, 2, 10, 499, 500, 501, 1000): + d = deque('abcdef', 500) + d *= n + self.assertEqual(d, deque(('abcdef' * n)[-500:])) + self.assertEqual(d.maxlen, 500) + + def test_mul(self): + d = deque('abc') + self.assertEqual(d * -5, deque()) + self.assertEqual(d * 0, deque()) + self.assertEqual(d * 1, deque('abc')) + self.assertEqual(d * 2, deque('abcabc')) + self.assertEqual(d * 3, deque('abcabcabc')) + self.assertIsNot(d * 1, d) + + self.assertEqual(deque() * 0, deque()) + self.assertEqual(deque() * 1, deque()) + self.assertEqual(deque() * 5, deque()) + + self.assertEqual(-5 * d, deque()) + self.assertEqual(0 * d, deque()) + self.assertEqual(1 * d, deque('abc')) + self.assertEqual(2 * d, deque('abcabc')) + self.assertEqual(3 * d, deque('abcabcabc')) + + d = deque('abc', maxlen=5) + self.assertEqual(d * -5, deque()) + self.assertEqual(d * 0, deque()) + self.assertEqual(d * 1, deque('abc')) + self.assertEqual(d * 2, deque('bcabc')) + self.assertEqual(d * 30, deque('bcabc')) + + def test_setitem(self): + n = 200 + d = deque(range(n)) + for i in range(n): + d[i] = 10 * i + self.assertEqual(list(d), [10*i for i in range(n)]) + l = list(d) + for i in range(1-n, 0, -1): + d[i] = 7*i + l[i] = 7*i + self.assertEqual(list(d), l) + + def test_delitem(self): + n = 500 # O(n**2) test, don't make this too big + d = deque(range(n)) + self.assertRaises(IndexError, d.__delitem__, -n-1) + self.assertRaises(IndexError, d.__delitem__, n) + j = random.randrange(-len(d), len(d)) + val = d[j] + self.assertIn(val, d) + del d[j] + self.assertNotIn(val, d) + + + j = random.randrange(-len(d), len(d)) + val = d[j] + self.assertIn(val, d) + del d[j] + self.assertNotIn(val, d) + + j = random.randrange(-len(d), len(d)) + val = d[j] + self.assertIn(val, d) + del d[j] + self.assertNotIn(val, d) + + + j = random.randrange(-len(d), len(d)) + val = d[j] + self.assertIn(val, d) + del d[j] + self.assertNotIn(val, d) + + j = random.randrange(-len(d), len(d)) + val = d[j] + self.assertIn(val, d) + del d[j] + self.assertNotIn(val, d) + + + def test_reverse(self): + n = 500 # O(n**2) test, don't make this too big + data = [random.random() for i in range(n)] + for i in range(n): + d = deque(data[:i]) + r = d.reverse() + self.assertEqual(list(d), list(reversed(data[:i]))) + self.assertIs(r, None) + d.reverse() + self.assertEqual(list(d), data[:i]) + + def test_rotate(self): + s = tuple('abcde') + n = len(s) + + d = deque(s) + d.rotate(1) # verify rot(1) + self.assertEqual(''.join(d), 'eabcd') + + d = deque(s) + d.rotate(-1) # verify rot(-1) + self.assertEqual(''.join(d), 'bcdea') + d.rotate() # check default to 1 + self.assertEqual(tuple(d), s) + + for i in range(n*3): + d = deque(s) + e = deque(d) + d.rotate(i) # check vs. rot(1) n times + for j in range(i): + e.rotate(1) + self.assertEqual(tuple(d), tuple(e)) + d.rotate(-i) # check that it works in reverse + self.assertEqual(tuple(d), s) + e.rotate(n-i) # check that it wraps forward + self.assertEqual(tuple(e), s) + + for i in range(n*3): + d = deque(s) + e = deque(d) + d.rotate(-i) + for j in range(i): + e.rotate(-1) # check vs. rot(-1) n times + self.assertEqual(tuple(d), tuple(e)) + d.rotate(i) # check that it works in reverse + self.assertEqual(tuple(d), s) + e.rotate(i-n) # check that it wraps backaround + self.assertEqual(tuple(e), s) + + d = deque(s) + e = deque(s) + e.rotate(BIG+17) # verify on long series of rotates + dr = d.rotate + for i in range(BIG+17): + dr() + self.assertEqual(tuple(d), tuple(e)) + + # self.assertRaises(TypeError, d.rotate, 'x') # Wrong arg type + # self.assertRaises(TypeError, d.rotate, 1, 10) # Too many args + + d = deque() + d.rotate() # rotate an empty deque + self.assertEqual(d, deque()) + + def test_len(self): + d = deque('ab') + self.assertEqual(len(d), 2) + d.popleft() + self.assertEqual(len(d), 1) + d.pop() + self.assertEqual(len(d), 0) + # self.assertRaises(IndexError, d.pop) + self.assertEqual(len(d), 0) + d.append('c') + self.assertEqual(len(d), 1) + d.appendleft('d') + self.assertEqual(len(d), 2) + d.clear() + self.assertEqual(len(d), 0) + + def test_underflow(self): + d = deque() + self.assertRaises(IndexError, d.pop) + self.assertRaises(IndexError, d.popleft) + + def test_clear(self): + d = deque(range(100)) + self.assertEqual(len(d), 100) + d.clear() + self.assertEqual(len(d), 0) + self.assertEqual(list(d), []) + d.clear() # clear an empty deque + self.assertEqual(list(d), []) + + def test_remove(self): + d = deque('abcdefghcij') + d.remove('c') + self.assertEqual(d, deque('abdefghcij')) + d.remove('c') + self.assertEqual(d, deque('abdefghij')) + self.assertRaises(ValueError, d.remove, 'c') + self.assertEqual(d, deque('abdefghij')) + + # # Handle comparison errors + # d = deque(['a', 'b', BadCmp(), 'c']) + # e = deque(d) + # self.assertRaises(RuntimeError, d.remove, 'c') + # for x, y in zip(d, e): + # # verify that original order and values are retained. + # self.assertTrue(x is y) + + # Handle evil mutator + # for match in (True, False): + # d = deque(['ab']) + # d.extend([MutateCmp(d, match), 'c']) + # self.assertRaises(IndexError, d.remove, 'c') + # self.assertEqual(d, deque()) + def test_repr(self): + d = deque(range(200)) + # e = eval(repr(d)) + # self.assertEqual(list(d), list(e)) + d.append(d) + self.assertIn('...', repr(d)) + + def test_init(self): + self.assertRaises(TypeError, deque, 'abc', 2, 3); + self.assertRaises(TypeError, deque, 1); + + def test_hash(self): + self.assertRaises(TypeError, hash, deque('abc')) + + def test_long_steadystate_queue_popleft(self): + for size in (0, 1, 2, 100, 1000): + d = deque(range(size)) + append, pop = d.append, d.popleft + for i in range(size, BIG): + append(i) + x = pop() + if x != i - size: + self.assertEqual(x, i-size) + self.assertEqual(list(d), list(range(BIG-size, BIG))) + + def test_long_steadystate_queue_popright(self): + for size in (0, 1, 2, 100, 1000): + d = deque(reversed(range(size))) + append, pop = d.appendleft, d.pop + for i in range(size, BIG): + append(i) + x = pop() + if x != i - size: + self.assertEqual(x, i-size) + self.assertEqual(list(reversed(list(d))), + list(range(BIG-size, BIG))) + + def test_big_queue_popleft(self): + pass + d = deque() + append, pop = d.append, d.popleft + for i in range(BIG): + append(i) + for i in range(BIG): + x = pop() + if x != i: + self.assertEqual(x, i) + + def test_big_queue_popright(self): + d = deque() + append, pop = d.appendleft, d.pop + for i in range(BIG): + append(i) + for i in range(BIG): + x = pop() + if x != i: + self.assertEqual(x, i) + def test_big_stack_right(self): + d = deque() + append, pop = d.append, d.pop + for i in range(BIG): + append(i) + for i in reversed(range(BIG)): + x = pop() + if x != i: + self.assertEqual(x, i) + self.assertEqual(len(d), 0) + + def test_big_stack_left(self): + d = deque() + append, pop = d.appendleft, d.popleft + for i in range(BIG): + append(i) + for i in reversed(range(BIG)): + x = pop() + if x != i: + self.assertEqual(x, i) + self.assertEqual(len(d), 0) + + def test_roundtrip_iter_init(self): + d = deque(range(200)) + e = deque(d) + self.assertNotEqual(id(d), id(e)) + self.assertEqual(list(d), list(e)) + + + + def test_reversed(self): + for s in ('abcd', range(2000)): + self.assertEqual(list(reversed(deque(s))), list(reversed(s))) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/unit3/test_dict.py b/test/unit3/test_dict.py index 8e6727dd8a..101f55c98f 100644 --- a/test/unit3/test_dict.py +++ b/test/unit3/test_dict.py @@ -8,6 +8,10 @@ def test_constructor(self): # calling built-in types without argument must return empty self.assertEqual(dict(), {}) self.assertIsNot(dict(), {}) + d = {1: 2, 3: 4} + self.assertEqual(d, dict(d)) + d1 = { 1: 4, 1.0: 5 } + self.assertEqual(d1, {1:5}) def test_bool(self): self.assertIs(not {}, True) @@ -15,6 +19,24 @@ def test_bool(self): self.assertIs(bool({}), False) self.assertIs(bool({1: 2}), True) + def test_assignment(self): + x = {} + x[1] = 2 + self.assertEqual(x, {1:2}) + y = {3:4} + self.assertEqual(y[3], 4) + d = {} + d["__proto__"]="testing" + self.assertEqual(d, {'__proto__': 'testing'}) + + def test_len(self): + self.assertEqual(len({1:2}),2) + # Test that re-setting the value in a dict doesn't mess with its length + d = {'foo':2} + x = len(d) + d['foo'] = 13 + self.assertEqual(x, len(d)) + def test_keys(self): d = {} self.assertEqual(list(d.keys()), []) @@ -76,6 +98,7 @@ def test_contains(self): self.assertNotIn('c', d) self.assertTrue(d.__contains__('a')) self.assertFalse(d.__contains__('c')) + self.assertFalse(1 in d) def test_len(self): d = {} @@ -90,6 +113,11 @@ def test_clear(self): self.assertRaises(TypeError, d.clear, None) + def test_delitem(self): + x = {1:2} + del x[1] + self.assertEqual(x, {}) + def test_update(self): d = {} d.update({1:100}) @@ -176,7 +204,6 @@ def test_repr(self): d[1] = d self.assertEqual(repr(d), '{1: {...}}') - # we cannot subclass Exceptions -.- ''' class Exc(Exception): pass @@ -189,6 +216,9 @@ def __repr__(self): #self.assertRaises(Exc, repr, d) ''' + def test_str(self): + self.assertEqual(str({1:'ok', 2:'stuff'}), "{1: 'ok', 2: 'stuff'}") + def test_get(self): d = {} self.assertIs(d.get('c'), None) @@ -200,6 +230,17 @@ def test_get(self): self.assertEqual(d.get('a', 3), 1) self.assertRaises(TypeError, d.get) self.assertRaises(TypeError, d.get, None, None, None) + def test_getitem(self): + class Foo: + + def __init__(self, arg): + self.x = None + + def __getitem__(self,key): + return self.x + + x = Foo(5) + self.assertEqual(x[1], None) def test_attrib(self): d = {} @@ -207,6 +248,34 @@ def do_set(): d.x = 42 self.assertRaises(AttributeError, do_set) self.assertRaises(AttributeError, lambda: d.x) + + def test_key_types(self): + x = (1,3) + d1 = {x:"OK"} + self.assertEqual(d1[x], "OK") + y = (1,3) + self.assertEqual(d1[y], "OK") + def keyassn(x): + return {x:1} + self.assertRaises(TypeError, keyassn, [4,5]) + self.assertRaises(TypeError, keyassn, {1:2}) + a = {1:2} + self.assertRaises(KeyError, lambda x: a[x], 2) + + def test_nesting(self): + a = {'a':[1,2,3], 'b':(5,6,7)} + a[999] = {'ok':1, 'stuff':2} + self.assertEqual(a, {'a':[1,2,3], 'b':(5,6,7), 999: {'ok':1, 'stuff':2}}) + + def test_max_min(self): + d = {'foo':2, 'bar':3, 'abc':4} + self.assertEqual(min(d), 'abc') + self.assertEqual(max(d), 'foo') + def test_comprehension(self): + s = { i*i for i in range(100) if i&1 == 1 } + self.assertEqual(s, {1, 3969, 4225, 9, 3721, 4489, 5625, 529, 1681, 7569, 25, 3481, 4761, 289, 2209, 6561, 169, 2601, 5929, 49, 3249, 5041, 2809, 441, 1849, 7225, 961, 1089, 9025, 9409, 841, 1225, 8649, 9801, 81, 3025, 5329, 1369, 729, 8281, 225, 2401, 6241, 361, 2025, 6889, 625, 1521, 7921, 121}) + s2 = { 2*y + x + 1 for x in (0,) for y in (1,) } + self.assertEqual(s2, {3}) if __name__ == '__main__': unittest.main() diff --git a/test/unit3/test_enumerate.py b/test/unit3/test_enumerate.py index 969817bc5a..af7c3a616a 100644 --- a/test/unit3/test_enumerate.py +++ b/test/unit3/test_enumerate.py @@ -71,7 +71,35 @@ def test_basicfunction(self): self.assertEqual(iter(e), e) self.assertEqual(list(self.enum(self.seq)), self.res) # self.enum.__doc__ - + a = [] + for x in enumerate([14, 8, 2, "abc", -7], 2): + a.append(x) + self.assertEqual(a, [(2, 14), (3, 8), (4, 2), (5, 'abc'), (6, -7)]) + def enumerate_helper(iterable,start=0): + x = [] + for i in enumerate(iterable,start): + x.append(i) + return x + # list + self.assertEqual(enumerate_helper([1,2,3,4]), [(0, 1), (1, 2), (2, 3), (3, 4)]) + self.assertEqual(enumerate_helper([1,2,3,4],10), [(10, 1), (11, 2), (12, 3), (13, 4)]) + + # string + self.assertEqual(enumerate_helper("hello"), [(0, 'h'), (1, 'e'), (2, 'l'), (3, 'l'), (4, 'o')]) + self.assertEqual(enumerate_helper("WORLD",2), [(2, 'W'), (3, 'O'), (4, 'R'), (5, 'L'), (6, 'D')]) + + # tuple + self.assertEqual(enumerate_helper((1,2,3,)), [(0, 1), (1, 2), (2, 3)]) + self.assertEqual(enumerate_helper((1,2,3,),-1), [(-1, 1), (0, 2), (1, 3)]) + + # dict + self.assertEqual(enumerate_helper({1:'a',2:'b',3:'c'}), [(0, 1), (1, 2), (2, 3)]) + self.assertEqual(enumerate_helper({1:'a',2:'b',3:'c'},5), [(5, 1), (6, 2), (7, 3)]) + + # start and list + self.assertEqual(enumerate_helper(range(1, 4), start=1), [(1, 1), (2, 2), (3, 3)]) + grocery = ['bread', 'milk', 'butter'] # issue 954 + self.assertEqual(list(enumerate(grocery, start=10)), [(10, "bread"), (11, "milk"), (12, "butter")]) # def test_getitemseqn(self): # self.assertEqual(list(self.enum(G(self.seq))), self.res) # e = self.enum(G('')) @@ -108,6 +136,11 @@ def test_tuple_reuse(self): self.assertEqual(len(set(map(id, list(enumerate(self.seq))))), len(self.seq)) # self.assertEqual(len(set(map(id, enumerate(self.seq)))), min(1,len(self.seq))) + def test_repr(self): + self.assertEqual(str(enumerate), "") + e = enumerate([4, 8, 12], -3) + self.assertEqual(repr(e), "") + class MyEnum(enumerate): pass @@ -212,7 +245,6 @@ def __len__(self): return 2 b = Blocked() self.assertRaises(TypeError, reversed, b) - # class EnumerateStartTestCase(EnumerateTestCase): # # def test_basicfunction(self): diff --git a/test/unit3/test_errors.py b/test/unit3/test_errors.py new file mode 100644 index 0000000000..673c55d1ea --- /dev/null +++ b/test/unit3/test_errors.py @@ -0,0 +1,102 @@ +""" Unit test for error handling""" +import unittest + +class TryExceptFinallyTest(unittest.TestCase): + def test_try(self): + a,b,c,d,e,f,g = False, False, False, False, False, False, False + try: + a = True + try: + b = True + i = int('badint'); + c = True + except: + d = True + e = True + i = float('otherbadint') + f = True + except: + g = True + self.assertTrue(a) + self.assertTrue(b) + self.assertFalse(c) + self.assertTrue(d) + self.assertTrue(e) + self.assertFalse(f) + self.assertTrue(g) + + def test_except(self): + def test(i): + f = 3 + try: + return f == 5 + except ValueError: + return True + self.assertFalse(test(12)) + + def test_errors(self): + error1, error2, error3, error4, error5, error6, error7 = None, None, None, None, None, None, None + try: + assert 1 > 10 + except AssertionError: + error1 = "caught error" + except: + error1 = "missed error" + self.assertEqual(error1, "caught error") + try: + error2 = None.notAnAttribute + except AttributeError: + error2 = "Caught AttributeError" + except: + error2 = "Did not catch AttributeError" + self.assertEqual(error2, "Caught AttributeError") + try: + import notAModule + except ImportError: + error3 = "Caught ImportError" + except: + error3 = "Did not catch ImportError" + self.assertEqual(error3, "Caught ImportError") + try: + error4 = [0,1,2,3,4][5] + except IndexError: + error4 = "Caught IndexError" + except: + error4 = "Did not catch IndexError" + self.assertEqual(error4, "Caught IndexError") + try: + print({1:2, 3:4}[5]) + except KeyError: + error5 = "Caught KeyError" + except: + error5 = "Did not catch KeyError" + self.assertEqual(error5, "Caught KeyError") + try: + error6 = x + except NameError: + error6 = "Caught NameError" + except: + error6 = "Did not catch NameError" + self.assertEqual(error6, "Caught NameError") + try: + print(0.0000000000000000000000000000000000000000000000000000000000000001**-30) + except OverflowError: + error7 = "Caught OverflowError" + except: + error7 = "Did not catch OverflowError" + self.assertEqual(error7, "Caught OverflowError") + + + def test_exception(self): + class C: + def __init__(self): + try: + raise Exception("Oops") + except: + self.x = "Caught" + c = C() + self.assertEqual(c.x, "Caught") + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_filter_map_zip.py b/test/unit3/test_filter_map_zip.py new file mode 100644 index 0000000000..b1671d788e --- /dev/null +++ b/test/unit3/test_filter_map_zip.py @@ -0,0 +1,230 @@ +""" Unit testing for builtin objects filter, map, and zip""" +import unittest +#examples for testing +def add_one(num): + """function for testing""" + if num != 2 and num != 6.0 and num != -151.5: + return num + 1 + +lst1 = [1, 2, 3, 4, 5] +lst2 = [2, 4, 6, 8, 10] +lst3 = [-150, -151, -151.49, -151.50000, -151.500001, -152.0] +str1 = 'ABCD' +str2 = 'xy' +str3 = 'ABCDE' +str4 = 'abcde' + +class Squares: + """class for testing, creates list of squares from 0 to i - 1""" + + def __init__(self, max): + self.max = max + self.sofar = [] + + def __len__(self): return len(self.sofar) + + def __getitem__(self, i): + if not 0 <= i < self.max: raise IndexError + n = len(self.sofar) + while n <= i: + self.sofar.append(n*n) + n += 1 + return self.sofar[i] + +class BasicIterClass: + def __init__(self, low, high): + self.current = low + self.low = low + self.high = high + + def __next__(self): + #res = self.i + if self.current > self.high: + self.current = self.low + raise StopIteration + else: + self.current += 1 + return self.current - 1 + + def __iter__(self): + return self + + def change_high(self, y): + self.high = y + self.current = self.low + +class TestFailingIter: + def __iter__(self): + raise RuntimeError + +class FilterTest(unittest.TestCase): + def test_filter(self): + self.assertEqual(list(filter(lambda c: 'a' <= c <= 'z', 'Hello World')), list('elloorld')) + self.assertEqual(list(filter(None, [1, 'hello', [], [3], '', None, 9, 0])), [1, 'hello', [3], 9]) + self.assertEqual(list(filter(lambda x: x > 0, [1, -3, 9, 0, 2])), [1, 9, 2]) + self.assertEqual(list(filter(None, Squares(10))), [1, 4, 9, 16, 25, 36, 49, 64, 81]) + self.assertEqual(list(filter(lambda x: x%2, Squares(10))), [1, 9, 25, 49, 81]) + self.assertEqual(list(filter(add_one, lst1)), [1, 3, 4, 5]) + def identity(item): + return 1 + filter(identity, Squares(5)) + self.assertRaises(TypeError, filter) + class BadSeq(object): + def __getitem__(self, index): + if index<4: + return 42 + raise ValueError + self.assertRaises(ValueError, list, filter(lambda x: x, BadSeq())) + def badfunc(): + pass + self.assertRaises(TypeError, list, filter(badfunc, range(5))) + + # test bltinmodule.c::filtertuple() + self.assertEqual(list(filter(None, (1, 2))), [1, 2]) + self.assertEqual(list(filter(lambda x: x>=3, (1, 2, 3, 4))), [3, 4]) + self.assertRaises(TypeError, list, filter(42, (1, 2))) + + self.assertEqual(str(filter(add_one, lst1))[:7], "") + self.assertEqual(list(zip(lst1, str3, str4)), [(1, 'A', 'a'), (2, 'B', 'b'), (3, 'C', 'c'), (4, 'D', 'd'), (5, 'E', 'e')]) + self.assertEqual(str(zip(lst1, str3, str4))[1:11], "zip object") + self.assertEqual(str(type(zip(lst1, str3, str4))), "") + lst1b, str3b, str4b = zip(*zip(lst1, str3, str4)) + self.assertEqual(list(lst1b), lst1) + self.assertEqual(''.join(str3b), str3) + self.assertEqual(''.join(str4b), str4) + a = (1, 2, 3) + b = (4, 5, 6) + t = [(1, 4), (2, 5), (3, 6)] + self.assertEqual(list(zip(a, b)), t) + b = [4, 5, 6] + self.assertEqual(list(zip(a, b)), t) + b = (4, 5, 6, 7) + self.assertEqual(list(zip(a, b)), t) + class I: + def __getitem__(self, i): + if i < 0 or i > 2: raise IndexError + return i + 4 + self.assertEqual(list(zip(a, I())), t) + self.assertEqual(list(zip()), []) + self.assertEqual(list(zip(*[])), []) + self.assertRaises(TypeError, zip, None) + class G: + pass + self.assertRaises(TypeError, zip, a, G()) + self.assertRaises(RuntimeError, zip, a, TestFailingIter()) + + # Make sure zip doesn't try to allocate a billion elements for the + # result list when one of its arguments doesn't say how long it is. + # A MemoryError is the most likely failure mode. + class SequenceWithoutALength: + def __getitem__(self, i): + if i == 5: + raise IndexError + else: + return i + self.assertEqual(list(zip(SequenceWithoutALength(), range(2**10))), list(enumerate(range(5)))) + + a = BasicIterClass(0,6) + b = BasicIterClass(10, 16) + z = zip(a, b) + self.assertEqual(list(z), [(0, 10), (1, 11), (2, 12), (3, 13), (4, 14), (5, 15), (6, 16)]) + a.change_high(8) + b.change_high(18) + self.assertEqual(list(z), [(0, 10), (1, 11), (2, 12), (3, 13), (4, 14), (5, 15), (6, 16), (7, 17), (8, 18)]) + self.assertEqual(type(z), zip) + + self.assertRaises(TypeError, zip, 1) + self.assertRaises(TypeError, zip, [1], 1) + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_float.py b/test/unit3/test_float.py index e3b2346b9e..dd72e789b2 100644 --- a/test/unit3/test_float.py +++ b/test/unit3/test_float.py @@ -12,7 +12,30 @@ def test_inf(self): self.assertFalse(math.isinf(42)) self.assertFalse(math.isinf(42.1)) self.assertRaises(TypeError, lambda: math.isinf("42")) + self.assertTrue(math.isinf(float('inf'))) + self.assertTrue(math.isinf(float('-inf'))) + def test_type_conversion(self): + self.assertEqual(int(3.0),3) + self.assertEqual(float(3), 3.0) + self.assertEqual(type(float(1)), float) + self.assertEqual(float("12.3"), 12.3) + self.assertEqual(float("0."+"123456789"*3), 0.12345678912345678) + self.assertEqual(float("123456789"*3), 1.2345678912345679e+26) + + def test_nan(self): + self.assertTrue(math.isnan(float('nan'))) + self.assertTrue(math.isnan(float('-nan'))) + self.assertTrue(math.isnan(float('NAN'))) + self.assertTrue(math.isnan(float('-NAN'))) + self.assertTrue(math.isnan(float('+nAn'))) + + def test_repr(self): + self.assertEqual(repr(1.5), '1.5') + + def test_overflow(self): + self.assertRaises(OverflowError, float, 2**1024) + self.assertRaises(OverflowError, float, -2**1024) if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/test/unit3/test_frozenset.py b/test/unit3/test_frozenset.py new file mode 100644 index 0000000000..7d23ccc9f3 --- /dev/null +++ b/test/unit3/test_frozenset.py @@ -0,0 +1,188 @@ +""" Unit testing for frozensets (2)""" +import unittest + +s = frozenset(range(9)) +s1 = frozenset(range(5)) +s2 = frozenset(range(4,9)) + +class FrozenSetTests(unittest.TestCase): + def test_basic(self): + s = frozenset([1, 2, 3, 4]) + self.assertEqual(frozenset(), frozenset([])) + l = [1, 2, 3, 4] + self.assertEqual(frozenset(l), s) + t = (1, 2, 3, 4) + self.assertEqual(frozenset(t), s) + d = {1:2, 3:4} + self.assertEqual(frozenset(d), frozenset([1, 3])) + self.assertEqual(frozenset(d.keys()), frozenset([1, 3])) + self.assertEqual(frozenset(d.values()), frozenset([2, 4])) + + self.assertEqual(len(frozenset([])), 0) + self.assertEqual(len(s), 4) + self.assertEqual(len(s), 4) + self.assertTrue(4 in s1) + self.assertTrue(4 in s2) + self.assertFalse(8 in s1) + self.assertFalse(1 in s2) + self.assertFalse(1 not in s1) + self.assertFalse(8 not in s2) + self.assertTrue(8 not in s1) + self.assertTrue(1 not in s2) + + def test_union(self): + s = frozenset([2,3,4]) + t = frozenset([4,5,6]) + u = frozenset([1,2,3,4,5]) + a = s.union(t) + b = s.union(u) + c = t.union(s) + d = t.union(u) + e = u.union(s) + f = u.union(t) + self.assertEqual(a, c) + self.assertEqual(a, frozenset([2,3,4,5,6])) + self.assertEqual(b, e) + self.assertEqual(b, frozenset([1,2,3,4,5])) + self.assertEqual(d, f) + self.assertEqual(d, frozenset([1,2,3,4,5,6])) + + a = s.union(t, u) + b = s.union(u, t) + c = t.union(s, u) + d = t.union(u, s) + e = u.union(s, t) + f = u.union(t, s) + self.assertEqual(f, frozenset([1, 2, 3, 4, 5, 6])) + self.assertEqual(a, frozenset([1,2,3,4,5,6])) + self.assertEqual(a, b) + self.assertEqual(a, c) + self.assertEqual(a, d) + self.assertEqual(a, e) + self.assertEqual(a, f) + + self.assertEqual(frozenset([]).union(s1), s1) + self.assertEqual(s1.union(frozenset([])), s1) + self.assertEqual(s1.union(s2), frozenset([0, 1, 2, 3, 4, 5, 6, 7, 8])) + self.assertEqual(s1.union(s2,frozenset([4,5,6])), frozenset([0, 1, 2, 3, 4, 5, 6, 7, 8])) + + def test_symm_difference(self): + s = frozenset([1,2,3]) + t = frozenset([3,4,5]) + a = s.symmetric_difference(t) + b = t.symmetric_difference(s) + self.assertEqual(a, frozenset([1, 2, 4, 5])) + self.assertEqual(a, b) + self.assertEqual(a, frozenset([1,2,4,5])) + + + def test_intersection(self): + s = frozenset([2,3,4]) + t = frozenset([3,4,5]) + u = frozenset([1,3,5]) + a = s.intersection(t) + b = u.intersection(s) + c = u.intersection(t) + self.assertEqual(a, frozenset([3, 4])) + self.assertEqual(b, frozenset([3])) + self.assertEqual(c, frozenset([3, 5])) + d = s.intersection(t, u) + self.assertEqual(d, frozenset([3])) + + self.assertEqual(frozenset([]).intersection(s1), frozenset([])) + self.assertEqual(s1.intersection(frozenset([])), frozenset([])) + self.assertEqual(s1.intersection(s2), frozenset([4])) + self.assertEqual(s.intersection(s1,s2), frozenset([4])) + + def test_copy(self): + s = frozenset([1,2,3]) + copy_s = s.copy() + new_s = frozenset(s) + + self.assertEqual(s, frozenset([1, 2, 3])) + self.assertEqual(s, copy_s) + self.assertEqual(frozenset([]).copy(), frozenset([])) + + s1 = frozenset(range(5)) + self.assertEqual(s1.copy(), s1) + s3 = s1.copy() + s1 = frozenset(range(1,5)) + self.assertNotEqual(s1, s3) + + + def test_difference(self): + s = frozenset([2,3,4]) + t = frozenset([3,4,5]) + u = frozenset([1,3,5]) + + a = s.difference(t) + b = u.difference(s) + c = u.difference(t) + self.assertEqual(a, frozenset([2])) + self.assertEqual(b, frozenset([1, 5])) + self.assertEqual(c, frozenset([1])) + d = s.difference(t, u) + self.assertEqual(d, frozenset([2])) + + + def test_compare(self): + self.assertFalse(frozenset([]) == []) + self.assertFalse(frozenset(["a"]) == ["a"]) + self.assertFalse(frozenset(["a", "b"]) == ["a", "b"]) + self.assertFalse(frozenset(["b", "a"]) == ["a", "b"]) + self.assertFalse(frozenset(["a", "c", "b"]) == ["c", "b", "a"]) + self.assertTrue(frozenset(['a']) == frozenset(['a'])) + + set_1 = frozenset([(), (1,), (5,), (1, 2), (2, 2), (1, 2, 2)]) + set_2 = frozenset([(), (1,), (2,), (1, 2), (2, 2), (1, 2, 2)]) + self.assertFalse(set_1 == set_2) + self.assertTrue(set_1 != set_2) + set_1 = frozenset([(), (1,), (2,), (1, 2), (2, 2), (1, 2, 2)]) + set_2 = frozenset([(), (1,), (2,), (1, 2), (2, 2), (1, 2, 2)]) + self.assertTrue(set_1 == set_2) + self.assertFalse(set_1 != set_2) + + self.assertTrue(s1 <= s) + self.assertTrue(s2 <= s) + self.assertTrue(s <= s) + self.assertFalse(s1 <= s2) + self.assertTrue(s1 < s) + self.assertTrue(s2 < s) + self.assertFalse(s < s) + self.assertFalse(s1 < s2) + self.assertTrue(s >= s1) + self.assertTrue(s >= s2) + self.assertTrue(s >= s) + self.assertFalse(s1 >= s2) + self.assertTrue(s > s1) + self.assertTrue(s > s2) + self.assertFalse(s > s) + self.assertFalse(s1 > s2) + + def test_isdisjoint(self): + self.assertFalse(s1.isdisjoint(s2)) + self.assertTrue(s1.isdisjoint(frozenset(range(5,10)))) + + def test_issubset(self): + self.assertTrue(s1.issubset(s1)) + self.assertTrue(s1.issubset(s)) + self.assertFalse(s1.issubset(s2)) + + # Copy from set unit test + def test_ops(self): + a = frozenset(range(5)) + b = frozenset(range(3, 8)) + c = list(b) + self.assertEqual(a & b, frozenset(range(3, 5))) + self.assertEqual(a | b, frozenset(range(8))) + self.assertEqual(a ^ b, frozenset([0, 1, 2, 5, 6, 7])) + self.assertEqual(a - b, frozenset([0, 1, 2])) + self.assertRaises(TypeError, lambda: a & c) + self.assertRaises(TypeError, lambda: a | c) + self.assertRaises(TypeError, lambda: a ^ c) + self.assertRaises(TypeError, lambda: a - c) + + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_fstring.py b/test/unit3/test_fstring.py new file mode 100644 index 0000000000..80596af63b --- /dev/null +++ b/test/unit3/test_fstring.py @@ -0,0 +1,1082 @@ +# These tests are taken from Python 3.7: +# https://raw.githubusercontent.com/python/cpython/3.7/Lib/test/test_fstring.py + +# The AST module is not yet implemeted in Skulpt +# import ast +import types +# The decimal module is not yet implemented in Skulpt +#import decimal +import unittest + +a_global = 'global variable' + +# You could argue that I'm too strict in looking for specific error +# values with assertRaisesRegex, but without it it's way too easy to +# make a syntax error in the test strings. Especially with all of the +# triple quotes, raw strings, backslashes, etc. I think it's a +# worthwhile tradeoff. When I switched to this method, I found many +# examples where I wasn't testing what I thought I was. + +class TestCase(unittest.TestCase): + def assertAllRaise(self, exception_type, regex, error_strings): + for str in error_strings: + with self.subTest(str=str): + with self.assertRaisesRegex(exception_type, regex): + eval(str) + + + # Skulpt TODO: We look up __format__ on the instance, not the type, + # and instantiating types.MethodType() doesn't work properly. + # + # def test__format__lookup(self): + # # Make sure __format__ is looked up on the type, not the instance. + # class X: + # def __format__(self, spec): + # return 'class' + + # x = X() + + # # Add a bound __format__ method to the 'y' instance, but not + # # the 'x' instance. + # y = X() + # y.__format__ = types.MethodType(lambda self, spec: 'instance', y) + + # self.assertEqual(f'{y}', format(y)) + # self.assertEqual(f'{y}', 'class') + # self.assertEqual(format(x), format(y)) + + # # __format__ is not called this way, but still make sure it + # # returns what we expect (so we can make sure we're bypassing + # # it). + # self.assertEqual(x.__format__(''), 'class') + # self.assertEqual(y.__format__(''), 'instance') + + # # This is how __format__ is actually called. + # self.assertEqual(type(x).__format__(x, ''), 'class') + # self.assertEqual(type(y).__format__(y, ''), 'class') + + +# Skulpt does not yet support the 'ast' module +# def test_ast(self): +# # Inspired by http://bugs.python.org/issue24975 +# class X: +# def __init__(self): +# self.called = False +# def __call__(self): +# self.called = True +# return 4 +# x = X() +# expr = """ +# a = 10 +# f'{a * x()}'""" +# t = ast.parse(expr) +# c = compile(t, '', 'exec') + +# # Make sure x was not called. +# self.assertFalse(x.called) + +# # Actually run the code. +# exec(c) + +# # Make sure x was called. +# self.assertTrue(x.called) + +# def test_ast_line_numbers(self): +# expr = """ +# a = 10 +# f'{a * x()}'""" +# t = ast.parse(expr) +# self.assertEqual(type(t), ast.Module) +# self.assertEqual(len(t.body), 2) +# # check `a = 10` +# self.assertEqual(type(t.body[0]), ast.Assign) +# self.assertEqual(t.body[0].lineno, 2) +# # check `f'...'` +# self.assertEqual(type(t.body[1]), ast.Expr) +# self.assertEqual(type(t.body[1].value), ast.JoinedStr) +# self.assertEqual(len(t.body[1].value.values), 1) +# self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue) +# self.assertEqual(t.body[1].lineno, 3) +# self.assertEqual(t.body[1].value.lineno, 3) +# self.assertEqual(t.body[1].value.values[0].lineno, 3) +# # check the binop location +# binop = t.body[1].value.values[0].value +# self.assertEqual(type(binop), ast.BinOp) +# self.assertEqual(type(binop.left), ast.Name) +# self.assertEqual(type(binop.op), ast.Mult) +# self.assertEqual(type(binop.right), ast.Call) +# self.assertEqual(binop.lineno, 3) +# self.assertEqual(binop.left.lineno, 3) +# self.assertEqual(binop.right.lineno, 3) +# self.assertEqual(binop.col_offset, 3) +# self.assertEqual(binop.left.col_offset, 3) +# self.assertEqual(binop.right.col_offset, 7) + +# def test_ast_line_numbers_multiple_formattedvalues(self): +# expr = """ +# f'no formatted values' +# f'eggs {a * x()} spam {b + y()}'""" +# t = ast.parse(expr) +# self.assertEqual(type(t), ast.Module) +# self.assertEqual(len(t.body), 2) +# # check `f'no formatted value'` +# self.assertEqual(type(t.body[0]), ast.Expr) +# self.assertEqual(type(t.body[0].value), ast.JoinedStr) +# self.assertEqual(t.body[0].lineno, 2) +# # check `f'...'` +# self.assertEqual(type(t.body[1]), ast.Expr) +# self.assertEqual(type(t.body[1].value), ast.JoinedStr) +# self.assertEqual(len(t.body[1].value.values), 4) +# self.assertEqual(type(t.body[1].value.values[0]), ast.Str) +# self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue) +# self.assertEqual(type(t.body[1].value.values[2]), ast.Str) +# self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue) +# self.assertEqual(t.body[1].lineno, 3) +# self.assertEqual(t.body[1].value.lineno, 3) +# self.assertEqual(t.body[1].value.values[0].lineno, 3) +# self.assertEqual(t.body[1].value.values[1].lineno, 3) +# self.assertEqual(t.body[1].value.values[2].lineno, 3) +# self.assertEqual(t.body[1].value.values[3].lineno, 3) +# # check the first binop location +# binop1 = t.body[1].value.values[1].value +# self.assertEqual(type(binop1), ast.BinOp) +# self.assertEqual(type(binop1.left), ast.Name) +# self.assertEqual(type(binop1.op), ast.Mult) +# self.assertEqual(type(binop1.right), ast.Call) +# self.assertEqual(binop1.lineno, 3) +# self.assertEqual(binop1.left.lineno, 3) +# self.assertEqual(binop1.right.lineno, 3) +# self.assertEqual(binop1.col_offset, 8) +# self.assertEqual(binop1.left.col_offset, 8) +# self.assertEqual(binop1.right.col_offset, 12) +# # check the second binop location +# binop2 = t.body[1].value.values[3].value +# self.assertEqual(type(binop2), ast.BinOp) +# self.assertEqual(type(binop2.left), ast.Name) +# self.assertEqual(type(binop2.op), ast.Add) +# self.assertEqual(type(binop2.right), ast.Call) +# self.assertEqual(binop2.lineno, 3) +# self.assertEqual(binop2.left.lineno, 3) +# self.assertEqual(binop2.right.lineno, 3) +# self.assertEqual(binop2.col_offset, 23) +# self.assertEqual(binop2.left.col_offset, 23) +# self.assertEqual(binop2.right.col_offset, 27) + +# def test_ast_line_numbers_nested(self): +# expr = """ +# a = 10 +# f'{a * f"-{x()}-"}'""" +# t = ast.parse(expr) +# self.assertEqual(type(t), ast.Module) +# self.assertEqual(len(t.body), 2) +# # check `a = 10` +# self.assertEqual(type(t.body[0]), ast.Assign) +# self.assertEqual(t.body[0].lineno, 2) +# # check `f'...'` +# self.assertEqual(type(t.body[1]), ast.Expr) +# self.assertEqual(type(t.body[1].value), ast.JoinedStr) +# self.assertEqual(len(t.body[1].value.values), 1) +# self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue) +# self.assertEqual(t.body[1].lineno, 3) +# self.assertEqual(t.body[1].value.lineno, 3) +# self.assertEqual(t.body[1].value.values[0].lineno, 3) +# # check the binop location +# binop = t.body[1].value.values[0].value +# self.assertEqual(type(binop), ast.BinOp) +# self.assertEqual(type(binop.left), ast.Name) +# self.assertEqual(type(binop.op), ast.Mult) +# self.assertEqual(type(binop.right), ast.JoinedStr) +# self.assertEqual(binop.lineno, 3) +# self.assertEqual(binop.left.lineno, 3) +# self.assertEqual(binop.right.lineno, 3) +# self.assertEqual(binop.col_offset, 3) +# self.assertEqual(binop.left.col_offset, 3) +# self.assertEqual(binop.right.col_offset, 7) +# # check the nested call location +# self.assertEqual(len(binop.right.values), 3) +# self.assertEqual(type(binop.right.values[0]), ast.Str) +# self.assertEqual(type(binop.right.values[1]), ast.FormattedValue) +# self.assertEqual(type(binop.right.values[2]), ast.Str) +# self.assertEqual(binop.right.values[0].lineno, 3) +# self.assertEqual(binop.right.values[1].lineno, 3) +# self.assertEqual(binop.right.values[2].lineno, 3) +# call = binop.right.values[1].value +# self.assertEqual(type(call), ast.Call) +# self.assertEqual(call.lineno, 3) +# self.assertEqual(call.col_offset, 11) + +# def test_ast_line_numbers_duplicate_expression(self): +# """Duplicate expression + +# NOTE: this is currently broken, always sets location of the first +# expression. +# """ +# expr = """ +# a = 10 +# f'{a * x()} {a * x()} {a * x()}' +# """ +# t = ast.parse(expr) +# self.assertEqual(type(t), ast.Module) +# self.assertEqual(len(t.body), 2) +# # check `a = 10` +# self.assertEqual(type(t.body[0]), ast.Assign) +# self.assertEqual(t.body[0].lineno, 2) +# # check `f'...'` +# self.assertEqual(type(t.body[1]), ast.Expr) +# self.assertEqual(type(t.body[1].value), ast.JoinedStr) +# self.assertEqual(len(t.body[1].value.values), 5) +# self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue) +# self.assertEqual(type(t.body[1].value.values[1]), ast.Str) +# self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue) +# self.assertEqual(type(t.body[1].value.values[3]), ast.Str) +# self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue) +# self.assertEqual(t.body[1].lineno, 3) +# self.assertEqual(t.body[1].value.lineno, 3) +# self.assertEqual(t.body[1].value.values[0].lineno, 3) +# self.assertEqual(t.body[1].value.values[1].lineno, 3) +# self.assertEqual(t.body[1].value.values[2].lineno, 3) +# self.assertEqual(t.body[1].value.values[3].lineno, 3) +# self.assertEqual(t.body[1].value.values[4].lineno, 3) +# # check the first binop location +# binop = t.body[1].value.values[0].value +# self.assertEqual(type(binop), ast.BinOp) +# self.assertEqual(type(binop.left), ast.Name) +# self.assertEqual(type(binop.op), ast.Mult) +# self.assertEqual(type(binop.right), ast.Call) +# self.assertEqual(binop.lineno, 3) +# self.assertEqual(binop.left.lineno, 3) +# self.assertEqual(binop.right.lineno, 3) +# self.assertEqual(binop.col_offset, 3) +# self.assertEqual(binop.left.col_offset, 3) +# self.assertEqual(binop.right.col_offset, 7) +# # check the second binop location +# binop = t.body[1].value.values[2].value +# self.assertEqual(type(binop), ast.BinOp) +# self.assertEqual(type(binop.left), ast.Name) +# self.assertEqual(type(binop.op), ast.Mult) +# self.assertEqual(type(binop.right), ast.Call) +# self.assertEqual(binop.lineno, 3) +# self.assertEqual(binop.left.lineno, 3) +# self.assertEqual(binop.right.lineno, 3) +# self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong +# self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong +# self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong +# # check the third binop location +# binop = t.body[1].value.values[4].value +# self.assertEqual(type(binop), ast.BinOp) +# self.assertEqual(type(binop.left), ast.Name) +# self.assertEqual(type(binop.op), ast.Mult) +# self.assertEqual(type(binop.right), ast.Call) +# self.assertEqual(binop.lineno, 3) +# self.assertEqual(binop.left.lineno, 3) +# self.assertEqual(binop.right.lineno, 3) +# self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong +# self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong +# self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong + +# def test_ast_line_numbers_multiline_fstring(self): +# # FIXME: This test demonstrates invalid behavior due to JoinedStr's +# # immediate child nodes containing the wrong lineno. The enclosed +# # expressions have valid line information and column offsets. +# # See bpo-16806 and bpo-30465 for details. +# expr = """ +# a = 10 +# f''' +# {a +# * +# x()} +# non-important content +# ''' +# """ +# t = ast.parse(expr) +# self.assertEqual(type(t), ast.Module) +# self.assertEqual(len(t.body), 2) +# # check `a = 10` +# self.assertEqual(type(t.body[0]), ast.Assign) +# self.assertEqual(t.body[0].lineno, 2) +# # check `f'...'` +# self.assertEqual(type(t.body[1]), ast.Expr) +# self.assertEqual(type(t.body[1].value), ast.JoinedStr) +# self.assertEqual(len(t.body[1].value.values), 3) +# self.assertEqual(type(t.body[1].value.values[0]), ast.Str) +# self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue) +# self.assertEqual(type(t.body[1].value.values[2]), ast.Str) +# # NOTE: the following invalid behavior is described in bpo-16806. +# # - line number should be the *first* line (3), not the *last* (8) +# # - column offset should not be -1 +# self.assertEqual(t.body[1].lineno, 8) +# self.assertEqual(t.body[1].value.lineno, 8) +# self.assertEqual(t.body[1].value.values[0].lineno, 8) +# self.assertEqual(t.body[1].value.values[1].lineno, 8) +# self.assertEqual(t.body[1].value.values[2].lineno, 8) +# self.assertEqual(t.body[1].col_offset, -1) +# self.assertEqual(t.body[1].value.col_offset, -1) +# self.assertEqual(t.body[1].value.values[0].col_offset, -1) +# self.assertEqual(t.body[1].value.values[1].col_offset, -1) +# self.assertEqual(t.body[1].value.values[2].col_offset, -1) +# # NOTE: the following lineno information and col_offset is correct for +# # expressions within FormattedValues. +# binop = t.body[1].value.values[1].value +# self.assertEqual(type(binop), ast.BinOp) +# self.assertEqual(type(binop.left), ast.Name) +# self.assertEqual(type(binop.op), ast.Mult) +# self.assertEqual(type(binop.right), ast.Call) +# self.assertEqual(binop.lineno, 4) +# self.assertEqual(binop.left.lineno, 4) +# self.assertEqual(binop.right.lineno, 6) +# self.assertEqual(binop.col_offset, 3) +# self.assertEqual(binop.left.col_offset, 3) +# self.assertEqual(binop.right.col_offset, 7) + + # Skulpt: __doc__ attribute not implemented + # def test_docstring(self): + # def f(): + # f'''Not a docstring''' + # self.assertIsNone(f.__doc__) + # def g(): + # '''Not a docstring''' \ + # f'' + # self.assertIsNone(g.__doc__) + + + # Skulpt: ast module not yet implemented + # def test_literal_eval(self): + # with self.assertRaisesRegex(ValueError, 'malformed node or string'): + # ast.literal_eval("f'x'") + + # def test_ast_compile_time_concat(self): + # x = [''] + + # expr = """x[0] = 'foo' f'{3}'""" + # t = ast.parse(expr) + # c = compile(t, '', 'exec') + # exec(c) + # self.assertEqual(x[0], 'foo3') + + # Skulpt: unittest functionality not yet implemented + # def test_compile_time_concat_errors(self): + # self.assertAllRaise(SyntaxError, + # 'cannot mix bytes and nonbytes literals', + # [r"""f'' b''""", + # r"""b'' f''""", + # ]) + + def test_literal(self): + self.assertEqual(f'', '') + self.assertEqual(f'a', 'a') + self.assertEqual(f' ', ' ') + + # Skulpt: unittest functionality not yet implemented + # def test_unterminated_string(self): + # self.assertAllRaise(SyntaxError, 'f-string: unterminated string', + # [r"""f'{"x'""", + # r"""f'{"x}'""", + # r"""f'{("x'""", + # r"""f'{("x}'""", + # ]) + + # def test_mismatched_parens(self): + # self.assertAllRaise(SyntaxError, 'f-string: mismatched', + # ["f'{((}'", + # ]) + + def test_double_braces(self): + self.assertEqual(f'{{', '{') + self.assertEqual(f'a{{', 'a{') + self.assertEqual(f'{{b', '{b') + self.assertEqual(f'a{{b', 'a{b') + self.assertEqual(f'}}', '}') + self.assertEqual(f'a}}', 'a}') + self.assertEqual(f'}}b', '}b') + self.assertEqual(f'a}}b', 'a}b') + self.assertEqual(f'{{}}', '{}') + self.assertEqual(f'a{{}}', 'a{}') + self.assertEqual(f'{{b}}', '{b}') + self.assertEqual(f'{{}}c', '{}c') + self.assertEqual(f'a{{b}}', 'a{b}') + self.assertEqual(f'a{{}}c', 'a{}c') + self.assertEqual(f'{{b}}c', '{b}c') + self.assertEqual(f'a{{b}}c', 'a{b}c') + + self.assertEqual(f'{{{10}', '{10') + self.assertEqual(f'}}{10}', '}10') + self.assertEqual(f'}}{{{10}', '}{10') + self.assertEqual(f'}}a{{{10}', '}a{10') + + self.assertEqual(f'{10}{{', '10{') + self.assertEqual(f'{10}}}', '10}') + self.assertEqual(f'{10}}}{{', '10}{') + self.assertEqual(f'{10}}}a{{' '}', '10}a{}') + + # Inside of strings, don't interpret doubled brackets. + self.assertEqual(f'{"{{}}"}', '{{}}') + + # Skulpt: unittest functionality not implemented + # self.assertAllRaise(TypeError, 'unhashable type', + # ["f'{ {{}} }'", # dict in a set + # ]) + + def test_compile_time_concat(self): + x = 'def' + self.assertEqual('abc' f'## {x}ghi', 'abc## defghi') + self.assertEqual('abc' f'{x}' 'ghi', 'abcdefghi') + self.assertEqual('abc' f'{x}' 'gh' f'i{x:4}', 'abcdefghidef ') + self.assertEqual('{x}' f'{x}', '{x}def') + self.assertEqual('{x' f'{x}', '{xdef') + self.assertEqual('{x}' f'{x}', '{x}def') + self.assertEqual('{{x}}' f'{x}', '{{x}}def') + self.assertEqual('{{x' f'{x}', '{{xdef') + self.assertEqual('x}}' f'{x}', 'x}}def') + self.assertEqual(f'{x}' 'x}}', 'defx}}') + self.assertEqual(f'{x}' '', 'def') + self.assertEqual('' f'{x}' '', 'def') + self.assertEqual('' f'{x}', 'def') + self.assertEqual(f'{x}' '2', 'def2') + self.assertEqual('1' f'{x}' '2', '1def2') + self.assertEqual('1' f'{x}', '1def') + self.assertEqual(f'{x}' f'-{x}', 'def-def') + self.assertEqual('' f'', '') + self.assertEqual('' f'' '', '') + self.assertEqual('' f'' '' f'', '') + self.assertEqual(f'', '') + self.assertEqual(f'' '', '') + self.assertEqual(f'' '' f'', '') + self.assertEqual(f'' '' f'' '', '') + + # Skulpt: This unittest functionality isn't implemented + # self.assertAllRaise(SyntaxError, "f-string: expecting '}'", + # ["f'{3' f'}'", # can't concat to get a valid f-string + # ]) + + def test_comments(self): + # These aren't comments, since they're in strings. + d = {'#': 'hash'} + self.assertEqual(f'{"#"}', '#') + self.assertEqual(f'{d["#"]}', 'hash') + + # Skulpt: This unittest functionality isn't implemented + # self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'", + # ["f'{1#}'", # error because the expression becomes "(1#)" + # "f'{3(#)}'", + # "f'{#}'", + # "f'{)#}'", # When wrapped in parens, this becomes + # # '()#)'. Make sure that doesn't compile. + # ]) + + # Skulpt: "eval" not implemented + # def test_many_expressions(self): + # # Create a string with many expressions in it. Note that + # # because we have a space in here as a literal, we're actually + # # going to use twice as many ast nodes: one for each literal + # # plus one for each expression. + # def build_fstr(n, extra=''): + # return "f'" + ('{x} ' * n) + extra + "'" + + # x = 'X' + # width = 1 + + # # Test around 256. + # for i in range(250, 260): + # self.assertEqual(eval(build_fstr(i)), (x+' ')*i) + + # # Test concatenating 2 largs fstrings. + # self.assertEqual(eval(build_fstr(255)*256), (x+' ')*(255*256)) + + # s = build_fstr(253, '{x:{width}} ') + # self.assertEqual(eval(s), (x+' ')*254) + + # # Test lots of expressions and constants, concatenated. + # s = "f'{1}' 'x' 'y'" * 1024 + # self.assertEqual(eval(s), '1xy' * 1024) + + def test_format_specifier_expressions(self): + width = 10 + precision = 4 + # Skulpt: The decimal module is not yet implemented + #value = decimal.Decimal('12.34567') + value = 12.34567 + self.assertEqual(f'result: {value:{width}.{precision}}', 'result: 12.35') + self.assertEqual(f'result: {value:{width!r}.{precision}}', 'result: 12.35') + self.assertEqual(f'result: {value:{width:0}.{precision:1}}', 'result: 12.35') + self.assertEqual(f'result: {value:{1}{0:0}.{precision:1}}', 'result: 12.35') + self.assertEqual(f'result: {value:{ 1}{ 0:0}.{ precision:1}}', 'result: 12.35') + self.assertEqual(f'{10:#{1}0x}', ' 0xa') + self.assertEqual(f'{10:{"#"}1{0}{"x"}}', ' 0xa') + self.assertEqual(f'{-10:-{"#"}1{0}x}', ' -0xa') + self.assertEqual(f'{-10:{"-"}#{1}0{"x"}}', ' -0xa') + self.assertEqual(f'{10:#{3 != {4:5} and width}x}', ' 0xa') + + # Skulpt: unittest functionality not implemented + # self.assertAllRaise(SyntaxError, "f-string: expecting '}'", + # ["""f'{"s"!r{":10"}}'""", + + # # This looks like a nested format spec. + # ]) + + # self.assertAllRaise(SyntaxError, "invalid syntax", + # [# Invalid syntax inside a nested spec. + # "f'{4:{/5}}'", + # ]) + + # self.assertAllRaise(SyntaxError, "f-string: expressions nested too deeply", + # [# Can't nest format specifiers. + # "f'result: {value:{width:{0}}.{precision:1}}'", + # ]) + + # self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character', + # [# No expansion inside conversion or for + # # the : or ! itself. + # """f'{"s"!{"r"}}'""", + # ]) + + def test_side_effect_order(self): + class X: + def __init__(self): + self.i = 0 + def __format__(self, spec): + self.i += 1 + return str(self.i) + + x = X() + self.assertEqual(f'{x} {x}', '1 2') + + # Skulpt: unittest functionality not implemented + # def test_missing_expression(self): + # self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed', + # ["f'{}'", + # "f'{ }'" + # "f' {} '", + # "f'{!r}'", + # "f'{ !r}'", + # "f'{10:{ }}'", + # "f' { } '", + + # # The Python parser ignores also the following + # # whitespace characters in additional to a space. + # "f'''{\t\f\r\n}'''", + + # # Catch the empty expression before the + # # invalid conversion. + # "f'{!x}'", + # "f'{ !xr}'", + # "f'{!x:}'", + # "f'{!x:a}'", + # "f'{ !xr:}'", + # "f'{ !xr:a}'", + + # "f'{!}'", + # "f'{:}'", + + # # We find the empty expression before the + # # missing closing brace. + # "f'{!'", + # "f'{!s:'", + # "f'{:'", + # "f'{:x'", + # ]) + + # # Different error message is raised for other whitespace characters. + # self.assertAllRaise(SyntaxError, 'invalid character in identifier', + # ["f'''{\xa0}'''", + # "\xa0", + # ]) + + def test_parens_in_expressions(self): + self.assertEqual(f'{3,}', '(3,)') + + # Skulpt: unittest functionality not implemented + + # # Add these because when an expression is evaluated, parens + # # are added around it. But we shouldn't go from an invalid + # # expression to a valid one. The added parens are just + # # supposed to allow whitespace (including newlines). + # self.assertAllRaise(SyntaxError, 'invalid syntax', + # ["f'{,}'", + # "f'{,}'", # this is (,), which is an error + # ]) + + # self.assertAllRaise(SyntaxError, "f-string: expecting '}'", + # ["f'{3)+(4}'", + # ]) + + # self.assertAllRaise(SyntaxError, 'EOL while scanning string literal', + # ["f'{\n}'", + # ]) + + def test_backslashes_in_string_part(self): + self.assertEqual(f'\t', '\t') + self.assertEqual(r'\t', '\\t') + self.assertEqual(rf'\t', '\\t') + self.assertEqual(f'{2}\t', '2\t') + self.assertEqual(f'{2}\t{3}', '2\t3') + self.assertEqual(f'\t{3}', '\t3') + + # Skulpt TODO: These will work after PR#983 (real unicode strings) lands + # self.assertEqual(f'\u0394', '\u0394') + # self.assertEqual(r'\u0394', '\\u0394') + # self.assertEqual(rf'\u0394', '\\u0394') + # self.assertEqual(f'{2}\u0394', '2\u0394') + # self.assertEqual(f'{2}\u0394{3}', '2\u03943') + # self.assertEqual(f'\u0394{3}', '\u03943') + + # self.assertEqual(f'\U00000394', '\u0394') + # self.assertEqual(r'\U00000394', '\\U00000394') + # self.assertEqual(rf'\U00000394', '\\U00000394') + # self.assertEqual(f'{2}\U00000394', '2\u0394') + # self.assertEqual(f'{2}\U00000394{3}', '2\u03943') + # self.assertEqual(f'\U00000394{3}', '\u03943') + + # Skulpt doesn't support \N{} escapes, because we don't bundle the + # Unicode database + # + # self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394') + # self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394') + # self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943') + # self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943') + # self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394') + # self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943') + # self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943') + + self.assertEqual(f'\x20', ' ') + self.assertEqual(r'\x20', '\\x20') + self.assertEqual(rf'\x20', '\\x20') + self.assertEqual(f'{2}\x20', '2 ') + self.assertEqual(f'{2}\x20{3}', '2 3') + self.assertEqual(f'\x20{3}', ' 3') + + self.assertEqual(f'2\x20', '2 ') + self.assertEqual(f'2\x203', '2 3') + self.assertEqual(f'\x203', ' 3') + + # Skulpt: This warning, and all warnings, are not implemented + # with self.assertWarns(DeprecationWarning): # invalid escape sequence + # value = eval(r"f'\{6*7}'") + value = f'\{6*7}' + self.assertEqual(value, '\\42') + self.assertEqual(f'\\{6*7}', '\\42') + self.assertEqual(fr'\{6*7}', '\\42') + + # Skulpt: \N escapes (named Unicode chars) not supported + # AMPERSAND = 'spam' + # # Get the right unicode character (&), or pick up local variable + # # depending on the number of backslashes. + # self.assertEqual(f'\N{AMPERSAND}', '&') + # self.assertEqual(f'\\N{AMPERSAND}', '\\Nspam') + # self.assertEqual(fr'\N{AMPERSAND}', '\\Nspam') + # self.assertEqual(f'\\\N{AMPERSAND}', '\\&') + + + # Skulpt: \N escapes (named Unicode chars) not implemented + # def test_misformed_unicode_character_name(self): + # # These test are needed because unicode names are parsed + # # differently inside f-strings. + # self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape", + # [r"f'\N'", + # r"f'\N{'", + # r"f'\N{GREEK CAPITAL LETTER DELTA'", + + # # Here are the non-f-string versions, + # # which should give the same errors. + # r"'\N'", + # r"'\N{'", + # r"'\N{GREEK CAPITAL LETTER DELTA'", + # ]) + + # Skulpt: unittest functionality not implemented + # def test_no_backslashes_in_expression_part(self): + # self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash', + # [r"f'{\'a\'}'", + # r"f'{\t3}'", + # r"f'{\}'", + # r"rf'{\'a\'}'", + # r"rf'{\t3}'", + # r"rf'{\}'", + # r"""rf'{"\N{LEFT CURLY BRACKET}"}'""", + # r"f'{\n}'", + # ]) + + # TODO fixing this will be much easier once we merge PR#983 + # def test_no_escapes_for_braces(self): + # """ + # Only literal curly braces begin an expression. + # """ + # # \x7b is '{'. + # self.assertEqual(f'\x7b1+1}}', '{1+1}') + # self.assertEqual(f'\x7b1+1', '{1+1') + # self.assertEqual(f'\u007b1+1', '{1+1') + # self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}') + + def test_newlines_in_expressions(self): + self.assertEqual(f'{0}', '0') + self.assertEqual(rf'''{3+ +4}''', '7') + + def test_lambda(self): + x = 5 + self.assertEqual(f'{(lambda y:x*y)("8")!r}', "'88888'") + self.assertEqual(f'{(lambda y:x*y)("8")!r:10}', "'88888' ") + self.assertEqual(f'{(lambda y:x*y)("8"):10}', "88888 ") + + # Skulpt: unittest functionality not implemented + # # lambda doesn't work without parens, because the colon + # # makes the parser think it's a format_spec + # self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing', + # ["f'{lambda x:x}'", + # ]) + + def test_yield(self): + # Not terribly useful, but make sure the yield turns + # a function into a generator + def fn(y): + f'y:{yield y*2}' + + g = fn(4) + self.assertEqual(next(g), 8) + + # Skulpt: The closure/free-var stuff here is badly broken + # def test_yield_send(self): + # def fn(x): + # yield f'x:{yield (lambda i: x * i)}' + + # g = fn(10) + # the_lambda = next(g) + # self.assertEqual(the_lambda(4), 40) + # self.assertEqual(g.send('string'), 'x:string') + + def test_expressions_with_triple_quoted_strings(self): + self.assertEqual(f"{'''x'''}", 'x') + self.assertEqual(f"{'''eric's'''}", "eric's") + + # Test concatenation within an expression + self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy') + self.assertEqual(f'{"x" """eric"s"""}', 'xeric"s') + self.assertEqual(f'{"""eric"s""" "y"}', 'eric"sy') + self.assertEqual(f'{"""x""" """eric"s""" "y"}', 'xeric"sy') + self.assertEqual(f'{"""x""" """eric"s""" """y"""}', 'xeric"sy') + self.assertEqual(f'{r"""x""" """eric"s""" """y"""}', 'xeric"sy') + + def test_multiple_vars(self): + x = 98 + y = 'abc' + self.assertEqual(f'{x}{y}', '98abc') + + self.assertEqual(f'X{x}{y}', 'X98abc') + self.assertEqual(f'{x}X{y}', '98Xabc') + self.assertEqual(f'{x}{y}X', '98abcX') + + self.assertEqual(f'X{x}Y{y}', 'X98Yabc') + self.assertEqual(f'X{x}{y}Y', 'X98abcY') + self.assertEqual(f'{x}X{y}Y', '98XabcY') + + self.assertEqual(f'X{x}Y{y}Z', 'X98YabcZ') + + def test_closure(self): + def outer(x): + def inner(): + return f'x:{x}' + return inner + + self.assertEqual(outer('987')(), 'x:987') + self.assertEqual(outer(7)(), 'x:7') + + def test_arguments(self): + y = 2 + def f(x, width): + return f'x={x*y:{width}}' + + self.assertEqual(f('foo', 10), 'x=foofoo ') + x = 'bar' + self.assertEqual(f(10, 10), 'x= 20') + + def test_locals(self): + value = 123 + self.assertEqual(f'v:{value}', 'v:123') + + def test_missing_variable(self): + self.assertRaises(NameError, lambda: f'v:{value}') + + def test_missing_format_spec(self): + class O: + def __format__(self, spec): + if not spec: + return '*' + return spec + + self.assertEqual(f'{O():x}', 'x') + self.assertEqual(f'{O()}', '*') + self.assertEqual(f'{O():}', '*') + + self.assertEqual(f'{3:}', '3') + self.assertEqual(f'{3!s:}', '3') + + def test_global(self): + self.assertEqual(f'g:{a_global}', 'g:global variable') + self.assertEqual(f'g:{a_global!r}', "g:'global variable'") + + a_local = 'local variable' + self.assertEqual(f'g:{a_global} l:{a_local}', + 'g:global variable l:local variable') + self.assertEqual(f'g:{a_global!r}', + "g:'global variable'") + self.assertEqual(f'g:{a_global} l:{a_local!r}', + "g:global variable l:'local variable'") + + self.assertIn("module 'unittest' from", f'{unittest}') + + def test_shadowed_global(self): + a_global = 'really a local' + self.assertEqual(f'g:{a_global}', 'g:really a local') + self.assertEqual(f'g:{a_global!r}', "g:'really a local'") + + a_local = 'local variable' + self.assertEqual(f'g:{a_global} l:{a_local}', + 'g:really a local l:local variable') + self.assertEqual(f'g:{a_global!r}', + "g:'really a local'") + self.assertEqual(f'g:{a_global} l:{a_local!r}', + "g:really a local l:'local variable'") + + def test_call(self): + def foo(x): + return 'x=' + str(x) + + self.assertEqual(f'{foo(10)}', 'x=10') + + def test_nested_fstrings(self): + y = 5 + self.assertEqual(f'{f"{0}"*3}', '000') + self.assertEqual(f'{f"{y}"*3}', '555') + + # Skulpt: unittest functionality not implemented + # def test_invalid_string_prefixes(self): + # self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing', + # ["fu''", + # "uf''", + # "Fu''", + # "fU''", + # "Uf''", + # "uF''", + # "ufr''", + # "urf''", + # "fur''", + # "fru''", + # "rfu''", + # "ruf''", + # "FUR''", + # "Fur''", + # "fb''", + # "fB''", + # "Fb''", + # "FB''", + # "bf''", + # "bF''", + # "Bf''", + # "BF''", + # ]) + + def test_leading_trailing_spaces(self): + self.assertEqual(f'{ 3}', '3') + self.assertEqual(f'{ 3}', '3') + self.assertEqual(f'{3 }', '3') + self.assertEqual(f'{3 }', '3') + + self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}', + 'expr={1: 2}') + self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }', + 'expr={1: 2}') + + def test_not_equal(self): + # There's a special test for this because there's a special + # case in the f-string parser to look for != as not ending an + # expression. Normally it would, while looking for !s or !r. + + self.assertEqual(f'{3!=4}', 'True') + self.assertEqual(f'{3!=4:}', 'True') + self.assertEqual(f'{3!=4!s}', 'True') + self.assertEqual(f'{3!=4!s:.3}', 'Tru') + + def test_conversions(self): + self.assertEqual(f'{3.14:10.10}', ' 3.14') + self.assertEqual(f'{3.14!s:10.10}', '3.14 ') + self.assertEqual(f'{3.14!r:10.10}', '3.14 ') + self.assertEqual(f'{3.14!a:10.10}', '3.14 ') + + self.assertEqual(f'{"a"}', 'a') + self.assertEqual(f'{"a"!r}', "'a'") + self.assertEqual(f'{"a"!a}', "'a'") + + # Not a conversion. + self.assertEqual(f'{"a!r"}', "a!r") + + # Not a conversion, but show that ! is allowed in a format spec. + self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!') + + # Skulpt: unittest functionality not yet implemented + # self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character', + # ["f'{3!g}'", + # "f'{3!A}'", + # "f'{3!3}'", + # "f'{3!G}'", + # "f'{3!!}'", + # "f'{3!:}'", + # "f'{3! s}'", # no space before conversion char + # ]) + + # self.assertAllRaise(SyntaxError, "f-string: expecting '}'", + # ["f'{x!s{y}}'", + # "f'{3!ss}'", + # "f'{3!ss:}'", + # "f'{3!ss:s}'", + # ]) + + # Skulpt: This unittest functionality (and, indeed, eval()) is not yet implemented + # def test_assignment(self): + # self.assertAllRaise(SyntaxError, 'invalid syntax', + # ["f'' = 3", + # "f'{0}' = x", + # "f'{x}' = x", + # ]) + + # def test_del(self): + # self.assertAllRaise(SyntaxError, 'invalid syntax', + # ["del f''", + # "del '' f''", + # ]) + + def test_mismatched_braces(self): + # Skulpt: This unittest functionality (and, indeed, eval()) is not yet implemented + # self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed", + # ["f'{{}'", + # "f'{{}}}'", + # "f'}'", + # "f'x}'", + # "f'x}x'", + # r"f'\u007b}'", + + # # Can't have { or } in a format spec. + # "f'{3:}>10}'", + # "f'{3:}}>10}'", + # ]) + + # self.assertAllRaise(SyntaxError, "f-string: expecting '}'", + # ["f'{3:{{>10}'", + # "f'{3'", + # "f'{3!'", + # "f'{3:'", + # "f'{3!s'", + # "f'{3!s:'", + # "f'{3!s:3'", + # "f'x{'", + # "f'x{x'", + # "f'{x'", + # "f'{3:s'", + # "f'{{{'", + # "f'{{}}{'", + # "f'{'", + # ]) + + # But these are just normal strings. + self.assertEqual(f'{"{"}', '{') + self.assertEqual(f'{"}"}', '}') + self.assertEqual(f'{3:{"}"}>10}', '}}}}}}}}}3') + self.assertEqual(f'{2:{"{"}>10}', '{{{{{{{{{2') + + def test_if_conditional(self): + # There's special logic in compile.c to test if the + # conditional for an if (and while) are constants. Exercise + # that code. + + def test_fstring(x, expected): + flag = 0 + if f'{x}': + flag = 1 + else: + flag = 2 + self.assertEqual(flag, expected) + + def test_concat_empty(x, expected): + flag = 0 + if '' f'{x}': + flag = 1 + else: + flag = 2 + self.assertEqual(flag, expected) + + def test_concat_non_empty(x, expected): + flag = 0 + if ' ' f'{x}': + flag = 1 + else: + flag = 2 + self.assertEqual(flag, expected) + + test_fstring('', 2) + test_fstring(' ', 1) + + test_concat_empty('', 2) + test_concat_empty(' ', 1) + + test_concat_non_empty('', 1) + test_concat_non_empty(' ', 1) + + def test_empty_format_specifier(self): + x = 'test' + self.assertEqual(f'{x}', 'test') + self.assertEqual(f'{x:}', 'test') + self.assertEqual(f'{x!s:}', 'test') + self.assertEqual(f'{x!r:}', "'test'") + + def test_str_format_differences(self): + d = {'a': 'string', + 0: 'integer', + } + a = 0 + self.assertEqual(f'{d[0]}', 'integer') + self.assertEqual(f'{d["a"]}', 'string') + self.assertEqual(f'{d[a]}', 'integer') + self.assertEqual('{d[a]}'.format(d=d), 'string') + self.assertEqual('{d[0]}'.format(d=d), 'integer') + + # Skulpt: unittest functionality not implemented + # def test_invalid_expressions(self): + # self.assertAllRaise(SyntaxError, 'invalid syntax', + # [r"f'{a[4)}'", + # r"f'{a(4]}'", + # ]) + + # def test_errors(self): + # # see issue 26287 + # self.assertAllRaise(TypeError, 'unsupported', + # [r"f'{(lambda: 0):x}'", + # r"f'{(0,):x}'", + # ]) + # self.assertAllRaise(ValueError, 'Unknown format code', + # [r"f'{1000:j}'", + # r"f'{1000:j}'", + # ]) + + def test_loop(self): + for i in range(1000): + self.assertEqual(f'i:{i}', 'i:' + str(i)) + + def test_dict(self): + d = {'"': 'dquote', + "'": 'squote', + 'foo': 'bar', + } + self.assertEqual(f'''{d["'"]}''', 'squote') + self.assertEqual(f"""{d['"']}""", 'dquote') + + self.assertEqual(f'{d["foo"]}', 'bar') + self.assertEqual(f"{d['foo']}", 'bar') + + # Skulpt: eval not yet implemented + # def test_backslash_char(self): + # # Check eval of a backslash followed by a control char. + # # See bpo-30682: this used to raise an assert in pydebug mode. + # self.assertEqual(eval('f"\\\n"'), '') + # self.assertEqual(eval('f"\\\r"'), '') + + +if __name__ == '__main__': + unittest.main() diff --git a/test/unit3/test_functions.py b/test/unit3/test_functions.py new file mode 100644 index 0000000000..735833c7c1 --- /dev/null +++ b/test/unit3/test_functions.py @@ -0,0 +1,466 @@ +""" Unit test for functions""" +import unittest + +class FunctionTests(unittest.TestCase): + def test_variable_assignment(self): + def test(): + return 1 + x = test + self.assertEqual(x(), 1) + + def test_output(self): + def test(x): + return x + y = test(1)*2 + test(3)*4 + test(5)*6 + self.assertEqual(y, 44) + def adder(a,b): + return a + b + z = adder(1,1) + adder(1,1) + self.assertEqual(z, 4) + def f(x): + return None + self.assertRaises(TypeError, lambda x: f(1) + x, 3) + + def test_function_element(self): + def test(): + return 3 + x = [1, test, 2] + self.assertEqual(x[1](), 3) + + def test_variable_scope(self): + X = "OK" + def test(): + X = 4 + return(X) + test() + self.assertEqual(X, "OK") + a = 4 + def test2(z): + for i in range(0,a): + z += i + return z + self.assertEqual(test2(1), 7) + y = "OK" + def test3(): return y + self.assertEqual(test3(),"OK") + + def test_global_vars(self): + def test(): + global x + x = "OK" + test() + self.assertEqual(x, "OK") + + def test_default_args(self): + def test(y='K',x='Z'): return x + y + self.assertEqual(test('O'), 'ZO') + def wee(waa, woo=True, wii=False): + return [waa, woo, wii] + self.assertEqual(wee("OK"), ["OK", True, False]) + def wee(waa, woo=False, wii=True): + return ["OK", waa, woo, wii] + self.assertEqual(wee("stuff"), ['OK', 'stuff', False, True]) + self.assertEqual(wee("stuff", "dog"), ['OK', 'stuff', 'dog', True]) + self.assertEqual(wee("stuff", "dog", "cat"), ['OK', 'stuff', 'dog', 'cat']) + self.assertEqual(wee("stuff", wii="lamma"), ['OK', 'stuff', False, 'lamma']) + self.assertEqual(wee(wii="lamma", waa="pocky"), ['OK', 'pocky', False, 'lamma']) + self.assertEqual(wee(wii="lamma", waa="pocky", woo="blorp"), ['OK', 'pocky', 'blorp', 'lamma']) + wee = lambda waa, woo=False, wii=True: ("OK", waa, woo, wii) + self.assertEqual(wee("stuff"), ('OK', 'stuff', False, True)) + self.assertEqual(wee("stuff", "dog"),('OK', 'stuff', 'dog', True)) + self.assertEqual(wee("stuff", "dog", "cat"),('OK', 'stuff', 'dog', 'cat')) + self.assertEqual(wee("stuff", wii="lamma"),('OK', 'stuff', False, 'lamma')) + self.assertEqual(wee(wii="lamma", waa="pocky"),('OK', 'pocky', False, 'lamma')) + self.assertEqual(wee(wii="lamma", waa="pocky", woo="blorp"),('OK', 'pocky', 'blorp', 'lamma')) + def default_outside(x=[]): + return x + a = default_outside() + a.append(1) + self.assertEqual(a, [1]) + b = default_outside() + b.append(2) + self.assertEqual(b, [1,2]) + + d = {'x':1,'y':2,'z':3} + + def a(x,y,z): + return x,y,z + + self.assertEqual(a(1,2,3), (1, 2, 3)) + self.assertEqual([a(z=3,x=1,y=2), a(z=3,y=2,x=1), a(y=2,z=3,x=1), a(y=2,x=1,z=3)], [(1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3)]) + + def b(x=0,y=0,z=0): + return x,y,z + + self.assertEqual(b(), (0, 0, 0)) + self.assertEqual(b(1,2,3), (1, 2, 3)) + self.assertEqual([b(1), b(2), b(3)], [(1, 0, 0), (2, 0, 0), (3, 0, 0)]) + self.assertEqual([b(x=1), b(y=2), b(z=3)], [(1, 0, 0), (0, 2, 0), (0, 0, 3)]) + self.assertEqual([b(x=1,z=3), b(z=3,x=1)], [(1, 0, 3), (1, 0, 3)]) + self.assertEqual([b(x=1,y=2), b(y=2,x=1)], [(1, 2, 0), (1, 2, 0)]) + self.assertEqual([b(z=3,y=2), b(y=2,z=3)], [(0, 2, 3), (0, 2, 3)]) + self.assertEqual([b(z=3,x=1,y=2), b(z=3,y=2,x=1), b(y=2,z=3,x=1), b(y=2,x=1,z=3)], [(1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3)]) + + class A(): + def __init__(self,x,y,z): + self.x = x + self.y = y + self.z = z + def __str__(self): + return str((self.x,self.y,self.z)) + + self.assertEqual(str(A(1,2,3)), '(1, 2, 3)') + + class B(): + def __init__(self,x=0,y=0,z=0): + self.x = x + self.y = y + self.z = z + def __str__(self): + return str((self.x,self.y,self.z)) + + self.assertEqual(str(B()), '(0, 0, 0)') + self.assertEqual(str(B(1,2,3)), '(1, 2, 3)') + self.assertEqual([str(B(1)), str(B(2)), str(B(3))], ['(1, 0, 0)', '(2, 0, 0)', '(3, 0, 0)']) + + class B(): + def __init__(self,x=0,y=0,z=0): + self.x = x + self.y = y + self.z = z + def __str__(self): + return str((self.x,self.y,self.z)) + + self.assertEqual([str(B(1)), str(B(2)), str(B(3))], ['(1, 0, 0)', '(2, 0, 0)', '(3, 0, 0)']) + self.assertEqual([str(B(x=1)), str(B(y=2)), str(B(z=3))], ['(1, 0, 0)', '(0, 2, 0)', '(0, 0, 3)']) + self.assertEqual([str(B(x=1,z=3)), str(B(z=3,x=1))], ['(1, 0, 3)', '(1, 0, 3)']) + self.assertEqual([str(B(x=1,y=2)), str(B(y=2,x=1))], ['(1, 2, 0)', '(1, 2, 0)']) + self.assertEqual([str(B(z=3,y=2)), str(B(y=2,z=3))], ['(0, 2, 3)', '(0, 2, 3)']) + self.assertEqual([str(B(z=3,x=1,y=2)), str(B(z=3,y=2,x=1)), str(B(y=2,z=3,x=1)), str(B(y=2,x=1,z=3))], ['(1, 2, 3)', '(1, 2, 3)', '(1, 2, 3)', '(1, 2, 3)']) + class MyClass: + def my_method(self, mandatory_arg, x=0, y=0, **more_args): + return ["Hello! x = " + str(x), "Hello! bla = " + str(more_args['bla'])] + + def my_method2(self): + return self.my_method("hi", y=2, bla='from method2') + k = MyClass() + self.assertEqual(k.my_method('test', x=5, bla='seven'), ['Hello! x = 5', 'Hello! bla = seven']) + self.assertEqual(k.my_method2(), ['Hello! x = 0', 'Hello! bla = from method2']) + + def test_variable_number_args(self): + def f(*a): + return a + + def g(x, *a): + return x, a + + def h(x, y, *a): + return x, y, a + + def i(x, y=4, *a): + return x, y, a + self.assertTrue(f() == ()) + self.assertTrue(f(1) == (1,)) + self.assertTrue(f(1, 2, 3) == (1, 2, 3)) + self.assertTrue(g(1) == (1, ())) + self.assertTrue(g(1, 2, 3) == (1, (2, 3))) + self.assertTrue(h(1, 2) == (1, 2, ())) + self.assertTrue(h(1, 2, 3) == (1, 2, (3,))) + self.assertTrue(h(1, 2, 3, 4) == (1, 2, (3, 4))) + self.assertTrue(i(1) == (1, 4, ())) + self.assertTrue(i(1, 2, 3) == (1, 2, (3,))) + self.assertTrue(i(1, 2, 3, 4) == (1, 2, (3, 4))) + def f(a, b, c): + return a, b, c + args = [5, 6, 7] + self.assertEqual(f(*args), (5, 6, 7)) + def f(a, b): + return (a, b) + self.assertEqual(f(1, 2), (1, 2)) + self.assertEqual(f(*[1, 2]), (1, 2)) + self.assertEqual(f(*(1, 2)), (1, 2)) + def g(a, b, *c): + return (a, b, c) + self.assertEqual(g(1, 2, 3), (1, 2, (3,))) + self.assertEqual(g(1, 2, 3, 4, 5, 6), (1, 2, (3, 4, 5, 6))) + self.assertEqual(g(*[1, 2]), (1, 2, ())) + self.assertEqual(g(*[1, 2, 3, 4]), (1, 2, (3, 4))) + self.assertEqual(g(*[1, 2, 3, 4, 5, 6, 7]), (1, 2, (3, 4, 5, 6, 7))) + self.assertEqual(g(*(1, 2, 3, 4, 5, 6, 7)), (1, 2, (3, 4, 5, 6, 7))) + self.assertEqual(g(1, *[7]), (1, 7, ())) + self.assertEqual(g(1, *[7, 8, 9]), (1, 7, (8, 9))) + self.assertEqual(g(1, 2, *(7,)), (1, 2, (7,))) + self.assertEqual(g(1, 2, 3, *(7, 8, 9)), (1, 2, (3, 7, 8, 9))) + + def test_variable_number_kwargs(self): + def f(**kw): + return kw + self.assertEqual(f(a=4, b=5), {'a': 4, 'b': 5}) + def f(a, b, **c): + sortc = [(x,y) for x,y in c.items()] + sortc.sort() + return (a, b, sortc) + self.assertEqual(f(1, 2, d=4, e=5),(1, 2, [('d', 4), ('e', 5)])) + self.assertEqual(f(1, b=4, e=5), (1, 4, [('e', 5)])) + self.assertEqual(f(a=1, b=4, e=5, f=6, g=7), (1, 4, [('e', 5), ('f', 6), ('g', 7)])) + def f(a,b,c=10,d=20,*e,**f): + sortf = [(x,y) for x,y in f.items()] + sortf.sort() + return (a,b,c,d,e,sortf) + self.assertEqual(f(1,2), (1, 2, 10, 20, (), [])) + self.assertEqual(f(1,2,3), (1, 2, 3, 20, (), [])) + self.assertEqual(f(1,2,3,5), (1, 2, 3, 5, (), [])) + self.assertEqual(f(1,2,d=3,c=5), (1, 2, 5, 3, (), [])) + self.assertEqual(f(1,2,e=['x','y','z']), (1, 2, 10, 20, (), [('e', ['x', 'y', 'z'])])) + self.assertEqual(f(1,2,d=3,c=5,e=['x','y','z']), (1, 2, 5, 3, (), [('e', ['x', 'y', 'z'])])) + self.assertEqual(f(1,2,3,5,['x','y','z']), (1, 2, 3, 5, (['x', 'y', 'z'],), [])) + self.assertEqual(f(1,2,3,5,['x','y','z'],z=5,y=9), (1, 2, 3, 5, (['x', 'y', 'z'],), [('y', 9), ('z', 5)])) + self.assertEqual(f(1,2,3,5,['x','y','z'],'blorp','wee',z=5,y=9), (1, 2, 3, 5, (['x', 'y', 'z'], 'blorp', 'wee'), [('y', 9), ('z', 5)])) + + def test_empty_return(self): + def test(): return + x = 1 + self.assertEqual(test(), None) + + def test_pass(self): + def test(): pass + x = 1 + self.assertEqual(test(), None) + + def test_linebreak(self): + def test(): + x = "OK"; return x + self.assertEqual(test(), "OK") + + def test_nested_funcs(self): + def test(): + def func(): + global x + x = 3 + func() + test() + self.assertEqual(x, 3) + + def test_lambda(self): + z = lambda x: x + self.assertEqual(z(4),4) + self.assertEqual(z("stuff"), "stuff") + def x(): + y = lambda x,y,z: x*y+z + return y(5, 10, 15) + self.assertEqual(x(), 65) + square = lambda x:x**2 + def test1(f,x): + return f(x) + + def test2(f,x,y): + return f(x,y) + + self.assertEqual(square(2), test1(square,2)) + self.assertEqual((lambda x:x+5)(4), test1(lambda x:x+5,4)) + self.assertEqual((lambda x,y:x-y)(5,4), test2(lambda x,y:x-y,5,4)) + self.assertEqual((lambda x,y:x[y]*2)([0,1,2,3,4],4), test2(lambda x,y:x[y]*2,[0,1,2,3,4],4)) + + def test3(f,g,x,y): + return f(x), f(y), g(x,y), g(f(x),f(y)), f(g(x,y)), f(g(y,x)) + + f = lambda x:x*27 + g = lambda x,y: y+12*x + + h = lambda x:f(x) + i = lambda x,y:g(x,y) + + self.assertEqual((f(3),f(4),g(3,4),g(f(3),f(4)),f(g(3,4)),f(g(4,3))), test3(f,g,3,4)) + self.assertEqual(test3(f,g,3,4), test3(h,i,3,4)) + + j = lambda lst,num,func:lst[func(lst,num)]*(lambda y:10*y)(num) + k = lambda x,y:len(x)-y + + def test4(f,x,y,z): + return f(x,y,z) + self.assertEqual(j([1,2,3,4,5,6],2,k), test4(j,[1,2,3,4,5,6],2,k)) + + def test_iter_input(self): + def f(iter): + a = [] + for v in iter: + a.append(v) + return a + self.assertEqual(f(x*y for x in range(10) for y in range(x)), [0, 0, 2, 0, 3, 6, 0, 4, 8, 12, 0, 5, 10, 15, 20, 0, 6, 12, 18, 24, 30, 0, 7, 14, 21, 28, 35, 42, 0, 8, 16, 24, 32, 40, 48, 56, 0, 9, 18, 27, 36, 45, 54, 63, 72]) + + def test_docstring(self): + class Stuff: + def __init__(self): + self.x = 0 + """ + weewaa + + """ + def thing(self): + return self.x + self.assertEqual(Stuff().thing(), 0) + + def test_misc(self): + # using obj[token] in JS doesn't work as a generic string dict + # make sure to use *both* hasOwnProperty and then get it, otherwise object + # builtins will return existence. + def toString(): + return "wee" + + class stuff: + def toString(self): + return "waa" + def valueOf(self): + return "stuff" + s = stuff() + self.assertEqual([toString(), s.toString(), s.valueOf()], ['wee', 'waa', 'stuff']) + + def test_send(self): + z = [] + def mygen(upto): + for i in range(0, upto): + z.append(i) + got = yield i + handle = mygen(3) + first = True + a = [] + b = [] + for num in handle: + a.append(num) + if first: + foo = handle.send('sig') + b.append(foo) + first = False + self.assertEqual((a,b,z), ([0, 2], [1], [0, 1, 2])) + + def test_scope_cpython(self): + ''' + Adapted from http://hg.python.org/cpython/file/936621d33c38/Lib/test/test_scope.py + ''' + #Skulpt fails a lot of the following tests (commented out), it shouldn't + # testSimpleNesting + def make_adder(x): + def adder(y): + return x + y + return adder + + inc = make_adder(1) + plus10 = make_adder(10) + self.assertEqual(inc(1), 2) + self.assertEqual(inc(-4), -3) + self.assertEqual(plus10(8), 18) + self.assertEqual(plus10(-2), 8) + # testSimpleAndRebinding + def make_adder3(x): + def adder(y): + return x + y + x = x+1 # check tracking of assignment to x in defining scope + return adder + inc = make_adder3(0) + plus10 = make_adder3(9) + self.assertEqual(inc(1), 2) + self.assertEqual(inc(-4), -3) + self.assertEqual(plus10(8), 18) + self.assertEqual(plus10(-2), 8) + # testNestingGlobalNoFree + #Skulpt throws an error on line 370, it shouldn't +## def make_adder4(): #XXX add extra level of indrection +## def nest(): +## def nest(): +## def adder(y): +## return global_x + y #check that globals work +## return adder +## return nest() +## return nest() +## global_x = 1 +## adder = make_adder4() +## x = adder(1) +## self.assertEqual(x, 2) +## global_x = 10 +## x = adder(-2) +## self.assertEqual(x, 8) + # testNestingPlusFreeRefToGlobal + def make_adder6(x): + global global_nest_x + def adder(y): + return global_nest_x + y + global_nest_x = x + return adder + inc = make_adder6(1) + self.assertEqual(inc(1), 2) + self.assertEqual(inc(-4), -3) + plus10 = make_adder6(10) + self.assertEqual(plus10(8), 18) + self.assertEqual(plus10(-2), 8) + # testNearestEnclosingScope + def f(x): + def g(y): + x = 42 # check that this masks binding in f() + def h(z): + return x + z + return h + return g(2) + test_func = f(10) + self.assertEqual(test_func(5), 47) + #Skulpt throws an error in this block as well +## # testMixedFreevarsAndCellvars +## def identity(x): +## return x +## def f(x,y,z): +## def g(a,b,c): +## a = a + x # 3 +## def h(): +## #z * (4+9) +## #3 * 13 +## return identity(z*(b+y)) +## y = c + z #9 +## return h +## return g +## g = f(1,2,3) +## h = g(2,4,6) +## self.assertEqual(h(), 39) + #testFreeVarInMethod + method_and_var = "var" + class Test: + # this class is not nested, so the rules are different + def method_and_var(self): + return "method" + def test(self): + return method_and_var + def actual_global(self): + return str("global") + def str(self): + return str(self) + + t = Test() + self.assertEqual(t.test(), "var") + self.assertEqual(t.method_and_var(), "method") + self.assertEqual(t.actual_global(), "global") + #Skulpt throws an error in this block as well +## # testRecursion +## def f(x): +## def fact(n): +## if n == 0: +## return 1 +## else: +## return n * fact(n-1) +## if x>=0: +## return fact(x) +## else: +## raise ValueError +## self.assertEqual(f(6), 720) +## # testLambdas +## f1 = lambda x: lambda y: x + y +## inc = f1(1) +## plus10 = f1(10) +## self.assertEqual(inc(1), 2) +## self.assertEqual(inc(-4), -3) +## self.assertEqual(plus10(8), 18) +## self.assertEqual(plus10(-2), 8) +## f3 = lambda x: lambda y: global_x + y +## global_x = 1 +## inc = f3(None) +## self.assertEqual(inc(2), 3) + +if __name__ == '__main__': + unittest.main() diff --git a/test/unit3/test_hash.py b/test/unit3/test_hash.py new file mode 100644 index 0000000000..69851653bb --- /dev/null +++ b/test/unit3/test_hash.py @@ -0,0 +1,30 @@ +""" Unit test for hash""" +import unittest + +class HashTest(unittest.TestCase): + def test_integers(self): + self.assertEqual(hash(int()),hash(int())) + self.assertEqual(hash(332), hash(332)) + self.assertEqual(hash(-47), hash(-47)) + + def test_float(self): + self.assertEqual(hash(float()), hash(float())) + self.assertEqual(hash(33.2), hash(33.2)) + self.assertEqual(hash(0.05), hash(0.05)) + self.assertEqual(hash(-11.85), hash(-11.85)) + + def test_strings(self): + self.assertEqual(hash(''), hash('')) + self.assertEqual(hash('hello'), hash('hello')) + + def test_tuples(self): + self.assertEqual(hash(()), hash(())) + self.assertEqual(hash((1,2,3)), hash((1,2,3,))) + + def test_int_and_float(self): + self.assertEqual(hash(1), hash(1.0)) + self.assertEqual(hash(-5), hash(-5.0)) + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_imports.py b/test/unit3/test_imports.py new file mode 100644 index 0000000000..b56b775b0f --- /dev/null +++ b/test/unit3/test_imports.py @@ -0,0 +1,22 @@ +""" Unit testing for imports""" +import unittest +import t221_sub +#import pkga.pkgb.modc as c_me +#from pkga.pkgb.modc import stuff as mystuff +#from pkga.pkgb.modc import things as mythings + +class ImportsTests(unittest.TestCase): + def test_basic(self): + self.assertEqual(t221_sub.x, 444) + self.assertEqual(t221_sub.f("wee"), "OK: wee, 444") + + #Skulpt fails the following tests (it throws an error when importing + #pkga), it shouldn't + #self.assertEqual(c_me.stuff, 942) + #self.assertEqual(c_me.things, "squirrel") + #self.assertEqual(mystuff, 942) + #self.assertEqual(mythings, "squirrel") + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_inheritance.py b/test/unit3/test_inheritance.py new file mode 100644 index 0000000000..c60a4763d8 --- /dev/null +++ b/test/unit3/test_inheritance.py @@ -0,0 +1,109 @@ +"""Unit testing for inheritance """ +import unittest + +class InheritanceTesting(unittest.TestCase): + def test_methods(self): + class Base(object): + def myfunc(self): + return "Base.myfunc" + + def stuff(self): + return "Base.stuff" + self.myfunc() + + class Derived(Base): + def myfunc(self): + Base.myfunc(self) + return "Derived.myfunc" + d = Derived() + self.assertEqual(d.myfunc(), "Derived.myfunc") + b = Derived() + self.assertEqual(b.stuff(), "Base.stuff") + def test_override_method(self): + class SuperClass: + def apply(self): + return "SuperClass" + class SubClassA(SuperClass): + def apply(self): + return "SubClassA" + x = SubClassA() + self.assertEqual(x.apply(), "SubClassA") + + def test_mro(self): + class O(object): pass + class F(O): pass + class E(O): pass + class D(O): pass + class C(D,F): pass + class B(D,E): pass + class A(B,C): pass + self.assertEqual(A.__bases__, (B, C)) + self.assertEqual(A.__mro__, (A, B, C, D, E, F, O, object)) + class O(object): pass + class F(O): pass + class E(O): pass + class D(O): pass + class C(D,F): pass + class B(E,D): pass + class A(B,C): pass + self.assertEqual(A.__mro__,(A, B, E, C, D, F, O, object)) + class O(object): pass + class A(O): pass + class B(O): pass + class C(O): pass + class D(O): pass + class E(O): pass + class K1(A,B,C): pass + class K2(D,B,E): pass + class K3(D,A): pass + class Z(K1,K2,K3): pass + self.assertEqual(K1.__mro__, (K1, A, B, C, O, object)) + self.assertEqual(K2.__mro__, (K2, D, B, E, O, object)) + self.assertEqual(K3.__mro__, (K3, D, A, O, object)) + self.assertEqual(Z.__mro__, (Z, K1, K2, K3, D, A, B, C, E, O, object)) + self.assertEqual(object.__bases__, ()) + self.assertEqual(object.__mro__, tuple([object])) + class X(object): pass + class Y(X): pass + self.assertEqual(X.__bases__, tuple([object])) + self.assertEqual(X.__mro__, (X, object)) + self.assertEqual(Y.__bases__, tuple([X])) + self.assertEqual(Y.__mro__, (Y, X, object)) + + def test_issubclass(self): + class Foo: + pass + + class Bar(Foo): + pass + + class Baz(Bar): + pass + + class XXX: + pass + + class Frob(Baz,XXX): + pass + + self.assertTrue(issubclass(Bar,Foo)) + self.assertFalse(issubclass(Foo,Bar)) + self.assertTrue(issubclass(Baz,Foo)) + self.assertTrue(issubclass(Baz,Bar)) + self.assertTrue(issubclass(Foo,object)) + self.assertTrue(issubclass(Frob,XXX)) + + def test_builtins(self): + for _cls in (int, dict, set, list, object, tuple): + self.assertTrue(issubclass(_cls, object)) + + for error in (Exception, TypeError, ValueError, AttributeError): + self.assertTrue(issubclass(error, Exception)) + + self.assertEqual(int.__mro__, (int, object)) + self.assertEqual(Exception.__mro__, (Exception, BaseException, object)) + self.assertEqual(TypeError.__bases__, (Exception,)) + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_int.py b/test/unit3/test_int.py index 7f983d47da..842ced1123 100644 --- a/test/unit3/test_int.py +++ b/test/unit3/test_int.py @@ -39,6 +39,7 @@ def test_basic(self): self.assertEqual(int(-3.5), -3) self.assertEqual(int("-3"), -3) self.assertEqual(int(" -3 "), -3) + self.assertEqual(int("0", base=2), 0) # self.assertEqual(int("\N{EM SPACE}-3\N{EN SPACE}"), -3) # Different base: self.assertEqual(int("10",16), 16) @@ -80,6 +81,7 @@ def test_basic(self): self.assertRaises(TypeError, int, 1, 12) + self.assertRaises(TypeError, lambda x: int(x, 2), 10) self.assertEqual(int('0o123', 0), 83) self.assertEqual(int('0x123', 16), 291) @@ -230,11 +232,12 @@ def test_no_args(self): def test_keyword_args(self): # Test invoking int() using keyword arguments. + # no longer supported # self.assertEqual(int(x=1.2), 1) self.assertEqual(int('100', base=2), 4) # self.assertEqual(int(x='100', base=2), 4) - # self.assertRaises(TypeError, int, base=10) - # self.assertRaises(TypeError, int, base=0) + self.assertRaises(TypeError, int, base=10) + self.assertRaises(TypeError, int, base=0) def test_int_base_limits(self): """Testing the supported limits of the int() base parameter.""" @@ -381,20 +384,20 @@ def __int__(self): # # self.assertRaises(TypeError, lambda: int(TruncReturnsBadInt())) - # def test_int_subclass_with_int(self): - # class MyInt(int): - # def __int__(self): - # return 42 - # - # class BadInt(int): - # def __int__(self): - # return 42.0 - # - # my_int = MyInt(7) - # self.assertEqual(my_int, 7) - # self.assertEqual(int(my_int), 42) - # - # self.assertRaises(TypeError, int, BadInt()) + def test_int_subclass_with_int(self): + class MyInt(int): + def __int__(self): + return 42 + + class BadInt(int): + def __int__(self): + return 42.0 + + my_int = MyInt(7) + self.assertEqual(my_int, 7) + self.assertEqual(int(my_int), 42) + + self.assertRaises(TypeError, int, BadInt()) def test_int_returns_int_subclass(self): class BadInt: @@ -471,5 +474,8 @@ def test_skulpt_overflow(self): z = x * y self.assertEqual(z, 22572168101082499047084554138087) + def test_repr(self): + self.assertEqual(repr(1), '1') + if __name__ == "__main__": unittest.main() diff --git a/test/unit3/test_int_literal.py b/test/unit3/test_int_literal.py index 391726cf2d..3b9e48e221 100644 --- a/test/unit3/test_int_literal.py +++ b/test/unit3/test_int_literal.py @@ -90,9 +90,29 @@ def test_oct_unsigned(self): self.assertEqual(-0o1000000000000000000000, -9223372036854775808) self.assertEqual(-0o1777777777777777777777, -18446744073709551615) - """ - we do not support binary literals - """ + def test_binary_literal(self): + self.assertEqual(0b1010, 10) + self.assertEqual(str(0b1010), '10') + self.assertEqual(-0b1010, -10) + self.assertEqual(str(-0b1010), '-10') + self.assertEqual(0, -0) + + def test_big_numbers(self): + def helper(func,x): + return (func(x), func(-x)) + big = 123456789123456789123456789123456789 + self.assertEqual(helper(hex,10), ('0xa', '-0xa')) + self.assertEqual(helper(hex,0xff), ('0xff', '-0xff')) + self.assertEqual(helper(hex,0b1110011), ('0x73', '-0x73')) + self.assertEqual(helper(hex,big), ('0x17c6e3c032f89045ad746684045f15', '-0x17c6e3c032f89045ad746684045f15')) + self.assertEqual(helper(oct,10), ('0o12', '-0o12')) + self.assertEqual(helper(oct,0xff), ('0o377', '-0o377')) + self.assertEqual(helper(oct,0b1110011), ('0o163', '-0o163')) + self.assertEqual(helper(oct,big), ('0o574334360031370440426553506320401057425', '-0o574334360031370440426553506320401057425')) + self.assertEqual(helper(bin,10), ('0b1010', '-0b1010')) + self.assertEqual(helper(bin,0xff), ('0b11111111', '-0b11111111')) + self.assertEqual(helper(bin,0b1110011), ('0b1110011', '-0b1110011')) + self.assertEqual(helper(bin,big), ('0b101111100011011100011110000000011001011111000100100000100010110101101011101000110011010000100000001000101111100010101', '-0b101111100011011100011110000000011001011111000100100000100010110101101011101000110011010000100000001000101111100010101')) if __name__ == '__main__': unittest.main() diff --git a/test/unit3/test_isinstance.py b/test/unit3/test_isinstance.py new file mode 100644 index 0000000000..9f13736c58 --- /dev/null +++ b/test/unit3/test_isinstance.py @@ -0,0 +1,139 @@ +""" Unit test for isinstance()""" +import unittest + +#self.assertTrue() +#self.assertFalse() +class IsInstanceTest(unittest.TestCase): + + def test_int(self): + self.assertTrue(isinstance(3,int)) + self.assertTrue(isinstance(3,type(3))) + self.assertFalse(isinstance(3.2,int)) + self.assertTrue(isinstance(4, (float,int))) + self.assertTrue(isinstance(4, (int, float, 5))) + class A: pass + self.assertTrue(isinstance(4, (int, float, A()))) + + def test_float(self): + self.assertTrue(isinstance(3.1,float)) + self.assertFalse(isinstance(3,float)) + self.assertTrue(isinstance(3.1,type(3.1))) + + def test_string(self): + self.assertTrue(isinstance("foo",str)) + self.assertTrue(isinstance("foo",type("foo"))) + var1 = "foo" + class A: pass + self.assertTrue(isinstance(var1, str)) + self.assertFalse(isinstance(var1, list)) + self.assertFalse(isinstance(var1, dict)) + self.assertFalse(isinstance(var1, tuple)) + self.assertFalse(isinstance(var1, A)) + + def test_list(self): + self.assertTrue(isinstance([],list)) + self.assertTrue(isinstance([],type([1,2,3]))) + + def test_none(self): + self.assertTrue(isinstance(None, type(None))) + self.assertTrue(isinstance(None, type(None))) + + def test_bool(self): + self.assertTrue(isinstance(True, bool)) + self.assertTrue(isinstance(True, type(True))) + self.assertIs(isinstance(True, bool), True) + self.assertIs(isinstance(False, bool), True) + self.assertIs(isinstance(True, int), True) + self.assertIs(isinstance(False, int), True) + self.assertIs(isinstance(1, bool), False) + self.assertIs(isinstance(0, bool), False) + self.assertTrue(isinstance(True, type(False))) + + def test_classes(self): + class A: pass + var1 = A() + self.assertTrue(isinstance(var1, A)) + self.assertFalse(isinstance(var1, str)) + self.assertFalse(isinstance(var1, list)) + self.assertFalse(isinstance(var1, tuple)) + self.assertFalse(isinstance(var1, dict)) + self.assertFalse(isinstance(A, A)) + class B: pass + class C: pass + class D(A): pass + class E(A,B): pass + class F(E,C): pass + a,b,c,d,e,f = A(),B(),C(),D(),E(),F() + self.assertTrue(isinstance(a, A)) + self.assertFalse(isinstance(a, B)) + self.assertFalse(isinstance(a, C)) + self.assertFalse(isinstance(a, D)) + self.assertFalse(isinstance(a, E)) + self.assertFalse(isinstance(a, F)) + self.assertFalse(isinstance(b, A)) + self.assertTrue(isinstance(b, B)) + self.assertFalse(isinstance(b, C)) + self.assertFalse(isinstance(b, D)) + self.assertFalse(isinstance(b, E)) + self.assertFalse(isinstance(b, F)) + self.assertFalse(isinstance(c, A)) + self.assertFalse(isinstance(c, B)) + self.assertTrue(isinstance(c, C)) + self.assertFalse(isinstance(c, D)) + self.assertFalse(isinstance(c, E)) + self.assertFalse(isinstance(c, F)) + self.assertTrue(isinstance(d, A)) + self.assertFalse(isinstance(d, B)) + self.assertFalse(isinstance(d, C)) + self.assertTrue(isinstance(d, D)) + self.assertFalse(isinstance(d, E)) + self.assertFalse(isinstance(d, F)) + self.assertTrue(isinstance(e, A)) + self.assertTrue(isinstance(e, B)) + self.assertFalse(isinstance(e, C)) + self.assertFalse(isinstance(e, D)) + self.assertTrue(isinstance(e, E)) + self.assertFalse(isinstance(e, F)) + self.assertTrue(isinstance(f, A)) + self.assertTrue(isinstance(f, B)) + self.assertTrue(isinstance(f, C)) + self.assertFalse(isinstance(f, D)) + self.assertTrue(isinstance(f, E)) + self.assertTrue(isinstance(f, F)) + + def test_dict(self): + var1 = {} + class A: pass + self.assertTrue(isinstance(var1, dict)) + self.assertFalse(isinstance(var1, str)) + self.assertFalse(isinstance(var1, list)) + self.assertFalse(isinstance(var1, tuple)) + self.assertFalse(isinstance(var1, A)) + self.assertTrue(isinstance({1:2}, type({3:4}))) + + def test_list(self): + var1 = [] + class A: pass + self.assertTrue(isinstance(var1, list)) + self.assertFalse(isinstance(var1, str)) + self.assertFalse(isinstance(var1, dict)) + self.assertFalse(isinstance(var1, tuple)) + self.assertFalse(isinstance(var1, A)) + self.assertTrue(isinstance([1,2,3], type([5,6]))) + + def test_tuple(self): + var1 = () + class A: pass + self.assertTrue(isinstance(var1, tuple)) + self.assertFalse(isinstance(var1, str)) + self.assertFalse(isinstance(var1, list)) + self.assertFalse(isinstance(var1, dict)) + self.assertFalse(isinstance(var1, A)) + + def test_set(self): + self.assertTrue(isinstance(set([1,2]), type(set([3,4])))) + + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_itertools.py b/test/unit3/test_itertools.py new file mode 100644 index 0000000000..c13ce56a11 --- /dev/null +++ b/test/unit3/test_itertools.py @@ -0,0 +1,2464 @@ +import unittest +# from test import support +from itertools import * +# import weakref +# from decimal import Decimal +# from fractions import Fraction +import operator +# import random +import copy +# import pickle +# from functools import reduce +import sys +# import struct +# import threading +# maxsize = support.MAX_Py_ssize_t +# minsize = -maxsize-1 +maxsize = int(sys.maxsize**.5) # maxsize is too large to handle! +minsize = -maxsize-1 + +def lzip(*args): + return list(zip(*args)) + +def onearg(x): + 'Test function of one argument' + return 2*x + +def errfunc(*args): + 'Test function that raises an error' + raise ValueError + +def gen3(): + 'Non-restartable source sequence' + for i in (0, 1, 2): + yield i + +def isEven(x): + 'Test predicate' + return x%2==0 + +def isOdd(x): + 'Test predicate' + return x%2==1 + +def tupleize(*args): + return args + +def irange(n): + for i in range(n): + yield i + +class StopNow: + 'Class emulating an empty iterable.' + def __iter__(self): + return self + def __next__(self): + raise StopIteration + +def take(n, seq): + 'Convenience function for partially consuming a long of infinite iterable' + return list(islice(seq, n)) + +def prod(iterable): + return reduce(operator.mul, iterable, 1) + +def fact(n): + 'Factorial' + return prod(range(1, n+1)) + +# root level methods for pickling ability +def testR(r): + return r[0] + +def testR2(r): + return r[2] + +def underten(x): + return x<10 + +# picklecopiers = [lambda s, proto=proto: pickle.loads(pickle.dumps(s, proto)) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1)] + +class TestBasicOps(unittest.TestCase): + pass + +# def pickletest(self, protocol, it, stop=4, take=1, compare=None): +# """Test that an iterator is the same after pickling, also when part-consumed""" +# def expand(it, i=0): +# # Recursively expand iterables, within sensible bounds +# if i > 10: +# raise RuntimeError("infinite recursion encountered") +# if isinstance(it, str): +# return it +# try: +# l = list(islice(it, stop)) +# except TypeError: +# return it # can't expand it +# return [expand(e, i+1) for e in l] + +# # Test the initial copy against the original +# dump = pickle.dumps(it, protocol) +# i2 = pickle.loads(dump) +# self.assertEqual(type(it), type(i2)) +# a, b = expand(it), expand(i2) +# self.assertEqual(a, b) +# if compare: +# c = expand(compare) +# self.assertEqual(a, c) + +# # Take from the copy, and create another copy and compare them. +# i3 = pickle.loads(dump) +# took = 0 +# try: +# for i in range(take): +# next(i3) +# took += 1 +# except StopIteration: +# pass #in case there is less data than 'take' +# dump = pickle.dumps(i3, protocol) +# i4 = pickle.loads(dump) +# a, b = expand(i3), expand(i4) +# self.assertEqual(a, b) +# if compare: +# c = expand(compare[took:]) +# self.assertEqual(a, c); + + def test_accumulate(self): + self.assertEqual(list(accumulate(range(10))), # one positional arg + [0, 1, 3, 6, 10, 15, 21, 28, 36, 45]) + self.assertEqual(list(accumulate(iterable=range(10))), # kw arg + [0, 1, 3, 6, 10, 15, 21, 28, 36, 45]) + for typ in int, complex: #, Decimal, Fraction: # multiple types + self.assertEqual( + list(accumulate(map(typ, range(10)))), + list(map(typ, [0, 1, 3, 6, 10, 15, 21, 28, 36, 45]))) + self.assertEqual(list(accumulate('abc')), ['a', 'ab', 'abc']) # works with non-numeric + self.assertEqual(list(accumulate([])), []) # empty iterable + self.assertEqual(list(accumulate([7])), [7]) # iterable of length one + self.assertRaises(TypeError, accumulate, range(10), 5, 6) # too many args + self.assertRaises(TypeError, accumulate) # too few args + # self.assertRaises(TypeError, accumulate, x=range(10)) # unexpected kwd arg + self.assertRaises(TypeError, list, accumulate([1, []])) # args that don't add + + s = [2, 8, 9, 5, 7, 0, 3, 4, 1, 6] + self.assertEqual(list(accumulate(s, min)), + [2, 2, 2, 2, 2, 0, 0, 0, 0, 0]) + self.assertEqual(list(accumulate(s, max)), + [2, 8, 9, 9, 9, 9, 9, 9, 9, 9]) + self.assertEqual(list(accumulate(s, operator.mul)), + [2, 16, 144, 720, 5040, 0, 0, 0, 0, 0]) + self.assertEqual(list(accumulate([10, 5, 1], initial=None)), [10, 15, 16]) + self.assertEqual(list(accumulate([10, 5, 1], initial=100)), [100, 110, 115, 116]) + self.assertEqual(list(accumulate([], initial=100)), [100]) + self.assertRaises(TypeError, list, accumulate(s, chr)) # unary-operation +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, accumulate(range(10))) # test pickling + + def test_chain(self): + + # def chain2(*iterables): + # 'Pure python version in the docs' + # for it in iterables: + # for element in it: + # yield element + + for c in (chain,):# chain2): + self.assertEqual(list(c('abc', 'def')), list('abcdef')) + self.assertEqual(list(c('abc')), list('abc')) + self.assertEqual(list(c('')), []) + self.assertEqual(take(4, c('abc', 'def')), list('abcd')) + self.assertRaises(TypeError, list,c(2, 3)) + + def test_chain_from_iterable(self): + self.assertEqual(list(chain.from_iterable(['abc', 'def'])), list('abcdef')) + self.assertEqual(list(chain.from_iterable(['abc'])), list('abc')) + self.assertEqual(list(chain.from_iterable([''])), []) + self.assertEqual(take(4, chain.from_iterable(['abc', 'def'])), list('abcd')) + self.assertRaises(TypeError, list, chain.from_iterable([2, 3])) + +# def test_chain_reducible(self): +# for oper in [copy.deepcopy] + picklecopiers: +# it = chain('abc', 'def') +# self.assertEqual(list(oper(it)), list('abcdef')) +# self.assertEqual(next(it), 'a') +# self.assertEqual(list(oper(it)), list('bcdef')) + +# self.assertEqual(list(oper(chain(''))), []) +# self.assertEqual(take(4, oper(chain('abc', 'def'))), list('abcd')) +# self.assertRaises(TypeError, list, oper(chain(2, 3))) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, chain('abc', 'def'), compare=list('abcdef')) + +# def test_chain_setstate(self): +# self.assertRaises(TypeError, chain().__setstate__, ()) +# self.assertRaises(TypeError, chain().__setstate__, []) +# self.assertRaises(TypeError, chain().__setstate__, 0) +# self.assertRaises(TypeError, chain().__setstate__, ([],)) +# self.assertRaises(TypeError, chain().__setstate__, (iter([]), [])) +# it = chain() +# it.__setstate__((iter(['abc', 'def']),)) +# self.assertEqual(list(it), ['a', 'b', 'c', 'd', 'e', 'f']) +# it = chain() +# it.__setstate__((iter(['abc', 'def']), iter(['ghi']))) +# self.assertEqual(list(it), ['ghi', 'a', 'b', 'c', 'd', 'e', 'f']) + + def test_combinations(self): + self.assertRaises(TypeError, combinations, 'abc') # missing r argument + self.assertRaises(TypeError, combinations, 'abc', 2, 1) # too many arguments + self.assertRaises(TypeError, combinations, None) # pool is not iterable + self.assertRaises(ValueError, combinations, 'abc', -2) # r is negative + +# for op in [lambda a:a] + picklecopiers: +# self.assertEqual(list(op(combinations('abc', 32))), []) # r > n + +# self.assertEqual(list(op(combinations('ABCD', 2))), +# [('A','B'), ('A','C'), ('A','D'), ('B','C'), ('B','D'), ('C','D')]) +# testIntermediate = combinations('ABCD', 2) +# next(testIntermediate) +# self.assertEqual(list(op(testIntermediate)), +# [('A','C'), ('A','D'), ('B','C'), ('B','D'), ('C','D')]) + +# self.assertEqual(list(op(combinations(range(4), 3))), +# [(0,1,2), (0,1,3), (0,2,3), (1,2,3)]) +# testIntermediate = combinations(range(4), 3) +# next(testIntermediate) +# self.assertEqual(list(op(testIntermediate)), +# [(0,1,3), (0,2,3), (1,2,3)]) + + +# def combinations1(iterable, r): +# 'Pure python version shown in the docs' +# pool = tuple(iterable) +# n = len(pool) +# if r > n: +# return +# indices = list(range(r)) +# yield tuple(pool[i] for i in indices) +# while 1: +# for i in reversed(range(r)): +# if indices[i] != i + n - r: +# break +# else: +# return +# indices[i] += 1 +# for j in range(i+1, r): +# indices[j] = indices[j-1] + 1 +# yield tuple(pool[i] for i in indices) + +# def combinations2(iterable, r): +# 'Pure python version shown in the docs' +# pool = tuple(iterable) +# n = len(pool) +# for indices in permutations(range(n), r): +# if sorted(indices) == list(indices): +# yield tuple(pool[i] for i in indices) + +# def combinations3(iterable, r): +# 'Pure python version from cwr()' +# pool = tuple(iterable) +# n = len(pool) +# for indices in combinations_with_replacement(range(n), r): +# if len(set(indices)) == r: +# yield tuple(pool[i] for i in indices) + + for n in range(7): + values = [5*x-12 for x in range(n)] + for r in range(n+2): + result = list(combinations(values, r)) + self.assertEqual(len(result), 0 if r>n else fact(n) / fact(r) / fact(n-r)) # right number of combs + self.assertEqual(len(result), len(set(result))) # no repeats + self.assertEqual(result, sorted(result)) # lexicographic order + for c in result: + self.assertEqual(len(c), r) # r-length combinations + self.assertEqual(len(set(c)), r) # no duplicate elements + self.assertEqual(list(c), sorted(c)) # keep original ordering + self.assertTrue(all(e in values for e in c)) # elements taken from input iterable + self.assertEqual(list(c), + [e for e in values if e in c]) # comb is a subsequence of the input iterable +# self.assertEqual(result, list(combinations1(values, r))) # matches first pure python version +# self.assertEqual(result, list(combinations2(values, r))) # matches second pure python version +# self.assertEqual(result, list(combinations3(values, r))) # matches second pure python version + +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, combinations(values, r)) # test pickling + +# @support.bigaddrspacetest +# def test_combinations_overflow(self): +# with self.assertRaises((OverflowError, MemoryError)): +# combinations("AA", 2**29) + +# # Test implementation detail: tuple re-use +# @support.impl_detail("tuple reuse is specific to CPython") +# def test_combinations_tuple_reuse(self): +# self.assertEqual(len(set(map(id, combinations('abcde', 3)))), 1) +# self.assertNotEqual(len(set(map(id, list(combinations('abcde', 3))))), 1) + + def test_combinations_with_replacement(self): + cwr = combinations_with_replacement + self.assertRaises(TypeError, cwr, 'abc') # missing r argument + self.assertRaises(TypeError, cwr, 'abc', 2, 1) # too many arguments + self.assertRaises(TypeError, cwr, None) # pool is not iterable + self.assertRaises(ValueError, cwr, 'abc', -2) # r is negative + +# for op in [lambda a:a] + picklecopiers: +# self.assertEqual(list(op(cwr('ABC', 2))), +# [('A','A'), ('A','B'), ('A','C'), ('B','B'), ('B','C'), ('C','C')]) +# testIntermediate = cwr('ABC', 2) +# next(testIntermediate) +# self.assertEqual(list(op(testIntermediate)), +# [('A','B'), ('A','C'), ('B','B'), ('B','C'), ('C','C')]) + + +# def cwr1(iterable, r): +# 'Pure python version shown in the docs' +# # number items returned: (n+r-1)! / r! / (n-1)! when n>0 +# pool = tuple(iterable) +# n = len(pool) +# if not n and r: +# return +# indices = [0] * r +# yield tuple(pool[i] for i in indices) +# while 1: +# for i in reversed(range(r)): +# if indices[i] != n - 1: +# break +# else: +# return +# indices[i:] = [indices[i] + 1] * (r - i) +# yield tuple(pool[i] for i in indices) + +# def cwr2(iterable, r): +# 'Pure python version shown in the docs' +# pool = tuple(iterable) +# n = len(pool) +# for indices in product(range(n), repeat=r): +# if sorted(indices) == list(indices): +# yield tuple(pool[i] for i in indices) + + def numcombs(n, r): + if not n: + return 0 if r else 1 + return fact(n+r-1) / fact(r)/ fact(n-1) + + for n in range(7): + values = [5*x-12 for x in range(n)] + for r in range(n+2): + result = list(cwr(values, r)) + + self.assertEqual(len(result), numcombs(n, r)) # right number of combs + self.assertEqual(len(result), len(set(result))) # no repeats + self.assertEqual(result, sorted(result)) # lexicographic order + + regular_combs = list(combinations(values, r)) # compare to combs without replacement + if n == 0 or r <= 1: + self.assertEqual(result, regular_combs) # cases that should be identical + else: + self.assertTrue(set(result) >= set(regular_combs)) # rest should be supersets of regular combs + + for c in result: + self.assertEqual(len(c), r) # r-length combinations + noruns = [k for k,v in groupby(c)] # combo without consecutive repeats + self.assertEqual(len(noruns), len(set(noruns))) # no repeats other than consecutive + self.assertEqual(list(c), sorted(c)) # keep original ordering + self.assertTrue(all(e in values for e in c)) # elements taken from input iterable + self.assertEqual(noruns, + [e for e in values if e in c]) # comb is a subsequence of the input iterable +# self.assertEqual(result, list(cwr1(values, r))) # matches first pure python version +# self.assertEqual(result, list(cwr2(values, r))) # matches second pure python version + +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, cwr(values,r)) # test pickling + +# @support.bigaddrspacetest +# def test_combinations_with_replacement_overflow(self): +# with self.assertRaises((OverflowError, MemoryError)): +# combinations_with_replacement("AA", 2**30) + +# # Test implementation detail: tuple re-use +# @support.impl_detail("tuple reuse is specific to CPython") +# def test_combinations_with_replacement_tuple_reuse(self): +# cwr = combinations_with_replacement +# self.assertEqual(len(set(map(id, cwr('abcde', 3)))), 1) +# self.assertNotEqual(len(set(map(id, list(cwr('abcde', 3))))), 1) + + def test_permutations(self): + self.assertRaises(TypeError, permutations) # too few arguments + self.assertRaises(TypeError, permutations, 'abc', 2, 1) # too many arguments + self.assertRaises(TypeError, permutations, None) # pool is not iterable + self.assertRaises(ValueError, permutations, 'abc', -2) # r is negative + self.assertEqual(list(permutations('abc', 32)), []) # r > n + self.assertRaises(TypeError, permutations, 'abc', 's') # r is not an int or None + self.assertEqual(list(permutations(range(3), 2)), + [(0,1), (0,2), (1,0), (1,2), (2,0), (2,1)]) + +# def permutations1(iterable, r=None): +# 'Pure python version shown in the docs' +# pool = tuple(iterable) +# n = len(pool) +# r = n if r is None else r +# if r > n: +# return +# indices = list(range(n)) +# cycles = list(range(n-r+1, n+1))[::-1] +# yield tuple(pool[i] for i in indices[:r]) +# while n: +# for i in reversed(range(r)): +# cycles[i] -= 1 +# if cycles[i] == 0: +# indices[i:] = indices[i+1:] + indices[i:i+1] +# cycles[i] = n - i +# else: +# j = cycles[i] +# indices[i], indices[-j] = indices[-j], indices[i] +# yield tuple(pool[i] for i in indices[:r]) +# break +# else: +# return + +# def permutations2(iterable, r=None): +# 'Pure python version shown in the docs' +# pool = tuple(iterable) +# n = len(pool) +# r = n if r is None else r +# for indices in product(range(n), repeat=r): +# if len(set(indices)) == r: +# yield tuple(pool[i] for i in indices) + + for n in range(7): + values = [5*x-12 for x in range(n)] + for r in range(n+2): + result = list(permutations(values, r)) + self.assertEqual(len(result), 0 if r>n else fact(n) / fact(n-r)) # right number of perms + self.assertEqual(len(result), len(set(result))) # no repeats + self.assertEqual(result, sorted(result)) # lexicographic order + for p in result: + self.assertEqual(len(p), r) # r-length permutations + self.assertEqual(len(set(p)), r) # no duplicate elements + self.assertTrue(all(e in values for e in p)) # elements taken from input iterable +# self.assertEqual(result, list(permutations1(values, r))) # matches first pure python version +# self.assertEqual(result, list(permutations2(values, r))) # matches second pure python version +# if r == n: +# self.assertEqual(result, list(permutations(values, None))) # test r as None +# self.assertEqual(result, list(permutations(values))) # test default r + +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, permutations(values, r)) # test pickling + +# @support.bigaddrspacetest +# def test_permutations_overflow(self): +# with self.assertRaises((OverflowError, MemoryError)): +# permutations("A", 2**30) + +# @support.impl_detail("tuple reuse is specific to CPython") +# def test_permutations_tuple_reuse(self): +# self.assertEqual(len(set(map(id, permutations('abcde', 3)))), 1) +# self.assertNotEqual(len(set(map(id, list(permutations('abcde', 3))))), 1) + + def test_combinatorics(self): + # Test relationships between product(), permutations(), + # combinations() and combinations_with_replacement(). + + for n in range(5): + s = 'ABCDEFG'[:n] + for r in range(7): + prod = list(product(s, repeat=r)) + cwr = list(combinations_with_replacement(s, r)) + perm = list(permutations(s, r)) + comb = list(combinations(s, r)) + + # Check size + self.assertEqual(len(prod), n**r) + self.assertEqual(len(cwr), (fact(n+r-1) / fact(r)/ fact(n-1)) if n else (not r)) + self.assertEqual(len(perm), 0 if r>n else fact(n) / fact(n-r)) + self.assertEqual(len(comb), 0 if r>n else fact(n) / fact(r) / fact(n-r)) + + # Check lexicographic order without repeated tuples + self.assertEqual(prod, sorted(set(prod))) + self.assertEqual(cwr, sorted(set(cwr))) + self.assertEqual(perm, sorted(set(perm))) + self.assertEqual(comb, sorted(set(comb))) + + # Check interrelationships + self.assertEqual(cwr, [t for t in prod if sorted(t)==list(t)]) # cwr: prods which are sorted + self.assertEqual(perm, [t for t in prod if len(set(t))==r]) # perm: prods with no dups + self.assertEqual(comb, [t for t in perm if sorted(t)==list(t)]) # comb: perms that are sorted + self.assertEqual(comb, [t for t in cwr if len(set(t))==r]) # comb: cwrs without dups + # self.assertEqual(comb, list(filter(set(cwr).__contains__, perm))) # comb: perm that is a cwr + # self.assertEqual(comb, list(filter(set(perm).__contains__, cwr))) # comb: cwr that is a perm + self.assertEqual(comb, sorted(set(cwr) & set(perm))) # comb: both a cwr and a perm + + def test_compress(self): + self.assertEqual(list(compress(data='ABCDEF', selectors=[1,0,1,0,1,1])), list('ACEF')) + self.assertEqual(list(compress('ABCDEF', [1,0,1,0,1,1])), list('ACEF')) + self.assertEqual(list(compress('ABCDEF', [0,0,0,0,0,0])), list('')) + self.assertEqual(list(compress('ABCDEF', [1,1,1,1,1,1])), list('ABCDEF')) + self.assertEqual(list(compress('ABCDEF', [1,0,1])), list('AC')) + self.assertEqual(list(compress('ABC', [0,1,1,1,1,1])), list('BC')) + n = 10000 + data = chain.from_iterable(repeat(range(6), n)) + selectors = chain.from_iterable(repeat((0, 1))) + self.assertEqual(list(compress(data, selectors)), [1,3,5] * n) + self.assertRaises(TypeError, compress, None, range(6)) # 1st arg not iterable + self.assertRaises(TypeError, compress, range(6), None) # 2nd arg not iterable + self.assertRaises(TypeError, compress, range(6)) # too few args + self.assertRaises(TypeError, compress, range(6), None) # too many args + +# # check copy, deepcopy, pickle +# for op in [lambda a:copy.copy(a), lambda a:copy.deepcopy(a)] + picklecopiers: +# for data, selectors, result1, result2 in [ +# ('ABCDEF', [1,0,1,0,1,1], 'ACEF', 'CEF'), +# ('ABCDEF', [0,0,0,0,0,0], '', ''), +# ('ABCDEF', [1,1,1,1,1,1], 'ABCDEF', 'BCDEF'), +# ('ABCDEF', [1,0,1], 'AC', 'C'), +# ('ABC', [0,1,1,1,1,1], 'BC', 'C'), +# ]: + +# self.assertEqual(list(op(compress(data=data, selectors=selectors))), list(result1)) +# self.assertEqual(list(op(compress(data, selectors))), list(result1)) +# testIntermediate = compress(data, selectors) +# if result1: +# next(testIntermediate) +# self.assertEqual(list(op(testIntermediate)), list(result2)) + + + def test_count(self): + self.assertEqual(lzip('abc',count()), [('a', 0), ('b', 1), ('c', 2)]) + self.assertEqual(lzip('abc',count(3)), [('a', 3), ('b', 4), ('c', 5)]) + self.assertEqual(take(2, lzip('abc',count(3))), [('a', 3), ('b', 4)]) + self.assertEqual(take(2, zip('abc',count(-1))), [('a', -1), ('b', 0)]) + self.assertEqual(take(2, zip('abc',count(-3))), [('a', -3), ('b', -2)]) + self.assertRaises(TypeError, count, 2, 3, 4) + self.assertRaises(TypeError, count, 'a') + self.assertEqual(take(10, count(maxsize-5)), + list(range(maxsize-5, maxsize+5))) + self.assertEqual(take(10, count(-maxsize-5)), + list(range(-maxsize-5, -maxsize+5))) + self.assertEqual(take(3, count(3.25)), [3.25, 4.25, 5.25]) + self.assertEqual(take(3, count(3.25-4j)), [3.25-4j, 4.25-4j, 5.25-4j]) +# self.assertEqual(take(3, count(Decimal('1.1'))), +# [Decimal('1.1'), Decimal('2.1'), Decimal('3.1')]) +# self.assertEqual(take(3, count(Fraction(2, 3))), +# [Fraction(2, 3), Fraction(5, 3), Fraction(8, 3)]) + BIGINT = 1<<100 + self.assertEqual(take(3, count(BIGINT)), [BIGINT, BIGINT+1, BIGINT+2]) + c = count(3) + self.assertEqual(repr(c), 'count(3)') + next(c) + self.assertEqual(repr(c), 'count(4)') + c = count(-9) + self.assertEqual(repr(c), 'count(-9)') + next(c) + self.assertEqual(next(c), -8) + self.assertEqual(repr(count(10.25)), 'count(10.25)') + self.assertEqual(repr(count(10.0)), 'count(10.0)') + self.assertEqual(type(next(count(10.0))), float) + for i in (maxsize-5, maxsize+5 ,-10, -1, 0, 10, maxsize-5, maxsize+5): + # Test repr + r1 = repr(count(i)) + r2 = ('count(%r)' % i) + self.assertEqual(r1, r2) + +# # check copy, deepcopy, pickle +# for value in -3, 3, maxsize-5, maxsize+5: +# c = count(value) +# self.assertEqual(next(copy.copy(c)), value) +# self.assertEqual(next(copy.deepcopy(c)), value) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, count(value)) + +# #check proper internal error handling for large "step' sizes +# count(1, maxsize+5); sys.exc_info() + + def test_count_with_stride(self): + self.assertEqual(lzip('abc',count(2,3)), [('a', 2), ('b', 5), ('c', 8)]) + self.assertEqual(lzip('abc',count(start=2,step=3)), + [('a', 2), ('b', 5), ('c', 8)]) + self.assertEqual(lzip('abc',count(step=-1)), + [('a', 0), ('b', -1), ('c', -2)]) + self.assertRaises(TypeError, count, 'a', 'b') + self.assertEqual(lzip('abc',count(2,0)), [('a', 2), ('b', 2), ('c', 2)]) + self.assertEqual(lzip('abc',count(2,1)), [('a', 2), ('b', 3), ('c', 4)]) + self.assertEqual(lzip('abc',count(2,3)), [('a', 2), ('b', 5), ('c', 8)]) + self.assertEqual(take(20, count(maxsize-15, 3)), take(20, range(maxsize-15, maxsize+100, 3))) + self.assertEqual(take(20, count(-maxsize-15, 3)), take(20, range(-maxsize-15,-maxsize+100, 3))) + self.assertEqual(take(3, count(10, maxsize+5)), + list(range(10, 10+3*(maxsize+5), maxsize+5))) + self.assertEqual(take(3, count(2, 1.25)), [2, 3.25, 4.5]) + self.assertEqual(take(3, count(2, 3.25-4j)), [2, 5.25-4j, 8.5-8j]) +# self.assertEqual(take(3, count(Decimal('1.1'), Decimal('.1'))), +# [Decimal('1.1'), Decimal('1.2'), Decimal('1.3')]) +# self.assertEqual(take(3, count(Fraction(2,3), Fraction(1,7))), +# [Fraction(2,3), Fraction(17,21), Fraction(20,21)]) + BIGINT = 1<<100 + self.assertEqual(take(3, count(step=BIGINT)), [0, BIGINT, 2*BIGINT]) + self.assertEqual(repr(take(3, count(10, 2.5))), repr([10, 12.5, 15.0])) + c = count(3, 5) + self.assertEqual(repr(c), 'count(3, 5)') + next(c) + self.assertEqual(repr(c), 'count(8, 5)') + c = count(-9, 0) + self.assertEqual(repr(c), 'count(-9, 0)') + next(c) + self.assertEqual(repr(c), 'count(-9, 0)') + c = count(-9, -3) + self.assertEqual(repr(c), 'count(-9, -3)') + next(c) + self.assertEqual(repr(c), 'count(-12, -3)') + self.assertEqual(repr(c), 'count(-12, -3)') + self.assertEqual(repr(count(10.5, 1.25)), 'count(10.5, 1.25)') + self.assertEqual(repr(count(10.5, 1)), 'count(10.5)') # suppress step=1 when it's an int + self.assertEqual(repr(count(10.5, 1.00)), 'count(10.5, 1.0)') # do show float values lilke 1.0 + self.assertEqual(repr(count(10, 1.00)), 'count(10, 1.0)') + c = count(10, 1.0) + self.assertEqual(type(next(c)), int) + self.assertEqual(type(next(c)), float) + for i in (maxsize-5, maxsize+5 ,-10, -1, 0, 10, maxsize-5, sys.maxsize+5): + for j in (maxsize-5, maxsize+5 ,-10, -1, 0, 1, 10, maxsize-5, maxsize+5): + # Test repr + r1 = repr(count(i, j)) + if j == 1: + r2 = ('count(%r)' % i) + else: + r2 = ('count(%r, %r)' % (i, j)) + self.assertEqual(r1, r2) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, count(i, j)) + + def test_cycle(self): + self.assertEqual(take(10, cycle('abc')), list('abcabcabca')) + self.assertEqual(list(cycle('')), []) + self.assertRaises(TypeError, cycle) + self.assertRaises(TypeError, cycle, 5) + self.assertEqual(list(islice(cycle(gen3()),10)), [0,1,2,0,1,2,0,1,2,0]) + +# # check copy, deepcopy, pickle +# c = cycle('abc') +# self.assertEqual(next(c), 'a') +# #simple copy currently not supported, because __reduce__ returns +# #an internal iterator +# #self.assertEqual(take(10, copy.copy(c)), list('bcabcabcab')) +# self.assertEqual(take(10, copy.deepcopy(c)), list('bcabcabcab')) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.assertEqual(take(10, pickle.loads(pickle.dumps(c, proto))), +# list('bcabcabcab')) +# next(c) +# self.assertEqual(take(10, pickle.loads(pickle.dumps(c, proto))), +# list('cabcabcabc')) +# next(c) +# next(c) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, cycle('abc')) + +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# # test with partial consumed input iterable +# it = iter('abcde') +# c = cycle(it) +# _ = [next(c) for i in range(2)] # consume 2 of 5 inputs +# p = pickle.dumps(c, proto) +# d = pickle.loads(p) # rebuild the cycle object +# self.assertEqual(take(20, d), list('cdeabcdeabcdeabcdeab')) + +# # test with completely consumed input iterable +# it = iter('abcde') +# c = cycle(it) +# _ = [next(c) for i in range(7)] # consume 7 of 5 inputs +# p = pickle.dumps(c, proto) +# d = pickle.loads(p) # rebuild the cycle object +# self.assertEqual(take(20, d), list('cdeabcdeabcdeabcdeab')) + +# def test_cycle_setstate(self): +# # Verify both modes for restoring state + +# # Mode 0 is efficient. It uses an incompletely consumed input +# # iterator to build a cycle object and then passes in state with +# # a list of previously consumed values. There is no data +# # overlap between the two. +# c = cycle('defg') +# c.__setstate__((list('abc'), 0)) +# self.assertEqual(take(20, c), list('defgabcdefgabcdefgab')) + +# # Mode 1 is inefficient. It starts with a cycle object built +# # from an iterator over the remaining elements in a partial +# # cycle and then passes in state with all of the previously +# # seen values (this overlaps values included in the iterator). +# c = cycle('defg') +# c.__setstate__((list('abcdefg'), 1)) +# self.assertEqual(take(20, c), list('defgabcdefgabcdefgab')) + +# # The first argument to setstate needs to be a tuple +# with self.assertRaises(TypeError): +# cycle('defg').__setstate__([list('abcdefg'), 0]) + +# # The first argument in the setstate tuple must be a list +# with self.assertRaises(TypeError): +# c = cycle('defg') +# c.__setstate__((tuple('defg'), 0)) +# take(20, c) + +# # The second argument in the setstate tuple must be an int +# with self.assertRaises(TypeError): +# cycle('defg').__setstate__((list('abcdefg'), 'x')) + +# self.assertRaises(TypeError, cycle('').__setstate__, ()) +# self.assertRaises(TypeError, cycle('').__setstate__, ([],)) + + def test_groupby(self): + # Check whether it accepts arguments correctly + self.assertEqual([], list(groupby([]))) + self.assertEqual([], list(groupby([], key=id))) + self.assertRaises(TypeError, list, groupby('abc', [])) + self.assertRaises(TypeError, groupby, None) + self.assertRaises(TypeError, groupby, 'abc', lambda x:x, 10) + + # Check normal input + s = [(0, 10, 20), (0, 11,21), (0,12,21), (1,13,21), (1,14,22), + (2,15,22), (3,16,23), (3,17,23)] + dup = [] + for k, g in groupby(s, lambda r:r[0]): + for elem in g: + self.assertEqual(k, elem[0]) + dup.append(elem) + self.assertEqual(s, dup) + +# # Check normal pickled +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# dup = [] +# for k, g in pickle.loads(pickle.dumps(groupby(s, testR), proto)): +# for elem in g: +# self.assertEqual(k, elem[0]) +# dup.append(elem) +# self.assertEqual(s, dup) + +# # Check nested case + dup = [] + for k, g in groupby(s, testR): + for ik, ig in groupby(g, testR2): + for elem in ig: + self.assertEqual(k, elem[0]) + self.assertEqual(ik, elem[2]) + dup.append(elem) + self.assertEqual(s, dup) + +# # Check nested and pickled +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# dup = [] +# for k, g in pickle.loads(pickle.dumps(groupby(s, testR), proto)): +# for ik, ig in pickle.loads(pickle.dumps(groupby(g, testR2), proto)): +# for elem in ig: +# self.assertEqual(k, elem[0]) +# self.assertEqual(ik, elem[2]) +# dup.append(elem) +# self.assertEqual(s, dup) + + +# # Check case where inner iterator is not used + keys = [k for k, g in groupby(s, testR)] + expectedkeys = set([r[0] for r in s]) + self.assertEqual(set(keys), expectedkeys) + self.assertEqual(len(keys), len(expectedkeys)) + +# # Check case where inner iterator is used after advancing the groupby +# # iterator + s = list(zip('AABBBAAAA', range(9))) + it = groupby(s, testR) + _, g1 = next(it) + _, g2 = next(it) + _, g3 = next(it) + self.assertEqual(list(g1), []) + self.assertEqual(list(g2), []) + self.assertEqual(next(g3), ('A', 5)) + list(it) # exhaust the groupby iterator + self.assertEqual(list(g3), []) + +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# it = groupby(s, testR) +# _, g = next(it) +# next(it) +# next(it) +# self.assertEqual(list(pickle.loads(pickle.dumps(g, proto))), []) + + # Exercise pipes and filters style + s = 'abracadabra' + # sort s | uniq + r = [k for k, g in groupby(sorted(s))] + self.assertEqual(r, ['a', 'b', 'c', 'd', 'r']) + # sort s | uniq -d + r = [k for k, g in groupby(sorted(s)) if list(islice(g,1,2))] + self.assertEqual(r, ['a', 'b', 'r']) + # sort s | uniq -c + r = [(len(list(g)), k) for k, g in groupby(sorted(s))] + self.assertEqual(r, [(5, 'a'), (2, 'b'), (1, 'c'), (1, 'd'), (2, 'r')]) + # sort s | uniq -c | sort -rn | head -3 + r = sorted([(len(list(g)) , k) for k, g in groupby(sorted(s))], reverse=True)[:3] + self.assertEqual(r, [(5, 'a'), (2, 'r'), (2, 'b')]) + + # iter.__next__ failure + class ExpectedError(Exception): + pass + def delayed_raise(n=0): + for i in range(n): + yield 'yo' + raise ExpectedError + def gulp(iterable, keyp=None, func=list): + return [func(g) for k, g in groupby(iterable, keyp)] + + # iter.__next__ failure on outer object + self.assertRaises(ExpectedError, gulp, delayed_raise(0)) + # iter.__next__ failure on inner object + self.assertRaises(ExpectedError, gulp, delayed_raise(1)) + + # __eq__ failure + class DummyCmp: + def __eq__(self, dst): + raise ExpectedError + s = [DummyCmp(), DummyCmp(), None] + + # __eq__ failure on outer object + # self.assertRaises(ExpectedError, gulp, s, func=id) + # __eq__ failure on inner object + self.assertRaises(ExpectedError, gulp, s) + + # keyfunc failure + def keyfunc(obj): + if keyfunc.skip > 0: + keyfunc.skip -= 1 + return obj + else: + raise ExpectedError + + # keyfunc failure on outer object + keyfunc.skip = 0 + self.assertRaises(ExpectedError, gulp, [None], keyfunc) + keyfunc.skip = 1 + self.assertRaises(ExpectedError, gulp, [None, None], keyfunc) + + def test_filter(self): + self.assertEqual(list(filter(isEven, range(6))), [0,2,4]) + self.assertEqual(list(filter(None, [0,1,0,2,0])), [1,2]) + self.assertEqual(list(filter(bool, [0,1,0,2,0])), [1,2]) + self.assertEqual(take(4, filter(isEven, count())), [0,2,4,6]) + self.assertRaises(TypeError, filter) + self.assertRaises(TypeError, filter, lambda x:x) + self.assertRaises(TypeError, filter, lambda x:x, range(6), 7) + self.assertRaises(TypeError, filter, isEven, 3) + self.assertRaises(TypeError, next, filter(range(6), range(6))) + +# # check copy, deepcopy, pickle +# ans = [0,2,4] + +# c = filter(isEven, range(6)) +# self.assertEqual(list(copy.copy(c)), ans) +# c = filter(isEven, range(6)) +# self.assertEqual(list(copy.deepcopy(c)), ans) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# c = filter(isEven, range(6)) +# self.assertEqual(list(pickle.loads(pickle.dumps(c, proto))), ans) +# next(c) +# self.assertEqual(list(pickle.loads(pickle.dumps(c, proto))), ans[1:]) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# c = filter(isEven, range(6)) +# self.pickletest(proto, c) + + def test_filterfalse(self): + self.assertEqual(list(filterfalse(isEven, range(6))), [1,3,5]) + self.assertEqual(list(filterfalse(None, [0,1,0,2,0])), [0,0,0]) + self.assertEqual(list(filterfalse(bool, [0,1,0,2,0])), [0,0,0]) + self.assertEqual(take(4, filterfalse(isEven, count())), [1,3,5,7]) + self.assertRaises(TypeError, filterfalse) + self.assertRaises(TypeError, filterfalse, lambda x:x) + self.assertRaises(TypeError, filterfalse, lambda x:x, range(6), 7) + self.assertRaises(TypeError, filterfalse, isEven, 3) + self.assertRaises(TypeError, next, filterfalse(range(6), range(6))) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, filterfalse(isEven, range(6))) + + def test_zip(self): + # XXX This is rather silly now that builtin zip() calls zip()... + ans = [(x,y) for x, y in zip('abc',count())] + self.assertEqual(ans, [('a', 0), ('b', 1), ('c', 2)]) + self.assertEqual(list(zip('abc', range(6))), lzip('abc', range(6))) + self.assertEqual(list(zip('abcdef', range(3))), lzip('abcdef', range(3))) + self.assertEqual(take(3,zip('abcdef', count())), lzip('abcdef', range(3))) + self.assertEqual(list(zip('abcdef')), lzip('abcdef')) + self.assertEqual(list(zip()), lzip()) + self.assertRaises(TypeError, zip, 3) + self.assertRaises(TypeError, zip, range(3), 3) + self.assertEqual([tuple(list(pair)) for pair in zip('abc', 'def')], + lzip('abc', 'def')) + self.assertEqual([pair for pair in zip('abc', 'def')], + lzip('abc', 'def')) + +# @support.impl_detail("tuple reuse is specific to CPython") +# def test_zip_tuple_reuse(self): +# ids = list(map(id, zip('abc', 'def'))) +# self.assertEqual(min(ids), max(ids)) +# ids = list(map(id, list(zip('abc', 'def')))) +# self.assertEqual(len(dict.fromkeys(ids)), len(ids)) + +# # check copy, deepcopy, pickle +# ans = [(x,y) for x, y in copy.copy(zip('abc',count()))] +# self.assertEqual(ans, [('a', 0), ('b', 1), ('c', 2)]) + +# ans = [(x,y) for x, y in copy.deepcopy(zip('abc',count()))] +# self.assertEqual(ans, [('a', 0), ('b', 1), ('c', 2)]) + +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# ans = [(x,y) for x, y in pickle.loads(pickle.dumps(zip('abc',count()), proto))] +# self.assertEqual(ans, [('a', 0), ('b', 1), ('c', 2)]) + +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# testIntermediate = zip('abc',count()) +# next(testIntermediate) +# ans = [(x,y) for x, y in pickle.loads(pickle.dumps(testIntermediate, proto))] +# self.assertEqual(ans, [('b', 1), ('c', 2)]) + +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, zip('abc', count())) + + def test_ziplongest(self): + for args in [ + ['abc', range(6)], + [range(6), 'abc'], + [range(1000), range(2000,2100), range(3000,3050)], + [range(1000), range(0), range(3000,3050), range(1200), range(1500)], + [range(1000), range(0), range(3000,3050), range(1200), range(1500), range(0)], + ]: + target = [tuple([arg[i] if i < len(arg) else None for arg in args]) + for i in range(max(map(len, args)))] + self.assertEqual(list(zip_longest(*args)), target) + self.assertEqual(list(zip_longest(*args, **{})), target) + target = [tuple((e is None and 'X' or e) for e in t) for t in target] # Replace None fills with 'X' + self.assertEqual(list(zip_longest(*args, **dict(fillvalue='X'))), target) + + self.assertEqual(take(3,zip_longest('abcdef', count())), list(zip('abcdef', range(3)))) # take 3 from infinite input + + self.assertEqual(list(zip_longest()), list(zip())) + self.assertEqual(list(zip_longest([])), list(zip([]))) + self.assertEqual(list(zip_longest('abcdef')), list(zip('abcdef'))) + + self.assertEqual(list(zip_longest('abc', 'defg', **{})), + list(zip(list('abc')+[None], 'defg'))) # empty keyword dict + self.assertRaises(TypeError, zip_longest, 3) + self.assertRaises(TypeError, zip_longest, range(3), 3) + +# for stmt in [ +# "zip_longest('abc', fv=1)", +# "zip_longest('abc', fillvalue=1, bogus_keyword=None)", +# ]: +# try: +# eval(stmt, globals(), locals()) +# except TypeError: +# pass +# else: +# self.fail('Did not raise Type in: ' + stmt) + + self.assertEqual([tuple(list(pair)) for pair in zip_longest('abc', 'def')], + list(zip('abc', 'def'))) + self.assertEqual([pair for pair in zip_longest('abc', 'def')], + list(zip('abc', 'def'))) + +# @support.impl_detail("tuple reuse is specific to CPython") +# def test_zip_longest_tuple_reuse(self): +# ids = list(map(id, zip_longest('abc', 'def'))) +# self.assertEqual(min(ids), max(ids)) +# ids = list(map(id, list(zip_longest('abc', 'def')))) +# self.assertEqual(len(dict.fromkeys(ids)), len(ids)) + +# def test_zip_longest_pickling(self): +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, zip_longest("abc", "def")) +# self.pickletest(proto, zip_longest("abc", "defgh")) +# self.pickletest(proto, zip_longest("abc", "defgh", fillvalue=1)) +# self.pickletest(proto, zip_longest("", "defgh")) + + def test_bug_7244(self): + + class Repeater: + # this class is similar to itertools.repeat + def __init__(self, o, t, e): + self.o = o + self.t = int(t) + self.e = e + def __iter__(self): # its iterator is itself + return self + def __next__(self): + if self.t > 0: + self.t -= 1 + return self.o + else: + raise self.e + + # Formerly this code in would fail in debug mode + # with Undetected Error and Stop Iteration + r1 = Repeater(1, 3, StopIteration) + r2 = Repeater(2, 4, StopIteration) + def run(r1, r2): + result = [] + for i, j in zip_longest(r1, r2, fillvalue=0): + # with support.captured_output('stdout'): + # print((i, j)) + result.append((i, j)) + return result + self.assertEqual(run(r1, r2), [(1,2), (1,2), (1,2), (0,2)]) + + # Formerly, the RuntimeError would be lost + # and StopIteration would stop as expected + r1 = Repeater(1, 3, RuntimeError) + r2 = Repeater(2, 4, StopIteration) + it = zip_longest(r1, r2, fillvalue=0) + self.assertEqual(next(it), (1, 2)) + self.assertEqual(next(it), (1, 2)) + self.assertEqual(next(it), (1, 2)) + self.assertRaises(RuntimeError, next, it) + + def test_product(self): + for args, result in [ + ([], [()]), # zero iterables + (['ab'], [('a',), ('b',)]), # one iterable + ([range(2), range(3)], [(0,0), (0,1), (0,2), (1,0), (1,1), (1,2)]), # two iterables + ([range(0), range(2), range(3)], []), # first iterable with zero length + ([range(2), range(0), range(3)], []), # middle iterable with zero length + ([range(2), range(3), range(0)], []), # last iterable with zero length + ]: + self.assertEqual(list(product(*args)), result) + for r in range(4): + self.assertEqual(list(product(*(args*r))), + list(product(*args, **dict(repeat=r)))) + self.assertEqual(len(list(product(*[range(7)]*6))), 7**6) + self.assertRaises(TypeError, product, range(6), None) + + # def product1(*args, **kwds): + # pools = list(map(tuple, args)) * kwds.get('repeat', 1) + # n = len(pools) + # if n == 0: + # yield () + # return + # if any(len(pool) == 0 for pool in pools): + # return + # indices = [0] * n + # yield tuple(pool[i] for pool, i in zip(pools, indices)) + # while 1: + # for i in reversed(range(n)): # right to left + # if indices[i] == len(pools[i]) - 1: + # continue + # indices[i] += 1 + # for j in range(i+1, n): + # indices[j] = 0 + # yield tuple(pool[i] for pool, i in zip(pools, indices)) + # break + # else: + # return + + # def product2(*args, **kwds): + # 'Pure python version used in docs' + # pools = list(map(tuple, args)) * kwds.get('repeat', 1) + # result = [[]] + # for pool in pools: + # result = [x+[y] for x in result for y in pool] + # for prod in result: + # yield tuple(prod) + + # argtypes = ['', 'abc', '', range(0), range(4), dict(a=1, b=2, c=3), + # set('abcdefg'), range(11), tuple(range(13))] + # for i in range(100): + # args = [random.choice(argtypes) for j in range(random.randrange(5))] + # expected_len = prod(map(len, args)) + # self.assertEqual(len(list(product(*args))), expected_len) + # self.assertEqual(list(product(*args)), list(product1(*args))) + # self.assertEqual(list(product(*args)), list(product2(*args))) + # args = map(iter, args) + # self.assertEqual(len(list(product(*args))), expected_len) + +# @support.bigaddrspacetest +# def test_product_overflow(self): +# with self.assertRaises((OverflowError, MemoryError)): +# product(*(['ab']*2**5), repeat=2**25) + +# @support.impl_detail("tuple reuse is specific to CPython") +# def test_product_tuple_reuse(self): +# self.assertEqual(len(set(map(id, product('abc', 'def')))), 1) +# self.assertNotEqual(len(set(map(id, list(product('abc', 'def'))))), 1) + +# def test_product_pickling(self): +# # check copy, deepcopy, pickle +# for args, result in [ +# ([], [()]), # zero iterables +# (['ab'], [('a',), ('b',)]), # one iterable +# ([range(2), range(3)], [(0,0), (0,1), (0,2), (1,0), (1,1), (1,2)]), # two iterables +# ([range(0), range(2), range(3)], []), # first iterable with zero length +# ([range(2), range(0), range(3)], []), # middle iterable with zero length +# ([range(2), range(3), range(0)], []), # last iterable with zero length +# ]: +# self.assertEqual(list(copy.copy(product(*args))), result) +# self.assertEqual(list(copy.deepcopy(product(*args))), result) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, product(*args)) + +# def test_product_issue_25021(self): +# # test that indices are properly clamped to the length of the tuples +# p = product((1, 2),(3,)) +# p.__setstate__((0, 0x1000)) # will access tuple element 1 if not clamped +# self.assertEqual(next(p), (2, 3)) +# # test that empty tuple in the list will result in an immediate StopIteration +# p = product((1, 2), (), (3,)) +# p.__setstate__((0, 0, 0x1000)) # will access tuple element 1 if not clamped +# self.assertRaises(StopIteration, next, p) + + def test_repeat(self): + self.assertEqual(list(repeat(object='a', times=3)), ['a', 'a', 'a']) +# self.assertEqual(lzip(range(3),repeat('a')), +# [(0, 'a'), (1, 'a'), (2, 'a')]) + self.assertEqual(list(repeat('a', 3)), ['a', 'a', 'a']) + self.assertEqual(take(3, repeat('a')), ['a', 'a', 'a']) + self.assertEqual(list(repeat('a', 0)), []) + self.assertEqual(list(repeat('a', -3)), []) + self.assertRaises(TypeError, repeat) + self.assertRaises(TypeError, repeat, None, 3, 4) + self.assertRaises(TypeError, repeat, None, 'a') + r = repeat(1+0j) + self.assertEqual(repr(r), 'repeat((1+0j))') + r = repeat(1+0j, 5) + self.assertEqual(repr(r), 'repeat((1+0j), 5)') + list(r) + self.assertEqual(repr(r), 'repeat((1+0j), 0)') + +# # check copy, deepcopy, pickle +# c = repeat(object='a', times=10) +# self.assertEqual(next(c), 'a') +# self.assertEqual(take(2, copy.copy(c)), list('a' * 2)) +# self.assertEqual(take(2, copy.deepcopy(c)), list('a' * 2)) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, repeat(object='a', times=10)) + + def test_repeat_with_negative_times(self): + self.assertEqual(repr(repeat('a', -1)), "repeat('a', 0)") + self.assertEqual(repr(repeat('a', -2)), "repeat('a', 0)") + self.assertEqual(repr(repeat('a', times=-1)), "repeat('a', 0)") + self.assertEqual(repr(repeat('a', times=-2)), "repeat('a', 0)") + + def test_map(self): + self.assertEqual(list(map(operator.pow, range(3), range(1,7))), + [0**1, 1**2, 2**3]) + self.assertEqual(list(map(tupleize, 'abc', range(5))), + [('a',0),('b',1),('c',2)]) + self.assertEqual(list(map(tupleize, 'abc', count())), + [('a',0),('b',1),('c',2)]) + self.assertEqual(take(2,map(tupleize, 'abc', count())), + [('a',0),('b',1)]) + self.assertEqual(list(map(operator.pow, [])), []) + self.assertRaises(TypeError, map) + # self.assertRaises(TypeError, list, map(None, range(3), range(3))) + self.assertRaises(TypeError, map, operator.neg) + self.assertRaises(TypeError, next, map(10, range(5))) + self.assertRaises(ValueError, next, map(errfunc, [4], [5])) + self.assertRaises(TypeError, next, map(onearg, [4], [5])) + +# # check copy, deepcopy, pickle +# ans = [('a',0),('b',1),('c',2)] + +# c = map(tupleize, 'abc', count()) +# self.assertEqual(list(copy.copy(c)), ans) + +# c = map(tupleize, 'abc', count()) +# self.assertEqual(list(copy.deepcopy(c)), ans) + +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# c = map(tupleize, 'abc', count()) +# self.pickletest(proto, c) + + def test_starmap(self): + self.assertEqual(list(starmap(operator.pow, zip(range(3), range(1,7)))), + [0**1, 1**2, 2**3]) + self.assertEqual(take(3, starmap(operator.pow, zip(count(), count(1)))), + [0**1, 1**2, 2**3]) + self.assertEqual(list(starmap(operator.pow, [])), []) + self.assertEqual(list(starmap(operator.pow, [iter([4,5])])), [4**5]) + self.assertRaises(TypeError, list, starmap(operator.pow, [None])) + self.assertRaises(TypeError, starmap) + self.assertRaises(TypeError, starmap, operator.pow, [(4,5)], 'extra') + self.assertRaises(TypeError, next, starmap(10, [(4,5)])) + self.assertRaises(ValueError, next, starmap(errfunc, [(4,5)])) + self.assertRaises(TypeError, next, starmap(onearg, [(4,5)])) + +# # check copy, deepcopy, pickle +# ans = [0**1, 1**2, 2**3] + +# c = starmap(operator.pow, zip(range(3), range(1,7))) +# self.assertEqual(list(copy.copy(c)), ans) + +# c = starmap(operator.pow, zip(range(3), range(1,7))) +# self.assertEqual(list(copy.deepcopy(c)), ans) + +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# c = starmap(operator.pow, zip(range(3), range(1,7))) +# self.pickletest(proto, c) + + def test_islice(self): + for args in [ # islice(args) should agree with range(args) + (10, 20, 3), + (10, 3, 20), + (10, 20), + (10, 10), + (10, 3), + (20,) + ]: + self.assertEqual(list(islice(range(100), *args)), + list(range(*args))) + + for args, tgtargs in [ # Stop when seqn is exhausted + ((10, 110, 3), ((10, 100, 3))), + ((10, 110), ((10, 100))), + ((110,), (100,)) + ]: + self.assertEqual(list(islice(range(100), *args)), + list(range(*tgtargs))) + + # Test stop=None + self.assertEqual(list(islice(range(10), None)), list(range(10))) + self.assertEqual(list(islice(range(10), None, None)), list(range(10))) + self.assertEqual(list(islice(range(10), None, None, None)), list(range(10))) + self.assertEqual(list(islice(range(10), 2, None)), list(range(2, 10))) + self.assertEqual(list(islice(range(10), 1, None, 2)), list(range(1, 10, 2))) + + # Test number of items consumed SF #1171417 + it = iter(range(10)) + self.assertEqual(list(islice(it, 3)), list(range(3))) + self.assertEqual(list(it), list(range(3, 10))) + + it = iter(range(10)) + self.assertEqual(list(islice(it, 3, 3)), []) + self.assertEqual(list(it), list(range(3, 10))) + + # # Test invalid arguments + ra = range(10) + self.assertRaises(TypeError, islice, ra) + self.assertRaises(TypeError, islice, ra, 1, 2, 3, 4) + self.assertRaises(ValueError, islice, ra, -5, 10, 1) + self.assertRaises(ValueError, islice, ra, 1, -5, -1) + self.assertRaises(ValueError, islice, ra, 1, 10, -1) + self.assertRaises(ValueError, islice, ra, 1, 10, 0) + self.assertRaises(ValueError, islice, ra, 'a') + self.assertRaises(ValueError, islice, ra, 'a', 1) + self.assertRaises(ValueError, islice, ra, 1, 'a') + self.assertRaises(ValueError, islice, ra, 'a', 1, 1) + self.assertRaises(ValueError, islice, ra, 1, 'a', 1) + self.assertEqual(len(list(islice(count(), 1, 10, maxsize))), 1) + + # Issue #10323: Less islice in a predictable state + c = count() + self.assertEqual(list(islice(c, 1, 3, 50)), [1]) + self.assertEqual(next(c), 3) + +# # check copy, deepcopy, pickle +# for args in [ # islice(args) should agree with range(args) +# (10, 20, 3), +# (10, 3, 20), +# (10, 20), +# (10, 3), +# (20,) +# ]: +# self.assertEqual(list(copy.copy(islice(range(100), *args))), +# list(range(*args))) +# self.assertEqual(list(copy.deepcopy(islice(range(100), *args))), +# list(range(*args))) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, islice(range(100), *args)) + +# # Issue #21321: check source iterator is not referenced +# # from islice() after the latter has been exhausted +# it = (x for x in (1, 2)) +# wr = weakref.ref(it) +# it = islice(it, 1) +# self.assertIsNotNone(wr()) +# list(it) # exhaust the iterator +# support.gc_collect() +# self.assertIsNone(wr()) + + # Issue #30537: islice can accept integer-like objects as + # arguments + class IntLike(object): + def __init__(self, val): + self.val = val + def __index__(self): + return self.val + self.assertEqual(list(islice(range(100), IntLike(10))), list(range(10))) + self.assertEqual(list(islice(range(100), IntLike(10), IntLike(50))), + list(range(10, 50))) + self.assertEqual(list(islice(range(100), IntLike(10), IntLike(50), IntLike(5))), + list(range(10,50,5))) + + def test_takewhile(self): + data = [1, 3, 5, 20, 2, 4, 6, 8] + self.assertEqual(list(takewhile(underten, data)), [1, 3, 5]) + self.assertEqual(list(takewhile(underten, [])), []) + self.assertRaises(TypeError, takewhile) + self.assertRaises(TypeError, takewhile, operator.pow) + self.assertRaises(TypeError, takewhile, operator.pow, [(4,5)], 'extra') + self.assertRaises(TypeError, next, takewhile(10, [(4,5)])) + self.assertRaises(ValueError, next, takewhile(errfunc, [(4,5)])) + t = takewhile(bool, [1, 1, 1, 0, 0, 0]) + self.assertEqual(list(t), [1, 1, 1]) + self.assertRaises(StopIteration, next, t) + +# # check copy, deepcopy, pickle +# self.assertEqual(list(copy.copy(takewhile(underten, data))), [1, 3, 5]) +# self.assertEqual(list(copy.deepcopy(takewhile(underten, data))), +# [1, 3, 5]) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, takewhile(underten, data)) + + def test_dropwhile(self): + data = [1, 3, 5, 20, 2, 4, 6, 8] + self.assertEqual(list(dropwhile(underten, data)), [20, 2, 4, 6, 8]) + self.assertEqual(list(dropwhile(underten, [])), []) + self.assertRaises(TypeError, dropwhile) + self.assertRaises(TypeError, dropwhile, operator.pow) + self.assertRaises(TypeError, dropwhile, operator.pow, [(4,5)], 'extra') + self.assertRaises(TypeError, next, dropwhile(10, [(4,5)])) + self.assertRaises(ValueError, next, dropwhile(errfunc, [(4,5)])) + +# # check copy, deepcopy, pickle +# self.assertEqual(list(copy.copy(dropwhile(underten, data))), [20, 2, 4, 6, 8]) +# self.assertEqual(list(copy.deepcopy(dropwhile(underten, data))), +# [20, 2, 4, 6, 8]) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, dropwhile(underten, data)) + +# def test_tee(self): +# n = 200 + +# a, b = tee([]) # test empty iterator +# self.assertEqual(list(a), []) +# self.assertEqual(list(b), []) + +# a, b = tee(irange(n)) # test 100% interleaved +# self.assertEqual(lzip(a,b), lzip(range(n), range(n))) + +# a, b = tee(irange(n)) # test 0% interleaved +# self.assertEqual(list(a), list(range(n))) +# self.assertEqual(list(b), list(range(n))) + +# a, b = tee(irange(n)) # test dealloc of leading iterator +# for i in range(100): +# self.assertEqual(next(a), i) +# del a +# self.assertEqual(list(b), list(range(n))) + +# a, b = tee(irange(n)) # test dealloc of trailing iterator +# for i in range(100): +# self.assertEqual(next(a), i) +# del b +# self.assertEqual(list(a), list(range(100, n))) + +# for j in range(5): # test randomly interleaved +# order = [0]*n + [1]*n +# random.shuffle(order) +# lists = ([], []) +# its = tee(irange(n)) +# for i in order: +# value = next(its[i]) +# lists[i].append(value) +# self.assertEqual(lists[0], list(range(n))) +# self.assertEqual(lists[1], list(range(n))) + +# # test argument format checking +# self.assertRaises(TypeError, tee) +# self.assertRaises(TypeError, tee, 3) +# self.assertRaises(TypeError, tee, [1,2], 'x') +# self.assertRaises(TypeError, tee, [1,2], 3, 'x') + +# # tee object should be instantiable +# a, b = tee('abc') +# c = type(a)('def') +# self.assertEqual(list(c), list('def')) + +# # test long-lagged and multi-way split +# a, b, c = tee(range(2000), 3) +# for i in range(100): +# self.assertEqual(next(a), i) +# self.assertEqual(list(b), list(range(2000))) +# self.assertEqual([next(c), next(c)], list(range(2))) +# self.assertEqual(list(a), list(range(100,2000))) +# self.assertEqual(list(c), list(range(2,2000))) + +# # test values of n +# self.assertRaises(TypeError, tee, 'abc', 'invalid') +# self.assertRaises(ValueError, tee, [], -1) +# for n in range(5): +# result = tee('abc', n) +# self.assertEqual(type(result), tuple) +# self.assertEqual(len(result), n) +# self.assertEqual([list(x) for x in result], [list('abc')]*n) + +# # tee pass-through to copyable iterator +# a, b = tee('abc') +# c, d = tee(a) +# self.assertTrue(a is c) + +# # test tee_new +# t1, t2 = tee('abc') +# tnew = type(t1) +# self.assertRaises(TypeError, tnew) +# self.assertRaises(TypeError, tnew, 10) +# t3 = tnew(t1) +# self.assertTrue(list(t1) == list(t2) == list(t3) == list('abc')) + +# # test that tee objects are weak referencable +# a, b = tee(range(10)) +# p = weakref.proxy(a) +# self.assertEqual(getattr(p, '__class__'), type(b)) +# del a +# self.assertRaises(ReferenceError, getattr, p, '__class__') + +# ans = list('abc') +# long_ans = list(range(10000)) + +# # check copy +# a, b = tee('abc') +# self.assertEqual(list(copy.copy(a)), ans) +# self.assertEqual(list(copy.copy(b)), ans) +# a, b = tee(list(range(10000))) +# self.assertEqual(list(copy.copy(a)), long_ans) +# self.assertEqual(list(copy.copy(b)), long_ans) + +# # check partially consumed copy +# a, b = tee('abc') +# take(2, a) +# take(1, b) +# self.assertEqual(list(copy.copy(a)), ans[2:]) +# self.assertEqual(list(copy.copy(b)), ans[1:]) +# self.assertEqual(list(a), ans[2:]) +# self.assertEqual(list(b), ans[1:]) +# a, b = tee(range(10000)) +# take(100, a) +# take(60, b) +# self.assertEqual(list(copy.copy(a)), long_ans[100:]) +# self.assertEqual(list(copy.copy(b)), long_ans[60:]) +# self.assertEqual(list(a), long_ans[100:]) +# self.assertEqual(list(b), long_ans[60:]) + +# # check deepcopy +# a, b = tee('abc') +# self.assertEqual(list(copy.deepcopy(a)), ans) +# self.assertEqual(list(copy.deepcopy(b)), ans) +# self.assertEqual(list(a), ans) +# self.assertEqual(list(b), ans) +# a, b = tee(range(10000)) +# self.assertEqual(list(copy.deepcopy(a)), long_ans) +# self.assertEqual(list(copy.deepcopy(b)), long_ans) +# self.assertEqual(list(a), long_ans) +# self.assertEqual(list(b), long_ans) + +# # check partially consumed deepcopy +# a, b = tee('abc') +# take(2, a) +# take(1, b) +# self.assertEqual(list(copy.deepcopy(a)), ans[2:]) +# self.assertEqual(list(copy.deepcopy(b)), ans[1:]) +# self.assertEqual(list(a), ans[2:]) +# self.assertEqual(list(b), ans[1:]) +# a, b = tee(range(10000)) +# take(100, a) +# take(60, b) +# self.assertEqual(list(copy.deepcopy(a)), long_ans[100:]) +# self.assertEqual(list(copy.deepcopy(b)), long_ans[60:]) +# self.assertEqual(list(a), long_ans[100:]) +# self.assertEqual(list(b), long_ans[60:]) + +# # check pickle +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# self.pickletest(proto, iter(tee('abc'))) +# a, b = tee('abc') +# self.pickletest(proto, a, compare=ans) +# self.pickletest(proto, b, compare=ans) + +# # Issue 13454: Crash when deleting backward iterator from tee() +# def test_tee_del_backward(self): +# forward, backward = tee(repeat(None, 20000000)) +# try: +# any(forward) # exhaust the iterator +# del backward +# except: +# del forward, backward +# raise + +# def test_tee_reenter(self): +# class I: +# first = True +# def __iter__(self): +# return self +# def __next__(self): +# first = self.first +# self.first = False +# if first: +# return next(b) + +# a, b = tee(I()) +# with self.assertRaisesRegex(RuntimeError, "tee"): +# next(a) + +# def test_tee_concurrent(self): +# start = threading.Event() +# finish = threading.Event() +# class I: +# def __iter__(self): +# return self +# def __next__(self): +# start.set() +# finish.wait() + +# a, b = tee(I()) +# thread = threading.Thread(target=next, args=[a]) +# thread.start() +# try: +# start.wait() +# with self.assertRaisesRegex(RuntimeError, "tee"): +# next(b) +# finally: +# finish.set() +# thread.join() + + def test_StopIteration(self): + self.assertRaises(StopIteration, next, zip()) + + for f in (chain, cycle, zip, groupby): + self.assertRaises(StopIteration, next, f([])) + self.assertRaises(StopIteration, next, f(StopNow())) + + self.assertRaises(StopIteration, next, islice([], None)) + self.assertRaises(StopIteration, next, islice(StopNow(), None)) + +# p, q = tee([]) +# self.assertRaises(StopIteration, next, p) +# self.assertRaises(StopIteration, next, q) +# p, q = tee(StopNow()) +# self.assertRaises(StopIteration, next, p) +# self.assertRaises(StopIteration, next, q) + + self.assertRaises(StopIteration, next, repeat(None, 0)) + + for f in (filter, filterfalse, map, takewhile, dropwhile, starmap): + self.assertRaises(StopIteration, next, f(lambda x:x, [])) + self.assertRaises(StopIteration, next, f(lambda x:x, StopNow())) + +class TestExamples(unittest.TestCase): + + def test_accumulate(self): + self.assertEqual(list(accumulate([1,2,3,4,5])), [1, 3, 6, 10, 15]) + +# def test_accumulate_reducible(self): +# # check copy, deepcopy, pickle +# data = [1, 2, 3, 4, 5] +# accumulated = [1, 3, 6, 10, 15] + +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# it = accumulate(data) +# self.assertEqual(list(pickle.loads(pickle.dumps(it, proto))), accumulated[:]) +# self.assertEqual(next(it), 1) +# self.assertEqual(list(pickle.loads(pickle.dumps(it, proto))), accumulated[1:]) +# it = accumulate(data) +# self.assertEqual(next(it), 1) +# self.assertEqual(list(copy.deepcopy(it)), accumulated[1:]) +# self.assertEqual(list(copy.copy(it)), accumulated[1:]) + + def test_accumulate_reducible_none(self): + # Issue #25718: total is None + it = accumulate([None, None, None], operator.is_) + self.assertEqual(next(it), None) +# for proto in range(pickle.HIGHEST_PROTOCOL + 1): +# it_copy = pickle.loads(pickle.dumps(it, proto)) +# self.assertEqual(list(it_copy), [True, False]) +# self.assertEqual(list(copy.deepcopy(it)), [True, False]) +# self.assertEqual(list(copy.copy(it)), [True, False]) + + def test_chain(self): + self.assertEqual(''.join(chain('ABC', 'DEF')), 'ABCDEF') + + def test_chain_from_iterable(self): + self.assertEqual(''.join(chain.from_iterable(['ABC', 'DEF'])), 'ABCDEF') + + def test_combinations(self): + self.assertEqual(list(combinations('ABCD', 2)), + [('A','B'), ('A','C'), ('A','D'), ('B','C'), ('B','D'), ('C','D')]) + self.assertEqual(list(combinations(range(4), 3)), + [(0,1,2), (0,1,3), (0,2,3), (1,2,3)]) + + def test_combinations_with_replacement(self): + self.assertEqual(list(combinations_with_replacement('ABC', 2)), + [('A','A'), ('A','B'), ('A','C'), ('B','B'), ('B','C'), ('C','C')]) + + def test_compress(self): + self.assertEqual(list(compress('ABCDEF', [1,0,1,0,1,1])), list('ACEF')) + + def test_count(self): + self.assertEqual(list(islice(count(10), 5)), [10, 11, 12, 13, 14]) + + def test_cycle(self): + self.assertEqual(list(islice(cycle('ABCD'), 12)), list('ABCDABCDABCD')) + + def test_dropwhile(self): + self.assertEqual(list(dropwhile(lambda x: x<5, [1,4,6,4,1])), [6,4,1]) + + def test_groupby(self): + self.assertEqual([k for k, g in groupby('AAAABBBCCDAABBB')], + list('ABCDAB')) + self.assertEqual([(list(g)) for k, g in groupby('AAAABBBCCD')], + [list('AAAA'), list('BBB'), list('CC'), list('D')]) + +# def test_filter(self): +# self.assertEqual(list(filter(lambda x: x%2, range(10))), [1,3,5,7,9]) + + def test_filterfalse(self): + self.assertEqual(list(filterfalse(lambda x: x%2, range(10))), [0,2,4,6,8]) + + def test_map(self): + self.assertEqual(list(map(pow, (2,3,10), (5,2,3))), [32, 9, 1000]) + + def test_islice(self): + self.assertEqual(list(islice('ABCDEFG', 2)), list('AB')) + self.assertEqual(list(islice('ABCDEFG', 2, 4)), list('CD')) + self.assertEqual(list(islice('ABCDEFG', 2, None)), list('CDEFG')) + self.assertEqual(list(islice('ABCDEFG', 0, None, 2)), list('ACEG')) + + def test_zip(self): + self.assertEqual(list(zip('ABCD', 'xy')), [('A', 'x'), ('B', 'y')]) + + def test_zip_longest(self): + self.assertEqual(list(zip_longest('ABCD', 'xy', fillvalue='-')), + [('A', 'x'), ('B', 'y'), ('C', '-'), ('D', '-')]) + + def test_permutations(self): + self.assertEqual(list(permutations('ABCD', 2)), + list(map(tuple, 'AB AC AD BA BC BD CA CB CD DA DB DC'.split()))) + self.assertEqual(list(permutations(range(3))), + [(0,1,2), (0,2,1), (1,0,2), (1,2,0), (2,0,1), (2,1,0)]) + + def test_product(self): + self.assertEqual(list(product('ABCD', 'xy')), + list(map(tuple, 'Ax Ay Bx By Cx Cy Dx Dy'.split()))) + self.assertEqual(list(product(range(2), repeat=3)), + [(0,0,0), (0,0,1), (0,1,0), (0,1,1), + (1,0,0), (1,0,1), (1,1,0), (1,1,1)]) + + def test_repeat(self): + self.assertEqual(list(repeat(10, 3)), [10, 10, 10]) + + def test_stapmap(self): + self.assertEqual(list(starmap(pow, [(2,5), (3,2), (10,3)])), + [32, 9, 1000]) + + def test_takewhile(self): + self.assertEqual(list(takewhile(lambda x: x<5, [1,4,6,4,1])), [1,4]) + + +# class TestPurePythonRoughEquivalents(unittest.TestCase): + +# @staticmethod +# def islice(iterable, *args): +# s = slice(*args) +# start, stop, step = s.start or 0, s.stop or sys.maxsize, s.step or 1 +# it = iter(range(start, stop, step)) +# try: +# nexti = next(it) +# except StopIteration: +# # Consume *iterable* up to the *start* position. +# for i, element in zip(range(start), iterable): +# pass +# return +# try: +# for i, element in enumerate(iterable): +# if i == nexti: +# yield element +# nexti = next(it) +# except StopIteration: +# # Consume to *stop*. +# for i, element in zip(range(i + 1, stop), iterable): +# pass + +# def test_islice_recipe(self): +# self.assertEqual(list(self.islice('ABCDEFG', 2)), list('AB')) +# self.assertEqual(list(self.islice('ABCDEFG', 2, 4)), list('CD')) +# self.assertEqual(list(self.islice('ABCDEFG', 2, None)), list('CDEFG')) +# self.assertEqual(list(self.islice('ABCDEFG', 0, None, 2)), list('ACEG')) +# # Test items consumed. +# it = iter(range(10)) +# self.assertEqual(list(self.islice(it, 3)), list(range(3))) +# self.assertEqual(list(it), list(range(3, 10))) +# it = iter(range(10)) +# self.assertEqual(list(self.islice(it, 3, 3)), []) +# self.assertEqual(list(it), list(range(3, 10))) +# # Test that slice finishes in predictable state. +# c = count() +# self.assertEqual(list(self.islice(c, 1, 3, 50)), [1]) +# self.assertEqual(next(c), 3) + + +class TestGC(unittest.TestCase): + + def makecycle(self, iterator, container): + container.append(iterator) + next(iterator) + del container, iterator + + def test_accumulate(self): + a = [] + self.makecycle(accumulate([1,2,a,3]), a) + + def test_chain(self): + a = [] + self.makecycle(chain(a), a) + + def test_chain_from_iterable(self): + a = [] + self.makecycle(chain.from_iterable([a]), a) + + def test_combinations(self): + a = [] + self.makecycle(combinations([1,2,a,3], 3), a) + + def test_combinations_with_replacement(self): + a = [] + self.makecycle(combinations_with_replacement([1,2,a,3], 3), a) + + def test_compress(self): + a = [] + self.makecycle(compress('ABCDEF', [1,0,1,0,1,0]), a) + + def test_count(self): + a = [] + Int = type('Int', (int,), dict(x=a)) + self.makecycle(count(Int(0), Int(1)), a) + + def test_cycle(self): + a = [] + self.makecycle(cycle([a]*2), a) + + def test_dropwhile(self): + a = [] + self.makecycle(dropwhile(bool, [0, a, a]), a) + + def test_groupby(self): + a = [] + self.makecycle(groupby([a]*2, lambda x:x), a) + +# def test_issue2246(self): +# # Issue 2246 -- the _grouper iterator was not included in GC +# n = 10 +# keyfunc = lambda x: x +# for i, j in groupby(range(n), key=keyfunc): +# keyfunc.__dict__.setdefault('x',[]).append(j) + + def test_filter(self): + a = [] + self.makecycle(filter(lambda x:True, [a]*2), a) + + def test_filterfalse(self): + a = [] + self.makecycle(filterfalse(lambda x:False, a), a) + + def test_zip(self): + a = [] + self.makecycle(zip([a]*2, [a]*3), a) + + def test_zip_longest(self): + a = [] + self.makecycle(zip_longest([a]*2, [a]*3), a) + b = [a, None] + self.makecycle(zip_longest([a]*2, [a]*3, fillvalue=b), a) + + def test_map(self): + a = [] + self.makecycle(map(lambda x:x, [a]*2), a) + + def test_islice(self): + a = [] + self.makecycle(islice([a]*2, None), a) + + def test_permutations(self): + a = [] + self.makecycle(permutations([1,2,a,3], 3), a) + + def test_product(self): + a = [] + self.makecycle(product([1,2,a,3], repeat=3), a) + + def test_repeat(self): + a = [] + self.makecycle(repeat(a), a) + + def test_starmap(self): + a = [] + self.makecycle(starmap(lambda *t: t, [(a,a)]*2), a) + + def test_takewhile(self): + a = [] + self.makecycle(takewhile(bool, [1, 0, a, a]), a) + +def R(seqn): + 'Regular generator' + for i in seqn: + yield i + +class G: + 'Sequence using __getitem__' + def __init__(self, seqn): + self.seqn = seqn + def __getitem__(self, i): + return self.seqn[i] + +class I: + 'Sequence using iterator protocol' + def __init__(self, seqn): + self.seqn = seqn + self.i = 0 + def __iter__(self): + return self + def __next__(self): + if self.i >= len(self.seqn): raise StopIteration + v = self.seqn[self.i] + self.i += 1 + return v + +class Ig: + 'Sequence using iterator protocol defined with a generator' + def __init__(self, seqn): + self.seqn = seqn + self.i = 0 + def __iter__(self): + for val in self.seqn: + yield val + +class X: + 'Missing __getitem__ and __iter__' + def __init__(self, seqn): + self.seqn = seqn + self.i = 0 + def __next__(self): + if self.i >= len(self.seqn): raise StopIteration + v = self.seqn[self.i] + self.i += 1 + return v + +class N: + 'Iterator missing __next__()' + def __init__(self, seqn): + self.seqn = seqn + self.i = 0 + def __iter__(self): + return self + +class E: + 'Test propagation of exceptions' + def __init__(self, seqn): + self.seqn = seqn + self.i = 0 + def __iter__(self): + return self + def __next__(self): + 3 // 0 + +class S: + 'Test immediate stop' + def __init__(self, seqn): + pass + def __iter__(self): + return self + def __next__(self): + raise StopIteration + +def L(seqn): + 'Test multiple tiers of iterators' + return chain(map(lambda x:x, R(Ig(G(seqn))))) + + +class TestVariousIteratorArgs(unittest.TestCase): + pass + + def test_accumulate(self): + s = [1,2,3,4,5] + r = [1,3,6,10,15] + n = len(s) + for g in (G, I, Ig, L, R): + self.assertEqual(list(accumulate(g(s))), r) + self.assertEqual(list(accumulate(S(s))), []) + self.assertRaises(TypeError, accumulate, X(s)) + self.assertRaises(TypeError, accumulate, N(s)) + self.assertRaises(ZeroDivisionError, list, accumulate(E(s))) + + def test_chain(self): + for s in ("123", "", range(1000), ('do', 1.2), range(2000,2200,5)): + for g in (G, I, Ig, S, L, R): + self.assertEqual(list(chain(g(s))), list(g(s))) + self.assertEqual(list(chain(g(s), g(s))), list(g(s))+list(g(s))) + self.assertRaises(TypeError, list, chain(X(s))) + self.assertRaises(TypeError, list, chain(N(s))) + self.assertRaises(ZeroDivisionError, list, chain(E(s))) + + def test_compress(self): + for s in ("123", "", range(1000), ('do', 1.2), range(2000,2200,5)): + n = len(s) + for g in (G, I, Ig, S, L, R): + self.assertEqual(list(compress(g(s), repeat(1))), list(g(s))) + self.assertRaises(TypeError, compress, X(s), repeat(1)) + self.assertRaises(TypeError, compress, N(s), repeat(1)) + self.assertRaises(ZeroDivisionError, list, compress(E(s), repeat(1))) + + def test_product(self): + for s in ("123", "", range(1000), ('do', 1.2), range(2000,2200,5)): + self.assertRaises(TypeError, product, X(s)) + self.assertRaises(TypeError, product, N(s)) + self.assertRaises(ZeroDivisionError, product, E(s)) + + def test_cycle(self): + for s in ("123", "", range(1000), ('do', 1.2), range(2000,2200,5)): + for g in (G, I, Ig, S, L, R): + tgtlen = len(s) * 3 + expected = list(g(s))*3 + actual = list(islice(cycle(g(s)), tgtlen)) + self.assertEqual(actual, expected) + self.assertRaises(TypeError, cycle, X(s)) + self.assertRaises(TypeError, cycle, N(s)) + self.assertRaises(ZeroDivisionError, list, cycle(E(s))) + + def test_groupby(self): + for s in (range(10), range(0), range(1000), (7,11), range(2000,2200,5)): + for g in (G, I, Ig, S, L, R): + self.assertEqual([k for k, sb in groupby(g(s))], list(g(s))) + self.assertRaises(TypeError, groupby, X(s)) + self.assertRaises(TypeError, groupby, N(s)) + self.assertRaises(ZeroDivisionError, list, groupby(E(s))) + + def test_filter(self): + for s in (range(10), range(0), range(1000), (7,11), range(2000,2200,5)): + for g in (G, I, Ig, S, L, R): + self.assertEqual(list(filter(isEven, g(s))), + [x for x in g(s) if isEven(x)]) + self.assertRaises(TypeError, filter, isEven, X(s)) + self.assertRaises(TypeError, filter, isEven, N(s)) + self.assertRaises(ZeroDivisionError, list, filter(isEven, E(s))) + + def test_filterfalse(self): + for s in (range(10), range(0), range(1000), (7,11), range(2000,2200,5)): + for g in (G, I, Ig, S, L, R): + self.assertEqual(list(filterfalse(isEven, g(s))), + [x for x in g(s) if isOdd(x)]) + self.assertRaises(TypeError, filterfalse, isEven, X(s)) + self.assertRaises(TypeError, filterfalse, isEven, N(s)) + self.assertRaises(ZeroDivisionError, list, filterfalse(isEven, E(s))) + + def test_zip(self): + for s in ("123", "", range(1000), ('do', 1.2), range(2000,2200,5)): + for g in (G, I, Ig, S, L, R): + self.assertEqual(list(zip(g(s))), lzip(g(s))) + self.assertEqual(list(zip(g(s), g(s))), lzip(g(s), g(s))) + self.assertRaises(TypeError, zip, X(s)) + self.assertRaises(TypeError, zip, N(s)) + self.assertRaises(ZeroDivisionError, list, zip(E(s))) + + def test_ziplongest(self): + for s in ("123", "", range(1000), ('do', 1.2), range(2000,2200,5)): + for g in (G, I, Ig, S, L, R): + self.assertEqual(list(zip_longest(g(s))), list(zip(g(s)))) + self.assertEqual(list(zip_longest(g(s), g(s))), list(zip(g(s), g(s)))) + self.assertRaises(TypeError, zip_longest, X(s)) + self.assertRaises(TypeError, zip_longest, N(s)) + self.assertRaises(ZeroDivisionError, list, zip_longest(E(s))) + + def test_map(self): + for s in (range(10), range(0), range(100), (7,11), range(20,50,5)): + for g in (G, I, Ig, S, L, R): + self.assertEqual(list(map(onearg, g(s))), + [onearg(x) for x in g(s)]) + self.assertEqual(list(map(operator.pow, g(s), g(s))), + [x**x for x in g(s)]) + self.assertRaises(TypeError, map, onearg, X(s)) + self.assertRaises(TypeError, map, onearg, N(s)) + self.assertRaises(ZeroDivisionError, list, map(onearg, E(s))) + + def test_islice(self): + for s in ("12345", "", range(1000), ('do', 1.2), range(2000,2200,5)): + for g in (G, I, Ig, S, L, R): + self.assertEqual(list(islice(g(s),1,None,2)), list(g(s))[1::2]) + self.assertRaises(TypeError, islice, X(s), 10) + self.assertRaises(TypeError, islice, N(s), 10) + self.assertRaises(ZeroDivisionError, list, islice(E(s), 10)) + + def test_starmap(self): + for s in (range(10), range(0), range(100), (7,11), range(20,50,5)): + for g in (G, I, Ig, S, L, R): + ss = lzip(s, s) + self.assertEqual(list(starmap(operator.pow, g(ss))), + [x**x for x in g(s)]) + self.assertRaises(TypeError, starmap, operator.pow, X(ss)) + self.assertRaises(TypeError, starmap, operator.pow, N(ss)) + self.assertRaises(ZeroDivisionError, list, starmap(operator.pow, E(ss))) + + def test_takewhile(self): + for s in (range(10), range(0), range(1000), (7,11), range(2000,2200,5)): + for g in (G, I, Ig, S, L, R): + tgt = [] + for elem in g(s): + if not isEven(elem): break + tgt.append(elem) + self.assertEqual(list(takewhile(isEven, g(s))), tgt) + self.assertRaises(TypeError, takewhile, isEven, X(s)) + self.assertRaises(TypeError, takewhile, isEven, N(s)) + self.assertRaises(ZeroDivisionError, list, takewhile(isEven, E(s))) + + def test_dropwhile(self): + for s in (range(10), range(0), range(1000), (7,11), range(2000,2200,5)): + for g in (G, I, Ig, S, L, R): + tgt = [] + for elem in g(s): + if not tgt and isOdd(elem): continue + tgt.append(elem) + self.assertEqual(list(dropwhile(isOdd, g(s))), tgt) + self.assertRaises(TypeError, dropwhile, isOdd, X(s)) + self.assertRaises(TypeError, dropwhile, isOdd, N(s)) + self.assertRaises(ZeroDivisionError, list, dropwhile(isOdd, E(s))) + +# def test_tee(self): +# for s in ("123", "", range(1000), ('do', 1.2), range(2000,2200,5)): +# for g in (G, I, Ig, S, L, R): +# it1, it2 = tee(g(s)) +# self.assertEqual(list(it1), list(g(s))) +# self.assertEqual(list(it2), list(g(s))) +# self.assertRaises(TypeError, tee, X(s)) +# self.assertRaises(TypeError, tee, N(s)) +# self.assertRaises(ZeroDivisionError, list, tee(E(s))[0]) + +# class LengthTransparency(unittest.TestCase): +# pass + +# def test_repeat(self): +# self.assertEqual(operator.length_hint(repeat(None, 50)), 50) +# self.assertEqual(operator.length_hint(repeat(None, 0)), 0) +# self.assertEqual(operator.length_hint(repeat(None), 12), 12) + +# def test_repeat_with_negative_times(self): +# self.assertEqual(operator.length_hint(repeat(None, -1)), 0) +# self.assertEqual(operator.length_hint(repeat(None, -2)), 0) +# self.assertEqual(operator.length_hint(repeat(None, times=-1)), 0) +# self.assertEqual(operator.length_hint(repeat(None, times=-2)), 0) + +class RegressionTests(unittest.TestCase): + +# def test_sf_793826(self): +# # Fix Armin Rigo's successful efforts to wreak havoc + +# def mutatingtuple(tuple1, f, tuple2): +# # this builds a tuple t which is a copy of tuple1, +# # then calls f(t), then mutates t to be equal to tuple2 +# # (needs len(tuple1) == len(tuple2)). +# def g(value, first=[1]): +# if first: +# del first[:] +# f(next(z)) +# return value +# items = list(tuple2) +# items[1:1] = list(tuple1) +# gen = map(g, items) +# z = zip(*[gen]*len(tuple1)) +# next(z) + +# def f(t): +# global T +# T = t +# first[:] = list(T) + +# first = [] +# mutatingtuple((1,2,3), f, (4,5,6)) +# second = list(T) +# self.assertEqual(first, second) + + + def test_sf_950057(self): + # Make sure that chain() and cycle() catch exceptions immediately + # rather than when shifting between input sources + + def gen1(): + hist.append(0) + yield 1 + hist.append(1) + raise AssertionError + hist.append(2) + + def gen2(x): + hist.append(3) + yield 2 + hist.append(4) + + hist = [] + self.assertRaises(AssertionError, list, chain(gen1(), gen2(False))) + self.assertEqual(hist, [0,1]) + + hist = [] + self.assertRaises(AssertionError, list, chain(gen1(), gen2(True))) + self.assertEqual(hist, [0,1]) + + hist = [] + self.assertRaises(AssertionError, list, cycle(gen1())) + self.assertEqual(hist, [0,1]) + + def test_long_chain_of_empty_iterables(self): + # Make sure itertools.chain doesn't run into recursion limits when + # dealing with long chains of empty iterables. Even with a high + # number this would probably only fail in Py_DEBUG mode. + it = chain.from_iterable(() for unused in range(100000)) + self.assertRaises(StopIteration, next, it) + +# def test_issue30347_1(self): +# def f(n): +# if n == 5: +# list(b) +# return n != 6 +# for (k, b) in groupby(range(10), f): +# list(b) # shouldn't crash + +# def test_issue30347_2(self): +# class K: +# def __init__(self, v): +# pass +# def __eq__(self, other): +# nonlocal i +# i += 1 +# if i == 1: +# next(g, None) +# return True +# i = 0 +# g = next(groupby(range(10), K))[1] +# for j in range(2): +# next(g, None) # shouldn't crash + + +# class SubclassWithKwargsTest(unittest.TestCase): +# def test_keywords_in_subclass(self): +# # count is not subclassable... +# for cls in (repeat, zip, filter, filterfalse, chain, map, +# starmap, islice, takewhile, dropwhile, cycle, compress): +# class Subclass(cls): +# def __init__(self, newarg=None, *args): +# cls.__init__(self, *args) +# try: +# Subclass(newarg=1) +# except TypeError as err: +# # we expect type errors because of wrong argument count +# self.assertNotIn("keyword arguments", err.args[0]) + +# @support.cpython_only +# class SizeofTest(unittest.TestCase): +# def setUp(self): +# self.ssize_t = struct.calcsize('n') + +# check_sizeof = support.check_sizeof + +# def test_product_sizeof(self): +# basesize = support.calcobjsize('3Pi') +# check = self.check_sizeof +# check(product('ab', '12'), basesize + 2 * self.ssize_t) +# check(product(*(('abc',) * 10)), basesize + 10 * self.ssize_t) + +# def test_combinations_sizeof(self): +# basesize = support.calcobjsize('3Pni') +# check = self.check_sizeof +# check(combinations('abcd', 3), basesize + 3 * self.ssize_t) +# check(combinations(range(10), 4), basesize + 4 * self.ssize_t) + +# def test_combinations_with_replacement_sizeof(self): +# cwr = combinations_with_replacement +# basesize = support.calcobjsize('3Pni') +# check = self.check_sizeof +# check(cwr('abcd', 3), basesize + 3 * self.ssize_t) +# check(cwr(range(10), 4), basesize + 4 * self.ssize_t) + +# def test_permutations_sizeof(self): +# basesize = support.calcobjsize('4Pni') +# check = self.check_sizeof +# check(permutations('abcd'), +# basesize + 4 * self.ssize_t + 4 * self.ssize_t) +# check(permutations('abcd', 3), +# basesize + 4 * self.ssize_t + 3 * self.ssize_t) +# check(permutations('abcde', 3), +# basesize + 5 * self.ssize_t + 3 * self.ssize_t) +# check(permutations(range(10), 4), +# basesize + 10 * self.ssize_t + 4 * self.ssize_t) + + +# libreftest = """ Doctest for examples in the library reference: libitertools.tex +# >>> amounts = [120.15, 764.05, 823.14] +# >>> for checknum, amount in zip(count(1200), amounts): +# ... print('Check %d is for $%.2f' % (checknum, amount)) +# ... +# Check 1200 is for $120.15 +# Check 1201 is for $764.05 +# Check 1202 is for $823.14 +# >>> import operator +# >>> for cube in map(operator.pow, range(1,4), repeat(3)): +# ... print(cube) +# ... +# 1 +# 8 +# 27 +# >>> reportlines = ['EuroPython', 'Roster', '', 'alex', '', 'laura', '', 'martin', '', 'walter', '', 'samuele'] +# >>> for name in islice(reportlines, 3, None, 2): +# ... print(name.title()) +# ... +# Alex +# Laura +# Martin +# Walter +# Samuele +# >>> from operator import itemgetter +# >>> d = dict(a=1, b=2, c=1, d=2, e=1, f=2, g=3) +# >>> di = sorted(sorted(d.items()), key=itemgetter(1)) +# >>> for k, g in groupby(di, itemgetter(1)): +# ... print(k, list(map(itemgetter(0), g))) +# ... +# 1 ['a', 'c', 'e'] +# 2 ['b', 'd', 'f'] +# 3 ['g'] +# # Find runs of consecutive numbers using groupby. The key to the solution +# # is differencing with a range so that consecutive numbers all appear in +# # same group. +# >>> data = [ 1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28] +# >>> for k, g in groupby(enumerate(data), lambda t:t[0]-t[1]): +# ... print(list(map(operator.itemgetter(1), g))) +# ... +# [1] +# [4, 5, 6] +# [10] +# [15, 16, 17, 18] +# [22] +# [25, 26, 27, 28] +# >>> def take(n, iterable): +# ... "Return first n items of the iterable as a list" +# ... return list(islice(iterable, n)) +# >>> def prepend(value, iterator): +# ... "Prepend a single value in front of an iterator" +# ... # prepend(1, [2, 3, 4]) -> 1 2 3 4 +# ... return chain([value], iterator) +# >>> def enumerate(iterable, start=0): +# ... return zip(count(start), iterable) +# >>> def tabulate(function, start=0): +# ... "Return function(0), function(1), ..." +# ... return map(function, count(start)) +# >>> import collections +# >>> def consume(iterator, n=None): +# ... "Advance the iterator n-steps ahead. If n is None, consume entirely." +# ... # Use functions that consume iterators at C speed. +# ... if n is None: +# ... # feed the entire iterator into a zero-length deque +# ... collections.deque(iterator, maxlen=0) +# ... else: +# ... # advance to the empty slice starting at position n +# ... next(islice(iterator, n, n), None) +# >>> def nth(iterable, n, default=None): +# ... "Returns the nth item or a default value" +# ... return next(islice(iterable, n, None), default) +# >>> def all_equal(iterable): +# ... "Returns True if all the elements are equal to each other" +# ... g = groupby(iterable) +# ... return next(g, True) and not next(g, False) +# >>> def quantify(iterable, pred=bool): +# ... "Count how many times the predicate is true" +# ... return sum(map(pred, iterable)) +# >>> def padnone(iterable): +# ... "Returns the sequence elements and then returns None indefinitely" +# ... return chain(iterable, repeat(None)) +# >>> def ncycles(iterable, n): +# ... "Returns the sequence elements n times" +# ... return chain(*repeat(iterable, n)) +# >>> def dotproduct(vec1, vec2): +# ... return sum(map(operator.mul, vec1, vec2)) +# >>> def flatten(listOfLists): +# ... return list(chain.from_iterable(listOfLists)) +# >>> def repeatfunc(func, times=None, *args): +# ... "Repeat calls to func with specified arguments." +# ... " Example: repeatfunc(random.random)" +# ... if times is None: +# ... return starmap(func, repeat(args)) +# ... else: +# ... return starmap(func, repeat(args, times)) +# >>> def pairwise(iterable): +# ... "s -> (s0,s1), (s1,s2), (s2, s3), ..." +# ... a, b = tee(iterable) +# ... try: +# ... next(b) +# ... except StopIteration: +# ... pass +# ... return zip(a, b) +# >>> def grouper(n, iterable, fillvalue=None): +# ... "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" +# ... args = [iter(iterable)] * n +# ... return zip_longest(*args, fillvalue=fillvalue) +# >>> def roundrobin(*iterables): +# ... "roundrobin('ABC', 'D', 'EF') --> A D E B F C" +# ... # Recipe credited to George Sakkis +# ... pending = len(iterables) +# ... nexts = cycle(iter(it).__next__ for it in iterables) +# ... while pending: +# ... try: +# ... for next in nexts: +# ... yield next() +# ... except StopIteration: +# ... pending -= 1 +# ... nexts = cycle(islice(nexts, pending)) +# >>> def powerset(iterable): +# ... "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)" +# ... s = list(iterable) +# ... return chain.from_iterable(combinations(s, r) for r in range(len(s)+1)) +# >>> def unique_everseen(iterable, key=None): +# ... "List unique elements, preserving order. Remember all elements ever seen." +# ... # unique_everseen('AAAABBBCCDAABBB') --> A B C D +# ... # unique_everseen('ABBCcAD', str.lower) --> A B C D +# ... seen = set() +# ... seen_add = seen.add +# ... if key is None: +# ... for element in iterable: +# ... if element not in seen: +# ... seen_add(element) +# ... yield element +# ... else: +# ... for element in iterable: +# ... k = key(element) +# ... if k not in seen: +# ... seen_add(k) +# ... yield element +# >>> def unique_justseen(iterable, key=None): +# ... "List unique elements, preserving order. Remember only the element just seen." +# ... # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B +# ... # unique_justseen('ABBCcAD', str.lower) --> A B C A D +# ... return map(next, map(itemgetter(1), groupby(iterable, key))) +# >>> def first_true(iterable, default=False, pred=None): +# ... '''Returns the first true value in the iterable. +# ... +# ... If no true value is found, returns *default* +# ... +# ... If *pred* is not None, returns the first item +# ... for which pred(item) is true. +# ... +# ... ''' +# ... # first_true([a,b,c], x) --> a or b or c or x +# ... # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x +# ... return next(filter(pred, iterable), default) +# >>> def nth_combination(iterable, r, index): +# ... 'Equivalent to list(combinations(iterable, r))[index]' +# ... pool = tuple(iterable) +# ... n = len(pool) +# ... if r < 0 or r > n: +# ... raise ValueError +# ... c = 1 +# ... k = min(r, n-r) +# ... for i in range(1, k+1): +# ... c = c * (n - k + i) // i +# ... if index < 0: +# ... index += c +# ... if index < 0 or index >= c: +# ... raise IndexError +# ... result = [] +# ... while r: +# ... c, n, r = c*r//n, n-1, r-1 +# ... while index >= c: +# ... index -= c +# ... c, n = c*(n-r)//n, n-1 +# ... result.append(pool[-1-n]) +# ... return tuple(result) +# This is not part of the examples but it tests to make sure the definitions +# perform as purported. +# >>> take(10, count()) +# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +# >>> list(prepend(1, [2, 3, 4])) +# [1, 2, 3, 4] +# >>> list(enumerate('abc')) +# [(0, 'a'), (1, 'b'), (2, 'c')] +# >>> list(islice(tabulate(lambda x: 2*x), 4)) +# [0, 2, 4, 6] +# >>> it = iter(range(10)) +# >>> consume(it, 3) +# >>> next(it) +# 3 +# >>> consume(it) +# >>> next(it, 'Done') +# 'Done' +# >>> nth('abcde', 3) +# 'd' +# >>> nth('abcde', 9) is None +# True +# >>> [all_equal(s) for s in ('', 'A', 'AAAA', 'AAAB', 'AAABA')] +# [True, True, True, False, False] +# >>> quantify(range(99), lambda x: x%2==0) +# 50 +# >>> a = [[1, 2, 3], [4, 5, 6]] +# >>> flatten(a) +# [1, 2, 3, 4, 5, 6] +# >>> list(repeatfunc(pow, 5, 2, 3)) +# [8, 8, 8, 8, 8] +# >>> import random +# >>> take(5, map(int, repeatfunc(random.random))) +# [0, 0, 0, 0, 0] +# >>> list(pairwise('abcd')) +# [('a', 'b'), ('b', 'c'), ('c', 'd')] +# >>> list(pairwise([])) +# [] +# >>> list(pairwise('a')) +# [] +# >>> list(islice(padnone('abc'), 0, 6)) +# ['a', 'b', 'c', None, None, None] +# >>> list(ncycles('abc', 3)) +# ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c'] +# >>> dotproduct([1,2,3], [4,5,6]) +# 32 +# >>> list(grouper(3, 'abcdefg', 'x')) +# [('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'x', 'x')] +# >>> list(roundrobin('abc', 'd', 'ef')) +# ['a', 'd', 'e', 'b', 'f', 'c'] +# >>> list(powerset([1,2,3])) +# [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] +# >>> all(len(list(powerset(range(n)))) == 2**n for n in range(18)) +# True +# >>> list(powerset('abcde')) == sorted(sorted(set(powerset('abcde'))), key=len) +# True +# >>> list(unique_everseen('AAAABBBCCDAABBB')) +# ['A', 'B', 'C', 'D'] +# >>> list(unique_everseen('ABBCcAD', str.lower)) +# ['A', 'B', 'C', 'D'] +# >>> list(unique_justseen('AAAABBBCCDAABBB')) +# ['A', 'B', 'C', 'D', 'A', 'B'] +# >>> list(unique_justseen('ABBCcAD', str.lower)) +# ['A', 'B', 'C', 'A', 'D'] +# >>> first_true('ABC0DEF1', '9', str.isdigit) +# '0' +# >>> population = 'ABCDEFGH' +# >>> for r in range(len(population) + 1): +# ... seq = list(combinations(population, r)) +# ... for i in range(len(seq)): +# ... assert nth_combination(population, r, i) == seq[i] +# ... for i in range(-len(seq), 0): +# ... assert nth_combination(population, r, i) == seq[i] +# """ + +# __test__ = {'libreftest' : libreftest} + +# def test_main(verbose=None): +# # test_classes = (TestBasicOps, TestVariousIteratorArgs, TestGC, +# # RegressionTests, LengthTransparency, +# # SubclassWithKwargsTest, TestExamples, +# # TestPurePythonRoughEquivalents, +# # SizeofTest) + +# # verify reference counting +# if verbose and hasattr(sys, "gettotalrefcount"): +# import gc +# counts = [None] * 5 +# for i in range(len(counts)): +# support.run_unittest(*test_classes) +# gc.collect() +# counts[i] = sys.gettotalrefcount() +# print(counts) + +# # doctest the examples in the library reference +# support.run_doctest(sys.modules[__name__], verbose) + +if __name__ == "__main__": + # test_main(verbose=True) + unittest.main() \ No newline at end of file diff --git a/test/unit3/test_list.py b/test/unit3/test_list.py index 8dff10b4de..bcbda7ee58 100644 --- a/test/unit3/test_list.py +++ b/test/unit3/test_list.py @@ -17,6 +17,9 @@ def counter(low, high): l = list(counter(1,12)) t = 4 in l self.assertTrue(t) + l1 = [1,2,3] + self.assertEqual(l1, list([1,2,3])) + self.assertEqual(list(tuple([1,2,3])), l1) def test_getitem(self): class Counter: @@ -56,8 +59,23 @@ def __next__(self): # Python 3: def __next__(self) def test_str(self): l = list("this is a sequence") self.assertTrue("q" in l) - - + x = ["hello"] + y = list(x) + x[0] = "hi" + self.assertEqual(y[0], "hello") + + def test_str_func(self): + self.assertEqual(str([1,2,3]), "[1, 2, 3]") + + def test_repr(self): + a = repr([1,2,3]) + self.assertEqual(a, "[1, 2, 3]") + + def test_len(self): + self.assertEqual(len([]), 0) + self.assertEqual(len([1,2,3]), 3) + self.assertEqual(len([0]*10), 10) + def test_reversed(self): a = list(range(20)) r = sorted(a,reverse=True) @@ -81,6 +99,9 @@ def test_delitem(self): self.assertEqual(a, [1]) del a[-1] self.assertEqual(a, []) + b = [1,2,3,4] + del b[:] + self.assertEqual(b,[]) # todo: why __delitem__ not found? # a = self.type2test([0, 1]) @@ -105,6 +126,41 @@ def test_set_subscript(self): 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])) + def test_assignment(self): + x = [2,4,6] + self.assertEqual(x[1], 4) + x[0] = 5 + self.assertEqual(x, [5,4,6]) + x[0] = [1,2] + self.assertEqual(x,[[1,2],4,6]) + #assign multiple indices + a = [1,2,3,4,5,6] + b = [9,9,9] + a[1:5] = b + self.assertEqual(a, [1, 9, 9, 9, 6]) + x = [1,2,3,4,5,6] + y = [9,9,9] + x[1:2] = y + self.assertEqual(x, [1, 9, 9, 9, 3, 4, 5, 6]) + mylist = ['a', 'b', 'c', 'd'] + d = {'1':1,'2':2} + mylist[0:2] = d + self.assertEqual(mylist, ['1', '2', 'c', 'd']) + mylist[1:3] = 'temp' + self.assertEqual(mylist, ['1', 't', 'e', 'm', 'p', 'd']) + mylist[:] = ['g','o','o','d'] + self.assertEqual(mylist, ['g', 'o', 'o', 'd']) + e = [1,2,3,4,5,6] + f = [9,10,11] + e[::2] = f + self.assertEqual(e, [9, 2, 10, 4, 11, 6]) + x = [10] * 5 + x[:3] += [100,100] + self.assertEqual(x, [10, 10, 10, 100, 100, 10, 10]) + a = [1, 2, 3] + a[1] += 4 + self.assertEqual(a, [1, 6, 3]) + def test_append(self): self.type2test = list a = self.type2test([]) @@ -133,7 +189,19 @@ def test_extend(self): a.extend("eggs") self.assertEqual(a, list("spameggs")) + l1 = [42] + l2 = l1 + l1 += [99] + self.assertEqual(l1, l2) + self.assertEqual(l1, [42, 99]) + l1 += l1 + self.assertEqual(l1, [42, 99, 42, 99]) + self.assertRaises(TypeError, a.extend, None) + m = [[1,2,3],2,3] + m.extend(m) + self.assertEqual(m, [[1, 2, 3], 2, 3, [1, 2, 3], 2, 3]) + self.assertRaises(TypeError, lambda x: x + 1, [1]) def test_insert(self): a = self.type2test([0, 1, 2]) @@ -148,6 +216,11 @@ def test_insert(self): b.insert(200, "right") self.assertEqual(b, self.type2test(["left",-2,-1,0,0,"foo",1,2,"right"])) + things = ['hi', 'a', 'b', 'c'] + things.insert(len(things), 'bye') + self.assertEqual(things, ['hi', 'a', 'b', 'c', 'bye']) + things.insert(len(things)+3, 'surpise') + self.assertEqual(things, ['hi', 'a', 'b', 'c', 'bye', 'surpise']) self.assertRaises(TypeError, a.insert) def test_pop(self): @@ -162,6 +235,9 @@ def test_pop(self): self.assertRaises(IndexError, a.pop) self.assertRaises(TypeError, a.pop, 42, 42) a = self.type2test([0, 10, 20, 30, 40]) + x = [0, 1, 2] + x.insert(2, x.pop(0)) + self.assertEqual(x, [1, 2, 0]) def test_remove(self): a = self.type2test([0, 0, 1]) @@ -195,10 +271,58 @@ def test_count(self): # # self.assertRaises(BadExc, a.count, BadCmp()) + def test_shallow_copy(self): + a = [0,[1]] + b = a.copy() + self.assertEqual(a,b) + a[1][0] = 2 + self.assertEqual(a,b) + a[0] = 5 + self.assertNotEqual(a,b) + l = [1,[2],3] + m = list(l) + self.assertEqual(l,m) + l[1][0] = 3 + self.assertEqual(l,m) + m[0] = 10 + self.assertNotEqual(l,m) + + def test_clear(self): + a = [1,2,3] + b = a + a.clear() + self.assertEqual(b,a) + self.assertEqual(a, []) + + def test_copy(self): + u = self.type2test([1, 2, 3]) + v = u.copy() + self.assertEqual(v, [1, 2, 3]) + + u = self.type2test([]) + v = u.copy() + self.assertEqual(v, []) + + # test that it's indeed a copy and not a reference + u = self.type2test(['a', 'b']) + v = u.copy() + v.append('i') + self.assertEqual(u, ['a', 'b']) + self.assertEqual(v, u + ['i']) + + # test that it's a shallow, not a deep copy + u = self.type2test([1, 2, [3, 4], 5]) + v = u.copy() + self.assertEqual(u, v) + self.assertIs(v[3], u[3]) + + self.assertRaises(TypeError, u.copy, None) + def test_index(self): u = self.type2test([0, 1]) self.assertEqual(u.index(0), 0) self.assertEqual(u.index(1), 1) + self.assertRaises(ValueError, u.index, 2) u = self.type2test([-2, -1, 0, 0, 1, 2]) @@ -209,7 +333,27 @@ def test_index(self): self.assertEqual(u.index(0, 3), 3) self.assertEqual(u.index(0, 3, 4), 3) self.assertRaises(ValueError, u.index, 2, 0, -10) - + self.assertRaises(TypeError, u.index) + myList = [1, 2, 3, "foo", 4, 5, True, False] + self.assertEqual(myList.index("foo"), 3) + self.assertEqual(myList.index(True), 0) + l = ['h','e','l','l','o'] + + self.assertRaises(ValueError, l.index, "l", 4) + self.assertRaises(ValueError, l.index, "l", -1) + self.assertRaises(ValueError, l.index, "l", 2, 2) + self.assertRaises(ValueError, l.index, "l", 3, 2) + self.assertRaises(ValueError, l.index, "l", 3, -2) + self.assertRaises(ValueError, l.index, "l", 3, 0) + self.assertRaises(TypeError, l.index, "l", 4.3) + self.assertRaises(TypeError, l.index, "l", 3, 0.6) + + def foo(lst): + i = 0 + while lst[i] != 0: + i += 2 + + self.assertRaises(IndexError, foo, [2,2,2,2]) self.assertRaises(TypeError, u.index) # class BadExc(Exception): @@ -224,10 +368,9 @@ def test_index(self): # a = self.type2test([0, 1, 2, 3]) # self.assertRaises(BadExc, a.index, BadCmp()) - def test_slice(self): - u = self.type2test("spam") - u[:2] = "h" - self.assertEqual(u, list("ham")) + def test_slicing(self): + b = [1,2,"OK",4] + self.assertEqual(b[-3:3][1], "OK") def test_extendedslicing(self): # subscript @@ -270,6 +413,21 @@ def test_extendedslicing(self): # test issue7788 a = self.type2test(range(10)) del a[9::1<<333] + x = [1,2,3,4,5] + y = x[::-1] + self.assertEqual(y, [5, 4, 3, 2, 1]) + l = [0,1,2,3,4] + self.assertTrue(l[0:3] == l[:3] == l[None:3] == [0,1,2]) + self.assertTrue(l[0:] == l[0:None] == l[:] == [0,1,2,3,4]) + l = [0, 1, 2, 3] + error1, error2, = None, None + + def foo(x, y): + return l[x: : y] + self.assertRaises(ValueError, foo, 1, 0) + def foo2(x, y, z): + return l[x : y : z] + self.assertRaises(ValueError, foo2, 1, 3, 0) def test_contains(self): a = self.type2test(range(15)) @@ -279,6 +437,36 @@ def test_contains(self): self.assertNotIn(42, a) self.assertFalse(-3 in a) self.assertFalse(a.__contains__(17)) + myList = [1, 2, 3, "foo", 4, 5, True, False] + self.assertTrue("foo" in myList) + self.assertTrue(2 in myList) + + def test_listcomprehension(self): + a = [x*x for x in range(10) if x % 2 == 0] + self.assertEqual(a, [0, 4, 16, 36, 64]) + b = [x*y for x in range(1,10) for y in range(1,x) if y%2 == 0] + self.assertEqual(b, [6, 8, 10, 20, 12, 24, 14, 28, 42, 16, 32, 48, 18, 36, 54, 72]) + c = [x*y for x in range(10) if x % 2 == 0 for y in range(10)] + self.assertEqual(c, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 0, 8, 16, 24, 32, 40, 48, 56, 64, 72]) + d = [x*x for x in range(20) if x > 10 if x % 2 == 0] + self.assertEqual(d, [144, 196, 256, 324]) + e = [y for x in range(10) for y in range(x)] + self.assertEqual(e, [0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8]) + + def test_multiplication(self): + self.assertEqual([5]*10, [5, 5, 5, 5, 5, 5, 5, 5, 5, 5]) + self.assertEqual([1,2,3]*4, [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]) + self.assertEqual(10*[5], [5, 5, 5, 5, 5, 5, 5, 5, 5, 5]) + self.assertEqual(4*[1,2,3], [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]) + + def test_comparisons(self): + l = [1,2,3,1] + self.assertFalse(l > l) + self.assertTrue(l >= l) + self.assertTrue(l == l) + self.assertFalse(l != l) + self.assertTrue(l <= l) + self.assertFalse(l < l) if __name__ == "__main__": - unittest.main(verbosity=2) + unittest.main() diff --git a/test/unit3/test_list_sort.py b/test/unit3/test_list_sort.py index b5dce13949..c65c60bca0 100644 --- a/test/unit3/test_list_sort.py +++ b/test/unit3/test_list_sort.py @@ -8,6 +8,11 @@ import unittest class ListSort(unittest.TestCase): + def test_regular(self): + x = [4,5,0] + list.sort(x) + self.assertEqual(x, [0,4,5]) + def test_sortReverseFalseShouldWork(self): x = [1,2,3] x.sort(reverse=False) diff --git a/test/unit3/test_logical_operators.py b/test/unit3/test_logical_operators.py new file mode 100644 index 0000000000..360b81a98b --- /dev/null +++ b/test/unit3/test_logical_operators.py @@ -0,0 +1,72 @@ +"""Unit test for logical operators """ +import unittest + +class Name(unittest.TestCase): + def test_or(self): + self.assertFalse(False or False) + self.assertTrue(True or False) + self.assertTrue(False or True) + self.assertTrue(True or True) + a = [] or 5 + self.assertEqual(a, 5) + b = ([x for x in range(1,10) if False] or ["hello" for x in range(1,10) if True]) + self.assertEqual(b, ['hello', 'hello', 'hello', 'hello', 'hello', 'hello', 'hello', 'hello', 'hello']) + self.assertEqual([] or 5, 5) + self.assertEqual({} or 5, 5) + self.assertEqual(False or 5, 5) + self.assertEqual(True or 5, True) + self.assertEqual(5 or [], 5) + self.assertEqual(5 or {}, 5) + self.assertEqual(5 or False, 5) + self.assertEqual(5 or True, 5) + self.assertEqual([] or {}, {}) + self.assertEqual({} or [], []) + self.assertEqual([] or False, False) + self.assertEqual([] or True, True) + + def test_and(self): + self.assertTrue(True and True) + self.assertFalse(True and False) + self.assertFalse(False and True) + self.assertFalse(False and False) + self.assertEqual([] and 5,[]) + self.assertEqual({} and 5, {}) + self.assertEqual(False and 5, False) + self.assertEqual(True and 5, 5) + self.assertEqual(5 and [], []) + self.assertEqual(5 and {}, {}) + self.assertEqual(5 and False, False) + self.assertEqual(5 and True, True) + + def test_orderofop(self): + self.assertFalse(False and False or False) + self.assertFalse(False or False and False) + + def test_None(self): + self.assertFalse(None == False) + flag = False + if not None: + flag = True + if None: + flag = False + self.assertTrue(flag) + + def test_numbers(self): + self.assertTrue(False == 0) + self.assertTrue(True == 1) + self.assertFalse(True == 2) + self.assertEqual(~True, -2) + self.assertEqual(~False, -1) + + def test_not(self): + self.assertFalse(not True) + self.assertTrue(not False) + self.assertEqual(( not True or False ), ( (not True) or False )) + self.assertEqual(( not False or False ), ( (not False) or False ) ) + self.assertEqual(( not True and True ),( (not True) and True )) + self.assertEqual(( not False and True ), ( (not False) and True )) + self.assertEqual(( not True and not False or False ), ( ( (not True) and (not False) ) or False )) + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_loops.py b/test/unit3/test_loops.py new file mode 100644 index 0000000000..b3a102c740 --- /dev/null +++ b/test/unit3/test_loops.py @@ -0,0 +1,236 @@ +""" Unit test for while and for loops""" +import unittest + +class SimpleLoopTests(unittest.TestCase): + def test_for_in_range(self): + a = 0 + b = 0 + for i in range(5): + b = i + a +=1 + self.assertEqual(a,5) + self.assertEqual(b, 4) + y = 0 + for t in range(1,4): + y += t + self.assertEqual(y, 6) + #test using step argument + n = 0 + for x in range(0,10,2): + n +=1 + self.assertEqual(n,5) + x = [0]*10 + for i in range(10): + x[i] += i + x[i] += i*2 + self.assertEqual(x, [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]) + def foo(x): + for i in x: + break + self.assertRaises(TypeError, foo, 2) + + + def test_for_in_list(self): + z = 0 + for x in [1,2,3]: + z += x + self.assertEqual(z,6) + + def test_for_in_dict(self): + a = [] + for k in {"OK":0}: a.append(k) + self.assertEqual(a, ["OK"]) + + def test_for_in_string(self): + a = [] + for i in "skulpt": a.append(i) + self.assertEqual(a, ["s","k","u","l","p","t"]) + + def test_for_in_tuple(self): + z = [] + a = (1,2,3) + b = ('a', 'b', 'c') + for x in a+b: + z.append(x) + self.assertEqual(z, [1,2,3,'a', 'b', 'c']) + + def test_while(self): + x = 1 + t = 0 + while x <=5: + t = t+x + x = x+1 + self.assertEqual(x,6) + self.assertEqual(t,15) + + def test_break(self): + x = 1 + while x < 3: + break + x = x + 1 + self.assertEqual(x,1) + def f(): + for i in 1,2,3,4,5: + if i == 3: break + yield i + self.assertEqual(list(f()), [1, 2]) + + def test_continue(self): + x = 1 + n = 0 + while x < 10: + x = x + 1 + if n == 2: + continue + n = n + 1 + self.assertEqual(n,2) + def f(): + for i in 1,2,3,4,5: + if i % 2 == 0: continue + yield i + self.assertEqual(list(f()), [1, 3, 5]) + + + def test_list_comprehension(self): + x = [v*v for v in range(0,5)] + self.assertEqual(x[3], 9) + t = [[y*10+x for x in range(0,10)] for y in range(0,10)] + self.assertEqual(t[2][3], 23) + a = [c for c in "asdf"] + self.assertEqual(a, ['a', 's', 'd', 'f']) + + def test_yield(self): + def f(n): + i = 0 + yield i + i += 1 + j = i + yield i + yield j + j *= 100 + i += j + yield j + yield i + yield n + i + a = [] + for i in f(10): # i to conflict with body + j = 999 + a.append(i) + self.assertEqual(a, [0, 1, 1, 100, 101, 111]) + + def f(n): + i = 0 + while i < n: + yield i + yield i * 10 + i += 1 + a = [] + for i in f(10): + a.append(i) + self.assertEqual(a, [0, 0, 1, 10, 2, 20, 3, 30, 4, 40, 5, 50, 6, 60, 7, 70, 8, 80, 9, 90]) + + def f(n): + i = 0 + while i < n: + yield i + i = 100 + yield i + i += 1 + a = [] + for i in f(50): + a.append(i) + self.assertEqual(a, [0, 100]) + def f(): + y = 0 + while y == 0: + y += 1 + yield y + a = [] + for i in f(): + a.append(i) + self.assertEqual(a, [1]) + def yrange(n): + for i in range(n): + yield i + self.assertEqual([0, 1, 2, 3, 4],list(yrange(5))) + def yrange(n): + for i in range(n): + yield i + + def zrange(n): + for y in yrange(n): + yield y + self.assertEqual(list(zrange(5)), [0, 1, 2, 3, 4]) + def f(n): + yield 1 + a, b = n, n + 1 + yield 2 + yield a + yield b + a = 9999 + b = 9999 + z = [] + for i in f(20): + z.append(i) + self.assertEqual(z, [1,2,20,21]) + def f(): + for i in 1,2,3,4,5: + if i == 4: return + yield i + self.assertEqual([1, 2, 3], list(f())) + def foo(value = None): + for i in [-1,0,1,2,3,4]: + if i < 0: + continue + elif i == 0: + yield 0 + elif i == 1: + yield 1 + yield value + yield 2 + else: + yield i + self.assertEqual(list(foo()), [0, 1, None, 2, 2, 3, 4]) + def f(): + if 1 == 2: + yield -1 + elif 1 == 1: + yield 3 + else: + yield -1 + self.assertEqual(list(f()),[3]) + class GeneratorClass: + test = "hi" + def __init__(self): + pass + def generator(self): + for i in range(10): + yield i + + gen = GeneratorClass() + a = [] + for g in gen.generator(): + a.append(g) + self.assertEqual(a, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + + + def test_generator(self): + a = (1 for x in range(3)) + self.assertEqual(str(a)[:17], "> i+1 < j <= n >> i; j odd}. In Python terms, # this set is range((n >> i+1) + 1 | 1, (n >> i) + 1 | 1, 2). + def count_set_bits(n): """Number of '1' bits in binary expansion of a nonnnegative integer.""" return 1 + count_set_bits(n & n - 1) if n else 0 @@ -187,7 +188,7 @@ def testConstants(self): # Ref: Abramowitz & Stegun (Dover, 1965) self.ftest('pi', math.pi, 3.141592653589793238462643) self.ftest('e', math.e, 2.718281828459045235360287) - # self.assertEqual(math.tau, 2*math.pi) + self.assertEqual(math.tau, 2*math.pi) def testAcos(self): self.assertRaises(TypeError, math.acos) @@ -209,6 +210,8 @@ def testAcosh(self): self.assertEqual(math.acosh(INF), INF) # self.assertRaises(ValueError, math.acosh, NINF) self.assertTrue(math.isnan(math.acosh(NAN))) + self.assertEqual(math.acosh(2), 1.3169578969248166) + self.assertAlmostEqual(math.acosh(1.2), 0.6223625037147786, 15) def testAsin(self): self.assertRaises(TypeError, math.asin) @@ -229,6 +232,9 @@ def testAsinh(self): self.assertEqual(math.asinh(INF), INF) # self.assertEqual(math.asinh(NINF), NINF) self.assertTrue(math.isnan(math.asinh(NAN))) + self.assertEqual(math.asinh(2), 1.4436354751788103) + self.assertAlmostEqual(math.asinh(-7.5), -2.712465305184344, 14) + self.assertEqual(math.asinh(0), 0.0) def testAtan(self): self.assertRaises(TypeError, math.atan) @@ -249,6 +255,9 @@ def testAtanh(self): # self.assertRaises(ValueError, math.atanh, INF) # self.assertRaises(ValueError, math.atanh, NINF) self.assertTrue(math.isnan(math.atanh(NAN))) + self.assertAlmostEqual(math.atanh(-0.2), -0.2027325540540822, 15) + self.assertEqual(math.atanh(0), 0.0) + self.assertAlmostEqual(math.atanh(0.5), 0.5493061443340549, 15) def testAtan2(self): self.assertRaises(TypeError, math.atan2) @@ -335,37 +344,37 @@ class TestNoCeil: self.assertRaises(TypeError, math.ceil, t, 0) def testCopysign(self): - # self.assertEqual(math.copysign(1, 42), 1.0) - # self.assertEqual(math.copysign(0., 42), 0.0) - # self.assertEqual(math.copysign(1., -42), -1.0) - # self.assertEqual(math.copysign(3, 0.), 3.0) - # self.assertEqual(math.copysign(4., -0.), -4.0) + self.assertEqual(math.copysign(1, 42), 1.0) + self.assertEqual(math.copysign(0., 42), 0.0) + self.assertEqual(math.copysign(1., -42), -1.0) + self.assertEqual(math.copysign(3, 0.), 3.0) + self.assertEqual(math.copysign(4., -0.), -4.0) self.assertRaises(TypeError, math.copysign) # copysign should let us distinguish signs of zeros - # self.assertEqual(math.copysign(1., 0.), 1.) - # self.assertEqual(math.copysign(1., -0.), -1.) - # self.assertEqual(math.copysign(INF, 0.), INF) - # self.assertEqual(math.copysign(INF, -0.), NINF) - # self.assertEqual(math.copysign(NINF, 0.), INF) - # self.assertEqual(math.copysign(NINF, -0.), NINF) + self.assertEqual(math.copysign(1., 0.), 1.) + self.assertEqual(math.copysign(1., -0.), -1.) + self.assertEqual(math.copysign(INF, 0.), INF) + self.assertEqual(math.copysign(INF, -0.), NINF) + self.assertEqual(math.copysign(NINF, 0.), INF) + self.assertEqual(math.copysign(NINF, -0.), NINF) # and of infinities - # self.assertEqual(math.copysign(1., INF), 1.) - # self.assertEqual(math.copysign(1., NINF), -1.) + self.assertEqual(math.copysign(1., INF), 1.) + self.assertEqual(math.copysign(1., NINF), -1.) self.assertEqual(math.copysign(INF, INF), INF) self.assertEqual(math.copysign(INF, NINF), NINF) - # self.assertEqual(math.copysign(NINF, INF), INF) + self.assertEqual(math.copysign(NINF, INF), INF) self.assertEqual(math.copysign(NINF, NINF), NINF) - # self.assertTrue(math.isnan(math.copysign(NAN, 1.))) - # self.assertTrue(math.isnan(math.copysign(NAN, INF))) - # self.assertTrue(math.isnan(math.copysign(NAN, NINF))) + self.assertTrue(math.isnan(math.copysign(NAN, 1.))) + self.assertTrue(math.isnan(math.copysign(NAN, INF))) + self.assertTrue(math.isnan(math.copysign(NAN, NINF))) self.assertTrue(math.isnan(math.copysign(NAN, NAN))) # copysign(INF, NAN) may be INF or it may be NINF, since # we don't know whether the sign bit of NAN is set on any # given platform. self.assertTrue(math.isinf(math.copysign(INF, NAN))) # similarly, copysign(2., NAN) could be 2. or -2. - # self.assertEqual(abs(math.copysign(2., NAN)), 2.) + self.assertEqual(abs(math.copysign(2., NAN)), 2.) def testCos(self): self.assertRaises(TypeError, math.cos) @@ -388,6 +397,9 @@ def testCosh(self): self.assertEqual(math.cosh(INF), INF) self.assertEqual(math.cosh(NINF), INF) self.assertTrue(math.isnan(math.cosh(NAN))) + self.assertAlmostEqual(math.cosh(2), 3.7621956910836314, 15) + self.assertAlmostEqual(math.cosh(-7.5), 904.0214837702167, 11) + self.assertEqual(math.cosh(0), 1.0) def testDegrees(self): self.assertRaises(TypeError, math.degrees) @@ -401,20 +413,109 @@ def testExp(self): self.ftest('exp(-1)', math.exp(-1), 1/math.e) self.ftest('exp(0)', math.exp(0), 1) self.ftest('exp(1)', math.exp(1), math.e) + self.assertEqual(math.exp(0), 1.0) + self.assertEqual(math.exp(1), 2.718281828459045) + self.assertEqual(math.exp(5), 148.4131591025766) + self.assertEqual(math.exp(-5), 0.006737946999085467) + self.assertEqual(math.exp(12.3), 219695.9886721379) + self.assertEqual(math.exp(-21.3), 5.6172989244173e-10) self.assertEqual(math.exp(INF), INF) self.assertEqual(math.exp(NINF), 0.) self.assertTrue(math.isnan(math.exp(NAN))) - # self.assertRaises(OverflowError, math.exp, 1000000) + self.assertRaises(OverflowError, math.exp, 1000000) + + def testExpm1(self): + # taken from cpython/Lib/test/math_testcases.txt + # special cases + self.assertEqual(math.expm1(0.0), 0.0) + self.assertEqual(math.expm1(-0.0), -0.0) + self.assertEqual(math.expm1(INF), INF) + self.assertEqual(math.expm1(-INF), -1.0) + self.assertTrue(math.isnan(math.expm1(NAN))) + + # timy x + self.assertEqual(math.expm1(5e-324), 5e-324) + self.assertEqual(math.expm1(1e-320), 1e-320) + self.assertEqual(math.expm1(1e-300), 1e-300) + self.assertEqual(math.expm1(1e-150), 1e-150) + self.assertEqual(math.expm1(1e-20), 1e-20) + self.assertEqual(math.expm1(-5e-324), -5e-324) + self.assertEqual(math.expm1(-1e-320), -1e-320) + self.assertEqual(math.expm1(-1e-300), -1e-300) + self.assertEqual(math.expm1(-1e-150), -1e-150) + self.assertEqual(math.expm1(-1e-20), -1e-20) + + # moderate size - direct evaluation runs into trouble + self.assertAlmostEqual(math.expm1(1e-10), 1.0000000000500000e-10, 15) + self.assertAlmostEqual(math.expm1(-9.9999999999999995e-08), -9.9999995000000163e-8, 15) + self.assertAlmostEqual(math.expm1(3.0000000000000001e-05), 3.0000450004500034e-5, 15) + self.assertEqual(math.expm1(-0.0070000000000000001), -0.0069755570667648951) + self.assertEqual(math.expm1(-0.071499208740094633), -0.069002985744820250) + self.assertAlmostEqual(math.expm1(-0.063296004180116799), -0.061334416373633009, 15) + self.assertEqual(math.expm1(0.02390954035597756), 0.024197665143819942) + self.assertEqual(math.expm1(0.085637352649044901), 0.089411184580357767) + self.assertAlmostEqual(math.expm1(0.5966174947411006), 0.81596588596501485, 15) + self.assertEqual(math.expm1(0.30247206212075139), 0.35319987035848677) + self.assertEqual(math.expm1(0.74574727375889516), 1.1080161116737459) + self.assertEqual(math.expm1(0.97767512926555711), 1.6582689207372185) + self.assertEqual(math.expm1(0.8450154566787712), 1.3280137976535897) + self.assertEqual(math.expm1(-0.13979260323125264), -0.13046144381396060) + self.assertAlmostEqual(math.expm1(-0.52899322039643271), -0.41080213643695923, 15) + self.assertEqual(math.expm1(-0.74083261478900631), -0.52328317124797097) + self.assertEqual(math.expm1(-0.93847766984546055), -0.60877704724085946) + self.assertEqual(math.expm1(10.0), 22025.465794806718) + self.assertEqual(math.expm1(27.0), 532048240600.79865) + self.assertEqual(math.expm1(123), 2.6195173187490626e+53) + self.assertEqual(math.expm1(-12.0), -0.99999385578764666) + self.assertEqual(math.expm1(-35.100000000000001), -0.99999999999999944) + + # extreme negative + self.assertEqual(math.expm1(-37.0), -0.99999999999999989) + self.assertEqual(math.expm1(-38.0), -1.0) + self.assertEqual(math.expm1(-710.0), -1.0) + self.assertEqual(math.expm1(-1420.0), -1.0) + self.assertEqual(math.expm1(-1450.0), -1.0) + self.assertEqual(math.expm1(-1500.0), -1.0) + self.assertEqual(math.expm1(-1e50), -1.0) + self.assertEqual(math.expm1(-1.79e308), -1.0) + + + # extreme positive + self.assertEqual(math.expm1(300), 1.9424263952412558e+130) + self.assertEqual(math.expm1(700), 1.0142320547350045e+304) + def testFabs(self): + self.assertEqual(math.fabs(-1), 1.0) + self.assertEqual(math.fabs(0), 0.0) self.assertRaises(TypeError, math.fabs) self.ftest('fabs(-1)', math.fabs(-1), 1) self.ftest('fabs(0)', math.fabs(0), 0) self.ftest('fabs(1)', math.fabs(1), 1) + + + def testFactorial(self): + self.assertEqual(math.factorial(0), 1) + self.assertEqual(math.factorial(0.0), 1) + total = 1 + for i in range(1, 100): # make the numbers smaller for speed + total *= i + self.assertEqual(math.factorial(i), total) + self.assertEqual(math.factorial(float(i)), total) + # self.assertEqual(math.factorial(i), py_factorial(i)) + self.assertRaises(ValueError, math.factorial, -1) + self.assertRaises(ValueError, math.factorial, -1.0) + self.assertRaises(ValueError, math.factorial, -10**100) + self.assertRaises(ValueError, math.factorial, -1e100) + self.assertRaises(ValueError, math.factorial, math.pi) + def testFloor(self): self.assertRaises(TypeError, math.floor) + self.assertEqual(math.floor(-0.1), -1) + self.assertEqual(math.floor(-0.9), -1) + self.assertEqual(math.floor(0.9), 0) self.assertEqual(int, type(math.floor(0.5))) self.ftest('floor(0.5)', math.floor(0.5), 0) self.ftest('floor(1.0)', math.floor(1.0), 1) @@ -443,88 +544,91 @@ class TestNoFloor: self.assertRaises(TypeError, math.floor, t) self.assertRaises(TypeError, math.floor, t, 0) - # def testFmod(self): - # self.assertRaises(TypeError, math.fmod) - # self.ftest('fmod(10, 1)', math.fmod(10, 1), 0.0) - # self.ftest('fmod(10, 0.5)', math.fmod(10, 0.5), 0.0) - # self.ftest('fmod(10, 1.5)', math.fmod(10, 1.5), 1.0) - # self.ftest('fmod(-10, 1)', math.fmod(-10, 1), -0.0) - # self.ftest('fmod(-10, 0.5)', math.fmod(-10, 0.5), -0.0) - # self.ftest('fmod(-10, 1.5)', math.fmod(-10, 1.5), -1.0) - # self.assertTrue(math.isnan(math.fmod(NAN, 1.))) - # self.assertTrue(math.isnan(math.fmod(1., NAN))) - # self.assertTrue(math.isnan(math.fmod(NAN, NAN))) - # self.assertRaises(ValueError, math.fmod, 1., 0.) - # self.assertRaises(ValueError, math.fmod, INF, 1.) - # self.assertRaises(ValueError, math.fmod, NINF, 1.) - # self.assertRaises(ValueError, math.fmod, INF, 0.) - # self.assertEqual(math.fmod(3.0, INF), 3.0) - # self.assertEqual(math.fmod(-3.0, INF), -3.0) - # self.assertEqual(math.fmod(3.0, NINF), 3.0) - # self.assertEqual(math.fmod(-3.0, NINF), -3.0) - # self.assertEqual(math.fmod(0.0, 3.0), 0.0) - # self.assertEqual(math.fmod(0.0, NINF), 0.0) - - # def testFrexp(self): - # self.assertRaises(TypeError, math.frexp) - # - # def testfrexp(name, result, expected): - # (mant, exp), (emant, eexp) = result, expected - # if abs(mant-emant) > eps or exp != eexp: - # self.fail('%s returned %r, expected %r'%\ - # (name, result, expected)) - # - # testfrexp('frexp(-1)', math.frexp(-1), (-0.5, 1)) - # testfrexp('frexp(0)', math.frexp(0), (0, 0)) - # testfrexp('frexp(1)', math.frexp(1), (0.5, 1)) - # testfrexp('frexp(2)', math.frexp(2), (0.5, 2)) - # - # self.assertEqual(math.frexp(INF)[0], INF) - # self.assertEqual(math.frexp(NINF)[0], NINF) - # self.assertTrue(math.isnan(math.frexp(NAN)[0])) - - # def testFsum(self): - # # math.fsum relies on exact rounding for correct operation. - # # There's a known problem with IA32 floating-point that causes - # # inexact rounding in some situations, and will cause the - # # math.fsum tests below to fail; see issue #2937. On non IEEE - # # 754 platforms, and on IEEE 754 platforms that exhibit the - # # problem described in issue #2937, we simply skip the whole - # # test. - # - # # Python version of math.fsum, for comparison. Uses a - # # different algorithm based on frexp, ldexp and integer - # # arithmetic. - # from sys import float_info - # mant_dig = float_info.mant_dig - # etiny = float_info.min_exp - mant_dig - - # def msum(iterable): - # """Full precision summation. Compute sum(iterable) without any - # intermediate accumulation of error. Based on the 'lsum' function - # at http://code.activestate.com/recipes/393090/ - # - # """ - # tmant, texp = 0, 0 - # for x in iterable: - # mant, exp = math.frexp(x) - # mant, exp = int(math.ldexp(mant, mant_dig)), exp - mant_dig - # if texp > exp: - # tmant <<= texp-exp - # texp = exp - # else: - # mant <<= exp-texp - # tmant += mant - # # Round tmant * 2**texp to a float. The original recipe - # # used float(str(tmant)) * 2.0**texp for this, but that's - # # a little unsafe because str -> float conversion can't be - # # relied upon to do correct rounding on all platforms. - # tail = max(len(bin(abs(tmant)))-2 - mant_dig, etiny - texp) - # if tail > 0: - # h = 1 << (tail-1) - # tmant = tmant // (2*h) + bool(tmant & h and tmant & 3*h-1) - # texp += tail - # return math.ldexp(tmant, texp) + def testFmod(self): + self.assertRaises(TypeError, math.fmod) + self.ftest('fmod(10, 1)', math.fmod(10, 1), 0.0) + self.ftest('fmod(10, 0.5)', math.fmod(10, 0.5), 0.0) + self.ftest('fmod(10, 1.5)', math.fmod(10, 1.5), 1.0) + self.ftest('fmod(-10, 1)', math.fmod(-10, 1), -0.0) + self.ftest('fmod(-10, 0.5)', math.fmod(-10, 0.5), -0.0) + self.ftest('fmod(-10, 1.5)', math.fmod(-10, 1.5), -1.0) + self.assertTrue(math.isnan(math.fmod(NAN, 1.))) + self.assertTrue(math.isnan(math.fmod(1., NAN))) + self.assertTrue(math.isnan(math.fmod(NAN, NAN))) + self.assertRaises(ValueError, math.fmod, 1., 0.) + self.assertRaises(ValueError, math.fmod, INF, 1.) + self.assertRaises(ValueError, math.fmod, NINF, 1.) + self.assertRaises(ValueError, math.fmod, INF, 0.) + self.assertEqual(math.fmod(3.0, INF), 3.0) + self.assertEqual(math.fmod(-3.0, INF), -3.0) + self.assertEqual(math.fmod(3.0, NINF), 3.0) + self.assertEqual(math.fmod(-3.0, NINF), -3.0) + self.assertEqual(math.fmod(0.0, 3.0), 0.0) + self.assertEqual(math.fmod(0.0, NINF), 0.0) + + def testFrexp(self): + self.assertRaises(TypeError, math.frexp) + + def testfrexp(name, result, expected): + (mant, exp), (emant, eexp) = result, expected + if abs(mant-emant) > eps or exp != eexp: + self.fail('%s returned %r, expected %r'%\ + (name, result, expected)) + + testfrexp('frexp(-1)', math.frexp(-1), (-0.5, 1)) + testfrexp('frexp(0)', math.frexp(0), (0, 0)) + testfrexp('frexp(1)', math.frexp(1), (0.5, 1)) + testfrexp('frexp(2)', math.frexp(2), (0.5, 2)) + + self.assertEqual(math.frexp(INF)[0], INF) + self.assertEqual(math.frexp(NINF)[0], NINF) + self.assertTrue(math.isnan(math.frexp(NAN)[0])) + + def testFsum(self): + # math.fsum relies on exact rounding for correct operation. + # There's a known problem with IA32 floating-point that causes + # inexact rounding in some situations, and will cause the + # math.fsum tests below to fail; see issue #2937. On non IEEE + # 754 platforms, and on IEEE 754 platforms that exhibit the + # problem described in issue #2937, we simply skip the whole + # test. + + # Python version of math.fsum, for comparison. Uses a + # different algorithm based on frexp, ldexp and integer + # arithmetic. + # from sys import float_info + # mant_dig = float_info.mant_dig + # etiny = float_info.min_exp - mant_dig + mant_dig = 53 # sys and float_info don't exist + etiny = -1020-mant_dig + + def msum(iterable): + """Full precision summation. Compute sum(iterable) without any + intermediate accumulation of error. Based on the 'lsum' function + at http://code.activestate.com/recipes/393090/ + """ + tmant, texp = 0, 0 + for x in iterable: + mant, exp = math.frexp(x) + mant, exp = int(math.ldexp(mant, mant_dig)), exp - mant_dig + if texp > exp: + tmant <<= texp-exp + texp = exp + else: + mant <<= exp-texp + tmant += mant + # Round tmant * 2**texp to a float. The original recipe + # used float(str(tmant)) * 2.0**texp for this, but that's + # a little unsafe because str -> float conversion can't be + # relied upon to do correct rounding on all platforms. + tail = max(len(bin(abs(tmant)))-2 - mant_dig, etiny - texp) + if tail > 0: + h = 1 << (tail-1) + tmant = tmant // (2*h) + bool(tmant & h and tmant & 3*h-1) + texp += tail + + # weird hack that fixes a bug when tmant is converted to a float incorrectly by skulpt + return math.ldexp(int(str(tmant)), texp) test_values = [ ([], 0.0), @@ -534,71 +638,88 @@ class TestNoFloor: ([2.0**53, 1.0, 2.0**-100], 2.0**53+2.0), ([2.0**53+10.0, 1.0, 2.0**-100], 2.0**53+12.0), ([2.0**53-4.0, 0.5, 2.0**-54], 2.0**53-3.0), - ([1./n for n in range(1, 1001)]), - ([(-1.)**n/n for n in range(1, 1001)]), + ([1./n for n in range(1, 1001)],7.485470860550345), + ([(-1.)**n/n for n in range(1, 1001)],-0.6926474305598203), ([1.7**(i+1)-1.7**i for i in range(1000)] + [-1.7**1000], -1.0), ([1e16, 1., 1e-16], 10000000000000002.0), ([1e16-2., 1.-2.**-53, -(1e16-2.), -(1.-2.**-53)], 0.0), # exercise code for resizing partials array ([2.**n - 2.**(n+50) + 2.**(n+52) for n in range(-1074, 972, 2)] + - [-2.**1022]) - ] - - # from random import random, gauss, shuffle - # for j in range(1000): - # vals = [7, 1e100, -7, -1e100, -9e-20, 8e-20] * 10 - # s = 0 - # for i in range(200): - # v = gauss(0, random()) ** 7 - s - # s += v - # vals.append(v) - # shuffle(vals) - # - # s = msum(vals) - # self.assertEqual(msum(vals), math.fsum(vals)) - - # def testGcd(self): - # gcd = math.gcd - # self.assertEqual(gcd(0, 0), 0) - # self.assertEqual(gcd(1, 0), 1) - # self.assertEqual(gcd(-1, 0), 1) - # self.assertEqual(gcd(0, 1), 1) - # self.assertEqual(gcd(0, -1), 1) - # self.assertEqual(gcd(7, 1), 1) - # self.assertEqual(gcd(7, -1), 1) - # self.assertEqual(gcd(-23, 15), 1) - # self.assertEqual(gcd(120, 84), 12) - # self.assertEqual(gcd(84, -120), 12) - # self.assertEqual(gcd(1216342683557601535506311712, - # 436522681849110124616458784), 32) - # c = 652560 - # x = 434610456570399902378880679233098819019853229470286994367836600566 - # y = 1064502245825115327754847244914921553977 - # a = x * c - # b = y * c - # self.assertEqual(gcd(a, b), c) - # self.assertEqual(gcd(b, a), c) - # self.assertEqual(gcd(-a, b), c) - # self.assertEqual(gcd(b, -a), c) - # self.assertEqual(gcd(a, -b), c) - # self.assertEqual(gcd(-b, a), c) - # self.assertEqual(gcd(-a, -b), c) - # self.assertEqual(gcd(-b, -a), c) - # c = 576559230871654959816130551884856912003141446781646602790216406874 - # a = x * c - # b = y * c - # self.assertEqual(gcd(a, b), c) - # self.assertEqual(gcd(b, a), c) - # self.assertEqual(gcd(-a, b), c) - # self.assertEqual(gcd(b, -a), c) - # self.assertEqual(gcd(a, -b), c) - # self.assertEqual(gcd(-b, a), c) - # self.assertEqual(gcd(-a, -b), c) - # self.assertEqual(gcd(-b, -a), c) - # - # self.assertRaises(TypeError, gcd, 120.0, 84) - # self.assertRaises(TypeError, gcd, 120, 84.0) - # self.assertEqual(gcd(MyIndexable(120), MyIndexable(84)), 12) + [-2.**1022],1.3305602063564798e+292) + ] + + for i, (vals, expected) in enumerate(test_values): + try: + actual = math.fsum(vals) + except OverflowError: + self.fail("test %d failed: got OverflowError, expected %r " + "for math.fsum(%.100r)" % (i, expected, vals)) + except ValueError: + self.fail("test %d failed: got ValueError, expected %r " + "for math.fsum(%.100r)" % (i, expected, vals)) + self.assertTrue(math.isclose(actual, expected, rel_tol=10**-15)) + # AssertEqual failed on testcase 3,4,6,10 only in the units digit + # correct in first 15 significant figures! + # hence using isclose rather than assertEqual + + + from random import random, gauss, shuffle, seed + seed(0) + for j in range(20): + vals = [7, 1e100, -7, -1e100, -9e-20, 8e-20] * 10 + s = 0 + for i in range(200): + v = gauss(0, random()) ** 7 - s + s += v + vals.append(v) + shuffle(vals) + + s = msum(vals) + self.assertAlmostEqual(msum(vals), math.fsum(vals), 12) + # self.assertEqual(msum(vals), math.fsum(vals)) + # assertEqual failed - this seemed the best compromise + + def testGcd(self): + gcd = math.gcd + self.assertEqual(gcd(0, 0), 0) + self.assertEqual(gcd(1, 0), 1) + self.assertEqual(gcd(-1, 0), 1) + self.assertEqual(gcd(0, 1), 1) + self.assertEqual(gcd(0, -1), 1) + self.assertEqual(gcd(7, 1), 1) + self.assertEqual(gcd(7, -1), 1) + self.assertEqual(gcd(-23, 15), 1) + self.assertEqual(gcd(120, 84), 12) + self.assertEqual(gcd(84, -120), 12) + self.assertEqual(gcd(1216342683557601535506311712, 436522681849110124616458784), 32) + c = 652560 + x = 434610456570399902378880679233098819019853229470286994367836600566 + y = 1064502245825115327754847244914921553977 + a = x * c + b = y * c + self.assertEqual(gcd(a, b), c) + self.assertEqual(gcd(b, a), c) + self.assertEqual(gcd(-a, b), c) + self.assertEqual(gcd(b, -a), c) + self.assertEqual(gcd(a, -b), c) + self.assertEqual(gcd(-b, a), c) + self.assertEqual(gcd(-a, -b), c) + self.assertEqual(gcd(-b, -a), c) + c = 576559230871654959816130551884856912003141446781646602790216406874 + a = x * c + b = y * c + self.assertEqual(gcd(a, b), c) + self.assertEqual(gcd(b, a), c) + self.assertEqual(gcd(-a, b), c) + self.assertEqual(gcd(b, -a), c) + self.assertEqual(gcd(a, -b), c) + self.assertEqual(gcd(-b, a), c) + self.assertEqual(gcd(-a, -b), c) + self.assertEqual(gcd(-b, -a), c) + + self.assertRaises(TypeError, gcd, 120.0, 84) + self.assertRaises(TypeError, gcd, 120, 84.0) + # self.assertEqual(gcd(MyIndexable(120), MyIndexable(84)), 12) def testHypot(self): self.assertRaises(TypeError, math.hypot) @@ -611,37 +732,37 @@ def testHypot(self): self.assertTrue(math.isnan(math.hypot(1.0, NAN))) self.assertTrue(math.isnan(math.hypot(NAN, -2.0))) - # def testLdexp(self): - # self.assertRaises(TypeError, math.ldexp) - # self.ftest('ldexp(0,1)', math.ldexp(0,1), 0) - # self.ftest('ldexp(1,1)', math.ldexp(1,1), 2) - # self.ftest('ldexp(1,-1)', math.ldexp(1,-1), 0.5) - # self.ftest('ldexp(-1,1)', math.ldexp(-1,1), -2) - # self.assertRaises(OverflowError, math.ldexp, 1., 1000000) - # self.assertRaises(OverflowError, math.ldexp, -1., 1000000) - # self.assertEqual(math.ldexp(1., -1000000), 0.) - # self.assertEqual(math.ldexp(-1., -1000000), -0.) - # self.assertEqual(math.ldexp(INF, 30), INF) - # self.assertEqual(math.ldexp(NINF, -213), NINF) - # self.assertTrue(math.isnan(math.ldexp(NAN, 0))) - # - # # large second argument - # for n in [10**5, 10**10, 10**20, 10**40]: - # self.assertEqual(math.ldexp(INF, -n), INF) - # self.assertEqual(math.ldexp(NINF, -n), NINF) - # self.assertEqual(math.ldexp(1., -n), 0.) - # self.assertEqual(math.ldexp(-1., -n), -0.) - # self.assertEqual(math.ldexp(0., -n), 0.) - # self.assertEqual(math.ldexp(-0., -n), -0.) - # self.assertTrue(math.isnan(math.ldexp(NAN, -n))) - # - # self.assertRaises(OverflowError, math.ldexp, 1., n) - # self.assertRaises(OverflowError, math.ldexp, -1., n) - # self.assertEqual(math.ldexp(0., n), 0.) - # self.assertEqual(math.ldexp(-0., n), -0.) - # self.assertEqual(math.ldexp(INF, n), INF) - # self.assertEqual(math.ldexp(NINF, n), NINF) - # self.assertTrue(math.isnan(math.ldexp(NAN, n))) + def testLdexp(self): + self.assertRaises(TypeError, math.ldexp) + self.ftest('ldexp(0,1)', math.ldexp(0,1), 0) + self.ftest('ldexp(1,1)', math.ldexp(1,1), 2) + self.ftest('ldexp(1,-1)', math.ldexp(1,-1), 0.5) + self.ftest('ldexp(-1,1)', math.ldexp(-1,1), -2) + self.assertRaises(OverflowError, math.ldexp, 1., 1000000) + self.assertRaises(OverflowError, math.ldexp, -1., 1000000) + self.assertEqual(math.ldexp(1., -1000000), 0.) + self.assertEqual(math.ldexp(-1., -1000000), -0.) + self.assertEqual(math.ldexp(INF, 30), INF) + self.assertEqual(math.ldexp(NINF, -213), NINF) + self.assertTrue(math.isnan(math.ldexp(NAN, 0))) + + # large second argument + for n in [10**5, 10**10, 10**20, 10**40]: + self.assertEqual(math.ldexp(INF, -n), INF) + self.assertEqual(math.ldexp(NINF, -n), NINF) + self.assertEqual(math.ldexp(1., -n), 0.) + self.assertEqual(math.ldexp(-1., -n), -0.) + self.assertEqual(math.ldexp(0., -n), 0.) + self.assertEqual(math.ldexp(-0., -n), -0.) + self.assertTrue(math.isnan(math.ldexp(NAN, -n))) + + self.assertRaises(OverflowError, math.ldexp, 1., n) + self.assertRaises(OverflowError, math.ldexp, -1., n) + self.assertEqual(math.ldexp(0., n), 0.) + self.assertEqual(math.ldexp(-0., n), -0.) + self.assertEqual(math.ldexp(INF, n), INF) + self.assertEqual(math.ldexp(NINF, n), NINF) + self.assertTrue(math.isnan(math.ldexp(NAN, n))) def testLog(self): self.assertRaises(TypeError, math.log) @@ -651,39 +772,108 @@ def testLog(self): self.ftest('log(32,2)', math.log(32,2), 5) self.ftest('log(10**40, 10)', math.log(10**40, 10), 40) self.ftest('log(10**40, 10**20)', math.log(10**40, 10**20), 2) - # self.ftest('log(10**1000)', math.log(10**1000), - # 2302.5850929940457) - # self.assertRaises(ValueError, math.log, -1.5) - # self.assertRaises(ValueError, math.log, -10**1000) - # self.assertRaises(ValueError, math.log, NINF) + self.ftest('log(10**1000)', math.log(10**1000), + 2302.5850929940457) + self.assertRaises(ValueError, math.log, -1.5) + self.assertRaises(ValueError, math.log, -10**1000) + self.assertRaises(ValueError, math.log, NINF) self.assertEqual(math.log(INF), INF) self.assertTrue(math.isnan(math.log(NAN))) - # def testLog1p(self): - # self.assertRaises(TypeError, math.log1p) - # for n in [2, 2**90, 2**300]: - # self.assertAlmostEqual(math.log1p(n), math.log1p(float(n))) - # self.assertRaises(ValueError, math.log1p, -1) - # self.assertEqual(math.log1p(INF), INF) - - # def testLog2(self): - # self.assertRaises(TypeError, math.log2) - # - # # Check some integer values - # self.assertEqual(math.log2(1), 0.0) - # self.assertEqual(math.log2(2), 1.0) - # self.assertEqual(math.log2(4), 2.0) - # - # # Large integer values - # self.assertEqual(math.log2(2**1023), 1023.0) - # self.assertEqual(math.log2(2**1024), 1024.0) - # self.assertEqual(math.log2(2**2000), 2000.0) - # - # self.assertRaises(ValueError, math.log2, -1.5) - # self.assertRaises(ValueError, math.log2, NINF) - # self.assertTrue(math.isnan(math.log2(NAN))) - - # log2() is not accurate enough on Mac OS X Tiger (10.4) + def testLog1p(self): + self.assertRaises(TypeError, math.log1p) + for n in [2, 2**90, 2**300]: + self.assertAlmostEqual(math.log1p(n), math.log1p(float(n))) + self.assertRaises(ValueError, math.log1p, -1) + self.assertEqual(math.log1p(INF), INF) + + # from cpython math_testcases.txt + # -- special values + self.assertEqual(math.log1p(0.0), 0.0) + self.assertEqual(math.log1p(-0.0), -0.0) + self.assertEqual(math.log1p(INF), INF) + self.assertRaises(ValueError, math.log1p,-INF) + self.assertTrue(math.isnan(math.log1p(NAN))) + + # -- singularity at -1.0 + self.assertRaises(ValueError, math.log1p, -1.0) + self.assertEqual(math.log1p(-0.9999999999999999), -36.736800569677101) + + # -- finite values < 1.0 are invalid + self.assertRaises(ValueError, math.log1p, -1.0000000000000002) + self.assertRaises(ValueError,math.log1p,-1.1 ) + self.assertRaises(ValueError,math.log1p,-2.0 ) + self.assertRaises(ValueError,math.log1p,-1e300 ) + + # -- tiny x: log1p(x) ~ x + self.assertEqual(math.log1p(5e-324), 5e-324) + self.assertEqual(math.log1p(1e-320), 1e-320) + self.assertEqual(math.log1p(1e-300), 1e-300) + self.assertEqual(math.log1p(1e-150), 1e-150) + self.assertEqual(math.log1p(1e-20), 1e-20) + + self.assertEqual(math.log1p(-5e-324), -5e-324) + self.assertEqual(math.log1p(-1e-320), -1e-320) + self.assertEqual(math.log1p(-1e-300), -1e-300) + self.assertEqual(math.log1p(-1e-150), -1e-150) + self.assertEqual(math.log1p(-1e-20), -1e-20) + + # -- some (mostly) random small and moderate-sized values + self.assertEqual(math.log1p(-0.89156889782277482), -2.2216403106762863) + self.assertEqual(math.log1p(-0.23858496047770464), -0.27257668276980057) + self.assertEqual(math.log1p(-0.011641726191307515), -0.011710021654495657) + self.assertEqual(math.log1p(-0.0090126398571693817), -0.0090534993825007650) + self.assertEqual(math.log1p(-0.00023442805985712781), -0.00023445554240995693) + self.assertEqual(math.log1p(-1.5672870980936349e-5), -1.5672993801662046e-5) + self.assertEqual(math.log1p(-7.9650013274825295e-6), -7.9650330482740401e-6) + self.assertEqual(math.log1p(-2.5202948343227410e-7), -2.5202951519170971e-7) + self.assertEqual(math.log1p(-8.2446372820745855e-11), -8.2446372824144559e-11) + self.assertEqual(math.log1p(-8.1663670046490789e-12), -8.1663670046824230e-12) + self.assertEqual(math.log1p(7.0351735084656292e-18), 7.0351735084656292e-18) + self.assertEqual(math.log1p(5.2732161907375226e-12), 5.2732161907236188e-12) + self.assertEqual(math.log1p(1.0000000000000000e-10), 9.9999999995000007e-11) + self.assertEqual(math.log1p(2.1401273266000197e-9), 2.1401273243099470e-9) + self.assertEqual(math.log1p(1.2668914653979560e-8), 1.2668914573728861e-8) + self.assertEqual(math.log1p(1.6250007816299069e-6), 1.6249994613175672e-6) + self.assertAlmostEqual(math.log1p(8.3740495645839399e-6), 8.3740145024266269e-6,15) + self.assertEqual(math.log1p(3.0000000000000001e-5), 2.9999550008999799e-5) + self.assertEqual(math.log1p(0.0070000000000000001), 0.0069756137364252423) + self.assertEqual(math.log1p(0.013026235315053002), 0.012942123564008787) + self.assertAlmostEqual(math.log1p(0.013497160797236184), 0.013406885521915038,15) + self.assertEqual(math.log1p(0.027625599078135284), 0.027250897463483054) + self.assertEqual(math.log1p(0.14179687245544870), 0.13260322540908789) + + # -- large values + self.assertEqual(math.log1p(1.7976931348623157e+308), 709.78271289338397) + self.assertEqual(math.log1p(1.0000000000000001e+300), 690.77552789821368) + self.assertEqual(math.log1p(1.0000000000000001e+70), 161.18095650958321) + self.assertEqual(math.log1p(10000000000.000000), 23.025850930040455) + + # -- other values transferred from testLog1p in test_math + self.assertEqual(math.log1p(-0.63212055882855767), -1.0000000000000000) + self.assertEqual(math.log1p(1.7182818284590451), 1.0000000000000000) + self.assertEqual(math.log1p(1.0000000000000000), 0.69314718055994529) + self.assertEqual(math.log1p(1.2379400392853803e+27), 62.383246250395075) + + + def testLog2(self): + self.assertRaises(TypeError, math.log2) + + # Check some integer values + self.assertEqual(math.log2(1), 0.0) + self.assertEqual(math.log2(2), 1.0) + self.assertEqual(math.log2(4), 2.0) + + # Large integer values + self.assertAlmostEqual(math.log2(2**1023), 1023.0,12) + self.assertEqual(math.log2(2**1024), 1024.0) + self.assertEqual(math.log2(2**2000), 2000.0) + + self.assertRaises(ValueError, math.log2, -1.5) + self.assertRaises(ValueError, math.log2, NINF) + self.assertTrue(math.isnan(math.log2(NAN))) + + # # log2() is not accurate enough on Mac OS X Tiger (10.4) # def testLog2Exact(self): # # Check that we get exact equality for log2 of powers of 2. # actual = [math.log2(math.ldexp(1.0, n)) for n in range(-1074, 1024)] @@ -695,31 +885,32 @@ def testLog10(self): self.ftest('log10(0.1)', math.log10(0.1), -1) self.ftest('log10(1)', math.log10(1), 0) self.ftest('log10(10)', math.log10(10), 1) - # self.ftest('log10(10**1000)', math.log10(10**1000), 1000.0) - # self.assertRaises(ValueError, math.log10, -1.5) - # self.assertRaises(ValueError, math.log10, -10**1000) - # self.assertRaises(ValueError, math.log10, NINF) + self.ftest('log10(10**1000)', math.log10(10**1000), 1000.0) + self.assertRaises(ValueError, math.log10, -1.5) + self.assertRaises(ValueError, math.log10, -10**1000) + self.assertRaises(ValueError, math.log10, NINF) self.assertEqual(math.log(INF), INF) self.assertTrue(math.isnan(math.log10(NAN))) - # def testModf(self): - # self.assertRaises(TypeError, math.modf) - # - # def testmodf(name, result, expected): - # (v1, v2), (e1, e2) = result, expected - # if abs(v1-e1) > eps or abs(v2-e2): - # self.fail('%s returned %r, expected %r'%\ - # (name, result, expected)) - # - # testmodf('modf(1.5)', math.modf(1.5), (0.5, 1.0)) - # testmodf('modf(-1.5)', math.modf(-1.5), (-0.5, -1.0)) - # - # self.assertEqual(math.modf(INF), (0.0, INF)) - # self.assertEqual(math.modf(NINF), (-0.0, NINF)) - # - # modf_nan = math.modf(NAN) - # self.assertTrue(math.isnan(modf_nan[0])) - # self.assertTrue(math.isnan(modf_nan[1])) + def testModf(self): + self.assertRaises(TypeError, math.modf) + + def testmodf(name, result, expected): + (v1, v2), (e1, e2) = result, expected + if abs(v1-e1) > eps or abs(v2-e2): + self.fail('%s returned %r, expected %r'%\ + (name, result, expected)) + + testmodf('modf(1.5)', math.modf(1.5), (0.5, 1.0)) + testmodf('modf(-1.5)', math.modf(-1.5), (-0.5, -1.0)) + testmodf('modf(-0.0)', math.modf(-0.0),(-0.0,-0.0)) + + self.assertEqual(math.modf(INF), (0.0, INF)) + self.assertEqual(math.modf(NINF), (-0.0, NINF)) + + modf_nan = math.modf(NAN) + self.assertTrue(math.isnan(modf_nan[0])) + self.assertTrue(math.isnan(modf_nan[1])) def testPow(self): self.assertRaises(TypeError, math.pow) @@ -729,12 +920,12 @@ def testPow(self): self.ftest('pow(2,-1)', math.pow(2,-1), 0.5) self.assertEqual(math.pow(INF, 1), INF) self.assertEqual(math.pow(NINF, 1), NINF) - # self.assertEqual((math.pow(1, INF)), 1.) - # self.assertEqual((math.pow(1, NINF)), 1.) + self.assertEqual((math.pow(1, INF)), 1.) + self.assertEqual((math.pow(1, NINF)), 1.) self.assertTrue(math.isnan(math.pow(NAN, 1))) self.assertTrue(math.isnan(math.pow(2, NAN))) self.assertTrue(math.isnan(math.pow(0, NAN))) - # self.assertEqual(math.pow(1, NAN), 1) + self.assertEqual(math.pow(1, NAN), 1) # pow(0., x) self.assertEqual(math.pow(0., INF), 0.) @@ -743,10 +934,10 @@ def testPow(self): self.assertEqual(math.pow(0., 2.), 0.) self.assertEqual(math.pow(0., 0.), 1.) self.assertEqual(math.pow(0., -0.), 1.) - # self.assertRaises(ValueError, math.pow, 0., -2.) - # self.assertRaises(ValueError, math.pow, 0., -2.3) - # self.assertRaises(ValueError, math.pow, 0., -3.) - # self.assertRaises(ValueError, math.pow, 0., NINF) + self.assertRaises(ValueError, math.pow, 0., -2.) + self.assertRaises(ValueError, math.pow, 0., -2.3) + self.assertRaises(ValueError, math.pow, 0., -3.) + self.assertRaises(ValueError, math.pow, 0., NINF) self.assertTrue(math.isnan(math.pow(0., NAN))) # pow(INF, x) @@ -769,10 +960,10 @@ def testPow(self): self.assertEqual(math.pow(-0., 2.), 0.) self.assertEqual(math.pow(-0., 0.), 1.) self.assertEqual(math.pow(-0., -0.), 1.) - # self.assertRaises(ValueError, math.pow, -0., -2.) - # self.assertRaises(ValueError, math.pow, -0., -2.3) - # self.assertRaises(ValueError, math.pow, -0., -3.) - # self.assertRaises(ValueError, math.pow, -0., NINF) + self.assertRaises(ValueError, math.pow, -0., -2.) + self.assertRaises(ValueError, math.pow, -0., -2.3) + self.assertRaises(ValueError, math.pow, -0., -3.) + self.assertRaises(ValueError, math.pow, -0., NINF) self.assertTrue(math.isnan(math.pow(-0., NAN))) # pow(NINF, x) @@ -789,20 +980,20 @@ def testPow(self): self.assertTrue(math.isnan(math.pow(NINF, NAN))) # pow(-1, x) - # self.assertEqual(math.pow(-1., INF), 1.) + self.assertEqual(math.pow(-1., INF), 1.) self.assertEqual(math.pow(-1., 3.), -1.) - # self.assertRaises(ValueError, math.pow, -1., 2.3) + self.assertRaises(ValueError, math.pow, -1., 2.3) self.assertEqual(math.pow(-1., 2.), 1.) self.assertEqual(math.pow(-1., 0.), 1.) self.assertEqual(math.pow(-1., -0.), 1.) self.assertEqual(math.pow(-1., -2.), 1.) - # self.assertRaises(ValueError, math.pow, -1., -2.3) + self.assertRaises(ValueError, math.pow, -1., -2.3) self.assertEqual(math.pow(-1., -3.), -1.) - # self.assertEqual(math.pow(-1., NINF), 1.) + self.assertEqual(math.pow(-1., NINF), 1.) self.assertTrue(math.isnan(math.pow(-1., NAN))) # pow(1, x) - # self.assertEqual(math.pow(1., INF), 1.) + self.assertEqual(math.pow(1., INF), 1.) self.assertEqual(math.pow(1., 3.), 1.) self.assertEqual(math.pow(1., 2.3), 1.) self.assertEqual(math.pow(1., 2.), 1.) @@ -811,8 +1002,8 @@ def testPow(self): self.assertEqual(math.pow(1., -2.), 1.) self.assertEqual(math.pow(1., -2.3), 1.) self.assertEqual(math.pow(1., -3.), 1.) - # self.assertEqual(math.pow(1., NINF), 1.) - # self.assertEqual(math.pow(1., NAN), 1.) + self.assertEqual(math.pow(1., NINF), 1.) + self.assertEqual(math.pow(1., NAN), 1.) # pow(x, 0) should be 1 for any x self.assertEqual(math.pow(2.3, 0.), 1.) @@ -823,8 +1014,8 @@ def testPow(self): self.assertEqual(math.pow(NAN, -0.), 1.) # pow(x, y) is invalid if x is negative and y is not integral - # self.assertRaises(ValueError, math.pow, -1., 2.3) - # self.assertRaises(ValueError, math.pow, -15., -3.1) + self.assertRaises(ValueError, math.pow, -1., 2.3) + self.assertRaises(ValueError, math.pow, -15., -3.1) # pow(x, NINF) self.assertEqual(math.pow(1.9, NINF), 0.) @@ -855,8 +1046,15 @@ def testPow(self): self.ftest('(-2.)**-1.', math.pow(-2.0, -1.0), -0.5) self.ftest('(-2.)**-2.', math.pow(-2.0, -2.0), 0.25) self.ftest('(-2.)**-3.', math.pow(-2.0, -3.0), -0.125) - # self.assertRaises(ValueError, math.pow, -2.0, -0.5) - # self.assertRaises(ValueError, math.pow, -2.0, 0.5) + self.assertRaises(ValueError, math.pow, -2.0, -0.5) + self.assertRaises(ValueError, math.pow, -2.0, 0.5) + + # pow(x,y) should raise OverFlow Error for large x,y + self.assertRaises(OverflowError, math.pow, 2, 1024) + self.assertRaises(OverflowError, math.pow, 3, 1024) + self.assertRaises(OverflowError, math.pow, 2, 2048) + self.assertRaises(OverflowError, math.pow, 2, 4096) + # the following tests have been commented out since they don't # really belong here: the implementation of ** for floats is @@ -876,6 +1074,270 @@ def testRadians(self): self.ftest('radians(90)', math.radians(90), math.pi/2) self.ftest('radians(-45)', math.radians(-45), -math.pi/4) self.ftest('radians(0)', math.radians(0), 0) + self.assertEqual("%10.5f" % math.radians(180), " 3.14159") + self.assertEqual("%10.5f" % math.degrees(math.radians(180)), " 180.00000") + + def testRemainder(self): + # from fractions import Fraction + + # def validate_spec(x, y, r): + # """ + # Check that r matches remainder(x, y) according to the IEEE 754 + # specification. Assumes that x, y and r are finite and y is nonzero. + # """ + # fx, fy, fr = Fraction(x), Fraction(y), Fraction(r) + # # r should not exceed y/2 in absolute value + # self.assertLessEqual(abs(fr), abs(fy/2)) + # # x - r should be an exact integer multiple of y + # n = (fx - fr) / fy + # self.assertEqual(n, int(n)) + # if abs(fr) == abs(fy/2): + # # If |r| == |y/2|, n should be even. + # self.assertEqual(n/2, int(n/2)) + + # triples (x, y, remainder(x, y)) in hexadecimal form. + testcases = [ #taken from cpython test_cases - converted to floats rather than float hex + # Remainders modulo 1, showing the ties-to-even behaviour. + '-4.0 1.0 -0.0', + '-3.5 1.0 0.5', + '-3.0 1.0 -0.0', + '-2.5 1.0 -0.5', + '-2.0 1.0 -0.0', + '-1.5 1.0 0.5', + '-1.0 1.0 -0.0', + '-0.5 1.0 -0.5', + '-0.0 1.0 -0.0', + '0.0 1.0 0.0', + '0.5 1.0 0.5', + '1.0 1.0 0.0', + '1.5 1.0 -0.5', + '2.0 1.0 0.0', + '2.5 1.0 0.5', + '3.0 1.0 0.0', + '3.5 1.0 -0.5', + '4.0 1.0 0.0', + + # Reductions modulo 2*pi + '0.0 6.283185307179586 0.0', + '1.5707963267948966 6.283185307179586 1.5707963267948966', + '3.1415926535897927 6.283185307179586 3.1415926535897927', + '3.141592653589793 6.283185307179586 3.141592653589793', + '3.1415926535897936 6.283185307179586 -3.1415926535897927', + '6.283185307179585 6.283185307179586 -8.881784197001252e-16', + '6.283185307179586 6.283185307179586 0.0', + '6.283185307179587 6.283185307179586 8.881784197001252e-16', + '9.424777960769378 6.283185307179586 3.1415926535897913', + '9.42477796076938 6.283185307179586 -3.141592653589793', + '9.424777960769381 6.283185307179586 -3.1415926535897913', + '12.56637061435917 6.283185307179586 -1.7763568394002505e-15', + '12.566370614359172 6.283185307179586 0.0', + '12.566370614359174 6.283185307179586 1.7763568394002505e-15', + '15.707963267948964 6.283185307179586 3.1415926535897913', + '15.707963267948966 6.283185307179586 3.141592653589793', + '15.707963267948967 6.283185307179586 -3.1415926535897913', + '34.55751918948772 6.283185307179586 3.1415926535897896', + '34.55751918948773 6.283185307179586 -3.1415926535897896', + # Symmetry with respect to signs. + '1.0 0.75 0.25', + '-1.0 0.75 -0.25', + '1.0 -0.75 0.25', + '-1.0 -0.75 -0.25', + '1.25 0.75 -0.25', + '-1.25 0.75 0.25', + '1.25 -0.75 -0.25', + '-1.25 -0.75 0.25', + # Huge modulus, to check that the underlying algorithm doesn't + # rely on 2.0 * modulus being representable. + '1.6291594034689738e+308 1.1235582092889474e+308 5.056011941800263e+307', + '1.6853373139334212e+308 1.1235582092889474e+308 -5.617791046444737e+307', + '1.7415152243978685e+308 1.1235582092889474e+308 -5.056011941800263e+307' + ] + # testcases = [ + # # Remainders modulo 1, showing the ties-to-even behaviour. + # '-4.0 1 -0.0', + # '-3.8 1 0.8', + # '-3.0 1 -0.0', + # '-2.8 1 -0.8', + # '-2.0 1 -0.0', + # '-1.8 1 0.8', + # '-1.0 1 -0.0', + # '-0.8 1 -0.8', + # '-0.0 1 -0.0', + # ' 0.0 1 0.0', + # ' 0.8 1 0.8', + # ' 1.0 1 0.0', + # ' 1.8 1 -0.8', + # ' 2.0 1 0.0', + # ' 2.8 1 0.8', + # ' 3.0 1 0.0', + # ' 3.8 1 -0.8', + # ' 4.0 1 0.0', + + # # Reductions modulo 2*pi + # '0x0.0p+0 0x1.921fb54442d18p+2 0x0.0p+0', + # '0x1.921fb54442d18p+0 0x1.921fb54442d18p+2 0x1.921fb54442d18p+0', + # '0x1.921fb54442d17p+1 0x1.921fb54442d18p+2 0x1.921fb54442d17p+1', + # '0x1.921fb54442d18p+1 0x1.921fb54442d18p+2 0x1.921fb54442d18p+1', + # '0x1.921fb54442d19p+1 0x1.921fb54442d18p+2 -0x1.921fb54442d17p+1', + # '0x1.921fb54442d17p+2 0x1.921fb54442d18p+2 -0x0.0000000000001p+2', + # '0x1.921fb54442d18p+2 0x1.921fb54442d18p+2 0x0p0', + # '0x1.921fb54442d19p+2 0x1.921fb54442d18p+2 0x0.0000000000001p+2', + # '0x1.2d97c7f3321d1p+3 0x1.921fb54442d18p+2 0x1.921fb54442d14p+1', + # '0x1.2d97c7f3321d2p+3 0x1.921fb54442d18p+2 -0x1.921fb54442d18p+1', + # '0x1.2d97c7f3321d3p+3 0x1.921fb54442d18p+2 -0x1.921fb54442d14p+1', + # '0x1.921fb54442d17p+3 0x1.921fb54442d18p+2 -0x0.0000000000001p+3', + # '0x1.921fb54442d18p+3 0x1.921fb54442d18p+2 0x0p0', + # '0x1.921fb54442d19p+3 0x1.921fb54442d18p+2 0x0.0000000000001p+3', + # '0x1.f6a7a2955385dp+3 0x1.921fb54442d18p+2 0x1.921fb54442d14p+1', + # '0x1.f6a7a2955385ep+3 0x1.921fb54442d18p+2 0x1.921fb54442d18p+1', + # '0x1.f6a7a2955385fp+3 0x1.921fb54442d18p+2 -0x1.921fb54442d14p+1', + # '0x1.1475cc9eedf00p+5 0x1.921fb54442d18p+2 0x1.921fb54442d10p+1', + # '0x1.1475cc9eedf01p+5 0x1.921fb54442d18p+2 -0x1.921fb54442d10p+1', + + # # Symmetry with respect to signs. + # ' 1 0.c 0.4', + # '-1 0.c -0.4', + # ' 1 -0.c 0.4', + # '-1 -0.c -0.4', + # ' 1.4 0.c -0.4', + # '-1.4 0.c 0.4', + # ' 1.4 -0.c -0.4', + # '-1.4 -0.c 0.4', + + # # Huge modulus, to check that the underlying algorithm doesn't + # # rely on 2.0 * modulus being representable. + # '0x1.dp+1023 0x1.4p+1023 0x0.9p+1023', + # '0x1.ep+1023 0x1.4p+1023 -0x0.ap+1023', + # '0x1.fp+1023 0x1.4p+1023 -0x0.9p+1023', + # ] + + for case in testcases: + # with self.subTest(case=case): + x, y, expected = case.split() + x = float(x) + y = float(y) + expected = float(expected) + # # validate_spec(x, y, expected) + actual = math.remainder(x, y) + # Cheap way of checking that the floats are + # as identical as we need them to be. + self.assertEqual(actual, expected) + + # Test tiny subnormal modulus: there's potential for + # getting the implementation wrong here (for example, + # by assuming that modulus/2 is exactly representable). + tiny_testcases = [ + '0.0 -1.24e-322 0.0', + '7.4e-323 -1.24e-322 -5e-323', + '1.5e-322 -1.24e-322 2.5e-323', + '2.2e-322 -1.24e-322 -2.5e-323', + '2.96e-322 -1.24e-322 5e-323', + '3.7e-322 -1.24e-322 0.0', + '4.45e-322 -1.24e-322 -5e-323', + '0.0 -1e-322 0.0', + '7.4e-323 -1e-322 -2.5e-323', + '1.5e-322 -1e-322 -5e-323', + '2.2e-322 -1e-322 2.5e-323', + '2.96e-322 -1e-322 0.0', + '3.7e-322 -1e-322 -2.5e-323', + '4.45e-322 -1e-322 5e-323', + '0.0 -7.4e-323 0.0', + '7.4e-323 -7.4e-323 0.0', + '1.5e-322 -7.4e-323 0.0', + '2.2e-322 -7.4e-323 0.0', + '2.96e-322 -7.4e-323 0.0', + '3.7e-322 -7.4e-323 0.0', + '4.45e-322 -7.4e-323 0.0', + '0.0 -5e-323 0.0', + '7.4e-323 -5e-323 -2.5e-323', + '1.5e-322 -5e-323 0.0', + '2.2e-322 -5e-323 2.5e-323', + '2.96e-322 -5e-323 0.0', + '3.7e-322 -5e-323 -2.5e-323', + '4.45e-322 -5e-323 0.0', + '0.0 -2.5e-323 0.0', + '7.4e-323 -2.5e-323 0.0', + '1.5e-322 -2.5e-323 0.0', + '2.2e-322 -2.5e-323 0.0', + '2.96e-322 -2.5e-323 0.0', + '3.7e-322 -2.5e-323 0.0', + '4.45e-322 -2.5e-323 0.0', + '0.0 2.5e-323 0.0', + '7.4e-323 2.5e-323 0.0', + '1.5e-322 2.5e-323 0.0', + '2.2e-322 2.5e-323 0.0', + '2.96e-322 2.5e-323 0.0', + '3.7e-322 2.5e-323 0.0', + '4.45e-322 2.5e-323 0.0', + '0.0 5e-323 0.0', + '7.4e-323 5e-323 -2.5e-323', + '1.5e-322 5e-323 0.0', + '2.2e-322 5e-323 2.5e-323', + '2.96e-322 5e-323 0.0', + '3.7e-322 5e-323 -2.5e-323', + '4.45e-322 5e-323 0.0', + '0.0 7.4e-323 0.0', + '7.4e-323 7.4e-323 0.0', + '1.5e-322 7.4e-323 0.0', + '2.2e-322 7.4e-323 0.0', + '2.96e-322 7.4e-323 0.0', + '3.7e-322 7.4e-323 0.0', + '4.45e-322 7.4e-323 0.0', + '0.0 1e-322 0.0', + '7.4e-323 1e-322 -2.5e-323', + '1.5e-322 1e-322 -5e-323', + '2.2e-322 1e-322 2.5e-323', + '2.96e-322 1e-322 0.0', + '3.7e-322 1e-322 -2.5e-323', + '4.45e-322 1e-322 5e-323'] + for case in tiny_testcases: + # with self.subTest(case=case): + x, y, expected = case.split() + x = float(x) + y = float(y) + expected = float(expected) + neg_expected = -expected + # # validate_spec(x, y, expected) + actual = math.remainder(x, y) + neg_actual = math.remainder(-x,y) + + self.assertEqual(actual, expected) + self.assertEqual(neg_actual, neg_expected) + # tiny = float('5e-324') # min +ve subnormal + # for n in range(-25, 25): + # if n == 0: + # continue + # y = n * tiny + # for m in range(100): + # x = m * tiny + # actual = math.remainder(x, y) + # # validate_spec(x, y, actual) + # actual = math.remainder(-x, y) + # # validate_spec(-x, y, actual) + + # Special values. + # NaNs should propagate as usual. + for value in [NAN, 0.0, -0.0, 2.0, -2.3, NINF, INF]: + self.assertTrue(math.isnan(math.remainder(NAN, value))) + self.assertTrue(math.isnan(math.remainder(value, NAN))) + + # remainder(x, inf) is x, for non-nan non-infinite x. + for value in [-2.3, -0.0, 0.0, 2.3]: + self.assertEqual(math.remainder(value, INF), value) + self.assertEqual(math.remainder(value, NINF), value) + + # remainder(x, 0) and remainder(infinity, x) for non-NaN x are invalid + # operations according to IEEE 754-2008 7.2(f), and should raise. + for value in [NINF, -2.3, -0.0, 0.0, 2.3, INF]: + with self.assertRaises(ValueError): + math.remainder(INF, value) + with self.assertRaises(ValueError): + math.remainder(NINF, value) + with self.assertRaises(ValueError): + math.remainder(value, 0.0) + with self.assertRaises(ValueError): + math.remainder(value, -0.0) + def testSin(self): self.assertRaises(TypeError, math.sin) @@ -889,6 +1351,25 @@ def testSin(self): self.assertRaises(ValueError, math.sin, INF) self.assertRaises(ValueError, math.sin, NINF) self.assertTrue(math.isnan(math.sin(NAN))) + self.assertRaises(TypeError, math.sin, "3") + + def differentiate(f, method, h=1.0E-5): + if method == 'Forward1': + def Forward1(x): + return (f(x+h) -f(x)) / h + return Forward1 + elif method == 'Backward1': + def Backward1(x): + return (f(x) -f(x-h)) / h + return Backward1 + + mycos = differentiate(math.sin, 'Forward1') + mysin = differentiate(mycos, 'Backward1', 1.0E-6) + x = math.pi + self.assertEqual(mycos(x), -0.9999999999898844) + self.assertEqual(math.cos(x), -1.0) + self.assertEqual(mysin(x), 4.500066985713147e-06) + self.assertEqual(-math.sin(x), -1.2246467991473532e-16) def testSinh(self): self.assertRaises(TypeError, math.sinh) @@ -898,6 +1379,9 @@ def testSinh(self): self.assertEqual(math.sinh(INF), INF) self.assertEqual(math.sinh(NINF), NINF) self.assertTrue(math.isnan(math.sinh(NAN))) + self.assertEqual(math.sinh(2), 3.6268604078470186) + self.assertAlmostEqual(math.sinh(-7.5), -904.0209306858466, 11) + self.assertEqual(math.sinh(0), 0.0) def testSqrt(self): self.assertRaises(TypeError, math.sqrt) @@ -927,9 +1411,12 @@ def testTanh(self): self.ftest('tanh(0)', math.tanh(0), 0) self.ftest('tanh(1)+tanh(-1)', math.tanh(1)+math.tanh(-1), 0, abs_tol=ulp(1)) - # self.ftest('tanh(inf)', math.tanh(INF), 1) - # self.ftest('tanh(-inf)', math.tanh(NINF), -1) + self.ftest('tanh(inf)', math.tanh(INF), 1) + self.ftest('tanh(-inf)', math.tanh(NINF), -1) self.assertTrue(math.isnan(math.tanh(NAN))) + self.assertAlmostEqual(math.tanh(2), 0.9640275800758169, 15) + self.assertAlmostEqual(math.tanh(-7.5), -0.9999993881955461, 15) + self.assertEqual(math.tanh(0), 0.0) def testTanhSign(self): # check that tanh(-0.) == -0. on IEEE 754 systems @@ -948,6 +1435,7 @@ def test_trunc(self): self.assertEqual(math.trunc(-1.999999), -1) self.assertEqual(math.trunc(-0.999999), -0) self.assertEqual(math.trunc(-100.999), -100) + self.assertEqual(math.trunc(2**2048), 2**2048) class TestTrunc(object): def __trunc__(self): @@ -962,14 +1450,16 @@ class TestNoTrunc(object): self.assertRaises(TypeError, math.trunc, 1, 2) self.assertRaises(TypeError, math.trunc, TestNoTrunc()) - # def testIsfinite(self): - # self.assertTrue(math.isfinite(0.0)) - # self.assertTrue(math.isfinite(-0.0)) - # self.assertTrue(math.isfinite(1.0)) - # self.assertTrue(math.isfinite(-1.0)) - # self.assertFalse(math.isfinite(float("nan"))) - # self.assertFalse(math.isfinite(float("inf"))) - # self.assertFalse(math.isfinite(float("-inf"))) + def testIsfinite(self): + self.assertTrue(math.isfinite(0.0)) + self.assertTrue(math.isfinite(-0.0)) + self.assertTrue(math.isfinite(1.0)) + self.assertTrue(math.isfinite(-1.0)) + self.assertFalse(math.isfinite(float("nan"))) + self.assertFalse(math.isfinite(float("inf"))) + self.assertFalse(math.isfinite(float("-inf"))) + self.assertTrue(math.isfinite(2**1024)) + self.assertTrue(math.isfinite(2**2048)) def testIsnan(self): self.assertTrue(math.isnan(float("nan"))) @@ -984,18 +1474,21 @@ def testIsinf(self): self.assertTrue(math.isinf(float("-inf"))) self.assertTrue(math.isinf(1E400)) self.assertTrue(math.isinf(-1E400)) - # self.assertFalse(math.isinf(float("nan"))) + self.assertFalse(math.isinf(float("nan"))) self.assertFalse(math.isinf(0.)) self.assertFalse(math.isinf(1.)) + self.assertFalse(math.isinf(2**1024)) + self.assertFalse(math.isinf(2**2048)) - # def test_nan_constant(self): - # self.assertTrue(math.isnan(math.nan)) + def test_nan_constant(self): + self.assertTrue(math.isnan(math.nan)) + + def test_inf_constant(self): + self.assertTrue(math.isinf(math.inf)) + self.assertGreater(math.inf, 0.0) + self.assertEqual(math.inf, float("inf")) + self.assertEqual(-math.inf, float("-inf")) - # def test_inf_constant(self): - # self.assertTrue(math.isinf(math.inf)) - # self.assertGreater(math.inf, 0.0) - # self.assertEqual(math.inf, float("inf")) - # self.assertEqual(-math.inf, float("-inf")) # RED_FLAG 16-Oct-2000 Tim # While 2.0 is more consistent about exceptions than previous releases, it @@ -1033,6 +1526,130 @@ def test_exceptions(self): # else: # self.fail("sqrt(-1) didn't raise ValueError") +class IsCloseTests(unittest.TestCase): + isclose = math.isclose # subclasses should override this + # def __init__(self): + # self.isclose = math.isclose + + def assertIsClose(self, a, b, **kwargs): + self.assertTrue(math.isclose(a, b, **kwargs)) + + def assertIsNotClose(self, a, b, **kwargs): + self.assertFalse(math.isclose(a, b, **kwargs)) + + def assertAllClose(self, examples, *args, **kwargs): + for a, b in examples: + self.assertIsClose(a, b, *args, **kwargs) + + def assertAllNotClose(self, examples, *args, **kwargs): + for a, b in examples: + self.assertIsNotClose(a, b, *args, **kwargs) + + def test_negative_tolerances(self): + # ValueError should be raised if either tolerance is less than zero + def negative_is_close(rel_tol=1e-100, abs_tol=1e10): + math.isclose(1,1,rel_tol=rel_tol,abs_tol=abs_tol) + + self.assertRaises(ValueError, negative_is_close, -1e-100) + self.assertRaises(ValueError, negative_is_close,1e-100,-1e-100 ) + + def test_identical(self): + # identical values must test as close + identical_examples = [(2.0, 2.0), + (0.1e200, 0.1e200), + (1.123e-300, 1.123e-300), + (12345, 12345.0), + (0.0, -0.0), + (345678, 345678)] + self.assertAllClose(identical_examples, rel_tol=0.0, abs_tol=0.0) + + def test_eight_decimal_places(self): + # examples that are close to 1e-8, but not 1e-9 + eight_decimal_places_examples = [(1e8, 1e8 + 1), + (-1e-8, -1.000000009e-8), + (1.12345678, 1.12345679)] + self.assertAllClose(eight_decimal_places_examples, rel_tol=1e-8) + self.assertAllNotClose(eight_decimal_places_examples, rel_tol=1e-9) + + def test_near_zero(self): + # values close to zero + near_zero_examples = [(1e-9, 0.0), + (-1e-9, 0.0), + (-1e-150, 0.0)] + # these should not be close to any rel_tol + self.assertAllNotClose(near_zero_examples, rel_tol=0.9) + # these should be close to abs_tol=1e-8 + self.assertAllClose(near_zero_examples, abs_tol=1e-8) + + def test_identical_infinite(self): + # these are close regardless of tolerance -- i.e. they are equal + self.assertIsClose(INF, INF) + self.assertIsClose(INF, INF, abs_tol=0.0) + self.assertIsClose(NINF, NINF) + self.assertIsClose(NINF, NINF, abs_tol=0.0) + + def test_inf_ninf_nan(self): + # these should never be close (following IEEE 754 rules for equality) + not_close_examples = [(NAN, NAN), + (NAN, 1e-100), + (1e-100, NAN), + (INF, NAN), + (NAN, INF), + (INF, NINF), + (INF, 1.0), + (1.0, INF), + (INF, 1e308), + (1e308, INF)] + # use largest reasonable tolerance + self.assertAllNotClose(not_close_examples, abs_tol=0.999999999999999) + + def test_zero_tolerance(self): + # test with zero tolerance + zero_tolerance_close_examples = [(1.0, 1.0), + (-3.4, -3.4), + (-1e-300, -1e-300)] + self.assertAllClose(zero_tolerance_close_examples, rel_tol=0.0) + + zero_tolerance_not_close_examples = [(1.0, 1.000000000000001), + (0.99999999999999, 1.0), + (1.0e200, .999999999999999e200)] + self.assertAllNotClose(zero_tolerance_not_close_examples, rel_tol=0.0) + + def test_asymmetry(self): + # test the asymmetry example from PEP 485 + self.assertAllClose([(9, 10), (10, 9)], rel_tol=0.1) + + def test_integers(self): + # test with integer values + integer_examples = [(100000001, 100000000), + (123456789, 123456788)] + + self.assertAllClose(integer_examples, rel_tol=1e-8) + self.assertAllNotClose(integer_examples, rel_tol=1e-9) + + # def test_decimals(self): + # # test with Decimal values + # from decimal import Decimal + + # decimal_examples = [(Decimal('1.00000001'), Decimal('1.0')), + # (Decimal('1.00000001e-20'), Decimal('1.0e-20')), + # (Decimal('1.00000001e-100'), Decimal('1.0e-100')), + # (Decimal('1.00000001e20'), Decimal('1.0e20'))] + # self.assertAllClose(decimal_examples, rel_tol=1e-8) + # self.assertAllNotClose(decimal_examples, rel_tol=1e-9) + + # def test_fractions(self): + # # test with Fraction values + # from fractions import Fraction + + # fraction_examples = [ + # (Fraction(1, 100000000) + 1, Fraction(1)), + # (Fraction(100000001), Fraction(100000000)), + # (Fraction(10**8 + 1, 10**28), Fraction(1, 10**20))] + # self.assertAllClose(fraction_examples, rel_tol=1e-8) + # self.assertAllNotClose(fraction_examples, rel_tol=1e-9) + + if __name__ == '__main__': unittest.main() diff --git a/test/unit3/test_matmul.py b/test/unit3/test_matmul.py new file mode 100644 index 0000000000..dfb715957e --- /dev/null +++ b/test/unit3/test_matmul.py @@ -0,0 +1,113 @@ +"""Unit testing for matrix multiplication operator, @""" +import unittest + +class MatMulTests(unittest.TestCase): + def test_matmul(self): + class A: + def __init__(self,x): + self.x = x + def __matmul__(self,other): + return self.x*(other.x) + a = A(2) + b = A(3) + c = a @ b + self.assertEqual(c, 6) + self.assertEqual((a.x, b.x), (2,3)) + self.assertEqual(a.__matmul__(b), 6) + #self.assertEqual(repr(a.__matmul__), '>') + class B: + def __init__(self,x): + self.x = x + a = B(2) + b = B(3) + self.assertRaises(TypeError, lambda x, y: x @ y, a, b) + self.assertRaises(AttributeError, lambda x: x.__matmul__, a) + + def test_imatmul(self): + class A: + def __init__(self,x): + self.x = x + def __imatmul__(self,other): + return A(self.x * other.x) + a = A(2) + b = A(3) + a @= b + self.assertEqual(a.x, 6) + self.assertEqual(a.__imatmul__(b).x, 18) + class B: + def __init__(self,x): + self.x = x + a = B(2) + b = B(3) + def foo(x, y): + x @= y + return x + self.assertRaises(TypeError, foo, a, b) + self.assertRaises(AttributeError, lambda x: x.__imatmul__, a) + class C: + def __init__(self,x): + self.x = x + def __matmult__(self, other): + return self.x * other.x + def __imatmul__(self,other): + return C(self.x * other.x) + a = C(2) + b = C(3) + a @= b + self.assertEqual(a.x, 6) + self.assertEqual(type(a), C) + self.assertEqual(a.__imatmul__(b).x, 18) + class D: + def __init__(self,x): + self.x = x + def __matmul__(self, other): + return self.x * other.x + a = D(2) + b = D(3) + a @= b + self.assertEqual(a, 6) + self.assertEqual(type(a), int) + self.assertRaises(AttributeError, lambda x: x.__imatmul__, a) + + def test_rmatmul(self): + class A: + def __init__(self,x): + self.x = x + class B: + def __init__(self, x): + self.x = x + def __rmatmul__(self, other): + return self.x * other.x + a = A(2) + b = B(3) + c = a @ b + self.assertEqual(c, 6) + def foo(x, y): + z = x @ y + return z + self.assertRaises(TypeError, foo, b, a) + self.assertRaises(AttributeError, lambda x: x.__rmatmul__, a) + class C: + def __init__(self,x): + self.x = x + def __matmul__(self,other): + return self.x*(other.x) + def __rmatmul__(self, other): + return 0 + class D: + def __init__(self, x): + self.x = x + def __matmul__(self, other): + return -1 + def __rmatmul__(self, other): + return -2 + a = C(2) + b = D(3) + c = a @ b + self.assertEqual(c, 6) + d = b @ a + self.assertEqual(d, -1) + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_none.py b/test/unit3/test_none.py new file mode 100644 index 0000000000..014893a866 --- /dev/null +++ b/test/unit3/test_none.py @@ -0,0 +1,57 @@ +"""Unit testing for None """ +import unittest + +class NoneTests(unittest.TestCase): + def test_basic(self): + self.assertEqual(str(None), 'None') + self.assertEqual(repr(None), 'None') + self.assertEqual(str(type(None)), "") + self.assertTrue(isinstance(None, type(None))) + flag = False + if None: + flag = True + self.assertFalse(flag) + + def test_compare(self): + self.assertFalse(0 == None) + self.assertFalse(0.0 == None) + self.assertFalse(None == 0) + self.assertFalse(None == 0.0) + self.assertTrue(0 != None) + self.assertTrue(0.0 != None) + self.assertTrue(None != 0) + self.assertTrue(None != 0.0) + + def test_in_list(self): + self.assertFalse(None in [1,2,3]) + self.assertTrue(None in [1,2,3,None]) + self.assertTrue(None not in [1,2,3]) + self.assertFalse(None not in [1,2,3,None]) + + def test_in_dict(self): + self.assertFalse(None in {1:2, 3:4}) + self.assertTrue(None in {1:2, 3:4, None:5}) + self.assertTrue(None not in {1:2, 3:4}) + self.assertFalse(None not in {1:2, 3:4, None:5}) + + def test_is_isnot(self): + self.assertTrue(None is None) + self.assertFalse(None is not None) + self.assertFalse(None is 3) + self.assertFalse(3 is None) + self.assertTrue(None is not 3) + self.assertTrue(3 is not None) + self.assertTrue(None == None) + + def test_errors(self): + #Skulpt fails the following 3 tests, it should pass them though + #self.assertRaises(TypeError, lambda x: x > 3, None) + #self.assertRaises(TypeError, lambda x: x > None, None) + #self.assertRaises(TypeError, abs, None) + self.assertRaises(TypeError, chr, None) + self.assertRaises(TypeError, int, None) + self.assertRaises(TypeError, float, None) + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_operator.py b/test/unit3/test_operator.py new file mode 100644 index 0000000000..43ed4c3d27 --- /dev/null +++ b/test/unit3/test_operator.py @@ -0,0 +1,183 @@ +"""Unit Test for operator module""" +import unittest +import operator + +class OperatorTests(unittest.TestCase): + def test_compare(self): + self.assertTrue(operator.lt(1, 2)) + self.assertFalse(operator.lt(2, 1)) + self.assertTrue(operator.le(1, 2)) + self.assertFalse(operator.le(2, 1)) + self.assertTrue(operator.le(2, 2)) + self.assertTrue(operator.eq(2, 2)) + self.assertFalse(operator.eq(3, 2)) + self.assertTrue(operator.ne(2, 3)) + self.assertFalse(operator.ne(2, 2)) + self.assertTrue(operator.ge(2, 1)) + self.assertFalse(operator.ge(1, 2)) + self.assertTrue(operator.ge(2, 2)) + self.assertTrue(operator.gt(2, 1)) + self.assertFalse(operator.gt(1, 2)) + + def test_truth(self): + self.assertTrue(operator.truth(True)) + self.assertFalse(operator.truth(False)) + self.assertTrue(operator.truth(1)) + self.assertFalse(operator.truth(0)) + + def test_is_(self): + self.assertTrue(operator.is_("hello", "hello")) + self.assertFalse(operator.is_("hello", "goodbye")) + self.assertTrue(operator.is_(1, 1)) + self.assertFalse(operator.is_(2, 1)) + + def test_is_not(self): + self.assertTrue(operator.is_not("hello", "goodbye")) + self.assertFalse(operator.is_not("hello", "hello")) + self.assertTrue(operator.is_not(1, 2)) + self.assertFalse(operator.is_not(1, 1)) + + def test_abs(self): + self.assertEqual(operator.abs(5), 5) + self.assertEqual(operator.abs(-5), 5) + self.assertEqual(operator.abs(1.1), 1.1) + self.assertEqual(operator.abs(-1.1), 1.1) + + def test_add(self): + self.assertEqual(operator.add(1, 2), 3) + self.assertEqual(operator.add(-4, 2), -2) + self.assertEqual(operator.add("he", "llo"), "hello") + + def test_and_(self): + self.assertEqual(operator.and_(2, 3), 2) + self.assertEqual(operator.and_(5, 3), 1) + self.assertEqual(operator.and_(-4, 3), 0) + + def test_floordiv(self): + self.assertEqual(operator.floordiv(10, 5), 2) + self.assertEqual(operator.floordiv(5, 2), 2) + self.assertEqual(operator.floordiv(2.2, 2), 1.0) + self.assertEqual(operator.floordiv(-5.0, 2), -3.0) + + def test_lshift(self): + self.assertEqual(operator.lshift(5, 2), 20) + self.assertEqual(operator.lshift(-5, 3), -40) + + def test_mod(self): + self.assertEqual(operator.mod(10, 5), 0) + self.assertEqual(operator.mod(10, 3), 1) + self.assertEqual(operator.mod(15, 4), 3) + + def test_mul(self): + self.assertEqual(operator.mul(2, 1), 2) + self.assertEqual(operator.mul(-2, 1), -2) + self.assertEqual(operator.mul(2, -1), -2) + self.assertEqual(operator.mul(10, 20), 200) + + def test_neg(self): + self.assertEqual(operator.neg(-5), 5) + self.assertEqual(operator.neg(5), -5) + self.assertEqual(operator.neg(True), -1) + self.assertEqual(operator.neg(False), 0) + + def test_or_(self): + self.assertEqual(operator.or_(1, 2), 3) + self.assertEqual(operator.or_(4, 3), 7) + self.assertEqual(operator.or_(5, 2), 7) + + def test_pos(self): + self.assertEqual(operator.pos(5), 5) + self.assertEqual(operator.pos(-5), -5) + self.assertEqual(operator.pos(True), 1) + self.assertEqual(operator.pos(False), 0) + + def test_pow(self): + self.assertEqual(operator.pow(2, 2), 4) + self.assertEqual(operator.pow(5, 3), 125) + + def test_rshift(self): + self.assertEqual(operator.rshift(5, 2), 1) + self.assertEqual(operator.rshift(-5, 3), -1) + + def test_sub(self): + self.assertEqual(operator.sub(4, 2), 2) + self.assertEqual(operator.sub(2, 4), -2) + self.assertEqual(operator.sub(-4, 2), -6) + + def test_xor(self): + self.assertEqual(operator.xor(4, 2), 6) + self.assertEqual(operator.xor(8, 5), 13) + + def test_concat(self): + self.assertEqual(operator.concat("he", "llo"), "hello") + self.assertEqual(operator.concat([1,2,3,4], [5,6,7]), [1, 2, 3, 4, 5, 6, 7]) + self.assertEqual(operator.concat((1,2), (3,4)), (1, 2, 3, 4)) + + def test_contains(self): + l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9] + s = "hello world" + t = ("a", "b", "c") + d = {1:1, 2:2, 3:3, 4:4, 5:5} + self.assertTrue(operator.contains(l, 2)) + self.assertFalse(operator.contains(l, 30)) + self.assertTrue(operator.contains(s, "ll")) + self.assertFalse(operator.contains(s, "z")) + self.assertTrue(operator.contains(t, "a")) + self.assertFalse(operator.contains(t, 2)) + self.assertTrue(operator.contains(d, 3)) + self.assertFalse(operator.contains(d, 0)) + + def test_countOf(self): + l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9] + s = "hello world" + t = ("a", "b", "c") + d = {1:1, 2:2, 3:3, 4:4, 5:5} + self.assertEqual(operator.countOf(l, 9), 4) + self.assertEqual(operator.countOf(l, 30), 0) + self.assertEqual(operator.countOf(s, "l"), 3) + self.assertEqual(operator.countOf(t, "a"), 1) + + def test_delitem(self): + l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9] + s = "hello world" + t = ("a", "b", "c") + d = {1:1, 2:2, 3:3, 4:4, 5:5} + operator.delitem(l, 9) + self.assertEqual(l, [1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9]) + operator.delitem(l, 0) + self.assertEqual(l, [2, 3, 4, 5, 6, 7, 8, 9, 9, 9]) + + def test_getitem(self): + l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9] + s = "hello world" + t = ("a", "b", "c") + d = {1:1, 2:2, 3:3, 4:4, 5:5} + self.assertEqual(operator.getitem(l, 2), 3) + self.assertEqual(operator.getitem(s, 0), "h") + self.assertEqual(operator.getitem(t, 1), "b") + self.assertEqual(operator.getitem(d, 4), 4) + + def test_indexOf(self): + l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9] + s = "hello world" + t = ("a", "b", "c") + d = {1:1, 2:2, 3:3, 4:4, 5:5} + self.assertEqual(operator.indexOf(l, 5), 4) + self.assertEqual(operator.indexOf(s, "l"), 2) + self.assertEqual(operator.indexOf(t, "a"), 0) + + def test_setitem(self): + l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9] + s = "hello world" + t = ("a", "b", "c") + d = {1:1, 2:2, 3:3, 4:4, 5:5} + operator.setitem(l, 0, 10) + self.assertEqual(l, [10, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9]) + operator.setitem(d, 1, 10) + self.assertEqual(d, {1: 10, 2: 2, 3: 3, 4: 4, 5: 5}) + operator.setitem(d, 6, 6) + self.assertEqual(d, {1: 10, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6}) + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_range.py b/test/unit3/test_range.py index 2e66b84138..0ea5965e6a 100644 --- a/test/unit3/test_range.py +++ b/test/unit3/test_range.py @@ -83,12 +83,14 @@ def test_range(self): self.assertRaises(TypeError, range, 0, "spam") self.assertRaises(TypeError, range, 0, 42, "spam") + self.assertRaises(TypeError, range, "2", 4) + self.assertEqual(len(range(0, sys.maxsize, sys.maxsize-1)), 2) def test_large_operands(self): - # x = range(10**20, 10**20+10, 3) - # self.assertEqual(len(x), 4) - # self.assertEqual(len(list(x)), 4) + x = range(10**20, 10**20+10, 3) + self.assertEqual(len(x), 4) + self.assertEqual(len(list(x)), 4) x = range(10**20+10, 10**20, 3) self.assertEqual(len(x), 0) @@ -98,9 +100,9 @@ def test_large_operands(self): self.assertEqual(len(x), 0) self.assertEqual(len(list(x)), 0) - # x = range(10**20+10, 10**20, -3) - # self.assertEqual(len(x), 4) - # self.assertEqual(len(list(x)), 4) + x = range(10**20+10, 10**20, -3) + self.assertEqual(len(x), 4) + self.assertEqual(len(list(x)), 4) # Now test range() with longs self.assertEqual(list(range(-2**100)), []) @@ -112,30 +114,30 @@ def test_large_operands(self): b = int(100 * sys.maxsize) c = int(50 * sys.maxsize) - # self.assertEqual(list(range(a, a+2)), [a, a+1]) - # self.assertEqual(list(range(a+2, a, -1)), [a+2, a+1]) - # self.assertEqual(list(range(a+4, a, -2)), [a+4, a+2]) + self.assertEqual(list(range(a, a+2)), [a, a+1]) + self.assertEqual(list(range(a+2, a, -1)), [a+2, a+1]) + self.assertEqual(list(range(a+4, a, -2)), [a+4, a+2]) - # seq = list(range(a, b, c)) - # self.assertIn(a, seq) - # self.assertNotIn(b, seq) - # self.assertEqual(len(seq), 2) - # self.assertEqual(seq[0], a) - # self.assertEqual(seq[-1], a+c) + seq = list(range(a, b, c)) + self.assertIn(a, seq) + self.assertNotIn(b, seq) + self.assertEqual(len(seq), 2) + self.assertEqual(seq[0], a) + self.assertEqual(seq[-1], a+c) - # seq = list(range(b, a, -c)) - # self.assertIn(b, seq) - # self.assertNotIn(a, seq) - # self.assertEqual(len(seq), 2) - # self.assertEqual(seq[0], b) - # self.assertEqual(seq[-1], b-c) + seq = list(range(b, a, -c)) + self.assertIn(b, seq) + self.assertNotIn(a, seq) + self.assertEqual(len(seq), 2) + self.assertEqual(seq[0], b) + self.assertEqual(seq[-1], b-c) seq = list(range(-a, -b, -c)) - # self.assertIn(-a, seq) + self.assertIn(-a, seq) self.assertNotIn(-b, seq) - # self.assertEqual(len(seq), 2) - # self.assertEqual(seq[0], -a) - # self.assertEqual(seq[-1], -a-c) + self.assertEqual(len(seq), 2) + self.assertEqual(seq[0], -a) + self.assertEqual(seq[-1], -a-c) def test_large_range(self): # Check long ranges (len > sys.maxsize) @@ -218,14 +220,14 @@ def test_index(self): class BadExc(Exception): pass - # class BadCmp: - # def __eq__(self, other): - # if other == 2: - # raise BadExc() - # return False + class BadCmp: + def __eq__(self, other): + if other == 2: + raise BadExc() + return False - # a = range(4) - # self.assertRaises(BadExc, a.index, BadCmp()) + a = range(4) + self.assertRaises(BadExc, a.index, BadCmp()) a = range(-2, 3) self.assertEqual(a.index(0), 2) @@ -254,14 +256,14 @@ def __init__(self, n): self.n = int(n) def __index__(self): return self.n - # self.assertEqual(list(range(I(bignum), I(bignum + 1))), [bignum]) - # self.assertEqual(list(range(I(smallnum), I(smallnum + 1))), [smallnum]) + self.assertEqual(list(range(I(bignum), I(bignum + 1))), [bignum]) + self.assertEqual(list(range(I(smallnum), I(smallnum + 1))), [smallnum]) # User-defined class with a failing __index__ method class IX: def __index__(self): raise RuntimeError - # self.assertRaises(RuntimeError, range, IX()) + self.assertRaises(RuntimeError, range, IX()) # User-defined class with an invalid __index__ method class IN: @@ -284,10 +286,10 @@ def __eq__(self, other): # self.assertEqual(len(range(sys.maxsize, sys.maxsize+10)), 10) - # def test_repr(self): - # self.assertEqual(repr(range(1)), 'range(0, 1)') - # self.assertEqual(repr(range(1, 2)), 'range(1, 2)') - # self.assertEqual(repr(range(1, 2, 3)), 'range(1, 2, 3)') + def test_repr(self): + self.assertEqual(repr(range(1)), 'range(0, 1)') + self.assertEqual(repr(range(1, 2)), 'range(1, 2)') + self.assertEqual(repr(range(1, 2, 3)), 'range(1, 2, 3)') def test_odd_bug(self): # This used to raise a "SystemError: NULL result without error" @@ -318,7 +320,7 @@ def __index__(self): return 1 # used for ints, not for instances of subclasses of int. class C3(int): def __eq__(self, other): return True - # self.assertIn(C3(11), range(10)) + self.assertIn(C3(11), range(10)) # self.assertIn(C3(11), list(range(10))) def test_strided_limits(self): @@ -375,25 +377,65 @@ def test_empty(self): # test_id = "reversed(range({}, {}, {}))".format(start, end, step) # self.assert_iterators_equal(iter1, iter2, test_id, limit=100) - # def test_slice(self): - # def check(start, stop, step=None): - # i = slice(start, stop, step) - # self.assertEqual(list(r[i]), list(r)[i]) - # self.assertEqual(len(r[i]), len(list(r)[i])) - # for r in [range(10), - # range(0), - # range(1, 9, 3), - # range(8, 0, -3), - # range(sys.maxsize+1, sys.maxsize+10), - # ]: - # check(0, 2) - # check(0, 20) - # check(1, 2) - # check(20, 30) - # check(-30, -20) - # check(-1, 100, 2) - # check(0, -1) - # check(-1, -3, -1) + def test_slice1(self): + def check(start, stop, step=None): + i = slice(start, stop, step) + self.assertEqual(list(r[i]), list(r)[i]) + self.assertEqual(len(r[i]), len(list(r)[i])) + for r in [range(10), + range(0), + range(1, 9, 3), + range(8, 0, -3), + range(sys.maxsize+1, sys.maxsize+10), + ]: + check(0, 2) + check(0, 20) + check(1, 2) + check(20, 30) + check(-30, -20) + check(-1, 100, 2) + check(0, -1) + check(-1, -3, -1) + + def test_slice2(self): + self.assertEqual(slice(1), slice(None, 1, None)) + a = range(10)[slice(0, 5, 2)] + self.assertEqual(a, range(0,5,2)) + b = slice(1,2) + self.assertEqual(b, slice(1, 2, None)) + self.assertEqual(slice(1,2,3), slice(1,2,3)) + l = range(10) + self.assertEqual(l[slice(1,6,2,)], range(1, 6, 2)) + self.assertEqual(l[slice(-6)], range(0, 4)) + self.assertEqual(l[slice(-1,-6,-3)], range(9, 4, -3)) + a = range(30) + b = a[-10::5] + self.assertEqual(b, range(20, 30, 5)) + c = a[-10::-6] + self.assertEqual(c, range(20, -1, -6)) + a = tuple(range(30)) + b = a[-10::5] + self.assertEqual(b, (20, 25)) + c = a[-10::-6] + self.assertEqual(c, (20, 14, 8, 2)) + self.assertEqual(range(4)[0], 0) + self.assertEqual(range(4)[1], 1) + self.assertEqual(range(4)[-1], 3) + self.assertEqual(range(0,5,3)[0], 0) + self.assertEqual(range(0,5,3)[1], 3) + self.assertEqual(range(0,5,3)[-1], 3) + self.assertEqual(range(-8,-4)[0], -8) + self.assertEqual(range(-8,-4)[1], -7) + self.assertEqual(range(-8,-4)[-1], -5) + self.assertEqual(range(-4,-8,-1)[0], -4) + self.assertEqual(range(-4,-8,-1)[1], -5) + self.assertEqual(range(-4,-8,-1)[-1], -7) + l = range(9) + self.assertEqual(l[-10:], range(0, 9)) + self.assertEqual(l[8:-10:-1], range(8, -1, -1)) + def foo(x): + x[4] = 4 + self.assertRaises(TypeError, foo, l) def test_contains(self): r = range(10) @@ -429,23 +471,23 @@ def test_contains(self): self.assertNotIn(10, r) self.assertNotIn("", r) - # def test_reverse_iteration(self): - # for r in [range(10), - # range(0), - # range(1, 9, 3), - # range(8, 0, -3), - # range(sys.maxsize+1, sys.maxsize+10), - # ]: - # self.assertEqual(list(reversed(r)), list(r)[::-1]) - - # def test_issue11845(self): - # r = range(*slice(1, 18, 2).indices(20)) - # values = {None, 0, 1, -1, 2, -2, 5, -5, 19, -19, - # 20, -20, 21, -21, 30, -30, 99, -99} - # for i in values: - # for j in values: - # for k in values - {0}: - # r[i:j:k] + def test_reverse_iteration(self): + for r in [range(10), + range(0), + range(1, 9, 3), + range(8, 0, -3), + range(sys.maxsize+1, sys.maxsize+10), + ]: + self.assertEqual(list(reversed(r)), list(r)[::-1]) + + def test_issue11845(self): + r = range(*slice(1, 18, 2).indices(20)) + values = {None, 0, 1, -1, 2, -2, 5, -5, 19, -19, + 20, -20, 21, -21, 30, -30, 99, -99} + for i in values: + for j in values: + for k in values - {0}: + r[i:j:k] def test_comparison(self): test_ranges = [range(0), range(0, -1), range(1, 1, 3), @@ -484,30 +526,59 @@ def test_comparison(self): # range(2**200, 2**201 + 1, 2**100)) # # Order comparisons are not implemented for ranges. - # self.assertRaises(TypeError, lambda: range(0) < range(0)) - # self.assertRaises(TypeError, lambda: range(0) > range(0)) - # self.assertRaises(TypeError, lambda: range(0) <= range(0)) - # self.assertRaises(TypeError, lambda: range(0) >= range(0)) - - - # def test_attributes(self): - # # test the start, stop and step attributes of range objects - # self.assert_attrs(range(0), 0, 0, 1) - # self.assert_attrs(range(10), 0, 10, 1) - # self.assert_attrs(range(-10), 0, -10, 1) - # self.assert_attrs(range(0, 10, 1), 0, 10, 1) - # self.assert_attrs(range(0, 10, 3), 0, 10, 3) - # self.assert_attrs(range(10, 0, -1), 10, 0, -1) - # self.assert_attrs(range(10, 0, -3), 10, 0, -3) - # - # def assert_attrs(self, rangeobj, start, stop, step): - # self.assertEqual(rangeobj.start, start) - # self.assertEqual(rangeobj.stop, stop) - # self.assertEqual(rangeobj.step, step) - # - # self.assertRaises(AttributeError, rangeobj, start, 0) - # self.assertRaises(AttributeError, rangeobj, stop, 10) - # self.assertRaises(AttributeError, rangeobj, step, 1) + self.assertRaises(TypeError, lambda: range(0) < range(0)) + self.assertRaises(TypeError, lambda: range(0) > range(0)) + self.assertRaises(TypeError, lambda: range(0) <= range(0)) + self.assertRaises(TypeError, lambda: range(0) >= range(0)) + + + def test_attributes(self): + # test the start, stop and step attributes of range objects + self.assert_attrs(range(0), 0, 0, 1) + self.assert_attrs(range(10), 0, 10, 1) + self.assert_attrs(range(-10), 0, -10, 1) + self.assert_attrs(range(0, 10, 1), 0, 10, 1) + self.assert_attrs(range(0, 10, 3), 0, 10, 3) + self.assert_attrs(range(10, 0, -1), 10, 0, -1) + self.assert_attrs(range(10, 0, -3), 10, 0, -3) + + def assert_attrs(self, rangeobj, start, stop, step): + self.assertEqual(rangeobj.start, start) + self.assertEqual(rangeobj.stop, stop) + self.assertEqual(rangeobj.step, step) + + with self.assertRaises(AttributeError): + rangeobj.start = 0 + with self.assertRaises(AttributeError): + rangeobj.stop = 10 + with self.assertRaises(AttributeError): + rangeobj.step = 1 + + # with self.assertRaises(AttributeError): + # del rangeobj.start + # with self.assertRaises(AttributeError): + # del rangeobj.stop + # with self.assertRaises(AttributeError): + # del rangeobj.step + + def test_str(self): + self.assertEqual(str(range(4))[:5], "range") + self.assertEqual(str(range(-44))[:5], "range") + self.assertEqual(str(range(0,5,3))[:5], "range") + self.assertEqual(str(range(-8,-4))[:5], "range") + self.assertEqual(str(range(-4,-8))[:5], "range") + self.assertEqual(str(range(-4,-8,-1))[:5], "range") + self.assertEqual(str(range(-8,-4,-1))[:5], "range") + + + def test_len(self): + self.assertEqual(len(range(4)), 4) + self.assertEqual(len(range(-4)), 0) + self.assertEqual(len(range(0,5,3)), 2) + self.assertEqual(len(range(-8,-4)), 4) + self.assertEqual(len(range(-4,-8)), 0) + self.assertEqual(len(range(-4,-8,-1)), 4) + self.assertEqual(len(range(-8,-4,-1)), 0) if __name__ == "__main__": unittest.main() diff --git a/test/unit3/test_re.py b/test/unit3/test_re.py new file mode 100644 index 0000000000..a55151dcff --- /dev/null +++ b/test/unit3/test_re.py @@ -0,0 +1,289 @@ +""" Unit test for re module""" +import unittest +import re + +class ReTests(unittest.TestCase): + def test_findall(self): + #Skulpt is failing all the commented out tests in test_findall and it shouldn't be + val = re.findall("From","dlkjdsljkdlkdsjlk") + self.assertEqual(len(val), 0) + val = re.findall("From","dlkjd From kdsjlk") + self.assertEqual(len(val), 1) + val = re.findall("From","From dlkjd From kdsjlk") + self.assertEqual(len(val), 2) + val = re.findall("[0-9]+/[0-9]+","1/2 1/3 3/4 1/8 fred 10/0") + self.assertEqual(len(val), 5) + a = re.findall(string="A stitch in time saves nine.", flags=re.IGNORECASE, pattern="a") + self.assertEqual(a, ['A', 'a']) + a = re.findall("[a-z]*ei[a-z]*", "Is Dr. Greiner your friend, Julie?", re.IGNORECASE) + self.assertEqual(a, ['Greiner']) + b = re.findall("[a-z]*(ei|ie)[a-z]*", "Is Dr. Greiner your friend, Julie?", re.IGNORECASE) + self.assertEqual(b, ['ei', 'ie', 'ie']) + c = re.findall("[a-z]*(ei|ie)([a-z]*)", "Is Dr. Greiner your friend, Julie?", re.IGNORECASE) + self.assertEqual(c, [('ei', 'ner'), ('ie', 'nd'), ('ie', '')]) + d = re.findall("[a-z]*(?:ei|ie)[a-z]*", "Is Dr. Greiner your friend, Julie?", re.IGNORECASE) + + self.assertEqual(d, ['Greiner', 'friend', 'Julie']) + self.assertEqual(re.findall('\w+', "Words, words, words."), ['Words', 'words', 'words']) + self.assertEqual(re.findall('(abc)(def)', 'abcdef'), [('abc', 'def')]) + self.assertEqual(re.findall('(abc)(def)', 'abcdefabcdefjaabcdef3sabc'), [('abc', 'def'), ('abc', 'def'), ('abc', 'def')]) + self.assertEqual(re.findall('(abc)', 'abcdef'), ['abc']) + self.assertEqual(re.findall('(abc)|(def)', 'abcdefabcdefjaabcdef3sabc'), [('abc', ''), ('', 'def'), ('abc', ''), ('', 'def'), ('abc', ''), ('', 'def'), ('abc', '')]) + self.assertEqual(re.findall("^\s*$", ""), ['']) + #self.assertEqual(re.findall("\s*|a", " a b"), [' ', '', 'a', ' ', '', '']) + self.assertEqual(re.findall("a|\s*", " a b"), [' ', 'a', ' ', '', '']) + #self.assertEqual(re.findall("\s*|a", " ba b"), [' ', '', '', 'a', ' ', '', '']) + self.assertEqual(re.findall("a|\s*", " ba b"), [' ', '', 'a', ' ', '', '']) + + self.assertEqual(re.findall(".",""), []) + self.assertEqual(re.findall(".","a"), ['a']) + self.assertEqual(re.findall(".a","a"), []) + self.assertEqual(re.findall("a","a"), ['a']) + self.assertEqual(re.findall("a.","a\n"), []) + self.assertEqual(re.findall(".a","ba"), ['ba']) + + self.assertEqual(re.findall("^",""), ['']) + self.assertEqual(re.findall("a^",""), []) + self.assertEqual(re.findall("^a","ba"), []) + self.assertEqual(re.findall("^a","ab"), ['a']) + self.assertEqual(re.findall("^a","\na"), []) + self.assertEqual(re.findall("a^","a"), []) + + self.assertEqual(re.findall("$",""), ['']) + self.assertEqual(re.findall("$a","a"), []) + self.assertEqual(re.findall("a$","a"), ['a']) + self.assertEqual(re.findall("a$","ab"), []) + self.assertEqual(re.findall("a$","a\nb"), []) + self.assertEqual(re.findall("a$","a\n"), ['a']) + + self.assertEqual(re.findall("a*",""), ['']) + self.assertEqual(re.findall("ab*","a"), ['a']) + self.assertEqual(re.findall("ab*","ab"), ['ab']) + self.assertEqual(re.findall("ab*","abbbbb"), ['abbbbb']) + self.assertEqual(re.findall("ab*","ba"), ['a']) + self.assertEqual(re.findall("ab*","bbbb"), []) + + self.assertEqual(re.findall("a+",""), []) + self.assertEqual(re.findall("ab+","a"), []) + self.assertEqual(re.findall("ab+","ab"), ['ab']) + self.assertEqual(re.findall("ab+","abbbbb"), ['abbbbb']) + self.assertEqual(re.findall("ab+","ba"), []) + self.assertEqual(re.findall("ab+","bbbb"), []) + + self.assertEqual(re.findall("a?",""), ['']) + self.assertEqual(re.findall("ab?","a"), ['a']) + self.assertEqual(re.findall("ab?","ab"), ['ab']) + self.assertEqual(re.findall("ab?","abbbbb"), ['ab']) + self.assertEqual(re.findall("ab?","ba"), ['a']) + self.assertEqual(re.findall("ab?","bbbb"), []) + + #self.assertEqual(re.findall("a*?","a"), ['', 'a', '']) + self.assertEqual(re.findall("ab*?","abbbb"), ['a']) + self.assertEqual(re.findall("ab*?","a"), ['a']) + self.assertEqual(re.findall("ab*?",""), []) + + self.assertEqual(re.findall("a+?","a"), ['a']) + self.assertEqual(re.findall("ab+?","abbbb"), ['ab']) + self.assertEqual(re.findall("ab+?","a"), []) + self.assertEqual(re.findall("ab+?",""), []) + + #self.assertEqual(re.findall("a??","a"), ['', 'a', '']) + self.assertEqual(re.findall("ab??","abbbb"), ['a']) + self.assertEqual(re.findall("ab??","a"), ['a']) + self.assertEqual(re.findall("ab??",""), []) + + self.assertEqual(re.findall("a{2}","a"), []) + self.assertEqual(re.findall("a{2}","aa"), ['aa']) + self.assertEqual(re.findall("a{2}","aaa"), ['aa']) + + self.assertEqual(re.findall("a{1,2}b","b"), []) + self.assertEqual(re.findall("a{1,2}b","ab"), ['ab']) + self.assertEqual(re.findall("a{1,2}b","aab"), ['aab']) + self.assertEqual(re.findall("a{1,2}b","aaab"), ['aab']) + self.assertEqual(re.findall("a{,2}b","b"), ['b']) + self.assertEqual(re.findall("a{,2}b","ab"), ['ab']) + self.assertEqual(re.findall("a{,2}b","aab"), ['aab']) + self.assertEqual(re.findall("a{,2}b","aaab"), ['aab']) + self.assertEqual(re.findall("a{2,}b","b"), []) + self.assertEqual(re.findall("a{2,}b","ab"), []) + self.assertEqual(re.findall("a{2,}b","aab"), ['aab']) + self.assertEqual(re.findall("a{2,}b","aaab"), ['aaab']) + self.assertEqual(re.findall("a{3,5}","aaaaaaaaaa"), ['aaaaa', 'aaaaa']) + self.assertEqual(re.findall("a{,5}","aaaaaaaaaa"), ['aaaaa', 'aaaaa', '']) + self.assertEqual(re.findall("a{3,}","aaaaaaaaaa"), ['aaaaaaaaaa']) + + self.assertEqual(re.findall("a{1,2}?b","b"), []) + self.assertEqual(re.findall("a{1,2}?b","ab"), ['ab']) + self.assertEqual(re.findall("a{1,2}?b","aab"), ['aab']) + self.assertEqual(re.findall("a{1,2}?b","aaab"), ['aab']) + self.assertEqual(re.findall("a{,2}?b","b"), ['b']) + self.assertEqual(re.findall("a{,2}?b","ab"), ['ab']) + self.assertEqual(re.findall("a{,2}?b","aab"), ['aab']) + self.assertEqual(re.findall("a{,2}?b","aaab"), ['aab']) + self.assertEqual(re.findall("a{2,}?b","b"), []) + self.assertEqual(re.findall("a{2,}?b","ab"), []) + self.assertEqual(re.findall("a{2,}?b","aab"), ['aab']) + self.assertEqual(re.findall("a{2,}?b","aaab"), ['aaab']) + self.assertEqual(re.findall("a{3,5}?","aaaaaaaaaa"), ['aaa', 'aaa', 'aaa']) + #self.assertEqual(re.findall("a{,5}?","aaaaaaaaaa"), ['', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '', 'a', '']) + self.assertEqual(re.findall("a{3,}?","aaaaaaaaaa"), ['aaa', 'aaa', 'aaa']) + + self.assertEqual(re.findall("[a,b,c]","abc"), ['a', 'b', 'c']) + self.assertEqual(re.findall("[a-z]","bc"), ['b', 'c']) + self.assertEqual(re.findall("[A-Z,0-9]","abcdefg"), []) + self.assertEqual(re.findall("[^A-Z]","ABCDEFGaHIJKL"), ['a']) + self.assertEqual(re.findall("[a*bc]","*"), ['*']) + + self.assertEqual(re.findall("|",""), ['']) + self.assertEqual(re.findall("|a",""), ['']) + self.assertEqual(re.findall("a|b","ba"), ['b', 'a']) + self.assertEqual(re.findall("h|ello","hello"), ['h', 'ello']) + + self.assertEqual(re.findall("(b*)","bbbba"), ['bbbb', '', '']) + + self.assertEqual(re.findall("(?:b*)","bbbba"), ['bbbb', '', '']) + self.assertEqual(re.findall("a(?=b)","a"), []) + self.assertEqual(re.findall("a(?=b)","ab"), ['a']) + self.assertEqual(re.findall("a(?!b)","a"), ['a']) + self.assertEqual(re.findall("a(?!b)","ab"), []) + + pattern = r"\n" + self.assertEqual(re.findall(pattern, "\n"), ['\n']) + self.assertEqual(re.findall(pattern, "\n\n"), ['\n', '\n']) + self.assertEqual(re.findall(pattern, "x\nx"), ['\n']) + self.assertEqual(re.findall(pattern, "x\nx\n"), ['\n', '\n']) + pattern = r"\t" + self.assertEqual(re.findall(pattern, "\t"), ['\t']) + self.assertEqual(re.findall(pattern, "\t\t"), ['\t', '\t']) + self.assertEqual(re.findall(pattern, "x\tx"), ['\t']) + self.assertEqual(re.findall(pattern, "x\tx\t"), ['\t', '\t']) + + + def test_search(self): + val = re.search("From","dlkjdsljkdlkdsjlk") + self.assertEqual(val, None) + val = re.search("From","dlkjd From kdsjlk") + self.assertTrue(val is not None) + val = re.search("From","From dlkjd From kdsjlk") + self.assertTrue(val is not None) + def helper(match,expected): + if type(expected) == str: + if match: + if match.group(0)==expected: return True + else: return False + else: return False + else: + if match: return True == expected + else: return False == expected + self.assertTrue(helper(re.search(".",""),False)) + self.assertTrue(helper(re.search(".","a"),True)) + self.assertTrue(helper(re.search(".a","a"),False)) + self.assertTrue(helper(re.search("a","a"),True)) + self.assertTrue(helper(re.search("a.","a\n"),False)) + self.assertTrue(helper(re.search(".a","ba"),True)) + self.assertTrue(helper(re.search("^",""),True)) + self.assertTrue(helper(re.search("a^",""),False)) + self.assertTrue(helper(re.search("^a","ba"),False)) + self.assertTrue(helper(re.search("^a","ab"),True)) + self.assertTrue(helper(re.search("^a","\na"),False)) + self.assertTrue(helper(re.search("a^","a"),False)) + self.assertTrue(helper(re.search("$",""),True)) + self.assertTrue(helper(re.search("$a","a"),False)) + self.assertTrue(helper(re.search("a$","a"),True)) + self.assertTrue(helper(re.search("a$","ab"),False)) + self.assertTrue(helper(re.search("a$","a\nb"),False)) + self.assertTrue(helper(re.search("a$","a\n"),True)) + self.assertTrue(helper(re.search("a*",""),"")) + self.assertTrue(helper(re.search("ab*","a"),"a")) + self.assertTrue(helper(re.search("ab*","ab"),"ab")) + self.assertTrue(helper(re.search("ab*","abbbbb"),"abbbbb")) + self.assertTrue(helper(re.search("ab*","ba"),"a")) + self.assertTrue(helper(re.search("ab*","bbbb"),False)) + self.assertTrue(helper(re.search("a+",""),False)) + self.assertTrue(helper(re.search("ab+","a"),False)) + self.assertTrue(helper(re.search("ab+","ab"),"ab")) + self.assertTrue(helper(re.search("ab+","abbbbb"),"abbbbb")) + self.assertTrue(helper(re.search("ab+","ba"),False)) + self.assertTrue(helper(re.search("ab+","bbbb"),False)) + self.assertTrue(helper(re.search("a?",""),"")) + self.assertTrue(helper(re.search("ab?","a"),"a")) + self.assertTrue(helper(re.search("ab?","ab"),"ab")) + self.assertTrue(helper(re.search("ab?","abbbbb"),"ab")) + self.assertTrue(helper(re.search("ab?","ba"),"a")) + self.assertTrue(helper(re.search("ab?","bbbb"),False)) + self.assertTrue(helper(re.search("a*?","a"),"")) + self.assertTrue(helper(re.search("ab*?","abbbb"),"a")) + self.assertTrue(helper(re.search("ab*?","a"),"a")) + self.assertTrue(helper(re.search("ab*?",""),False)) + self.assertTrue(helper(re.search("a+?","a"),"a")) + self.assertTrue(helper(re.search("ab+?","abbbb"),"ab")) + self.assertTrue(helper(re.search("ab+?","a"),False)) + self.assertTrue(helper(re.search("ab+?",""),False)) + self.assertTrue(helper(re.search("a??","a"),"")) + self.assertTrue(helper(re.search("ab??","abbbb"),"a")) + self.assertTrue(helper(re.search("ab??","a"),"a")) + self.assertTrue(helper(re.search("ab??",""),False)) + self.assertTrue(helper(re.search("a{2}","a"),False)) + self.assertTrue(helper(re.search("a{2}","aa"),"aa")) + self.assertTrue(helper(re.search("a{2}","aaa"),"aa")) + self.assertTrue(helper(re.search("a{1,2}b","b"),False)) + self.assertTrue(helper(re.search("a{1,2}b","ab"),"ab")) + self.assertTrue(helper(re.search("a{1,2}b","aab"),"aab")) + self.assertTrue(helper(re.search("a{1,2}b","aaab"),"aab")) + self.assertTrue(helper(re.search("a{,2}b","b"),"b")) + self.assertTrue(helper(re.search("a{,2}b","ab"),"ab")) + self.assertTrue(helper(re.search("a{,2}b","aab"),"aab")) + self.assertTrue(helper(re.search("a{,2}b","aaab"),"aab")) + self.assertTrue(helper(re.search("a{2,}b","b"),False)) + self.assertTrue(helper(re.search("a{2,}b","ab"),False)) + self.assertTrue(helper(re.search("a{2,}b","aab"),"aab")) + self.assertTrue(helper(re.search("a{2,}b","aaab"),"aaab")) + self.assertTrue(helper(re.search("a{3,5}","aaaaaaaaaa"),"aaaaa")) + self.assertTrue(helper(re.search("a{,5}","aaaaaaaaaa"),"aaaaa")) + self.assertTrue(helper(re.search("a{3,}","aaaaaaaaaa"),"aaaaaaaaaa")) + self.assertTrue(helper(re.search("[a,b,c]","abc"),"a")) + self.assertTrue(helper(re.search("[a-z]","bc"),"b")) + self.assertTrue(helper(re.search("[A-Z,0-9]","abcdefg"),False)) + self.assertTrue(helper(re.search("[^A-Z]","ABCDEFGaHIJKL"),"a")) + self.assertTrue(helper(re.search("[a*bc]","*"),"*")) + self.assertTrue(helper(re.search("|",""),"")) + self.assertTrue(helper(re.search("|a",""),"")) + self.assertTrue(helper(re.search("a|b","ba"),"b")) + self.assertTrue(helper(re.search("h|ello","hello"),"h")) + self.assertTrue(helper(re.search("(?:b*)","bbbba"),'bbbb')) + self.assertTrue(helper(re.search("a(?=b)","a"),False)) + self.assertTrue(helper(re.search("a(?=b)","ab"),"a")) + self.assertTrue(helper(re.search("a(?!b)","a"),"a")) + self.assertTrue(helper(re.search("a(?!b)","ab"),False)) + + def test_match(self): + val = re.match("From","dlkjdsljkdlkdsjlk") + self.assertEqual(val, None) + val = re.match("From","dlkjd From kdsjlk") + self.assertTrue(val is None) + val = re.match("From","From dlkjd From kdsjlk") + self.assertTrue(val is not None) + + def test_groups(self): + m = re.match('([0-9]+)([a-z]+)','345abu') + self.assertEqual(m.groups(), ('345', 'abu')) + self.assertEqual(m.group(0), "345abu") + self.assertEqual(m.group(1), "345") + self.assertEqual(m.group(2), "abu") + m = re.match('([0-9]+)([a-z]+)([A-Z]*)','345abu') + self.assertEqual(m.groups('default'), tuple(['345','abu',''])) + + def test_split(self): + a = re.split("a", "A stitch in time saves nine.", flags=re.IGNORECASE) + self.assertEqual(a, ['', ' stitch in time s', 'ves nine.']) + self.assertEqual(re.split("\W+", "Words, words, words."), ['Words', 'words', 'words', '']) + self.assertEqual(re.split("(\W+)", "Words, words, words."), ['Words', ', ', 'words', ', ', 'words', '.', '']) + self.assertEqual(re.split("\W+", "Words, words, words.", 1), ['Words', 'words, words.']) + self.assertEqual(re.split('[a-f]+', '0a3B9', 0, re.IGNORECASE), ['0', '3', '9']) + self.assertEqual(re.split("(\W+)", '...words, words...'), ['', '...', 'words', ', ', 'words', '...', '']) + #Skulpt fails the test below and it shouldn't + #self.assertEqual(re.split('x*', 'foo'), ['', 'f', 'o', 'o', '']) + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_set.py.disabled b/test/unit3/test_set.py.disabled index 2760c2aafb..a30f572cc7 100644 --- a/test/unit3/test_set.py.disabled +++ b/test/unit3/test_set.py.disabled @@ -50,8 +50,12 @@ class TestJointOps: # self.assertEqual(actual, expected) self.assertRaises(TypeError, self.thetype, [[]]) - # def test_len(self): + #def test_len(self): # self.assertEqual(len(self.s), len(self.d)) + def test_len(self): + l = [1,2,3,4,1,1] + s = set(l) + self.assertEqual(len(s), 4) # def test_contains(self): # for c in self.letters: @@ -403,6 +407,11 @@ class TestSet(TestJointOps, unittest.TestCase): self.assertNotIn(elem, self.s) self.assertRaises(KeyError, self.s.pop) + def test_max_min(self): + s = set([1,2,3]) + self.assertEqual(max(s), 3) + self.assertEqual(min(s), 1) + def test_update(self): retval = self.s.update(self.otherword) self.assertEqual(retval, None) @@ -731,7 +740,16 @@ class TestBasicOps: def test_copy(self): self.assertEqual(self.set.copy(), self.dup) - + l = [1,2,3,4,1,1] + s = set(l) + t = set(s) + self.assertEqual(s,t) + t.add(100) + self.assertNotEqual(t,s) + self.assertTrue(100 not in s) + self.assertTrue(len(t) > len(s)) + t.discard(2) + self.assertTrue(len(t) == len(s)) # def test_self_union(self): # result = self.set | self.set # self.assertEqual(result, self.dup) diff --git a/test/unit3/test_sets2.py b/test/unit3/test_sets2.py new file mode 100644 index 0000000000..c89e276fc2 --- /dev/null +++ b/test/unit3/test_sets2.py @@ -0,0 +1,251 @@ +""" Unit testing for sets (2)""" +import unittest + +s = set(range(9)) +s1 = set(range(5)) +s2 = set(range(4,9)) + +class SetTests(unittest.TestCase): + def test_basic(self): + s = set([1, 2, 3, 4]) + self.assertEqual(set(), set([])) + l = [1, 2, 3, 4] + self.assertEqual(set(l), s) + t = (1, 2, 3, 4) + self.assertEqual(set(t), s) + d = {1:2, 3:4} + self.assertEqual(set(d), set([1, 3])) + self.assertEqual(set(d.keys()), set([1, 3])) + self.assertEqual(set(d.values()), set([2, 4])) + + self.assertEqual(len(set([])), 0) + self.assertEqual(len(s), 4) + self.assertEqual(len(s), 4) + self.assertTrue(4 in s1) + self.assertTrue(4 in s2) + self.assertFalse(8 in s1) + self.assertFalse(1 in s2) + self.assertFalse(1 not in s1) + self.assertFalse(8 not in s2) + self.assertTrue(8 not in s1) + self.assertTrue(1 not in s2) + + def test_union(self): + s = set([2,3,4]) + t = set([4,5,6]) + u = set([1,2,3,4,5]) + a = s.union(t) + b = s.union(u) + c = t.union(s) + d = t.union(u) + e = u.union(s) + f = u.union(t) + self.assertEqual(a, c) + self.assertEqual(a, set([2,3,4,5,6])) + self.assertEqual(b, e) + self.assertEqual(b, set([1,2,3,4,5])) + self.assertEqual(d, f) + self.assertEqual(d, set([1,2,3,4,5,6])) + + a = s.union(t, u) + b = s.union(u, t) + c = t.union(s, u) + d = t.union(u, s) + e = u.union(s, t) + f = u.union(t, s) + self.assertEqual(f, set([1, 2, 3, 4, 5, 6])) + self.assertEqual(a, set([1,2,3,4,5,6])) + self.assertEqual(a, b) + self.assertEqual(a, c) + self.assertEqual(a, d) + self.assertEqual(a, e) + self.assertEqual(a, f) + + self.assertEqual(set([]).union(s1), s1) + self.assertEqual(s1.union(set([])), s1) + self.assertEqual(s1.union(s2), set([0, 1, 2, 3, 4, 5, 6, 7, 8])) + self.assertEqual(s1.union(s2,set([4,5,6])), set([0, 1, 2, 3, 4, 5, 6, 7, 8])) + + def test_symm_difference(self): + s = set([1,2,3]) + t = set([3,4,5]) + a = s.symmetric_difference(t) + b = t.symmetric_difference(s) + self.assertEqual(a, set([1, 2, 4, 5])) + self.assertEqual(a, b) + self.assertEqual(a, set([1,2,4,5])) + + s.symmetric_difference_update(t) + t.symmetric_difference_update(s) + self.assertEqual(s, set([1, 2, 4, 5])) + self.assertNotEqual(s, t) + self.assertNotEqual(s, set([1, 2, 3])) + + def test_intersection(self): + s = set([2,3,4]) + t = set([3,4,5]) + u = set([1,3,5]) + a = s.intersection(t) + b = u.intersection(s) + c = u.intersection(t) + self.assertEqual(a, set([3, 4])) + self.assertEqual(b, set([3])) + self.assertEqual(c, set([3, 5])) + d = s.intersection(t, u) + self.assertEqual(d, set([3])) + + s.intersection_update(t) + u.intersection_update(t) + self.assertEqual(s, set([3, 4])) + self.assertEqual(u, set([3, 5])) + t.intersection_update(s, u) + self.assertEqual(t, set([3])) + + self.assertEqual(set([]).intersection(s1), set([])) + self.assertEqual(s1.intersection(set([])), set([])) + self.assertEqual(s1.intersection(s2), set([4])) + self.assertEqual(s.intersection(s1,s2), set([4])) + + def test_copy(self): + s = set([1,2,3]) + copy_s = s.copy() + new_s = set(s) + copy_s.add(42) + new_s.add(13) + self.assertEqual(s, set([1, 2, 3])) + self.assertEqual(copy_s, set([1, 2, 3, 42])) + self.assertEqual(new_s, set([1, 2, 3, 13])) + self.assertEqual(set([]).copy(), set([])) + + s1 = set(range(5)) + self.assertEqual(s1.copy(), s1) + s3 = s1.copy() + s1 = set(range(1,5)) + self.assertNotEqual(s1, s3) + s3 = s1.copy() + s1.add(0) + self.assertNotEqual(s1, s3) + + def test_difference(self): + s = set([2,3,4]) + t = set([3,4,5]) + u = set([1,3,5]) + + a = s.difference(t) + b = u.difference(s) + c = u.difference(t) + self.assertEqual(a, set([2])) + self.assertEqual(b, set([1, 5])) + self.assertEqual(c, set([1])) + d = s.difference(t, u) + self.assertEqual(d, set([2])) + + s.difference_update(t) + u.difference_update(t) + self.assertEqual(s, set([2])) + self.assertEqual(u, set([1])) + s = set([2,3,4]) + t = set([3,4,5]) + t.difference_update(s, u) + self.assertEqual(t, set([5])) + + def test_compare(self): + self.assertFalse(set([]) == []) + self.assertFalse(set(["a"]) == ["a"]) + self.assertFalse(set(["a", "b"]) == ["a", "b"]) + self.assertFalse(set(["b", "a"]) == ["a", "b"]) + self.assertFalse(set(["a", "c", "b"]) == ["c", "b", "a"]) + self.assertTrue(set(['a']) == set(['a'])) + + set_1 = set([(), (1,), (5,), (1, 2), (2, 2), (1, 2, 2)]) + set_2 = set([(), (1,), (2,), (1, 2), (2, 2), (1, 2, 2)]) + self.assertFalse(set_1 == set_2) + self.assertTrue(set_1 != set_2) + set_1 = set([(), (1,), (2,), (1, 2), (2, 2), (1, 2, 2)]) + set_2 = set([(), (1,), (2,), (1, 2), (2, 2), (1, 2, 2)]) + self.assertTrue(set_1 == set_2) + self.assertFalse(set_1 != set_2) + + self.assertTrue(s1 <= s) + self.assertTrue(s2 <= s) + self.assertTrue(s <= s) + self.assertFalse(s1 <= s2) + self.assertTrue(s1 < s) + self.assertTrue(s2 < s) + self.assertFalse(s < s) + self.assertFalse(s1 < s2) + self.assertTrue(s >= s1) + self.assertTrue(s >= s2) + self.assertTrue(s >= s) + self.assertFalse(s1 >= s2) + self.assertTrue(s > s1) + self.assertTrue(s > s2) + self.assertFalse(s > s) + self.assertFalse(s1 > s2) + + def test_isdisjoint(self): + self.assertFalse(s1.isdisjoint(s2)) + self.assertTrue(s1.isdisjoint(set(range(5,10)))) + + def test_issubset(self): + self.assertTrue(s1.issubset(s1)) + self.assertTrue(s1.issubset(s)) + self.assertFalse(s1.issubset(s2)) + + def test_update(self): + empty = set([]) + empty.update(s1) + self.assertEqual(empty, s1) + empty.update(set([])) + self.assertEqual(empty, s1) + empty.update(s2) + self.assertEqual(empty, s) + empty.update(s1,s2,set([4,5,6])) + self.assertEqual(empty, s) + + def test_add(self): + empty = set([]) + empty.add(1) + self.assertEqual(empty, set([1])) + a = set(range(5)) + a.add(5) + self.assertEqual(a, set([0,1,2,3,4,5])) + + def test_remove(self): + empty = set([1]) + empty.remove(1) + self.assertEqual(empty, set([])) + a = set(range(6)) + a.remove(5) + self.assertEqual(a, set([0, 1, 2, 3, 4])) + + def test_discard(self): + empty = set([]) + empty.discard(500) + self.assertEqual(empty, set([])) + + def test_pop(self): + a = set(range(4)) + b = set([0,1,2,3]) + self.assertTrue(a.pop() in b) + self.assertTrue(a.pop() in b) + self.assertTrue(a.pop() in b) + self.assertTrue(a.pop() in b) + self.assertEqual(len(a), 0) + + def test_ops(self): + a = set(range(5)) + b = set(range(3, 8)) + c = list(b) + self.assertEqual(a & b, set(range(3, 5))) + self.assertEqual(a | b, set(range(8))) + self.assertEqual(a ^ b, set([0, 1, 2, 5, 6, 7])) + self.assertEqual(a - b, set([0, 1, 2])) + self.assertRaises(TypeError, lambda: a & c) + self.assertRaises(TypeError, lambda: a | c) + self.assertRaises(TypeError, lambda: a ^ c) + self.assertRaises(TypeError, lambda: a - c) + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_strformat.py b/test/unit3/test_strformat.py index 66bea287db..0eb5a8a714 100644 --- a/test/unit3/test_strformat.py +++ b/test/unit3/test_strformat.py @@ -10,35 +10,37 @@ def test_simple_position(self): self.assertEqual('c, b, a', '{2}, {1}, {0}'.format(*'abc')) self.assertEqual('abracadabra', '{0}{1}{0}'.format('abra', 'cad')) - #Kwargs don't work def test_arg_names(self): self.assertEqual('Coordinates: 37.24N, -115.81W', 'Coordinates: {latitude}, {longitude}'.format(latitude='37.24N', longitude='-115.81W')) - ## **kwargs does not work properly in Skulpt - # coord = {'latitude': '37.24N', 'longitude': '-115.81W'} - # self.assertEqual('Coordinates: 37.24N, -115.81W', 'Coordinates: {latitude}, {longitude}'.format(**coord)) + coord = {'latitude': '37.24N', 'longitude': '-115.81W'} + self.assertEqual('Coordinates: 37.24N, -115.81W', 'Coordinates: {latitude}, {longitude}'.format(**coord)) - ## Complex Numbers Currently unsupported - - # def test_arg_attr(self): - # c = 3-5j - # self.assertEqual('The complex number (3-5j) is formed from the real part 3.0 and the imaginary part -5.0.', ('The complex number {0} is formed from the real part {0.real} and the imaginary part {0.imag}.').format(c)) - # class Point(object): - # def __init__(self, x, y): - # self.x, self.y = x, y - # def __str__(self): - # return 'Point({self.x}, {self.y})'.format(self=self) - # self.assertEqual('Point(4, 2)', str(Point(4, 2))) + def test_arg_attr(self): + # Skulpt: Complex Numbers Currently unsupported + # c = 3-5j + # self.assertEqual('The complex number (3-5j) is formed from the real part 3.0 and the imaginary part -5.0.', ('The complex number {0} is formed from the real part {0.real} and the imaginary part {0.imag}.').format(c)) + + class Point(object): + def __init__(self, x, y): + self.x, self.y = x, y + def __str__(self): + return 'Point({self.x}, {self.y})'.format(self=self) + self.assertEqual('Point(4, 2)', str(Point(4, 2))) + + self.assertEqual('4, 2', "{0.x}, {0.y}".format(Point(4, 2))) + self.assertEqual('4, 2', "{.x}, {.y}".format(Point(4, 3), Point(3, 2))) def test_arg_items(self): coord = (3, 5) self.assertEqual('X: 3; Y: 5','X: {0[0]}; Y: {0[1]}'.format(coord)) -# self.assertEqual('My name is Fred',"My name is {0[name]}".format({'name':'Fred'})) + self.assertEqual('My name is Fred',"My name is {0[name]}".format({'name':'Fred'})) + + def test_width(self): + self.assertEqual(' 2,2',"{0:10},{0}".format(2)) + self.assertEqual('foo bar baz ',"{0:4}{1:4}{2:4}".format("foo","bar","baz")) + self.assertEqual('Fo', "{:.2}".format("Foo bar")) + self.assertEqual('Fo ', "{:4.2}".format("Foo bar")) -# TODO: make these pass -# def test_width(self): -# self.assertEqual(' 2,2',"{0:10},{0}".format(2)) -# self.assertEqual('foo bar baz ',"{0:4}{1:4}{2:4}".format("foo","bar","baz")) - def test_conversion(self): self.assertEqual("repr() shows quotes: 'test1'; str() doesn't: test2", "repr() shows quotes: {!r}; str() doesn't: {!s}".format('test1', 'test2')) @@ -49,29 +51,62 @@ def test_expansion(self): self.assertEqual(' centered ', '{:^30}'.format('centered')) self.assertEqual('***********centered***********', '{:*^30}'.format('centered')) + # Make sure it works with numbers too + self.assertEqual('2 ',"{:<10}".format(2)) + self.assertEqual(' 2 ',"{:^10}".format(2)) + self.assertEqual('- 2',"{:=10}".format(-2)) + self.assertEqual('+000000002',"{:+010}".format(2)) + def test_fixed_point(self): self.assertEqual('+3.140000; -3.140000', '{:+f}; {:+f}'.format(3.14, -3.14)) self.assertEqual(' 3.140000; -3.140000', '{: f}; {: f}'.format(3.14, -3.14)) self.assertEqual('3.140000; -3.140000', '{:-f}; {:-f}'.format(3.14, -3.14)) + def test_zeroes(self): + self.assertEqual('3.14', "{}".format(3.14)) + self.assertEqual('3.1', "{:.2}".format(3.14)) + self.assertEqual('3.14', "{:.5}".format(3.14)) + self.assertEqual('3.14', "{:.5g}".format(3.14)) + self.assertEqual('3', "{:.5g}".format(3.0)) + self.assertEqual('3.0', "{:.5}".format(3.0)) + def test_hex_oct(self): self.assertEqual('int: 42; hex: 2a; oct: 52; bin: 101010', "int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}".format(42)) self.assertEqual('int: 42; hex: 0x2a; oct: 0o52; bin: 0b101010', "int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}".format(42)) def test_comma_sep(self): self.assertEqual('1,234,567,890', '{:,}'.format(1234567890)) + self.assertEqual('1,234,567.89', '{:,.2f}'.format(1234567.888)) def test_percentage(self): points = 19.5 total = 22 self.assertEqual('Correct answers: 88.64%', 'Correct answers: {:.2%}'.format(points/total)) + self.assertEqual("-5.00%", "{:.2%}".format(-.05)) - ## Datetime requires more work. - - # def test_datetome(self): - # import datetime - # d = datetime.datetime(2010, 7, 4, 12, 15, 58) - # self.assertEqual('2010-07-04 12:15:58', '{:%Y-%m-%d %H:%M:%S}'.format(d)) + + def test_datetime(self): + import datetime + d = datetime.datetime(2010, 7, 4, 12, 15, 58) + self.assertEqual('2010-07-04 12:15:58', '{:%Y-%m-%d %H:%M:%S}'.format(d)) + + def test_fstrings(self): + self.assertEqual('1 + 2 = 3', f'1 + 2 = {1+2}') + self.assertEqual("'hello'", F'{"hello"!r}') + self.assertEqual("hi{there}", f"hi{{{'there'}}}") + + import datetime + d = datetime.datetime(2010, 7, 4, 12, 15, 58) + self.assertEqual('2010-07-04 12:15:58', f'{d:%Y-%m-%d %H:%M:%S}') + # Make sure we can format multiple objects next to each other (bug regression) + self.assertEqual('2010-07-04 12:15:58 TIME', f'{d:%Y-%m-%d %H:%M:%S} {"TIME"}') + + class Dummy: + def __format__(self, fmtstring): + return fmtstring + x = Dummy() + self.assertEqual('hello world!', f'hello {x:world}!') + self.assertEqual('hello world!!', f'{x:hello {x:world}!}!') if __name__ == '__main__': unittest.main() diff --git a/test/unit3/test_string.py b/test/unit3/test_string.py index de81f527e5..185db5892e 100644 --- a/test/unit3/test_string.py +++ b/test/unit3/test_string.py @@ -30,6 +30,5 @@ def test_capwords(self): # self.assertEqual(string.capwords('\taBc\tDeF\t'), 'Abc Def') # self.assertEqual(string.capwords('\taBc\tDeF\t', '\t'), '\tAbc\tDef\t') - if __name__ == '__main__': unittest.main() diff --git a/test/unit3/test_string_methods.py b/test/unit3/test_string_methods.py new file mode 100644 index 0000000000..1804295c13 --- /dev/null +++ b/test/unit3/test_string_methods.py @@ -0,0 +1,101 @@ +""" Unit test for string methods""" +import unittest +import re + +class StringMethodsTests(unittest.TestCase): + def test_concat(self): + self.assertEqual("O" + "K", "OK") + def test(t): + t = "O" + t + return t + self.assertEqual(test("K"), "OK") + self.assertRaises(TypeError, lambda x: "s" + x, None) + + def test_slicing(self): + a = "1234" + self.assertEqual(a[1:3], "23") + self.assertEqual(a[1:3], a[-3:3]) + self.assertEqual(a[:-1], "123") + self.assertEqual(a[1:], "234") + s = 'abcd' + self.assertEqual(s[::-1], 'dcba') + self.assertEqual(s[::2], 'ac') + x = "abcdefghjijk" + a = x[:0] + b = x[0:] + self.assertEqual(a, "") + self.assertEqual(b, x) + def badsplice(x): + return "abc"[x] + self.assertRaises(TypeError, lambda x:"abc"[x], 1.5) + s = "01234" + self.assertEqual(s[-6:0], "") + self.assertEqual(s[-6:], "01234") + self.assertEqual(s[-6:-3], "01") + self.assertEqual(s[-6:20], "01234") + def foo(x, y): + return "01234"[x:y] + self.assertRaises(TypeError, foo, "hi", [0,4]) + self.assertRaises(TypeError, foo, -3000, 4.5) + + def test_len(self): + self.assertEqual(len("abc"), 3) + self.assertEqual(len(""), 0) + self.assertEqual(len(""*10), 0) + + def test_contains(self): + self.assertTrue("x" in "xyz") + self.assertFalse("a" in "xyz") + self.assertTrue("" in "abc") + i = '(' + self.assertTrue(i not in '+-*/)') + self.assertFalse(i in '+-*/') + + def test_str_func(self): + self.assertEqual(str("weewaa"), "weewaa") + + def test_repr(self): + self.assertEqual(repr("weewaa"), "'weewaa'") + + def test_multiplication(self): + self.assertEqual("a"*15, "aaaaaaaaaaaaaaa") + self.assertEqual("dog"*19, "dogdogdogdogdogdogdogdogdogdogdogdogdogdogdogdogdogdogdog") + self.assertEqual(40*"weee", "weeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweeeweee") + + def foo(x, y): + return x * y + self.assertRaises(TypeError, foo, "a", "b") + self.assertRaises(TypeError, foo, "a", 3.4) + self.assertRaises(TypeError, foo, 3.4, "a") + self.assertRaises(TypeError, foo, "a", [2]) + self.assertRaises(TypeError, foo, [2], "b") + + def test_percent_operator(self): + self.assertEqual("formatting with just %d argument" % 1, "formatting with just 1 argument") + + self.assertEqual("%r is a repr and %s is a string" % ("this","this"), "'this' is a repr and this is a string") + self.assertEqual("I can also use a %(structure)s to format." % {'structure':'dictionary'}, "I can also use a dictionary to format.") + self.assertEqual("+%s+" % "hello","+hello+") + self.assertEqual("+%d+" % 10, "+10+") + self.assertEqual(("%c" % "a"),"a") + self.assertEqual('%c' % 34,'"') + self.assertEqual('%c' % 36,'$') + self.assertEqual('%d' % 10,"10") + self.assertEqual('%c' % 0x7f,'\x7f') + def f(x): + return str("f(%s) called" % x) + self.assertEqual(f(3), "f(3) called") + + def test_number_precision(self): + self.assertEqual("%d %i %o %x %X %e %E %f %F" % (12,-12,-0O7,0x4a,-0x4a,2.3e10,2.3E-10,1.23,-1.23), "12 -12 -7 4a -4A 2.300000e+10 2.300000E-10 1.230000 -1.230000") + self.assertEqual("%g %G %g %G" % (.00000123,.00000123,1.4,-1.4), "1.23e-06 1.23E-06 1.4 -1.4") + self.assertEqual("%g" % (.00000012), "1.2e-07") + self.assertEqual("%g" % (.0000012), "1.2e-06") + self.assertEqual("%g" % (.000012), "1.2e-05") + self.assertEqual("%g %g" % (.0000012, .000012), "1.2e-06 1.2e-05") + self.assertEqual("%g" % (.00012), "0.00012") + self.assertEqual("%d %i %o %x %X %e %E" % (12,-12,-0O7,0x4a,-0x4a,2.3e10,2.3E-10), "12 -12 -7 4a -4A 2.300000e+10 2.300000E-10") + self.assertEqual("%g %G %g %G" % (.00000123,.00000123,1.4,-1.4), "1.23e-06 1.23E-06 1.4 -1.4") +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_textwrap.py b/test/unit3/test_textwrap.py new file mode 100644 index 0000000000..d863441819 --- /dev/null +++ b/test/unit3/test_textwrap.py @@ -0,0 +1,1021 @@ +# +# Test suite for the textwrap module. +# +# Original tests written by Greg Ward . +# Converted to PyUnit by Peter Hansen . +# Currently maintained by Greg Ward. +# +# $Id$ +# + +import unittest + +from textwrap import TextWrapper, wrap, fill, dedent, indent, shorten + + +class BaseTestCase(unittest.TestCase): + '''Parent class with utility methods for textwrap tests.''' + def test_fake(self): + # keep unittest happy with this class + pass + + def show(self, textin): + if isinstance(textin, list): + result = [] + for i in range(len(textin)): + result.append(" %d: %r" % (i, textin[i])) + result = "\n".join(result) if result else " no lines" + elif isinstance(textin, str): + result = " %s\n" % repr(textin) + return result + + + def check(self, result, expect): + self.assertEqual(result, expect, + 'expected:\n%s\nbut got:\n%s' % ( + self.show(expect), self.show(result))) + + def check_wrap(self, text, width, expect, **kwargs): + result = wrap(text, width, **kwargs) + self.check(result, expect) + + def check_split(self, text, expect): + result = self.wrapper._split(text) + self.assertEqual(result, expect, + "\nexpected %r\n" + "but got %r" % (expect, result)) + + +class WrapTestCase(BaseTestCase): + + def setUp(self): + self.wrapper = TextWrapper(width=45) + + def test_simple(self): + # Simple case: just words, spaces, and a bit of punctuation + + text = "Hello there, how are you this fine day? I'm glad to hear it!" + + self.check_wrap(text, 12, + ["Hello there,", + "how are you", + "this fine", + "day? I'm", + "glad to hear", + "it!"]) + self.check_wrap(text, 42, + ["Hello there, how are you this fine day?", + "I'm glad to hear it!"]) + self.check_wrap(text, 80, [text]) + + def test_empty_string(self): + # Check that wrapping the empty string returns an empty list. + self.check_wrap("", 6, []) + self.check_wrap("", 6, [], drop_whitespace=False) + + def test_empty_string_with_initial_indent(self): + # Check that the empty string is not indented. + self.check_wrap("", 6, [], initial_indent="++") + self.check_wrap("", 6, [], initial_indent="++", drop_whitespace=False) + + def test_whitespace(self): + # Whitespace munging and end-of-sentence detection + + text = """\ +This is a paragraph that already has +line breaks. But some of its lines are much longer than the others, +so it needs to be wrapped. +Some lines are \ttabbed too. +What a mess! +""" + + expect = ["This is a paragraph that already has line", + "breaks. But some of its lines are much", + "longer than the others, so it needs to be", + "wrapped. Some lines are tabbed too. What a", + "mess!"] + + wrapper = TextWrapper(45, fix_sentence_endings=True) + result = wrapper.wrap(text) + self.check(result, expect) + + result = wrapper.fill(text) + self.check(result, '\n'.join(expect)) + + text = "\tTest\tdefault\t\ttabsize." + expect = [" Test default tabsize."] + self.check_wrap(text, 80, expect) + + text = "\tTest\tcustom\t\ttabsize." + expect = [" Test custom tabsize."] + self.check_wrap(text, 80, expect, tabsize=4) + + def test_fix_sentence_endings(self): + wrapper = TextWrapper(60, fix_sentence_endings=True) + + # SF #847346: ensure that fix_sentence_endings=True does the + # right thing even on input short enough that it doesn't need to + # be wrapped. + text = "A short line. Note the single space." + expect = ["A short line. Note the single space."] + self.check(wrapper.wrap(text), expect) + + # Test some of the hairy end cases that _fix_sentence_endings() + # is supposed to handle (the easy stuff is tested in + # test_whitespace() above). + text = "Well, Doctor? What do you think?" + expect = ["Well, Doctor? What do you think?"] + self.check(wrapper.wrap(text), expect) + + text = "Well, Doctor?\nWhat do you think?" + self.check(wrapper.wrap(text), expect) + + text = 'I say, chaps! Anyone for "tennis?"\nHmmph!' + expect = ['I say, chaps! Anyone for "tennis?" Hmmph!'] + self.check(wrapper.wrap(text), expect) + + wrapper.width = 20 + expect = ['I say, chaps!', 'Anyone for "tennis?"', 'Hmmph!'] + self.check(wrapper.wrap(text), expect) + + text = 'And she said, "Go to hell!"\nCan you believe that?' + expect = ['And she said, "Go to', + 'hell!" Can you', + 'believe that?'] + self.check(wrapper.wrap(text), expect) + + wrapper.width = 60 + expect = ['And she said, "Go to hell!" Can you believe that?'] + self.check(wrapper.wrap(text), expect) + + text = 'File stdio.h is nice.' + expect = ['File stdio.h is nice.'] + self.check(wrapper.wrap(text), expect) + + def test_wrap_short(self): + # Wrapping to make short lines longer + + text = "This is a\nshort paragraph." + + self.check_wrap(text, 20, ["This is a short", + "paragraph."]) + self.check_wrap(text, 40, ["This is a short paragraph."]) + + + def test_wrap_short_1line(self): + # Test endcases + + text = "This is a short line." + + self.check_wrap(text, 30, ["This is a short line."]) + self.check_wrap(text, 30, ["(1) This is a short line."], + initial_indent="(1) ") + + + def test_hyphenated(self): + # Test breaking hyphenated words + + text = ("this-is-a-useful-feature-for-" + "reformatting-posts-from-tim-peters'ly") + + self.check_wrap(text, 40, + ["this-is-a-useful-feature-for-", + "reformatting-posts-from-tim-peters'ly"]) + self.check_wrap(text, 41, + ["this-is-a-useful-feature-for-", + "reformatting-posts-from-tim-peters'ly"]) + self.check_wrap(text, 42, + ["this-is-a-useful-feature-for-reformatting-", + "posts-from-tim-peters'ly"]) + # The test tests current behavior but is not testing parts of the API. + expect = ("this-|is-|a-|useful-|feature-|for-|" + "reformatting-|posts-|from-|tim-|peters'ly").split('|') + # self.check_wrap(text, 1, expect, break_long_words=False) + # self.check_split(text, expect) + + self.check_split('e-mail', ['e-mail']) + self.check_split('Jelly-O', ['Jelly-O']) + # The test tests current behavior but is not testing parts of the API. + # self.check_split('half-a-crown', 'half-|a-|crown'.split('|')) + + def test_hyphenated_numbers(self): + # Test that hyphenated numbers (eg. dates) are not broken like words. + text = ("Python 1.0.0 was released on 1994-01-26. Python 1.0.1 was\n" + "released on 1994-02-15.") + + self.check_wrap(text, 30, ['Python 1.0.0 was released on', + '1994-01-26. Python 1.0.1 was', + 'released on 1994-02-15.']) + self.check_wrap(text, 40, ['Python 1.0.0 was released on 1994-01-26.', + 'Python 1.0.1 was released on 1994-02-15.']) + self.check_wrap(text, 1, text.split(), break_long_words=False) + + text = "I do all my shopping at 7-11." + self.check_wrap(text, 25, ["I do all my shopping at", + "7-11."]) + self.check_wrap(text, 27, ["I do all my shopping at", + "7-11."]) + self.check_wrap(text, 29, ["I do all my shopping at 7-11."]) + self.check_wrap(text, 1, text.split(), break_long_words=False) + + def test_em_dash(self): + # Test text with em-dashes + text = "Em-dashes should be written -- thus." + self.check_wrap(text, 25, + ["Em-dashes should be", + "written -- thus."]) + + # Probe the boundaries of the properly written em-dash, + # ie. " -- ". + self.check_wrap(text, 29, + ["Em-dashes should be written", + "-- thus."]) + expect = ["Em-dashes should be written --", + "thus."] + self.check_wrap(text, 30, expect) + self.check_wrap(text, 35, expect) + self.check_wrap(text, 36, + ["Em-dashes should be written -- thus."]) + + # The improperly written em-dash is handled too, because + # it's adjacent to non-whitespace on both sides. + text = "You can also do--this or even---this." + expect = ["You can also do", + "--this or even", + "---this."] + self.check_wrap(text, 15, expect) + self.check_wrap(text, 16, expect) + expect = ["You can also do--", + "this or even---", + "this."] + self.check_wrap(text, 17, expect) + self.check_wrap(text, 19, expect) + expect = ["You can also do--this or even", + "---this."] + self.check_wrap(text, 29, expect) + self.check_wrap(text, 31, expect) + expect = ["You can also do--this or even---", + "this."] + self.check_wrap(text, 32, expect) + self.check_wrap(text, 35, expect) + + # All of the above behaviour could be deduced by probing the + # _split() method. + text = "Here's an -- em-dash and--here's another---and another!" + expect = ["Here's", " ", "an", " ", "--", " ", "em-", "dash", " ", + "and", "--", "here's", " ", "another", "---", + "and", " ", "another!"] + self.check_split(text, expect) + + text = "and then--bam!--he was gone" + expect = ["and", " ", "then", "--", "bam!", "--", + "he", " ", "was", " ", "gone"] + self.check_split(text, expect) + + + def test_unix_options (self): + # Test that Unix-style command-line options are wrapped correctly. + # Both Optik (OptionParser) and Docutils rely on this behaviour! + + text = "You should use the -n option, or --dry-run in its long form." + self.check_wrap(text, 20, + ["You should use the", + "-n option, or --dry-", + "run in its long", + "form."]) + self.check_wrap(text, 21, + ["You should use the -n", + "option, or --dry-run", + "in its long form."]) + expect = ["You should use the -n option, or", + "--dry-run in its long form."] + self.check_wrap(text, 32, expect) + self.check_wrap(text, 34, expect) + self.check_wrap(text, 35, expect) + self.check_wrap(text, 38, expect) + expect = ["You should use the -n option, or --dry-", + "run in its long form."] + self.check_wrap(text, 39, expect) + self.check_wrap(text, 41, expect) + expect = ["You should use the -n option, or --dry-run", + "in its long form."] + self.check_wrap(text, 42, expect) + + # Again, all of the above can be deduced from _split(). + text = "the -n option, or --dry-run or --dryrun" + expect = ["the", " ", "-n", " ", "option,", " ", "or", " ", + "--dry-", "run", " ", "or", " ", "--dryrun"] + self.check_split(text, expect) + + def test_funky_hyphens (self): + # Screwy edge cases cooked up by David Goodger. All reported + # in SF bug #596434. + self.check_split("what the--hey!", ["what", " ", "the", "--", "hey!"]) + self.check_split("what the--", ["what", " ", "the--"]) + self.check_split("what the--.", ["what", " ", "the--."]) + self.check_split("--text--.", ["--text--."]) + + # When I first read bug #596434, this is what I thought David + # was talking about. I was wrong; these have always worked + # fine. The real problem is tested in test_funky_parens() + # below... + self.check_split("--option", ["--option"]) + self.check_split("--option-opt", ["--option-", "opt"]) + self.check_split("foo --option-opt bar", + ["foo", " ", "--option-", "opt", " ", "bar"]) + + def test_punct_hyphens(self): + # Oh bother, SF #965425 found another problem with hyphens -- + # hyphenated words in single quotes weren't handled correctly. + # In fact, the bug is that *any* punctuation around a hyphenated + # word was handled incorrectly, except for a leading "--", which + # was special-cased for Optik and Docutils. So test a variety + # of styles of punctuation around a hyphenated word. + # (Actually this is based on an Optik bug report, #813077). + self.check_split("the 'wibble-wobble' widget", + ['the', ' ', "'wibble-", "wobble'", ' ', 'widget']) + self.check_split('the "wibble-wobble" widget', + ['the', ' ', '"wibble-', 'wobble"', ' ', 'widget']) + self.check_split("the (wibble-wobble) widget", + ['the', ' ', "(wibble-", "wobble)", ' ', 'widget']) + self.check_split("the ['wibble-wobble'] widget", + ['the', ' ', "['wibble-", "wobble']", ' ', 'widget']) + + # The test tests current behavior but is not testing parts of the API. + # self.check_split("what-d'you-call-it.", + # "what-d'you-|call-|it.".split('|')) + + def test_funky_parens (self): + # Second part of SF bug #596434: long option strings inside + # parentheses. + # self.check_split("foo (--option) bar", + # ["foo", " ", "(--option)", " ", "bar"]) + + # Related stuff -- make sure parens work in simpler contexts. + self.check_split("foo (bar) baz", + ["foo", " ", "(bar)", " ", "baz"]) + self.check_split("blah (ding dong), wubba", + ["blah", " ", "(ding", " ", "dong),", + " ", "wubba"]) + + def test_drop_whitespace_false(self): + # Check that drop_whitespace=False preserves whitespace. + # SF patch #1581073 + text = " This is a sentence with much whitespace." + self.check_wrap(text, 10, + [" This is a", " ", "sentence ", + "with ", "much white", "space."], + drop_whitespace=False) + + def test_drop_whitespace_false_whitespace_only(self): + # Check that drop_whitespace=False preserves a whitespace-only string. + self.check_wrap(" ", 6, [" "], drop_whitespace=False) + + def test_drop_whitespace_false_whitespace_only_with_indent(self): + # Check that a whitespace-only string gets indented (when + # drop_whitespace is False). + self.check_wrap(" ", 6, [" "], drop_whitespace=False, + initial_indent=" ") + + def test_drop_whitespace_whitespace_only(self): + # Check drop_whitespace on a whitespace-only string. + self.check_wrap(" ", 6, []) + + def test_drop_whitespace_leading_whitespace(self): + # Check that drop_whitespace does not drop leading whitespace (if + # followed by non-whitespace). + # SF bug #622849 reported inconsistent handling of leading + # whitespace; let's test that a bit, shall we? + text = " This is a sentence with leading whitespace." + self.check_wrap(text, 50, + [" This is a sentence with leading whitespace."]) + self.check_wrap(text, 30, + [" This is a sentence with", "leading whitespace."]) + + def test_drop_whitespace_whitespace_line(self): + # Check that drop_whitespace skips the whole line if a non-leading + # line consists only of whitespace. + text = "abcd efgh" + # Include the result for drop_whitespace=False for comparison. + self.check_wrap(text, 6, ["abcd", " ", "efgh"], + drop_whitespace=False) + self.check_wrap(text, 6, ["abcd", "efgh"]) + + def test_drop_whitespace_whitespace_only_with_indent(self): + # Check that initial_indent is not applied to a whitespace-only + # string. This checks a special case of the fact that dropping + # whitespace occurs before indenting. + self.check_wrap(" ", 6, [], initial_indent="++") + + def test_drop_whitespace_whitespace_indent(self): + # Check that drop_whitespace does not drop whitespace indents. + # This checks a special case of the fact that dropping whitespace + # occurs before indenting. + self.check_wrap("abcd efgh", 6, [" abcd", " efgh"], + initial_indent=" ", subsequent_indent=" ") + + def test_split(self): + # Ensure that the standard _split() method works as advertised + # in the comments + + text = "Hello there -- you goof-ball, use the -b option!" + + result = self.wrapper._split(text) + self.check(result, + ["Hello", " ", "there", " ", "--", " ", "you", " ", "goof-", + "ball,", " ", "use", " ", "the", " ", "-b", " ", "option!"]) + + def test_break_on_hyphens(self): + # Ensure that the break_on_hyphens attributes work + text = "yaba daba-doo" + self.check_wrap(text, 10, ["yaba daba-", "doo"], + break_on_hyphens=True) + self.check_wrap(text, 10, ["yaba", "daba-doo"], + break_on_hyphens=False) + + def test_bad_width(self): + # Ensure that width <= 0 is caught. + text = "Whatever, it doesn't matter." + self.assertRaises(ValueError, wrap, text, 0) + self.assertRaises(ValueError, wrap, text, -1) + + # def test_no_split_at_umlaut(self): + # text = "Die Empf\xe4nger-Auswahl" + # self.check_wrap(text, 13, ["Die", "Empf\xe4nger-", "Auswahl"]) + + # def test_umlaut_followed_by_dash(self): + # text = "aa \xe4\xe4-\xe4\xe4" + # self.check_wrap(text, 7, ["aa \xe4\xe4-", "\xe4\xe4"]) + + # def test_non_breaking_space(self): + # text = 'This is a sentence with non-breaking\N{NO-BREAK SPACE}space.' + + # self.check_wrap(text, 20, + # ['This is a sentence', + # 'with non-', + # 'breaking\N{NO-BREAK SPACE}space.'], + # break_on_hyphens=True) + + # self.check_wrap(text, 20, + # ['This is a sentence', + # 'with', + # 'non-breaking\N{NO-BREAK SPACE}space.'], + # break_on_hyphens=False) + + # def test_narrow_non_breaking_space(self): + # text = ('This is a sentence with non-breaking' + # '\N{NARROW NO-BREAK SPACE}space.') + + # self.check_wrap(text, 20, + # ['This is a sentence', + # 'with non-', + # 'breaking\N{NARROW NO-BREAK SPACE}space.'], + # break_on_hyphens=True) + + # self.check_wrap(text, 20, + # ['This is a sentence', + # 'with', + # 'non-breaking\N{NARROW NO-BREAK SPACE}space.'], + # break_on_hyphens=False) + + +class MaxLinesTestCase(BaseTestCase): + text = "Hello there, how are you this fine day? I'm glad to hear it!" + + def test_simple(self): + self.check_wrap(self.text, 12, + ["Hello [...]"], + max_lines=0) + self.check_wrap(self.text, 12, + ["Hello [...]"], + max_lines=1) + self.check_wrap(self.text, 12, + ["Hello there,", + "how [...]"], + max_lines=2) + self.check_wrap(self.text, 13, + ["Hello there,", + "how are [...]"], + max_lines=2) + self.check_wrap(self.text, 80, [self.text], max_lines=1) + self.check_wrap(self.text, 12, + ["Hello there,", + "how are you", + "this fine", + "day? I'm", + "glad to hear", + "it!"], + max_lines=6) + + def test_spaces(self): + # strip spaces before placeholder + self.check_wrap(self.text, 12, + ["Hello there,", + "how are you", + "this fine", + "day? [...]"], + max_lines=4) + # placeholder at the start of line + self.check_wrap(self.text, 6, + ["Hello", + "[...]"], + max_lines=2) + # final spaces + self.check_wrap(self.text + ' ' * 10, 12, + ["Hello there,", + "how are you", + "this fine", + "day? I'm", + "glad to hear", + "it!"], + max_lines=6) + + def test_placeholder(self): + self.check_wrap(self.text, 12, + ["Hello..."], + max_lines=1, + placeholder='...') + self.check_wrap(self.text, 12, + ["Hello there,", + "how are..."], + max_lines=2, + placeholder='...') + # long placeholder and indentation + with self.assertRaises(ValueError): + wrap(self.text, 16, initial_indent=' ', + max_lines=1, placeholder=' [truncated]...') + with self.assertRaises(ValueError): + wrap(self.text, 16, subsequent_indent=' ', + max_lines=2, placeholder=' [truncated]...') + self.check_wrap(self.text, 16, + [" Hello there,", + " [truncated]..."], + max_lines=2, + initial_indent=' ', + subsequent_indent=' ', + placeholder=' [truncated]...') + self.check_wrap(self.text, 16, + [" [truncated]..."], + max_lines=1, + initial_indent=' ', + subsequent_indent=' ', + placeholder=' [truncated]...') + self.check_wrap(self.text, 80, [self.text], placeholder='.' * 1000) + + def test_placeholder_backtrack(self): + # Test special case when max_lines insufficient, but what + # would be last wrapped line so long the placeholder cannot + # be added there without violence. So, textwrap backtracks, + # adding placeholder to the penultimate line. + text = 'Good grief Python features are advancing quickly!' + self.check_wrap(text, 12, + ['Good grief', 'Python*****'], + max_lines=3, + placeholder='*****') + + +class LongWordTestCase (BaseTestCase): + def setUp(self): + self.wrapper = TextWrapper() + self.text = '''\ +Did you say "supercalifragilisticexpialidocious?" +How *do* you spell that odd word, anyways? +''' + + def test_break_long(self): + # Wrap text with long words and lots of punctuation + + self.check_wrap(self.text, 30, + ['Did you say "supercalifragilis', + 'ticexpialidocious?" How *do*', + 'you spell that odd word,', + 'anyways?']) + self.check_wrap(self.text, 50, + ['Did you say "supercalifragilisticexpialidocious?"', + 'How *do* you spell that odd word, anyways?']) + + # SF bug 797650. Prevent an infinite loop by making sure that at + # least one character gets split off on every pass. + self.check_wrap('-'*10+'hello', 10, + ['----------', + ' h', + ' e', + ' l', + ' l', + ' o'], + subsequent_indent = ' '*15) + + # bug 1146. Prevent a long word to be wrongly wrapped when the + # preceding word is exactly one character shorter than the width + self.check_wrap(self.text, 12, + ['Did you say ', + '"supercalifr', + 'agilisticexp', + 'ialidocious?', + '" How *do*', + 'you spell', + 'that odd', + 'word,', + 'anyways?']) + + def test_nobreak_long(self): + # Test with break_long_words disabled + self.wrapper.break_long_words = 0 + self.wrapper.width = 30 + expect = ['Did you say', + '"supercalifragilisticexpialidocious?"', + 'How *do* you spell that odd', + 'word, anyways?' + ] + result = self.wrapper.wrap(self.text) + self.check(result, expect) + + # Same thing with kwargs passed to standalone wrap() function. + result = wrap(self.text, width=30, break_long_words=0) + self.check(result, expect) + + def test_max_lines_long(self): + self.check_wrap(self.text, 12, + ['Did you say ', + '"supercalifr', + 'agilisticexp', + '[...]'], + max_lines=4) + + +class IndentTestCases(BaseTestCase): + + # called before each test method + def setUp(self): + self.text = '''\ +This paragraph will be filled, first without any indentation, +and then with some (including a hanging indent).''' + + + def test_fill(self): + # Test the fill() method + + expect = '''\ +This paragraph will be filled, first +without any indentation, and then with +some (including a hanging indent).''' + + result = fill(self.text, 40) + self.check(result, expect) + + + def test_initial_indent(self): + # Test initial_indent parameter + + expect = [" This paragraph will be filled,", + "first without any indentation, and then", + "with some (including a hanging indent)."] + result = wrap(self.text, 40, initial_indent=" ") + self.check(result, expect) + + expect = "\n".join(expect) + result = fill(self.text, 40, initial_indent=" ") + self.check(result, expect) + + + def test_subsequent_indent(self): + # Test subsequent_indent parameter + + expect = '''\ + * This paragraph will be filled, first + without any indentation, and then + with some (including a hanging + indent).''' + + result = fill(self.text, 40, + initial_indent=" * ", subsequent_indent=" ") + self.check(result, expect) + + +# Despite the similar names, DedentTestCase is *not* the inverse +# of IndentTestCase! +class DedentTestCase(unittest.TestCase): + + def assertUnchanged(self, text): + """assert that dedent() has no effect on 'text'""" + self.assertEqual(text, dedent(text)) + + def test_dedent_nomargin(self): + # No lines indented. + text = "Hello there.\nHow are you?\nOh good, I'm glad." + self.assertUnchanged(text) + + # Similar, with a blank line. + text = "Hello there.\n\nBoo!" + self.assertUnchanged(text) + + # Some lines indented, but overall margin is still zero. + text = "Hello there.\n This is indented." + self.assertUnchanged(text) + + # Again, add a blank line. + text = "Hello there.\n\n Boo!\n" + self.assertUnchanged(text) + + def test_dedent_even(self): + # All lines indented by two spaces. + text = " Hello there.\n How are ya?\n Oh good." + expect = "Hello there.\nHow are ya?\nOh good." + self.assertEqual(expect, dedent(text)) + + # Same, with blank lines. + text = " Hello there.\n\n How are ya?\n Oh good.\n" + expect = "Hello there.\n\nHow are ya?\nOh good.\n" + self.assertEqual(expect, dedent(text)) + + # Now indent one of the blank lines. + text = " Hello there.\n \n How are ya?\n Oh good.\n" + expect = "Hello there.\n\nHow are ya?\nOh good.\n" + self.assertEqual(expect, dedent(text)) + + # Same, with long blank lines. + text = " Hello there.\n \n How are ya?\n Oh good.\n" + expect = "Hello there.\n\nHow are ya?\nOh good.\n" + self.assertEqual(expect, dedent(text)) + + def test_dedent_uneven(self): + # Lines indented unevenly. + text = '''\ + def foo(): + while 1: + return foo + ''' + expect = '''\ +def foo(): + while 1: + return foo +''' + self.assertEqual(expect, dedent(text)) + + # Uneven indentation with a blank line. + text = " Foo\n Bar\n\n Baz\n" + expect = "Foo\n Bar\n\n Baz\n" + self.assertEqual(expect, dedent(text)) + + # Uneven indentation with a whitespace-only line. + text = " Foo\n Bar\n \n Baz\n" + expect = "Foo\n Bar\n\n Baz\n" + self.assertEqual(expect, dedent(text)) + + # Uneven indentation with a long whitespace-only line. + text = " Foo\n Bar\n \n Baz\n" + expect = "Foo\n Bar\n\n Baz\n" + self.assertEqual(expect, dedent(text)) + + def test_dedent_declining(self): + # Uneven indentation with declining indent level. + text = " Foo\n Bar\n" # 5 spaces, then 4 + expect = " Foo\nBar\n" + self.assertEqual(expect, dedent(text)) + + # Declining indent level with blank line. + text = " Foo\n\n Bar\n" # 5 spaces, blank, then 4 + expect = " Foo\n\nBar\n" + self.assertEqual(expect, dedent(text)) + + # Declining indent level with whitespace only line. + text = " Foo\n \n Bar\n" # 5 spaces, then 4, then 4 + expect = " Foo\n\nBar\n" + self.assertEqual(expect, dedent(text)) + + # dedent() should not mangle internal tabs + def test_dedent_preserve_internal_tabs(self): + text = " hello\tthere\n how are\tyou?" + expect = "hello\tthere\nhow are\tyou?" + self.assertEqual(expect, dedent(text)) + + # make sure that it preserves tabs when it's not making any + # changes at all + self.assertEqual(expect, dedent(expect)) + + # dedent() should not mangle tabs in the margin (i.e. + # tabs and spaces both count as margin, but are *not* + # considered equivalent) + def test_dedent_preserve_margin_tabs(self): + text = " hello there\n\thow are you?" + self.assertUnchanged(text) + + # same effect even if we have 8 spaces + text = " hello there\n\thow are you?" + self.assertUnchanged(text) + + # dedent() only removes whitespace that can be uniformly removed! + text = "\thello there\n\thow are you?" + expect = "hello there\nhow are you?" + self.assertEqual(expect, dedent(text)) + + text = " \thello there\n \thow are you?" + self.assertEqual(expect, dedent(text)) + + text = " \t hello there\n \t how are you?" + self.assertEqual(expect, dedent(text)) + + text = " \thello there\n \t how are you?" + expect = "hello there\n how are you?" + self.assertEqual(expect, dedent(text)) + + # test margin is smaller than smallest indent + text = " \thello there\n \thow are you?\n \tI'm fine, thanks" + expect = " \thello there\n \thow are you?\n\tI'm fine, thanks" + self.assertEqual(expect, dedent(text)) + + +# Test textwrap.indent +class IndentTestCase(unittest.TestCase): + # The examples used for tests. If any of these change, the expected + # results in the various test cases must also be updated. + # The roundtrip cases are separate, because textwrap.dedent doesn't + # handle Windows line endings + ROUNDTRIP_CASES = ( + # Basic test case + "Hi.\nThis is a test.\nTesting.", + # Include a blank line + "Hi.\nThis is a test.\n\nTesting.", + # Include leading and trailing blank lines + "\nHi.\nThis is a test.\nTesting.\n", + ) + CASES = ROUNDTRIP_CASES + ( + # Use Windows line endings + "Hi.\r\nThis is a test.\r\nTesting.\r\n", + # Pathological case + "\nHi.\r\nThis is a test.\n\r\nTesting.\r\n\n", + ) + + def test_indent_nomargin_default(self): + # indent should do nothing if 'prefix' is empty. + for text in self.CASES: + self.assertEqual(indent(text, ''), text) + + def test_indent_nomargin_explicit_default(self): + # The same as test_indent_nomargin, but explicitly requesting + # the default behaviour by passing None as the predicate + for text in self.CASES: + self.assertEqual(indent(text, '', None), text) + + def test_indent_nomargin_all_lines(self): + # The same as test_indent_nomargin, but using the optional + # predicate argument + predicate = lambda line: True + for text in self.CASES: + self.assertEqual(indent(text, '', predicate), text) + + def test_indent_no_lines(self): + # Explicitly skip indenting any lines + predicate = lambda line: False + for text in self.CASES: + self.assertEqual(indent(text, ' ', predicate), text) + + def test_roundtrip_spaces(self): + # A whitespace prefix should roundtrip with dedent + for text in self.ROUNDTRIP_CASES: + self.assertEqual(dedent(indent(text, ' ')), text) + + def test_roundtrip_tabs(self): + # A whitespace prefix should roundtrip with dedent + for text in self.ROUNDTRIP_CASES: + self.assertEqual(dedent(indent(text, '\t\t')), text) + + def test_roundtrip_mixed(self): + # A whitespace prefix should roundtrip with dedent + for text in self.ROUNDTRIP_CASES: + self.assertEqual(dedent(indent(text, ' \t \t ')), text) + + def test_indent_default(self): + # Test default indenting of lines that are not whitespace only + prefix = ' ' + expected = ( + # Basic test case + " Hi.\n This is a test.\n Testing.", + # Include a blank line + " Hi.\n This is a test.\n\n Testing.", + # Include leading and trailing blank lines + "\n Hi.\n This is a test.\n Testing.\n", + # Use Windows line endings + " Hi.\r\n This is a test.\r\n Testing.\r\n", + # Pathological case + "\n Hi.\r\n This is a test.\n\r\n Testing.\r\n\n", + ) + for text, expect in zip(self.CASES, expected): + self.assertEqual(indent(text, prefix), expect) + + def test_indent_explicit_default(self): + # Test default indenting of lines that are not whitespace only + prefix = ' ' + expected = ( + # Basic test case + " Hi.\n This is a test.\n Testing.", + # Include a blank line + " Hi.\n This is a test.\n\n Testing.", + # Include leading and trailing blank lines + "\n Hi.\n This is a test.\n Testing.\n", + # Use Windows line endings + " Hi.\r\n This is a test.\r\n Testing.\r\n", + # Pathological case + "\n Hi.\r\n This is a test.\n\r\n Testing.\r\n\n", + ) + for text, expect in zip(self.CASES, expected): + self.assertEqual(indent(text, prefix, None), expect) + + def test_indent_all_lines(self): + # Add 'prefix' to all lines, including whitespace-only ones. + prefix = ' ' + expected = ( + # Basic test case + " Hi.\n This is a test.\n Testing.", + # Include a blank line + " Hi.\n This is a test.\n \n Testing.", + # Include leading and trailing blank lines + " \n Hi.\n This is a test.\n Testing.\n", + # Use Windows line endings + " Hi.\r\n This is a test.\r\n Testing.\r\n", + # Pathological case + " \n Hi.\r\n This is a test.\n \r\n Testing.\r\n \n", + ) + predicate = lambda line: True + for text, expect in zip(self.CASES, expected): + self.assertEqual(indent(text, prefix, predicate), expect) + + def test_indent_empty_lines(self): + # Add 'prefix' solely to whitespace-only lines. + prefix = ' ' + expected = ( + # Basic test case + "Hi.\nThis is a test.\nTesting.", + # Include a blank line + "Hi.\nThis is a test.\n \nTesting.", + # Include leading and trailing blank lines + " \nHi.\nThis is a test.\nTesting.\n", + # Use Windows line endings + "Hi.\r\nThis is a test.\r\nTesting.\r\n", + # Pathological case + " \nHi.\r\nThis is a test.\n \r\nTesting.\r\n \n", + ) + predicate = lambda line: not line.strip() + for text, expect in zip(self.CASES, expected): + self.assertEqual(indent(text, prefix, predicate), expect) + + +class ShortenTestCase(BaseTestCase): + + def check_shorten(self, text, width, expect, **kwargs): + result = shorten(text, width, **kwargs) + self.check(result, expect) + + def test_simple(self): + # Simple case: just words, spaces, and a bit of punctuation + text = "Hello there, how are you this fine day? I'm glad to hear it!" + + self.check_shorten(text, 18, "Hello there, [...]") + self.check_shorten(text, len(text), text) + self.check_shorten(text, len(text) - 1, + "Hello there, how are you this fine day? " + "I'm glad to [...]") + + def test_placeholder(self): + text = "Hello there, how are you this fine day? I'm glad to hear it!" + + self.check_shorten(text, 17, "Hello there,$$", placeholder='$$') + self.check_shorten(text, 18, "Hello there, how$$", placeholder='$$') + self.check_shorten(text, 18, "Hello there, $$", placeholder=' $$') + self.check_shorten(text, len(text), text, placeholder='$$') + self.check_shorten(text, len(text) - 1, + "Hello there, how are you this fine day? " + "I'm glad to hear$$", placeholder='$$') + + def test_empty_string(self): + self.check_shorten("", 6, "") + + def test_whitespace(self): + # Whitespace collapsing + text = """ + This is a paragraph that already has + line breaks and \t tabs too.""" + self.check_shorten(text, 62, + "This is a paragraph that already has line " + "breaks and tabs too.") + self.check_shorten(text, 61, + "This is a paragraph that already has line " + "breaks and [...]") + + self.check_shorten("hello world! ", 12, "hello world!") + self.check_shorten("hello world! ", 11, "hello [...]") + # The leading space is trimmed from the placeholder + # (it would be ugly otherwise). + self.check_shorten("hello world! ", 10, "[...]") + + def test_width_too_small_for_placeholder(self): + shorten("x" * 20, width=8, placeholder="(......)") + with self.assertRaises(ValueError): + shorten("x" * 20, width=8, placeholder="(.......)") + + def test_first_word_too_long_but_placeholder_fits(self): + self.check_shorten("Helloo", 5, "[...]") + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/test/unit3/test_time.py b/test/unit3/test_time.py index 8dd56cc997..81d80a65fe 100644 --- a/test/unit3/test_time.py +++ b/test/unit3/test_time.py @@ -13,9 +13,6 @@ def test_data_attributes(self): time.timezone time.tzname - def test_clock(self): - time.clock() - def test_conversions(self): self.assertTrue(time.ctime(self.t) == time.asctime(time.localtime(self.t))) @@ -26,19 +23,84 @@ def test_conversions(self): def test_sleep(self): time.sleep(0.01) - def test_strftime(self): - # TODO: Test timezone handling by using localtime() rather than - # (or as well as) gmtime(). - # Although we're currently behaving the same as CPython, we don't - # actually understand why: - # https://github.com/skulpt/skulpt/issues/908 + class A(object): + def __getattr__(self, name): + time.sleep(0.001) + return name - self.assertEqual(time.strftime("%b %d %Y %H:%M:%S", time.gmtime(3661)), "Jan 01 1970 01:01:01"); + def __setattr__(self, name, value): + time.sleep(0.001) + object.__setattr__(self, name, value) + + class B(A): + pass + + + class C(A): + def __getattribute__(self, name): + time.sleep(0.001) + return "FOO" + b = B() + self.assertEqual(b.x, "x") + b.x = "BAR" + self.assertEqual(b.x, "BAR") + c = C() + c.x = "BAR" + self.assertEqual(c.x, "FOO") + + class A(object): + def __init__(self): + object.__setattr__(self, "x", 42) + + def __getattr__(self, attr): + if attr == "y": + return 41 + else: + return 43 + + a = A() + self.assertEqual(str(a.x), "42") + self.assertEqual(str(a.y), "41") + self.assertEqual(str(a.z), "43") + # Should not touch __getattr__ or __setattr__ at all + A.foo = "bar" + + class B(object): + def __getattr__(self, attr): + time.sleep(0.01) + return object.__getattr__(self, attr) + + def __setattr__(self, attr, value): + time.sleep(0.01) + return object.__setattr__(self, attr, value) + + b = B() + b.x = 42 + self.assertEqual(str(b.x), "42") + b.x += 1 + self.assertEqual(str(b.x), "43") + + class GeneratorClass: + def __init__(self): + pass + def generator(self): + for i in range(10): + yield i + def sleeping_generator(self): + for i in range(10): + time.sleep(0.01) + yield i + + gen = GeneratorClass() + + self.assertEqual(list(gen.generator()), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + self.assertEqual(list(gen.sleeping_generator()), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + self.assertEqual([x*2 for x in gen.generator()], [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]) + self.assertEqual([x*2 for x in gen.sleeping_generator()], [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]) + + def test_strftime(self): + self.assertEqual(time.strftime("%b %d %Y %H:%M:%S", time.localtime(3661 + time.timezone)), "Jan 01 1970 01:01:01"); - def test_strptime(self): - result = time.struct_time((1970, 1, 1, 1, 1, 1, 3, 1, -1)) - self.assertEqual(time.strptime("Jan 01 1970 01:01:01", "%b %d %Y %H:%M:%S"), result); - def _test_dir(self): # this test fails because the compare self.assertEqual(dir(time), [ diff --git a/test/unit3/test_tuple.py b/test/unit3/test_tuple.py index 7642736524..5b8dcdd581 100644 --- a/test/unit3/test_tuple.py +++ b/test/unit3/test_tuple.py @@ -4,8 +4,6 @@ class IterInheritsTestCase(unittest.TestCase): - - def test_generator(self): def counter(low, high): current = low @@ -15,7 +13,6 @@ def counter(low, high): l = tuple(counter(1,12)) t = 4 in l - print(t, l) self.assertTrue(t) def test_getitem(self): @@ -26,7 +23,6 @@ def __getitem__(self,idx): else: raise StopIteration l = tuple(Counter()) - print(l) self.assertTrue(5 in l) def test_dunderiter(self): @@ -56,9 +52,63 @@ def __next__(self): # Python 3: def __next__(self) # print(l) # self.assertTrue(105 in l) - - - + def test_slicing(self): + a = tuple([1,2,3,4,5,6,7,8]) + b = a[5::-4] + self.assertEqual(b, (6,2)) + + def test_str(self): + self.assertEqual(str((1,2,3)), "(1, 2, 3)") + + def test_repr(self): + self.assertEqual(repr((1,2,3)), "(1, 2, 3)") + self.assertEqual(repr(()), "()") + + def test_multiplication(self): + self.assertEqual((5,)*10, (5, 5, 5, 5, 5, 5, 5, 5, 5, 5)) + self.assertEqual((1,2,3)*4, (1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3)) + self.assertEqual(10*(5,), (5, 5, 5, 5, 5, 5, 5, 5, 5, 5)) + self.assertEqual(4*(1,2,3), (1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3)) + + def test_comparisons(self): + #equality + self.assertTrue(() == ()) + self.assertFalse(() == (1,)) + self.assertTrue((1,) == (1,)) + self.assertFalse((1,2) == (3,4)) + self.assertFalse((1,2) == (1,)) + self.assertTrue(() != (1,)) + self.assertFalse(() != ()) + self.assertTrue((1,2) != (1,)) + #greater/less than + self.assertTrue(() < (1,)) + self.assertTrue((1,2) < (3,4)) + self.assertTrue((1,2) != (1,)) + self.assertTrue(() != (1,)) + self.assertTrue((1,2,3) > (1,2)) + self.assertFalse((1,) > (1,)) + self.assertFalse((1,2,3) < (1,2,3)) + self.assertFalse((1,2) < (1,2)) + self.assertFalse((1,2) > (1,2,3)) + self.assertTrue(() <= (1,)) + self.assertTrue((1,2) <= (1,2)) + self.assertTrue((1,2) <= (1,2,3)) + self.assertFalse((1,2,3) <= (1,2)) + self.assertFalse(() >= (1,)) + + def test_index(self): + self.assertEqual((1, 2, 3).index(2), 1) + self.assertEqual((1, 2, 3).index(1), 0) + + def test_len(self): + self.assertEqual(len(()), 0) + self.assertEqual(len((1,2,3,4)), 4) + + def test_count(self): + t = (1,2,3,4,2,1) + self.assertEqual(t.count(1), 2) + self.assertEqual(t.count(2), 2) + self.assertEqual(t.count(4), 1) if __name__ == "__main__": unittest.main() diff --git a/test/unit3/test_type.py b/test/unit3/test_type.py new file mode 100644 index 0000000000..cf6ac2f782 --- /dev/null +++ b/test/unit3/test_type.py @@ -0,0 +1,103 @@ +""" Unit test for type() function """ +import unittest + +class TypeFunctionTest(unittest.TestCase): + def test_int(self): + self.assertTrue(type(1) == int) + self.assertTrue(type(2**10) == int) + self.assertTrue(type(2**1024) == int) + lst2 = [1, 2, 3, 4] + check = lst2.index(2) + self.assertEqual(type(check), int) + check = lst2.count(3) + self.assertEqual(type(check), int) + t = (1, 2, 3) + check = t.index(2) + self.assertEqual(type(check), int) + check = t.count(3) + self.assertEqual(type(check), int) + s = "abcabcabc" + check = s.count('a') + self.assertEqual(type(check), int) + check = s.find('bc') + self.assertEqual(type(check), int) + check = s.index('cab') + self.assertEqual(type(check), int) + check = s.rfind('bc') + self.assertEqual(type(check), int) + check = s.rindex('cab') + self.assertEqual(type(check), int) + self.assertEqual(type(hash(True)), int) + self.assertEqual(type(hash(None)), int) + self.assertEqual(type(hash("hello")), int) + x = (1,2,3) + self.assertEqual(type(hash(x)), int) + + def test_string(self): + self.assertTrue((type("wee") == str)) + + def test_float(self): + self.assertEqual(type(4.5), float) + self.assertNotEqual(type(4444), float) + + def test_classes(self): + class X: pass + self.assertEqual(type(X), type(type)) + x = X() + self.assertEqual(str(type(x)), "") + class Y(): pass + self.assertEqual(type(Y), type(type)) + y = Y() # changed since y takes no arguments + self.assertEqual(str(type(y)), "") + + def test_bool(self): + lst = [1, 2, 3] + check = 1 in lst + self.assertEqual(type(check), bool) + d = {1: 2} + check = 3 in d + self.assertEqual(type(check), bool) + s = set([1, 2, 3]) + check = s.isdisjoint(s) + self.assertEqual(type(check), bool) + s = set([1, 2, 3]) + check = s.issubset(s) + self.assertEqual(type(check), bool) + z = isinstance(5, int) + self.assertEqual(type(z), bool) + a = hasattr("hello", "not_a_method") + self.assertEqual(type(a), bool) + + def test_nonetype(self): + lst2 = [1, 2, 3, 4] + lst = [1, 2, 3] + check = lst2.reverse() + self.assertEqual(type(check), type(None)) + check = lst2.append(8) + self.assertEqual(type(check), type(None)) + check = lst2.insert(2, 3) + self.assertEqual(type(check), type(None)) + check = lst2.extend(lst) + self.assertEqual(type(check), type(None)) + check = lst2.remove(4) + self.assertEqual(type(check), type(None)) + s = set([1, 2, 3]) + check = s.update(s) + self.assertEqual(type(check), type(None)) + s2 = set([2, 3]) + check = s.intersection_update(s2) + self.assertEqual(type(check), type(None)) + check = s.difference_update(s2) + self.assertEqual(type(check), type(None)) + check = s.symmetric_difference_update(s2) + self.assertEqual(type(check), type(None)) + check = s.add(4) + self.assertEqual(type(check), type(None)) + check = s.discard(4) + self.assertEqual(type(check), type(None)) + check = s.remove(3) + self.assertEqual(type(check), type(None)) + +if __name__ == '__main__': + unittest.main() + diff --git a/test/unit3/test_variables.py b/test/unit3/test_variables.py new file mode 100644 index 0000000000..98810b9c18 --- /dev/null +++ b/test/unit3/test_variables.py @@ -0,0 +1,161 @@ +""" Unit testing for variables """ +import unittest + +#stuff for testing global variables +bar = 11 +global l +def foo(): + pass + +class MyClass: + def __init__(self): + self.l = [globals()['__name__'], 'MyClass' in globals(), type(globals()['baz'])] + +def baz(x): + return 'baz' in globals() + + +class VariableTests(unittest.TestCase): + def test_global_vars(self): + c = "squirrel" + time = 0 + global l + l = [] + def x(time): + time += 1 + if time == 1: + b = "dog" + else: + b = "banana" + l.append(b) + l.append(c) + def y(d): + a = "cat" + l.append(a) + l.append(b) + l.append(d) + def z(): + for i in range(2*time): + yield i,a,b,c,d + return z + return y("blorp") + l2 = [] + for v in x(time)(): + l2.append(v) + for v in x(time)(): + l2.append(v) + self.assertEqual((l, l2), (['dog', 'squirrel', 'cat', 'dog', 'blorp', 'dog', 'squirrel', 'cat', 'dog', 'blorp'], [(0, 'cat', 'dog', 'squirrel', 'blorp'), (1, 'cat', 'dog', 'squirrel', 'blorp'), (0, 'cat', 'dog', 'squirrel', 'blorp'), (1, 'cat', 'dog', 'squirrel', 'blorp')])) + + self.assertEqual(globals()['bar'], 11) + self.assertTrue('foo' in globals()) + + self.assertTrue(baz(10)) + y = MyClass() + self.assertEqual(y.l, ["__main__", True, type(foo)]) + + def test_mult_variable_assignment(self): + a,b,c = 1,2,3 + self.assertEqual(c,3) + x = 1,2 + self.assertEqual(x[0], 1) + a = b,c = 1,2 + self.assertEqual(a[0],1) + self.assertEqual(a[1],2) + self.assertEqual(b,1) + self.assertEqual(c,2) + + + def test_trailing_commas(self): + x = 'OK', + self.assertEqual(x[0],'OK') + a = (1,) + self.assertEqual(a, tuple([1])) + x = [1,2,] + self.assertEqual(x[1],2) + self.assertEqual(x,[1,2]) + x = 2,'OK', + self.assertEqual(len(x),2) + + def test_cell_vars(self): + # free and cell vars in y + l = [] + c = "squirrel" + def x(): + b = "dog" + l.append(b) + l.append(c) + def y(): + a = "cat" + l.append(a) + l.append(b) + def z(): + return a,b,c + return z + return y() + self.assertEqual(x()(), ('cat', 'dog', 'squirrel')) + self.assertEqual(l, ['dog', 'squirrel', 'cat', 'dog']) + + def test_misc(self): + def loc(): pass + def gbl(): pass + def free(): pass + def cell(): pass + def gen(): pass + def true(): pass + def var(): pass + def volatile(): pass + def package(): + loc = 4 + gbl = 42 + cell = 19 + instanceof = gbl * cell + static = instanceof + return(([loc, gbl, cell, instanceof, static], true == var, volatile != package)) + self.assertEqual(package(), ([4, 42, 19, 798, 798], False, True)) + + def test_methodnames(self): + class X: + x = 4 + x = X() + stuff = x.x + self.assertEqual(stuff, 4) + things =(X.x) + self.assertEqual(things, 4) + + def test_js_method_names(self): + def isPrototypeOf(x): + return x + + def toSource(x): + return x + + def hasOwnProperty(x): + return x + self.assertEqual(isPrototypeOf(1), 1) + self.assertEqual(toSource(2), 2) + self.assertEqual(hasOwnProperty(3), 3) + class MyTest: + def __init__(self,s): + self.w = s + + def length(self): + return len(self.w) + + x = MyTest("foo") + self.assertEqual(x.length(), 3) + + def test_del(self): + x = "hi" + y = 3 + #Skulpt fails these tests because locals() isn't implemented + #but it should pass them + + #self.assertTrue("x" in locals().keys()) + #self.assertTrue("y" in locals().keys()) + del x + #self.assertFalse("x" in locals().keys()) + #self.assertTrue("y" in locals().keys()) + +if __name__ == '__main__': + unittest.main() + diff --git a/webpack.config.js b/webpack.config.js index 3a47e0e704..c5a3b0bd87 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,23 +1,23 @@ -const path = require("path"); -const webpack = require("webpack"); -const shell = require("shelljs"); -const chalk = require("chalk"); +const path = require('path'); +const webpack = require('webpack'); +const shell = require('shelljs'); +const chalk = require('chalk'); -const ClosureWebpackPlugin = require("closure-webpack-plugin"); -const CleanWebpackPlugin = require("clean-webpack-plugin"); -const CopyWebpackPlugin = require("copy-webpack-plugin"); -const GitRevisionPlugin = require("git-revision-webpack-plugin"); -const CompressionWebpackPlugin = require("compression-webpack-plugin"); +const ClosureWebpackPlugin = require('closure-webpack-plugin'); +const CleanWebpackPlugin = require('clean-webpack-plugin'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const GitRevisionPlugin = require('git-revision-webpack-plugin'); +const CompressionWebpackPlugin = require('compression-webpack-plugin'); const git = new GitRevisionPlugin({branch: true}); const styleexcludes = /(node_modules)|(support)|(gen)|(tokenize.js)|(symtable.js)|(compile.js)|(ast.js)|(internalpython.js)/; -if (!shell.which("git")) { +if (!shell.which('git')) { console.log(chalk.red("WARNING: Cannot find git! Unsure if working directory is clean.")); } -var output = shell.exec("git diff-index --quiet HEAD"); +var output = shell.exec('git diff-index --quiet HEAD'); if (output.code !== 0) { console.log(chalk.red("WARNING: Working directory is not clean.")); } else { @@ -28,52 +28,52 @@ module.exports = (env, argv) => { var opt = { minimize: false }; - var outfile = "skulpt.js"; - var assertfile = "./assert-dev.js"; + var outfile = 'skulpt.js'; + var assertfile = './assert-dev.js'; var mod = {}; - if (argv.mode === "production") { + if (argv.mode === 'production') { opt = { noEmitOnErrors: true, minimizer: [ - new ClosureWebpackPlugin({mode: "STANDARD", platform: "java"}, { - jscomp_error: ["accessControls", "checkRegExp", "checkTypes", "checkVars", - "invalidCasts", "missingProperties", - "nonStandardJsDocs", "strictModuleDepCheck", "undefinedVars", - "unknownDefines", "visibility"], - jscomp_off: ["fileoverviewTags", "deprecated"], - languageOut: (env && env.languageOut) ? env.languageOut : "ECMASCRIPT_2015", - externs: "support/externs/sk.js" + new ClosureWebpackPlugin({mode: 'STANDARD'}, { + jscomp_error: ['accessControls', 'checkRegExp', 'checkVars', /*'checkTypes',*/ + 'invalidCasts', 'missingProperties', + 'nonStandardJsDocs', 'strictModuleDepCheck', 'undefinedVars', + 'unknownDefines', 'visibility'], + jscomp_off: ['fileoverviewTags', 'deprecated', 'uselessCode', 'suspiciousCode', 'checkTypes',], + languageOut: (env && env.languageOut) ? env.languageOut : 'ECMASCRIPT_2015', + externs: 'support/externs/sk.js', + // warningLevel: "QUIET", }) ] }; - outfile = "skulpt.min.js"; - assertfile = "./assert-prod.js"; + outfile = 'skulpt.min.js'; + assertfile = './assert-prod.js'; mod = { rules: [ { test: /\.js$/, - enforce: "pre", + enforce: 'pre', exclude: styleexcludes, - loader: "eslint-loader" + loader: 'eslint-loader' } ] }; } var config = { - entry: "./src/main.js", + entry: './src/main.js', output: { - path: path.resolve(__dirname, "dist"), + path: path.resolve(__dirname, 'dist'), devtoolFallbackModuleFilenameTemplate: "[absolute-resource-path]?[hash]", filename: outfile }, - devtool: "source-map", - module: mod, + devtool: 'source-map', plugins: [ new CleanWebpackPlugin(), new CopyWebpackPlugin([ - { from: "debugger/debugger.js", to: "debugger.js" } + { from: 'debugger/debugger.js', to: 'debugger.js' } ]), new webpack.DefinePlugin({ GITVERSION: JSON.stringify(git.version()), @@ -83,15 +83,21 @@ module.exports = (env, argv) => { }), new CompressionWebpackPlugin({ include: /^skulpt\.min\.js$/, - algorithm: "gzip" + algorithm: 'gzip' }) ], optimization: opt, resolve: { alias: { - "assert": assertfile - } + 'assert': assertfile } + }, + + module: mod, + // uncomment this while working on closure compiler errors + // externals: { + // jsbi: "JSBI", + // } }; return config; From 1fcc6d924743ce453036ebbb1e8016d790ed42e4 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 6 Aug 2020 04:53:03 -0400 Subject: [PATCH 38/68] Include more of S-corks's changes, bugfix for symtable --- src/abstract.js | 70 +------------- src/check.js | 2 +- src/compile.js | 2 +- src/dict.js | 4 +- src/errors.js | 8 +- src/import.js | 2 +- src/int.js | 6 +- src/lib/unittest/__init__.py | 4 +- src/misceval.js | 26 +++--- src/object.js | 49 +++++++++- src/set.js | 1 + src/slotdefs.js | 12 ++- src/str.js | 175 +++++++++++++++++++++++++++++------ src/super.js | 2 +- src/symtable.js | 3 + src/tuple.js | 1 + src/type.js | 11 ++- 17 files changed, 252 insertions(+), 126 deletions(-) diff --git a/src/abstract.js b/src/abstract.js index 34bff274bf..c74982b6ec 100644 --- a/src/abstract.js +++ b/src/abstract.js @@ -91,7 +91,7 @@ Sk.abstr.boNameToSlotFuncLhs_ = function (obj, name) { return obj.nb$or; } }; -/**@suppress {checkTypes} */ + Sk.abstr.boNameToSlotFuncRhs_ = function (obj, name) { switch (name) { case "Add": @@ -126,7 +126,7 @@ Sk.abstr.boNameToSlotFuncRhs_ = function (obj, name) { return obj.nb$reflected_or; } }; -/**@suppress {checkTypes} */ + Sk.abstr.iboNameToSlotFunc_ = function (obj, name) { switch (name) { case "Add": @@ -612,7 +612,7 @@ Sk.abstr.objectNegative = function (obj) { if (obj.nb$negative) { return obj.nb$negative(); } - throw new Sk.builtin.TypeError("bad operand type for unary +: '" + Sk.abstr.typeName(obj) + "'"); + throw new Sk.builtin.TypeError("bad operand type for unary -: '" + Sk.abstr.typeName(obj) + "'"); }; Sk.abstr.objectPositive = function (obj) { @@ -801,68 +801,6 @@ Sk.abstr.setUpInheritance = function (childName, child, parent, metaclass) { return child; }; -/** - * @summary - * Set up inheritance between type and object - * - * @description - * ```text - * type instanceof object => true - * object instanceof type => true - * type instanceof type => true - * object instanceof object => true - * - * type subclassof object => type.prototype instanceof object => true - * object subclassof type => object.prototype instanceof type => false - * ``` - * this algorithm achieves the equivalent with the following prototypical chains - * using `Object.setPrototypeOf` - * - * ``` - * type.__proto__ = type (type instanceof type) - * type.__proto__.__proto__ = object (type instanceof object) - * type.prototype.__proto__ = object (type subclassof object) - * object.__proto__ = type (object instanceof type) - * object.__proto__.__proto__ = object (object instanceof object) - * ``` - * - * while `Object.setPrototypeOf` is not considered [good practice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) - * this is a particularly unique use case and creates a lot of prototypical benefits - * all single inheritance classes (i.e. all builtins) now follow prototypical inheritance - * similarly it makes metclasses that much easier to implement - * Object.setPrototypeOf is also a feature built into the javascript language - * - * @function - * @suppress {checkTypes} - * - */ -Sk.abstr.setUpBaseInheritance = function () { - Object.setPrototypeOf(Sk.builtin.type.prototype, Sk.builtin.object.prototype); - Object.setPrototypeOf(Sk.builtin.type, Sk.builtin.type.prototype); - Object.setPrototypeOf(Sk.builtin.object, Sk.builtin.type.prototype); - - // required so that type objects can be called! - Object.defineProperties(Sk.builtin.type.prototype, /**@lends {Sk.builtin.type.prototype}*/{ - call: {value: Function.prototype.call}, - apply: {value: Function.prototype.apply}, - ob$type: {value: Sk.builtin.type, writable: true}, - tp$name: {value: "type", writable: true}, - tp$base: {value: Sk.builtin.object, writable: true}, - sk$type: {value: true}, - }); - Object.defineProperties(Sk.builtin.object.prototype, /**@lends {Sk.builtin.object.prototype}*/{ - ob$type: {value: Sk.builtin.object, writable: true}, - tp$name: {value: "object", writable: true}, - tp$base: {value: undefined, writable: true}, - sk$object: {value: true}, - }); - - Sk.builtin.object.sk$basetype = true; - Sk.builtin.type.sk$basetype = true; - - Sk.abstr.setUpBuiltinMro(Sk.builtin.type); - Sk.abstr.setUpBuiltinMro(Sk.builtin.object); -}; /** * This function is called in {@link Sk.doOneTimeInitialization} @@ -874,7 +812,7 @@ Sk.abstr.setUpBaseInheritance = function () { Sk.abstr.setUpBuiltinMro = function (child) { let parent = child.prototype.tp$base || undefined; const bases = parent === undefined ? [] : [parent]; - if (parent === Sk.builtin.object) { + if (parent === Sk.builtin.object || parent === undefined) { child.sk$basetype = true; } const mro = [child]; diff --git a/src/check.js b/src/check.js index 7d57839b68..39bb3ed362 100644 --- a/src/check.js +++ b/src/check.js @@ -179,7 +179,7 @@ Sk.exportSymbol("Sk.builtin.checkComplex", Sk.builtin.checkComplex); * @param {*} arg */ Sk.builtin.checkInt = function (arg) { - return arg != null && ((typeof arg === "number" && arg === (arg | 0)) || arg instanceof Sk.builtin.int_ || arg instanceof Sk.builtin.lng); + return arg instanceof Sk.builtin.int_ || (typeof arg === "number" && Number.isInteger(arg)); }; Sk.exportSymbol("Sk.builtin.checkInt", Sk.builtin.checkInt); diff --git a/src/compile.js b/src/compile.js index e1263fe5c3..90785a6be1 100644 --- a/src/compile.js +++ b/src/compile.js @@ -1968,7 +1968,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal this.u.varDeclsCode += "}"; // inject __class__ cell when running python3 - if (Sk.python3 && class_for_super) { + if (Sk.__future__.python3 && class_for_super) { this.u.varDeclsCode += "$gbl.__class__=$gbl." + class_for_super.v + ";"; } diff --git a/src/dict.js b/src/dict.js index 213667e748..f079375013 100644 --- a/src/dict.js +++ b/src/dict.js @@ -1,4 +1,3 @@ - /** @typedef {Sk.builtin.object} */ var pyObject; /** @typedef {Sk.builtin.type|Function} */ var typeObject; @@ -20,7 +19,7 @@ Sk.builtin.dict = Sk.abstr.buildNativeClass("dict", { if (L === undefined) { L = []; } - Sk.asserts.assert(Array.isArray(L) && this instanceof Sk.builtin.dict, "bad call to dict constructor"); + Sk.asserts.assert(Array.isArray(L) && L.length % 2 === 0 && this instanceof Sk.builtin.dict, "bad call to dict constructor"); this.size = 0; this.entries = {}; @@ -321,7 +320,6 @@ Sk.builtin.dict.prototype.quick$lookup = function (pyName) { return; }; - /** * NB: * We could put the following methods on the proto in the above object literal diff --git a/src/errors.js b/src/errors.js index afe7687bc4..38bcaa5eab 100644 --- a/src/errors.js +++ b/src/errors.js @@ -377,9 +377,11 @@ Sk.exportSymbol("Sk.builtin.NegativePowerError", Sk.builtin.NegativePowerError); * @param {*=} args */ Sk.builtin.ExternalError = function (...args) { - this.nativeError = args; - const msg = args.toString(); - Sk.builtin.Exception.call(this, msg); + this.nativeError = args[0]; + if (!Sk.builtin.checkString(this.nativeError)) { + args[0] = this.nativeError.toString(); + } + Sk.builtin.Exception.apply(this, args); }; Sk.abstr.setUpInheritance("ExternalError", Sk.builtin.ExternalError, Sk.builtin.Exception); Sk.exportSymbol("Sk.builtin.ExternalError", Sk.builtin.ExternalError); diff --git a/src/import.js b/src/import.js index b43ee5a948..e54558331c 100644 --- a/src/import.js +++ b/src/import.js @@ -201,7 +201,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela // - run module and set the module locals returned to the module __dict__ module = new Sk.builtin.module(); - if (suppliedPyBody) { + if (typeof suppliedPyBody === "string") { filename = name + ".py"; co = Sk.compile(suppliedPyBody, filename, "exec", canSuspend, true); } else { diff --git a/src/int.js b/src/int.js index a1a429213c..1730442615 100644 --- a/src/int.js +++ b/src/int.js @@ -1,4 +1,6 @@ -/**@constructor */ +/**@constructor + * @ignore +*/ const JSBI = require("jsbi"); /** @@ -28,7 +30,6 @@ Sk.builtin.int_ = Sk.abstr.buildNativeClass("int", { } else { Sk.asserts.fail("bad argument to int constructor"); } - /**@type {number|JSBI} */ this.v = v; }, slots: /** @lends {Sk.builtin.int_.prototype}*/{ @@ -381,6 +382,7 @@ function compareSlot(number_func, bigint_func) { * * @param {function(number): number} number_func * @param {function(JSBI): JSBI} bigint_func + * @ignore * */ function numberUnarySlot(number_func, bigint_func) { diff --git a/src/lib/unittest/__init__.py b/src/lib/unittest/__init__.py index e35a69f0c8..6d23647b7d 100644 --- a/src/lib/unittest/__init__.py +++ b/src/lib/unittest/__init__.py @@ -6,7 +6,7 @@ ''' -class _AssertRaisesContext(object): +class _AssertRaisesContext: """A context manager used to implement TestCase.assertRaises* methods.""" def __init__(self, expected, test_case): @@ -66,7 +66,7 @@ def __exit__(self, exc_type, exc_value, tb): return True -class TestCase(object): +class TestCase: def __init__(self): self.numPassed = 0 self.numFailed = 0 diff --git a/src/misceval.js b/src/misceval.js index b66193c78e..ead9e01783 100644 --- a/src/misceval.js +++ b/src/misceval.js @@ -631,9 +631,9 @@ Sk.misceval.loadname = function (name, other) { var bi; var v = other[name]; if (v !== undefined) { - // if (typeof v === "function" && v["$d"] === undefined && v["tp$name"] === undefined) { - // return v(); - // } + if (typeof v === "function" && v.sk$object === undefined) { + return v(); + } return v; } @@ -1117,7 +1117,7 @@ Sk.exportSymbol("Sk.misceval.iterFor", Sk.misceval.iterFor); * Convert a Python iterable into a javascript array * * @param {pyObject} iterable - * @param {boolean=} canSuspend - Can this funciton suspend + * @param {boolean=} canSuspend - Can this function suspend * * @returns {!Array} */ @@ -1127,21 +1127,17 @@ Sk.misceval.arrayFromIterable = function (iterable, canSuspend) { } const hptype = iterable.hp$type || undefined; if (hptype === undefined && iterable.sk$asarray !== undefined) { + // use sk$asarray only if we're a builtin return iterable.sk$asarray(); } const L = []; - const ret = Sk.misceval.iterFor(Sk.abstr.iter(iterable), (i) => { + const ret = Sk.misceval.chain( + Sk.misceval.iterFor(Sk.abstr.iter(iterable), (i) => { L.push(i); - }); - if (ret === undefined) { - return L; - } else if (canSuspend) { - return Sk.misceval.chain(ret, () => { - return L; - }); - } - Sk.misceval.retryOptionalSuspensionOrThrow(ret); - return L; + }), + () => L + ); + return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret); }; Sk.exportSymbol("Sk.misceval.arrayFromIterable", Sk.misceval.arrayFromIterable); diff --git a/src/object.js b/src/object.js index fffb56c64d..d329c2522f 100644 --- a/src/object.js +++ b/src/object.js @@ -11,9 +11,51 @@ Sk.builtin.object = function object() { Sk.asserts.assert(this instanceof Sk.builtin.object, "bad call to object, use 'new'"); }; -// now that object has been created we setup the base inheritances -// between type and object -Sk.abstr.setUpBaseInheritance(); +Object.defineProperties(Sk.builtin.object.prototype, /**@lends {Sk.builtin.object.prototype}*/ { + ob$type: { value: Sk.builtin.object, writable: true }, + tp$name: { value: "object", writable: true }, + tp$base: { value: undefined, writable: true }, + sk$object: { value: true }, +}); + +/** + * @description + * We aim to match python and javascript inheritance like + * type instanceof object => true + * object instanceof type => true + * type instanceof type => true + * object instanceof object => true + * + * type subclassof object => type.prototype instanceof object => true + * object subclassof type => object.prototype instanceof type => false + * + * this algorithm achieves the equivalent with the following prototypical chains + * using `Object.setPrototypeOf` + * + * ``` + * type.__proto__ = type (type instanceof type) + * type.__proto__.__proto__ = object (type instanceof object) + * type.prototype.__proto__ = object (type subclasssof object) + * object.__proto__ = type (object instanceof type) + * object.__proto__.__proto__ = object (object instanceof object) + * ``` + * + * while `Object.setPrototypeOf` is not considered [good practice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) + * this is a particularly unique use case and creates a lot of prototypical benefits + * all single inheritance classes (i.e. all builtins) now follow prototypical inheritance + * similarly it makes metclasses that much easier to implement + * Object.setPrototypeOf is also a feature built into the javascript language + * + * @ignore + */ +(function setUpBaseInheritance () { + Object.setPrototypeOf(Sk.builtin.type.prototype, Sk.builtin.object.prototype); + Object.setPrototypeOf(Sk.builtin.type, Sk.builtin.type.prototype); + Object.setPrototypeOf(Sk.builtin.object, Sk.builtin.type.prototype); + Sk.abstr.setUpBuiltinMro(Sk.builtin.type); + Sk.abstr.setUpBuiltinMro(Sk.builtin.object); +})(); + /** * worth noting that we don't use the new api for object since descr_objects are not yet initialized @@ -41,6 +83,7 @@ Sk.builtin.object.prototype.tp$new = function (args, kwargs) { /** * @param {Array} args * @param {Array=} kwargs + * @ignore */ Sk.builtin.object.prototype.tp$init = function (args, kwargs) { // see cypthon object_init for algorithm details diff --git a/src/set.js b/src/set.js index ce3772a3fe..a333cfe7ac 100644 --- a/src/set.js +++ b/src/set.js @@ -419,6 +419,7 @@ Sk.builtin.frozenset = Sk.abstr.buildNativeClass("frozenset", { /** * @param {Array} args * @param {Array=} kwargs + * @ignore */ tp$new: function (args, kwargs) { if (this !== Sk.builtin.frozenset.prototype) { diff --git a/src/slotdefs.js b/src/slotdefs.js index 947aedd9c4..250ffb27ac 100644 --- a/src/slotdefs.js +++ b/src/slotdefs.js @@ -28,6 +28,7 @@ function wrapperCallNoArgs(self, args, kwargs) { * @param {*} self * @param {Array} args * @param {Array=} kwargs + * @ignore */ function wrapperFastCall(self, args, kwargs) { // this = the wrapped function @@ -42,6 +43,7 @@ function wrapperFastCall(self, args, kwargs) { * @param {*} self * @param {Array} args * @param {Array=} kwargs + * @ignore */ function wrapperCallOneArg(self, args, kwargs) { // this = the wrapped function @@ -57,6 +59,7 @@ function wrapperCallOneArg(self, args, kwargs) { * @param {*} self * @param {!Array} args * @param {Array=} kwargs + * @ignore */ function wrapperCallTernary(self, args, kwargs) { // this = the wrapped function @@ -73,6 +76,7 @@ function wrapperCallTernary(self, args, kwargs) { * @param {*} self * @param {Array} args * @param {Array=} kwargs + * @ignore */ function wrapperSet(self, args, kwargs) { Sk.abstr.checkNoKwargs(this.$name, kwargs); @@ -84,6 +88,7 @@ function wrapperSet(self, args, kwargs) { * @param {*} self * @param {Array} args * @param {Array=} kwargs + * @ignore */ function wrapperRichCompare(self, args, kwargs) { const res = wrapperCallOneArg.call(this, self, args, kwargs); @@ -117,6 +122,7 @@ function slotFuncNoArgs(dunderFunc) { * @param {Function} checkFunc * @param {string} checkMsg * @param {Function=} f + * @ignore */ function slotFuncNoArgsWithCheck(dunderName, checkFunc, checkMsg, f) { return function (dunderFunc) { @@ -1803,7 +1809,7 @@ Sk.subSlots = { nb$invert: "__invert__", nb$remainder: "__mod__", nb$reflected_remainder: "__rmod__", - nb$inplace_mod: "__imod__", + nb$inplace_remainder: "__imod__", nb$divmod: "__divmod__", nb$reflected_divmod: "__rdivmod__", nb$power: "__pow__", @@ -1831,6 +1837,10 @@ Sk.subSlots = { nb$reflected_rshift: "__rrshift__", nb$inplace_lshift: "__ilshift__", nb$inplace_rshift: "__irshift__", + + nb$matrix_multiply: "__matmul__", + nb$reflected_matrix_multiply: "__rmatmul__", + nb$inplace_matrix_multiply: "__imatmul__", }, sequence_and_mapping_slots: { diff --git a/src/str.js b/src/str.js index 07b5aeb2fe..1a84b78c0a 100755 --- a/src/str.js +++ b/src/str.js @@ -20,7 +20,7 @@ Sk.builtin.str = function (x) { // Temporary // Sk.asserts.assert(typeof this === "string" || this === undefined || this.sk$object, "str called with an invalid JS object"); - let ret, interned; + let ret; if (typeof x === "string") { // the common case ret = x; @@ -45,9 +45,9 @@ Sk.builtin.str = function (x) { } else { throw new Sk.builtin.TypeError("could not convert object of type '" + Sk.abstr.typeName(x) + "' to str"); } - interned = getInterned(ret); - // interning required for strings in py + const interned = getInterned(ret); + // interning required for strings in py if (interned !== undefined) { return interned; } else { @@ -64,7 +64,7 @@ Sk.exportSymbol("Sk.builtin.str", Sk.builtin.str); Sk.abstr.setUpInheritance("str", Sk.builtin.str, Sk.builtin.object); Sk.builtin.str.prototype.tp$as_sequence_or_mapping = true; -// Sk.builtin.str.prototype.tp$as_number = true; // we currently don't support nb$mod +Sk.builtin.str.prototype.tp$as_number = true; Sk.builtin.str.prototype.tp$doc = "str(object='') -> str\nstr(bytes_or_buffer[, encoding[, errors]]) -> str\n\nCreate a new string object from the given object. If encoding or\nerrors is specified, then the object must expose a data buffer\nthat will be decoded using the given encoding and error handler.\nOtherwise, returns the result of object.__str__() (if defined)\nor repr(object).\nencoding defaults to sys.getdefaultencoding().\nerrors defaults to 'strict'."; @@ -470,21 +470,21 @@ Sk.builtin.str.methods.count = function (self, pat, start, end) { if (!Sk.builtin.checkString(pat)) { throw new Sk.builtin.TypeError("expected a character buffer object"); } - if (start !== undefined && !Sk.builtin.checkInt(start)) { + if ((start !== undefined) && !Sk.builtin.checkInt(start) && !Sk.builtin.checkNone(start)) { throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); } - if (end !== undefined && !Sk.builtin.checkInt(end)) { + if ((end !== undefined) && !Sk.builtin.checkInt(end)&& !Sk.builtin.checkNone(end)) { throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); } - if (start === undefined) { + if (start === undefined || Sk.builtin.checkNone(start)) { start = 0; } else { start = Sk.builtin.asnum$(start); start = start >= 0 ? start : self.v.length + start; } - if (end === undefined) { + if (end === undefined || Sk.builtin.checkNone(end)) { end = self.v.length; } else { end = Sk.builtin.asnum$(end); @@ -582,21 +582,21 @@ Sk.builtin.str.methods.find = function (self, tgt, start, end) { if (!Sk.builtin.checkString(tgt)) { throw new Sk.builtin.TypeError("expected a character buffer object"); } - if (start !== undefined && !Sk.builtin.checkInt(start)) { + if ((start !== undefined) && !Sk.builtin.checkInt(start) && !Sk.builtin.checkNone(start)) { throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); } - if (end !== undefined && !Sk.builtin.checkInt(end)) { + if ((end !== undefined) && !Sk.builtin.checkInt(end) && !Sk.builtin.checkNone(end)) { throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); } - if (start === undefined) { + if (start === undefined || Sk.builtin.checkNone(start)) { start = 0; } else { start = Sk.builtin.asnum$(start); start = start >= 0 ? start : self.v.length + start; } - if (end === undefined) { + if (end === undefined || Sk.builtin.checkNone(end)) { end = self.v.length; } else { end = Sk.builtin.asnum$(end); @@ -625,21 +625,21 @@ Sk.builtin.str.methods.rfind = function (self, tgt, start, end) { if (!Sk.builtin.checkString(tgt)) { throw new Sk.builtin.TypeError("expected a character buffer object"); } - if (start !== undefined && !Sk.builtin.checkInt(start)) { + if ((start !== undefined) && !Sk.builtin.checkInt(start) && !Sk.builtin.checkNone(start)) { throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); } - if (end !== undefined && !Sk.builtin.checkInt(end)) { + if ((end !== undefined) && !Sk.builtin.checkInt(end) && !Sk.builtin.checkNone(end)) { throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); } - if (start === undefined) { + if (start === undefined || Sk.builtin.checkNone(start)) { start = 0; } else { start = Sk.builtin.asnum$(start); start = start >= 0 ? start : self.v.length + start; } - if (end === undefined) { + if (end === undefined || Sk.builtin.checkNone(end)) { end = self.v.length; } else { end = Sk.builtin.asnum$(end); @@ -663,17 +663,130 @@ Sk.builtin.str.methods.rindex = function (self, tgt, start, end) { return idx; }; -Sk.builtin.str.methods.startswith = function (self, tgt) { - Sk.builtin.pyCheckArgsLen("startswith", arguments.length, 2, 2); - Sk.builtin.pyCheckType("tgt", "string", Sk.builtin.checkString(tgt)); - return new Sk.builtin.bool(self.v.indexOf(tgt.v) === 0); +Sk.builtin.str.methods.startswith = function (self, prefix, start, end) { + + Sk.builtin.pyCheckArgsLen("startswith", arguments.length, 1, 3, false, true); + if(Sk.abstr.typeName(prefix) != "str" && Sk.abstr.typeName(prefix) != "tuple"){ + throw new Sk.builtin.TypeError("startswith first arg must be str or a tuple of str, not " + Sk.abstr.typeName(prefix)); + } + + if ((start !== undefined) && !Sk.misceval.isIndex(start) && !Sk.builtin.checkNone(start)) { + throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); + } + if ((end !== undefined) && !Sk.misceval.isIndex(end) && !Sk.builtin.checkNone(end)) { + throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); + } + + if (start === undefined || Sk.builtin.checkNone(start)) { + start = 0; + } else { + start = Sk.misceval.asIndex(start); + start = start >= 0 ? start : self.v.length + start; + } + + if (end === undefined || Sk.builtin.checkNone(end)) { + end = self.v.length; + } else { + end = Sk.misceval.asIndex(end); + end = end >= 0 ? end : self.v.length + end; + } + + if(start > self.v.length){ + return Sk.builtin.bool.false$; + } + + var substr = self.v.slice(start, end); + + + if(Sk.abstr.typeName(prefix) == "tuple"){ + var tmpBool = false, resultBool = false; + if(start > end){ + tmpBool = start <= 0; + } + if(tmpBool){ + return Sk.builtin.bool.true$; + } + var it, i; + for (it = Sk.abstr.iter(prefix), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { + if(!tmpBool){ + tmpBool = substr.indexOf(i.v) === 0; + } + resultBool = resultBool || tmpBool; + if(resultBool){ + break; + } + } + return resultBool?Sk.builtin.bool.true$ : Sk.builtin.bool.false$; + } + + if(prefix.v == "" && start > end && end >= 0){ + return Sk.builtin.bool.false$; + } + + return new Sk.builtin.bool(substr.indexOf(prefix.v) === 0); }; // http://stackoverflow.com/questions/280634/endswith-in-javascript -Sk.builtin.str.methods.endswith = function (self, tgt) { - Sk.builtin.pyCheckArgsLen("endswith", arguments.length, 2, 2); - Sk.builtin.pyCheckType("tgt", "string", Sk.builtin.checkString(tgt)); - return new Sk.builtin.bool(self.v.indexOf(tgt.v, self.v.length - tgt.v.length) !== -1); +Sk.builtin.str.methods.endswith = function (self, suffix, start, end) { + Sk.builtin.pyCheckArgsLen("endswith", arguments.length, 1, 3, false, true); + + if(Sk.abstr.typeName(suffix) != "str" && Sk.abstr.typeName(suffix) != "tuple"){ + throw new Sk.builtin.TypeError("endswith first arg must be str or a tuple of str, not " + Sk.abstr.typeName(suffix)); + } + + if ((start !== undefined) && !Sk.misceval.isIndex(start) && !Sk.builtin.checkNone(start)) { + throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); + } + if ((end !== undefined) && !Sk.misceval.isIndex(end) && !Sk.builtin.checkNone(end)) { + throw new Sk.builtin.TypeError("slice indices must be integers or None or have an __index__ method"); + } + + if (start === undefined || Sk.builtin.checkNone(start)) { + start = 0; + } else { + start = Sk.misceval.asIndex(start); + start = start >= 0 ? start : self.v.length + start; + } + + if (end === undefined || Sk.builtin.checkNone(end)) { + end = self.v.length; + } else { + end = Sk.misceval.asIndex(end); + end = end >= 0 ? end : self.v.length + end; + } + + if(start > self.v.length){ + return Sk.builtin.bool.false$; + } + + //take out the substring + var substr = self.v.slice(start, end); + + if(Sk.abstr.typeName(suffix) == "tuple"){ + var tmpBool = false, resultBool = false; + if(start > end){ + tmpBool = start <= 0; + } + if(tmpBool){ + return Sk.builtin.bool.true$; + } + var it, i; + for (it = Sk.abstr.iter(suffix), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { + if(!tmpBool){ + tmpBool = substr.indexOf(i.v, substr.length - i.v.length) !== -1; + } + resultBool = resultBool || tmpBool; + if(resultBool){ + break; + } + } + return resultBool?Sk.builtin.bool.true$ : Sk.builtin.bool.false$; + } + + if(suffix.v == "" && start > end && end >= 0){ + return Sk.builtin.bool.false$; + } + return new Sk.builtin.bool(substr.indexOf(suffix.v, substr.length - suffix.v.length) !== -1); }; Sk.builtin.str.methods.replace = function (self, oldS, newS, count) { @@ -942,6 +1055,7 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { var leftAdjust; var zeroPad; var i; + fieldWidth = Sk.builtin.asnum$(fieldWidth); precision = Sk.builtin.asnum$(precision); @@ -1059,6 +1173,10 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { for (j = totLen; j < fieldWidth; ++j) { r = r + " "; } + if (Sk.__future__.python3) { + r += prefix; + prefix = ""; + } } else { for (j = totLen; j < fieldWidth; ++j) { prefix = " " + prefix; @@ -1067,7 +1185,6 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { } return prefix + r; }; - //print("Rhs:",rhs, "ctor", rhs.constructor); if (rhs.constructor === Sk.builtin.tuple) { value = rhs.v[i]; @@ -1083,7 +1200,13 @@ Sk.builtin.str.prototype.nb$remainder = function (rhs) { } base = 10; if (conversionType === "d" || conversionType === "i") { - return handleWidth(formatNumber(value, 10)); + let tmpData = formatNumber(value, base); + if (tmpData[1] === undefined){ + throw new Sk.builtin.TypeError("%"+ conversionType+" format: a number is required, not "+ Sk.abstr.typeName(value)); + } + let r = tmpData[1]; + tmpData[1] = r.indexOf(".") !== -1 ? parseInt(r, 10).toString() : r; + return handleWidth(tmpData); } else if (conversionType === "o") { return handleWidth(formatNumber(value, 8)); } else if (conversionType === "x") { diff --git a/src/super.js b/src/super.js index c53df0da2c..717721593c 100644 --- a/src/super.js +++ b/src/super.js @@ -134,7 +134,7 @@ Sk.builtin.super_ = Sk.abstr.buildNativeClass("super", { the normal case; the return value is obj.__class__. /* Check for first bullet above (special case) */ - if (Sk.builtin.checkClass(obj) && obj.ob$type.$isSubType(type)) { + if (Sk.builtin.checkClass(obj) && obj.$isSubType(type)) { return obj; } /* Normal case */ diff --git a/src/symtable.js b/src/symtable.js index 9a9d90edc3..3e6b8dae57 100644 --- a/src/symtable.js +++ b/src/symtable.js @@ -491,6 +491,9 @@ SymbolTable.prototype.visitStmt = function (s) { if (s.decorator_list) { this.SEQExpr(s.decorator_list); } + if (s.args.kw_defaults) { + this.SEQExpr(s.args.kw_defaults); + } this.enterBlock(s.name.v, FunctionBlock, s, s.lineno); this.visitArguments(s.args, s.lineno); this.SEQStmt(s.body); diff --git a/src/tuple.js b/src/tuple.js index b41a62170f..8693b6ef0e 100644 --- a/src/tuple.js +++ b/src/tuple.js @@ -30,6 +30,7 @@ Sk.builtin.tuple = Sk.abstr.buildNativeClass("tuple", { /** * @param {Array} args * @param {Array=} kwargs + * @ignore */ tp$new: function (args, kwargs) { // this = Sk.builtin.prototype or a prototype that inherits from Sk.builtin.tuple.prototype diff --git a/src/type.js b/src/type.js index 6a41344fcd..2a22e75937 100644 --- a/src/type.js +++ b/src/type.js @@ -20,9 +20,18 @@ if (Sk.builtin === undefined) { Sk.builtin.type = function type() { Sk.asserts.assert(false, "calling new Sk.builtin.type is not safe"); }; - /** @typedef {Sk.builtin.type|Function} */ var typeObject; + +Object.defineProperties(Sk.builtin.type.prototype, /**@lends {Sk.builtin.type.prototype}*/ { + call: { value: Function.prototype.call }, + apply: { value: Function.prototype.apply }, + ob$type: { value: Sk.builtin.type, writable: true }, + tp$name: { value: "type", writable: true }, + tp$base: { value: Sk.builtin.object, writable: true }, + sk$type: { value: true }, +}); + Sk.builtin.type.prototype.tp$doc = "type(object_or_name, bases, dict)\ntype(object) -> the object's type\ntype(name, bases, dict) -> a new type"; /** From 2afd4b2560016a04b0d468e92eb064a50d88e01b Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 6 Aug 2020 04:54:17 -0400 Subject: [PATCH 39/68] Pedal no longer lives in Skulpt --- src/lib/cs1014/__init__.py | 0 src/lib/cs1014/dictionaries.py | 782 ---------- src/lib/cs1014/input_mistakes.py | 24 - src/lib/cs1014/tests/__init__.py | 0 src/lib/cs1014/tests/test_dictionary.py | 1210 --------------- src/lib/pedal/__init__.py | 24 - src/lib/pedal/assertions/__init__.py | 13 - src/lib/pedal/assertions/assertions.py | 625 -------- src/lib/pedal/assertions/organizers.py | 167 --- src/lib/pedal/assertions/setup.py | 116 -- src/lib/pedal/assertions/tests.py | 147 -- src/lib/pedal/cait/__init__.py | 16 - src/lib/pedal/cait/ast_helpers.py | 59 - src/lib/pedal/cait/ast_map.py | 275 ---- src/lib/pedal/cait/cait_api.py | 272 ---- src/lib/pedal/cait/cait_node.py | 516 ------- src/lib/pedal/cait/stretchy_tree_matching.py | 666 --------- src/lib/pedal/mistakes/__init__.py | 0 src/lib/pedal/mistakes/instructor_append.py | 136 -- src/lib/pedal/mistakes/instructor_filter.py | 53 - .../pedal/mistakes/instructor_histogram.py | 124 -- .../pedal/mistakes/instructor_iteration.py | 153 -- src/lib/pedal/mistakes/iteration_context.py | 1194 --------------- src/lib/pedal/plugins/__init__.py | 12 - .../pedal/plugins/blockpy_compatibility.py | 106 -- src/lib/pedal/plugins/cmd_line.py | 129 -- src/lib/pedal/plugins/grade_magic.py | 389 ----- .../pedal/plugins/test_reference_solution.py | 142 -- src/lib/pedal/plugins/vpl.py | 148 -- src/lib/pedal/plugins/vpl_safe_runner.py | 10 - src/lib/pedal/plugins/vpl_unittest.py | 112 -- src/lib/pedal/questions/__init__.py | 123 -- src/lib/pedal/questions/design.md | 92 -- src/lib/pedal/questions/graders.py | 106 -- src/lib/pedal/questions/loader.py | 496 ------- src/lib/pedal/questions/setup.py | 42 - src/lib/pedal/report/__init__.py | 8 - src/lib/pedal/report/feedback.py | 102 -- src/lib/pedal/report/imperative.py | 94 -- src/lib/pedal/report/report.py | 164 --- src/lib/pedal/resolvers/__init__.py | 19 - src/lib/pedal/resolvers/core.py | 22 - src/lib/pedal/resolvers/readme.md | 3 - src/lib/pedal/resolvers/sectional.py | 77 - src/lib/pedal/resolvers/simple.py | 156 -- src/lib/pedal/sandbox/__init__.py | 34 - src/lib/pedal/sandbox/compatibility.py | 124 -- src/lib/pedal/sandbox/exceptions.py | 191 --- src/lib/pedal/sandbox/messages.py | 61 - src/lib/pedal/sandbox/mocked.py | 336 ----- src/lib/pedal/sandbox/result.py | 369 ----- src/lib/pedal/sandbox/sandbox.py | 726 --------- src/lib/pedal/sandbox/timeout.py | 2 - src/lib/pedal/sandbox/tracer.py | 108 -- src/lib/pedal/sk_mod_instructor_list.txt | 40 - src/lib/pedal/source/__init__.py | 110 -- src/lib/pedal/source/sections.py | 134 -- src/lib/pedal/tifa/.gitignore | 1 - src/lib/pedal/tifa/__init__.py | 104 -- src/lib/pedal/tifa/builtin_definitions.py | 212 --- src/lib/pedal/tifa/identifier.py | 24 - src/lib/pedal/tifa/messages.py | 167 --- src/lib/pedal/tifa/readme.md | 5 - src/lib/pedal/tifa/state.py | 77 - src/lib/pedal/tifa/tifa.py | 1293 ----------------- src/lib/pedal/tifa/type_definitions.py | 599 -------- src/lib/pedal/tifa/type_operations.py | 153 -- src/lib/pedal/toolkit/__init__.py | 0 src/lib/pedal/toolkit/files.py | 56 - src/lib/pedal/toolkit/functions.py | 401 ----- src/lib/pedal/toolkit/imports.py | 25 - src/lib/pedal/toolkit/plotting.py | 184 --- src/lib/pedal/toolkit/printing.py | 22 - src/lib/pedal/toolkit/records.py | 32 - src/lib/pedal/toolkit/signatures.py | 426 ------ src/lib/pedal/toolkit/upload.py | 54 - src/lib/pedal/toolkit/utilities.py | 361 ----- 77 files changed, 15555 deletions(-) delete mode 100644 src/lib/cs1014/__init__.py delete mode 100644 src/lib/cs1014/dictionaries.py delete mode 100644 src/lib/cs1014/input_mistakes.py delete mode 100644 src/lib/cs1014/tests/__init__.py delete mode 100644 src/lib/cs1014/tests/test_dictionary.py delete mode 100644 src/lib/pedal/__init__.py delete mode 100644 src/lib/pedal/assertions/__init__.py delete mode 100644 src/lib/pedal/assertions/assertions.py delete mode 100644 src/lib/pedal/assertions/organizers.py delete mode 100644 src/lib/pedal/assertions/setup.py delete mode 100644 src/lib/pedal/assertions/tests.py delete mode 100644 src/lib/pedal/cait/__init__.py delete mode 100644 src/lib/pedal/cait/ast_helpers.py delete mode 100644 src/lib/pedal/cait/ast_map.py delete mode 100644 src/lib/pedal/cait/cait_api.py delete mode 100644 src/lib/pedal/cait/cait_node.py delete mode 100644 src/lib/pedal/cait/stretchy_tree_matching.py delete mode 100644 src/lib/pedal/mistakes/__init__.py delete mode 100644 src/lib/pedal/mistakes/instructor_append.py delete mode 100644 src/lib/pedal/mistakes/instructor_filter.py delete mode 100644 src/lib/pedal/mistakes/instructor_histogram.py delete mode 100644 src/lib/pedal/mistakes/instructor_iteration.py delete mode 100644 src/lib/pedal/mistakes/iteration_context.py delete mode 100644 src/lib/pedal/plugins/__init__.py delete mode 100644 src/lib/pedal/plugins/blockpy_compatibility.py delete mode 100644 src/lib/pedal/plugins/cmd_line.py delete mode 100644 src/lib/pedal/plugins/grade_magic.py delete mode 100644 src/lib/pedal/plugins/test_reference_solution.py delete mode 100644 src/lib/pedal/plugins/vpl.py delete mode 100644 src/lib/pedal/plugins/vpl_safe_runner.py delete mode 100644 src/lib/pedal/plugins/vpl_unittest.py delete mode 100644 src/lib/pedal/questions/__init__.py delete mode 100644 src/lib/pedal/questions/design.md delete mode 100644 src/lib/pedal/questions/graders.py delete mode 100644 src/lib/pedal/questions/loader.py delete mode 100644 src/lib/pedal/questions/setup.py delete mode 100644 src/lib/pedal/report/__init__.py delete mode 100644 src/lib/pedal/report/feedback.py delete mode 100644 src/lib/pedal/report/imperative.py delete mode 100644 src/lib/pedal/report/report.py delete mode 100644 src/lib/pedal/resolvers/__init__.py delete mode 100644 src/lib/pedal/resolvers/core.py delete mode 100644 src/lib/pedal/resolvers/readme.md delete mode 100644 src/lib/pedal/resolvers/sectional.py delete mode 100644 src/lib/pedal/resolvers/simple.py delete mode 100644 src/lib/pedal/sandbox/__init__.py delete mode 100644 src/lib/pedal/sandbox/compatibility.py delete mode 100644 src/lib/pedal/sandbox/exceptions.py delete mode 100644 src/lib/pedal/sandbox/messages.py delete mode 100644 src/lib/pedal/sandbox/mocked.py delete mode 100644 src/lib/pedal/sandbox/result.py delete mode 100644 src/lib/pedal/sandbox/sandbox.py delete mode 100644 src/lib/pedal/sandbox/timeout.py delete mode 100644 src/lib/pedal/sandbox/tracer.py delete mode 100644 src/lib/pedal/sk_mod_instructor_list.txt delete mode 100644 src/lib/pedal/source/__init__.py delete mode 100644 src/lib/pedal/source/sections.py delete mode 100644 src/lib/pedal/tifa/.gitignore delete mode 100644 src/lib/pedal/tifa/__init__.py delete mode 100644 src/lib/pedal/tifa/builtin_definitions.py delete mode 100644 src/lib/pedal/tifa/identifier.py delete mode 100644 src/lib/pedal/tifa/messages.py delete mode 100644 src/lib/pedal/tifa/readme.md delete mode 100644 src/lib/pedal/tifa/state.py delete mode 100644 src/lib/pedal/tifa/tifa.py delete mode 100644 src/lib/pedal/tifa/type_definitions.py delete mode 100644 src/lib/pedal/tifa/type_operations.py delete mode 100644 src/lib/pedal/toolkit/__init__.py delete mode 100644 src/lib/pedal/toolkit/files.py delete mode 100644 src/lib/pedal/toolkit/functions.py delete mode 100644 src/lib/pedal/toolkit/imports.py delete mode 100644 src/lib/pedal/toolkit/plotting.py delete mode 100644 src/lib/pedal/toolkit/printing.py delete mode 100644 src/lib/pedal/toolkit/records.py delete mode 100644 src/lib/pedal/toolkit/signatures.py delete mode 100644 src/lib/pedal/toolkit/upload.py delete mode 100644 src/lib/pedal/toolkit/utilities.py diff --git a/src/lib/cs1014/__init__.py b/src/lib/cs1014/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/lib/cs1014/dictionaries.py b/src/lib/cs1014/dictionaries.py deleted file mode 100644 index 705cd43994..0000000000 --- a/src/lib/cs1014/dictionaries.py +++ /dev/null @@ -1,782 +0,0 @@ -from pedal.report.imperative import gently_r, explain_r -from pedal.cait.cait_api import * -from pedal.mistakes.instructor_append import app_assign - - -# dict_acc_group -def dict_acc_group(all_keys, unused_keys, used_keys): - print_dict_key(all_keys) - var_instead_of_key(all_keys) - parens_in_dict(all_keys) - missing_key(used_keys) - str_list(all_keys) - dict_parens_brack() - comma_dict_acc() - var_key(all_keys) - miss_dict_acc() - comp_in_dict_acc() - key_comp(all_keys) - col_dict() - wrong_keys(unused_keys) - - -# dict_list_group -def dict_list_group(all_keys): - list_str_dict(all_keys) - list_var_dict_acc() - list_str_as_list_var(all_keys) - fetch_acc_dict(all_keys) - list_as_dict() - iter_as_key(all_keys) - iter_prop_dict_acc() - - append_and_sum() - - dict_out_of_loop(all_keys) - dict_access_not_in_loop() - no_dict_in_loop() - app_assign() - - -# dict_decision -def dict_decision_group(all_keys, c_value, num_slices): - func_filter(all_keys) - filt_key(c_value, num_slices) - compare_key(c_value) - str_equality() - fetch_acc_dict([c_value]) - - -# dict_plot -def dict_plot_group(): - show_args() - dict_plot() - - -# dict_chain -def dict_chain_group(key_sets): - for key_set in key_sets: - key_order(key_set) - key_order_unchained(key_set) - - -def var_check(expr, keys=None): - """ - - :param expr: Expression to be evaluated - :type expr: CaitNode - :param keys: List of keys - :type keys: list of Str - :return: Key value if expression was a name node assigned a key value or if the value is a string key value, - otherwise returns False - :rtype: bool/Str - """ - if expr.is_ast('Str') and (keys is None or expr.value in keys): - return expr.value - elif expr.is_ast("Name"): - matches = find_matches("{} = __key__".format(expr.value)) # TODO: Relies on .value returning id for Name nodes - for match in matches: - __key__ = match["__key__"] - if __key__.is_ast('Str') and (keys is None or __key__.value in keys): - return __key__.value - return False - - -def list_dict_indices(expr): - """ - Takes the first key slice of a dictionary and returns a list of all the slices. - :param expr: a single key slice value at the one level of slicing - :type expr: CaitNode - :return: A list of index ast nodes (each slice), .value should get the ast_node unit that was used for the slice - :rtype: CaitNode(Index) - """ - return expr.parent.parent.parent.find_all('Index') # TODO: Relies on AST Structure - - -def uncover_type(name, tifa_type): - """ - - :param name: A Name Ast Node whose type we are looking through - :type name: CaitNode (Name) - :param tifa_type: The data type of the item - :type tifa_type: str - :return: the data_state object representing when name was of the specified type - :rtype: Tifa State or None - """ - state = name.get_data_state() - if name.was_type(tifa_type) and state: - while state and str(state.type) != tifa_type: - state = state.trace[0] - return state - return None - - -def dict_detect(expr): - if expr.find_match("_var_[__expr__]", use_previous=False): - return expr - elif expr.is_ast("Name"): - # TODO: This match is buggy because in the case slicing, the dictionary dives deeper instead of making siblings - matches = find_matches("{} = __expr__".format(expr.id)) - submatch = None - for match in matches: - __expr__ = match["__expr__"] - submatch = __expr__.find_match("__expr__[__expr2__]", use_previous=False) - if submatch: - return __expr__ - # TODO: Rework to chase down all indirect accesses part of the same chain as a string (and then CaitNode) - # fall through return None - return None - - -# dict_hard_codes -def dict_hard_codes_group(print_vals, list_vals): - hard_coding(print_vals) - hard_coded_list(list_vals) - - -# dict_hard_codes -def hard_coding(val_list): - message = ("Please show code that makes the computer extract " - "the value from the dictionary.") - code = "hard_code" - tldr = "Printing raw value" - # Pattern 1 possibility - matches = find_matches("print(__exp__)") - for match in matches: - __exp__ = match["__exp__"] - value = __exp__.value - if value in val_list: - return explain_r(message, code, label=tldr) - - # Pattern 2 possibility - matches = find_matches("__exp__\n" - "print(_var_)") - for match in matches: - __exp__ = match["__exp__"] - _var_ = match["_var_"] - submatches = __exp__.find_matches("_var_ = __exp2__") - for submatch in submatches: - __exp2__ = submatch["__exp2__"] - value = __exp2__.value - if value in val_list: - return explain_r(message, code, label=tldr) - return False - - -# dict_acc_group -def print_dict_key(keys): - message = ('You\'ve printed the dictionary key "{}" instead of using an extracted value and ' - 'printing it. Use the Dictionary access syntax to print the value associated with a key') - code = "dict_k_print" - tldr = "Printing key, not value" - matches = find_matches("print(__str__)") - matches += find_matches("print([__str__])") - - for match in matches: - __str__ = match["__str__"] - key = var_check(__str__, keys) - if key: - return explain_r(message.format(key), code, label=tldr) - return False - - -# dict_acc_group -def var_instead_of_key(keys): - message = ("It looks like you are trying to use ({}) as a dictionary key. " - "Use the dictionary access syntax to get values from a dictionary") - code = "var_as_k" - tldr = "Using Variable instead of key" - matches = find_matches("_var_") - matches += find_matches("[_var_]") - for match in matches: - _var_ = match["_var_"] - if _var_.id in keys: - submatch = find_match("_dict_['{}']".format(_var_.id)) - submatch2 = find_match("{} = ___".format(_var_.id)) - if submatch is None and submatch2 is None: - # If we don't find a dictionary access using this key and - # we don't see that this variable is assigned to a value... - return explain_r(message.format(_var_.id), code, label=tldr) - return False - - -# dict_acc_group -def parens_in_dict(keys): - """ - Checks fr the mistsake of using parenthesis as a dictionary access - :param keys: List of keys - :type keys: list of Str - :return: Feedback String - :rtype: Str - """ - message = ('It seems like you are having trouble with dictionary syntax. The dictionary key "{}"' - "should use brackets.") - code = "par_dict" - tldr = "Not Using Dictionary Brackets" - matches = find_matches("_var_(__str__)") - for match in matches: - __str__ = match['__str__'] - _var_ = match['_var_'] - key = var_check(__str__, keys) - if key and data_state(_var_.id): - return explain_r(message.format(key), code, label=tldr) - return False - - -# dict_list_group -def list_as_dict(): - message = ("The list of Dictionaries {} is not itself a dictionary. " - "To access key-value pairs of the dictionaries in the list, " - "you need to access each dictionary in the list one at a time.") - code = "list_dict" - tldr = "List is not a dictionary" - matches = find_matches("_list_[__exp__]") - for match in matches: - _list_ = match['_list_'] - type_check = uncover_type(_list_, "ListType") - if type_check and str(type_check.type.subtype) == "DictType": - return explain_r(message.format(_list_.id), code, label=tldr) - return False - - -# dict_list_group -def dict_out_of_loop(keys): - message = ("Remember that a list of dictionaries, like {}, " - "is still a list of individual items. Each dictionary needs to be accessed with " - "the appropriate key-value pair one at a time.") - code = "dict_out_loop" - tldr = "Dictionary Access Outside of Loop" - matches = find_matches("__exp__\n" - "for ___ in _var_:\n" - " pass") - matches += find_matches("for ___ in _var_:\n" - " pass\n" - "__exp__\n") - for match in matches: - __exp__ = match['__exp__'] - _var_ = match['_var_'] - submatches = __exp__.find_matches("{var}[__str__]".format(var=_var_.id)) - for submatch in submatches: - __str__ = submatch['__str__'] - if __str__.is_ast("Str") and __str__.value in keys: - return explain_r(message.format(_var_.id), code, label=tldr) - return False - - -# dict_acc_group -def wrong_keys(unused_keys): - message = 'This problem does not require the key "{}".\n' - code = "unused_key" - tldr = "Unnecessary Key Usage" - - matches = find_matches("_var_[__str__]") - for match in matches: - __str__ = match["__str__"] - indices = list_dict_indices(__str__) - for index in indices: - __str__ = index.value - key = var_check(__str__, unused_keys) - if key: - return explain_r(message.format(key), code, label=tldr) - return False - - -# dict_list_group -def dict_access_not_in_loop(): - message = ("You haven't used the dictionary access syntax in a for loop. " - "Remember that a list of dictionaries is still a list of individual items. " - "Each dictionary needs to be accessed with the appropriate key-value pair one at a time.") - code = "dict_acc_loop" - tldr = "Dictionary access not in loop" - - matches = find_matches("for ___ in ___:\n" - " __exp__") - for match in matches: - submatches = match["__exp__"].find_matches("_var_[__str__]") - if submatches: - return False - return explain_r(message, code, label=tldr) - - -def hard_coded_list(val_list): - message = ("In later abstractions, it's not possible to view the values of a specific key in a list." - "You should use a dictionary key-value pair to access values in the list of dictionaries.") - code = "hard_list" - tldr = "Don't use raw list" - matches = find_matches("[__exp__]") - for match in matches: - __exp__ = match['__exp__'].parent - if __exp__.ast_name == "List": - try: - vals = sum([x.value for x in __exp__.elts]) - if sum(val_list) == vals: - return explain_r(message, code, label=tldr) - except TypeError: - pass # This should be the only error - return False - - -# dict_list_group -def iter_as_key(keys): - message = ("It looks like you are using the iteration variable {}" - " to access a value of a specific key in a dictionary. " - "To access a key-value from a list of dictionaries, use ") - code = "iter_key" - tldr = "Iteration variable is not key" - matches = find_matches("for _var_ in ___:\n" - " pass") - for match in matches: - _var_ = match['_var_'] - submatches = find_matches("_var2_[__str__]") - missing = True - for submatch in submatches: - __str__ = submatch["__str__"] - if __str__.is_ast("Str") and __str__.value == _var_.id: - missing = False - break - if missing and _var_.id in keys: - return explain_r(message.format(_var_.id), code, label=tldr) - return False - - -# dict_list_group -def list_str_as_list_var(keys): - message = ("The list variable in an iteration can only take lists. " - "To grab individual values in a list of dictionaries, " - "you need to use the appropriate key for each dictionary.") - code = "list_var_dict" - tldr = "List variable cannot filter" - matches = find_matches("for ___ in [__str__]:\n" - " pass") - for match in matches: - __str__ = match["__str__"] - if __str__.is_ast("Str") and __str__.value in keys: - return explain_r(message, code, label=tldr) - return False - - -# dict_list_group -def append_and_sum(): - message = ("It looks like you're trying to build a list and " - "then calculate a value. While this will get you a " - "correct answer, you can calculate the value directly instead of first building a list.") - code = "app_sum" - tldr = "Unnecessary append and sum" - matches = find_match("for ___ in ___:\n" - " _var_.append()\n" - "for ___ in _var_:\n" - " ___ = ___ + ___") - if matches: - return explain_r(message, code, label=tldr) - return False - - -# dict_list_group -def iter_prop_dict_acc(): - message = ("Improper usage of iteration variable." - "The for statement gives the iteration variable a value, " - "in this case, a dictionary. That dictionary can only be accessed in the body of the iteration.") - code = "iter_dict_acc" - tldr = "Iteration variable only initializes" - match = find_match("for _var_[__str__] in ___:\n" - " pass") - if match: - return explain_r(message, code, label=tldr) - return False - - -# dict_list_group -def list_str_dict(keys): - message = ("When using dictionaries with iteration, the list cannot just be a key " - 'value like "{}", it must be the list of dictionaries.') - code = "list_str" - tldr = "List variable is string" - matches = find_matches("for ___ in __str__:\n" - " pass") - for match in matches: - __str__ = match['__str__'] - if __str__.is_ast("Str") and __str__.value in keys: - return explain_r(message.format(__str__.value), code, label=tldr) - return False - - -# dict_acc_group -def missing_key(keys): - """ - Checks if student is missing a key - - TODO: Should be good if run AFTER the var_instead_of_key check, although it doesn't appear to catch a key that's - been assigned as the value of an unused variable. - :param keys: list of keys - :type keys: list of Str - :return: Feedback String - :rtype: Str - """ - message = "You seem to be missing the following dictionary key(s):
      {}
    " - code = "miss_key" - tldr = "Missing necessary keys" - key_list = "" - first = False - for key in keys: - matches = find_matches("\"{}\"".format(key)) - if not matches: - if not first: - key_list += ", " - key_list += '
  • "' + key + '"
  • ' - if key_list != "": - return explain_r(message.format(key_list), code, label=tldr) - return False - - -def blank_key(keys): - message = "You seem to be missing the following dictionary keys:
      {}
    " - code = "blank_key" - tldr = "Missing Key" - key_list = "" - - first = False - for key in keys: - if not find_match("_var_['{}']".format(key)): - if not first: - key_list += ", " - key_list += '
  • "' + key + '"
  • ' - - if key_list != "": - return explain_r(message.format(key_list), code, label=tldr) - - -# dict_acc_group -def dict_parens_brack(): - message = ("It looks like you are trying to dictionary access {}. " - "The dictionary access syntax does not require parenthesis.") - code = "dict_parbrack" - tldr = "Improper dictionary access" - matches = find_matches("_var_([__str1__][__str2__])") - matches += find_matches("_var_([__str1__])") - for match in matches: - _var_ = match['_var_'] - __str1__ = match["__str1__"] - __str2__ = __str1__ - try: - __str2__ = match["__str2__"] - except KeyError: - pass - if __str1__.is_ast("Str") and __str2__.is_ast("Str") and data_state(_var_.id): - return explain_r(message.format(_var_.id), code, label=tldr) - return False - - -# dict_acc_group -def comma_dict_acc(): - message = ("It looks like you are trying to dictionary access {}. " - "Unlike with initializing dictionaries, keys don't need to be separated with commas " - "when accessing dictionary contents.") - code = "comma_dict" - tldr = "Improper dictionary access" - matches = find_matches("__exp__,[__str2__]") - for match in matches: - submatch = match['__exp__'].find_match("_dict_[__str1__]") - if submatch: - return explain_r(message.format(submatch['_dict_'].id), code, label=tldr) - return False - - -# dict_list_group -def no_dict_in_loop(): - message = "When working with a list of dictionaries, you need to use a dictionary access in your iteration." - code = "no_dict_loop" - tldr = "Missing dictionary access loop" - - matches = find_matches("for _item_ in _list_:\n" - " __expr__") - for match in matches: - _item_ = match['_item_'] - submatches = match['__expr__'].find_matches("_item_[__str__]") - for submatch in submatches: - key = var_check(submatch["__str__"]) - if key: - return False - return explain_r(message, code, label=tldr) - - -# dict_decision -def func_filter(keys): - message = "Please do not modify the function call to retrieve the data." - code = "func_filt" - tldr = "Attempting to filter using fetch" - matches = find_matches("_var_.get_weather(__str__)") - for match in matches: - __str__ = match["__str__"] - if __str__.value in keys: # TODO: Relies on .value returning id for Name nodes - return explain_r(message, code, label=tldr) - return False - - -# dict_acc_group -def str_list(keys): - message = ('If you are trying to use a string such as "{}" as a dictionary key, ' - 'it needs to be prefaced with a dictionary') - code = "str_list" - tldr = "String list used instead of Dictionary" - - for key in keys: - if find_match("['{}']".format(key)): - return explain_r(message.format(key), code, label=tldr) - return False - - -# dict_list_group -def list_var_dict_acc(): - message = ("The for statement only specifies a list target, in this case, a list of dictionaries. It does not " - "operate on the entire list. Keys should be used on the individual dictionaries of the list.") - code = "l_var_dacc" - tldr = "List variable cannot be dictionary accessed" - - matches = find_matches("for ___ in _var_[__str__]:\n" - " pass") - if matches: - return explain_r(message, code, label=tldr) - return False - - -# dict_acc_group -def key_comp(keys): - message = ('The strings "{}" and "{}" are keys. ' - 'Dictionary keys do not need to be compared to anything as they ' - 'are not filtering data. Dictionary keys are only used to access existing data.') - code = "key_comp" - tldr = "Comparing Keys" - """ - matches = find_matches("for _var_ in ___:\n" - " if _var_[__str1__] == __str2__:\n" - " pass") - """ - matches = find_matches("for _var_ in ___:\n" - " if __expr1__ == __expr2__:\n" - " pass") - for match in matches: - __str1__ = match["__expr1__"] - __str2__ = match["__expr2__"] - submatch1 = dict_detect(__str1__) - submatch2 = dict_detect(__str2__) - # __str1__ = match["__str1__"] - if submatch1: - __str1__ = submatch1.find_match("_var_[__str1__]", use_previous=False)["__str1__"] - elif submatch2: - __str2__ = submatch2.find_match("_var_[__str2__]", use_previous=False)["__str2__"] - if submatch1 or submatch2: - value1 = var_check(__str1__, keys) - value2 = var_check(__str2__, keys) - if value1 and value2: - return explain_r(message.format(__str1__.value, __str2__.value), code, label=tldr) - return False - - -# dict_acc_group -def col_dict(): - message = "When using multiple keys, each key should have it's own set of brackets." - code = "col_dict" - tldr = "Improper Dictionary Access" - - matches = find_matches("_var_[__str1__: __str2__]") - if matches: - return explain_r(message, code, label=tldr) - return False - - -# dict_acc_group -def var_key(keys): - # TODO: Could use this method for other methods to check if the code needs to use the value of the variable. - # In other words, if we have a variable in place of a key AND this test fails, it means that they have an - # initialized variable whose assigned value we should check as we are able to (statically). - message = ("It looks like you are trying to use {} as a key. Dictionary keys are string values. " - "Variable names don't have a meaning to a computer.") - code = "var_key" - tldr = "Variables are not keys" - - matches = find_matches("_var_[_key_]") - for match in matches: - _key_ = match['_key_'] - if _key_.id in keys and not _key_.was_type("StrType"): - return explain_r(message.format(_key_.id), code, label=tldr) - return False - - -# dict_plot -def key_order(keys): - # TODO: Is it possible to run this test after confirming (through other tests) that there are no unused keys and - # that all keys used are the correct keys, such that the feedback message can explicitly address JUST the case of - # wrong order? - message = "It looks like you aren't using the correct keys, or the correct key order. Double check your data map." - code = "key_order_c" - tldr = "Wrong key order" - - construct = None - # Assemble chain of dictionary slicing - find_chain = "_var_" - for a_slice in range(len(keys)): - find_chain += "[__str{}__]".format(a_slice) - # If we find a chain of dictionary accesses - if find_match(find_chain): - # Assemble a new match pattern using the provided key order - construct = "_var_" - for key in keys: - construct += "['{}']".format(key) - - if construct: - # check if we have a set of keys of the proper order - matches = find_matches(construct) - if not matches: - return explain_r(message, code, label=tldr) - return False - - -# dict_plot -def key_order_unchained(keys): - message = "It looks like you aren't using the correct keys, or the correct key order. Double check your data map." - code = "key_order_u" - tldr = "Wrong key order" - - construct = None - find_chain = "" - for a_slice in range(len(keys)): - find_chain += "_var{a2}_ = _var{a1}_[__str{a1}__]\n".format(a2=a_slice + 1, a1=a_slice) - if find_match(find_chain): - construct = "" - count = 0 - for key in keys: - construct += "_var{a2}_ = _var{a1}_['{key}']\n".format(a2=count + 1, a1=count, key=key) - count += 1 - - if construct: - matches = find_matches(construct) - if not matches: - return explain_r(message, code, label=tldr) - return False - - -# dict_decision -def filt_key(c_value, num_slices): - message = ('It looks like you\'re using "{c_value}" as a dictionary key to filter data. ' - "Dictionary keys don't filter data, they only access data that's already there. " - "You should be comparing data retrieved from the dictionary to '{c_value}'") - code = "filt_key" - tldr = "Attempting filter as Key" - - construct = "_var_" - for a_slice in range(num_slices): - construct += "[__str{}__]".format(a_slice) - matches = find_matches(construct) - for match in matches: - for num in range(a_slice + 1): - value = match["__str{}__".format(num)] - if value.is_ast("Str") and value.value == c_value: - return explain_r(message.format(c_value=value), code, label=tldr) - return False - - -# dict_acc_group -def miss_dict_acc(): - message = ("You are missing something that looks like a dictionary access. " - "In this unit, you should be using dictionary access") - code = "miss_acc" - tldr = "Missing Dictionary Access" - - if not find_matches("_var_[__str1__]"): - return explain_r(message, code, label=tldr) - return False - - -# dict_decision -def compare_key(c_value): - message = ('In this problem, "{}" is not a key, ' - 'but something you should compare against.'.format(c_value)) - code = "comp_key" - tldr = "Using filter value as key" - - matches = find_matches("__exp0__ == __exp1__") - for match in matches: - for num in range(2): - __exp__ = match["__exp{}__".format(num)] - submatches = __exp__.find_matches("[__str__]") - for submatch in submatches: - __str__ = submatch["__str__"] - if __str__.is_ast("Str") and __str__.value == c_value: - return explain_r(message, code, label=tldr) - return False - - -# dict_decision -def str_equality(): - message = ('You are comparing two different string values, "{}" and "{}". While dictionary keys are strings, ' - "they are only interpreted by the computer as keys when used with the dictionary access syntax") - code = "str_eq" - tldr = "Comparing equality of raw strings" - - matches = find_matches("__str1__ == __str2__") - for match in matches: - __str1__ = match["__str1__"] - __str2__ = match["__str2__"] - if __str1__.is_ast("Str") and __str2__.is_ast("Str"): - return explain_r(message.format(__str1__.value, __str2__.value), code, label=tldr) - return False - - -# dict_list_group and dict_decision_group -def fetch_acc_dict(values): - message = ("The code to fetch the list of dictionaries, {}.{}, cannot be used to select data. " - "Selection of data should be done with an if statement") - code = "fetch_acc" - tldr = "Malformed Dictionary List Fetch" - - matches = find_matches("_var_._func_[__str__]") - for match in matches: - _var_ = match["_var_"].id - _func_ = match["_func_"].id - __str__ = match["__str__"] - if __str__.is_ast("Str") and __str__.value in values: - return explain_r(message.format(_var_, _func_), code, label=tldr) - return False - - -# dict_plot -def show_args(): - # TODO: Add this to plotting mistakes? - message = ("The plt.show function only tells the computer to display the plot. " - "If you want to modify the plot, use other available plotting functions.") - code = "show_args" - tldr = "Show takes no arguments" - - matches = find_matches("plt.show(__exp__)") - if matches: - return explain_r(message, code, label=tldr) - return False - - -# dict_plot -def dict_plot(): - message = ("The list {} is a list of dictionaries. plt.plot only accepts a list" - " of numbers. You need to extract the numbers from the list of dictionaries first.") - code = "dict_plot" - tldr = "Plotting list of Dictionaries" - - matches = find_matches("plt._func_(_var_)") - for match in matches: - _var_ = match["_var_"] - var_state = _var_.get_data_state() - if var_state and str(var_state.type) == "ListType" and str(var_state.type.subtype) == "DictType": - return explain_r(message.format(_var_.id), code, label=tldr) - return False - - -# dict_acc_group -def comp_in_dict_acc(): - message = ("You are using a boolean expression in a dictionary access. Remember that the dictionary " - "access takes a key and provides a value. The comparison should be made with the value, not the key.") - code = "comp_acc" - tldr = "Comparison in key access" - - matches = find_matches("_var_[__exp__][__exp2__ == __exp3__]") - if matches: - return explain_r(message, code, label=tldr) - return False diff --git a/src/lib/cs1014/input_mistakes.py b/src/lib/cs1014/input_mistakes.py deleted file mode 100644 index 586525f8e7..0000000000 --- a/src/lib/cs1014/input_mistakes.py +++ /dev/null @@ -1,24 +0,0 @@ -from pedal.report.imperative import gently_r, explain_r -from pedal.cait.cait_api import * - - -def unnecessary_cast(needed_casts): - """ - - Args: - needed_casts: List of casts that are necessary to this problem - - Returns: - - """ - message = "Converting to {} is unnecessary in this problem" - code = "ex_cast" - tldr = "Unnecessary Conversion" - - known_casts = ["float", "int", "str"] - matches = find_matches("_cast_(___)") - for match in matches: - user_cast = match["_cast_"].id - if user_cast not in needed_casts and user_cast in known_casts: - return explain_r(message.format(user_cast), code, label=tldr) - return False diff --git a/src/lib/cs1014/tests/__init__.py b/src/lib/cs1014/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/lib/cs1014/tests/test_dictionary.py b/src/lib/cs1014/tests/test_dictionary.py deleted file mode 100644 index 29a183c573..0000000000 --- a/src/lib/cs1014/tests/test_dictionary.py +++ /dev/null @@ -1,1210 +0,0 @@ -import sys -import os - -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) - -from tests.mistake_test_template import * -from CS1014.dictionaries import * -from CS1014.input_mistakes import * -from pedal.mistakes.iteration_context import all_labels_present -from pedal.resolvers import simple -# import pedal.sandbox.compatibility as compatibility -# from tests.execution_helper import Execution -from pedal.toolkit.utilities import * - - -class DictionaryMistakeTest(MistakeTest): - def setUp(self): - self._dict_str = ("[{'City': 'Birmingham', 'Precipitation': 0.0, 'Temperature': 46}," - "{'City': 'Fairbanks' , 'Precipitation': 1.37, 'Temperature': 57}," - "{'City': 'Miami', 'Precipitation': 1.86, 'Temperature': 80}," - "{'City': 'Los Angeles', 'Precipitation': 0.5, 'Temperature': 73}," - "{'City': 'Denver', 'Precipitation': 0.0, 'Temperature': 49}," - "{'City': 'Chicago', 'Precipitation': 0.23, 'Temperature': 40}]") - - def test_hard_coding(self): - constants = [99.23, "99.23"] - self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print ("99.23")') - ret = hard_coding(constants) - self.assertTrue(ret, "Expected feedback message, got {} instead".format(ret)) - - self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'price = "99.23"\n' - 'print (price)') - ret = hard_coding(constants) - self.assertTrue(ret, "Expected feedback message, got {} instead".format(ret)) - - self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print (book["price"])') - ret = hard_coding(constants) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_print_dict_key(self): - # TODO: Check output string - key_list = ['price', 'number_of_pages', 'discount'] - self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'how_much= book["price"]\n' - 'print("price")') - ret = print_dict_key(key_list) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('price = "price"\n' - 'book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'how_much= book[price]\n' - 'print(price)') - ret = print_dict_key(key_list) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'how_much= book["price"]\n' - 'print(["price"])') - ret = print_dict_key(key_list) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print (book["price"])') - ret = print_dict_key(key_list) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('price = "price"\n' - 'book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print (book[price])') - ret = print_dict_key(key_list) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_var_instead_of_key(self): - # TODO: Check output string - key_list = ['price', 'number_of_pages', 'discount', 'title'] - - self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print(price)') - ret = var_instead_of_key(key_list) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print(book["price"])') - ret = var_instead_of_key(key_list) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'price = book["price"]\n' - 'print(price)') - ret = var_instead_of_key(key_list) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source("import weather\n" - "import matplotlib.pyplot as plt\n" - "weather_reports = weather.get_report()\n" - "list = []\n" - "City = input('City')\n" - "for report in weather_reports:\n" - " if City == report['Station']['City']:\n" - " list.append(report[\"Data\"][\"Precipitation\"])\n") - ret = var_instead_of_key(['City', 'Data', 'Precipitation']) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_parens_in_dict(self): - # TODO: Check output string - key_list = ['price', 'number_of_pages', 'discount'] - - self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print(book("price"))') - ret = parens_in_dict(key_list) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('price = "price"\n' - 'book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print(book(price))') - ret = parens_in_dict(key_list) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - self.assertTrue("price" in ret, "Message '{}' didn't include correct key".format(ret)) - - self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print(book["price"])') - ret = parens_in_dict(key_list) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('price = "price"' - 'book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print(book[price])') - ret = parens_in_dict(key_list) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('price = input("price")\n' - 'book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print(book[price])') - ret = parens_in_dict(key_list) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('for item in _list:\n' - ' print(item("price"))') - ret = parens_in_dict(key_list) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - def test_list_as_dict(self): - # TODO: Check output string - self.to_source("total = 0\n" - "weather_reports = {}\n" - "for precipitation in weather_reports:\n" - " total = total + weather_reports['Precipitation']\n" - "print (total)".format(self._dict_str)) - ret = list_as_dict() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source("total = 0\n" - "weather_reports = {}\n" - "for precipitation in weather_reports:\n" - " total = total + precipitation['Precipitation']\n" - "print (total)\n".format(self._dict_str)) - ret = list_as_dict() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source("earthquake_report = [{\"Location\" : \"California\", \"Magnitude\" : 2.3, \"Depth\" : 7.66},\n" - " {\"Location\" : \"Japan\", \"Magnitude\" : 5.3, \"Depth\" : 3.34},\n" - " {\"Location\" : \"Burma\", \"Magnitude\" : 4.9, \"Depth\" :97.07},\n" - " {\"Location\" : \"Alaska\", \"Magnitude\" : 4.6, \"Depth\" : 35.0},\n" - " {\"Location\" : \"Washington\", \"Magnitude\" : 2.19, \"Depth\" : 15.28},\n" - " {\"Location\" : \"China\", \"Magnitude\" : 4.3, \"Depth\" : 10.0}\n" - " ]\n" - "total = 0\n" - "number = 0\n" - "for earthquake_report in earthquake_reports:\n" - " total = total + earthquake_report['Magnitude']\n" - " number = 1 + number\n" - "average = total / number\n" - "print(average)" - ) - ret = list_as_dict() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - def test_dict_out_of_loop(self): - # TODO: Check output string - keys = ['Precipitation'] - self.to_source('rain = weather_reports["Precipitation"]\n' - 'total = 0\n' - 'for report in weather_reports:\n' - ' total = total + rain\n' - 'print(total)\n') - ret = dict_out_of_loop(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('weather_reports = {}\n' - 'total = 0\n' - 'for report in weather_reports:\n' - ' total = total + report["Precipitation"]\n' - 'print(total)\n'.format(self._dict_str)) - ret = dict_out_of_loop(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('import matplotlib.pyplot as plt\n' - 'import weather\n' - 'weather_reports = weather.get_weather()\n' - 'BB_min = []\n' - 'BB_max = []\n' - 'for weather in weather_reports: \n' - ' if ("Blacksburg" in weather["Station"]["City"]): \n' - ' BB_min.append(weather["Data"]["Temperature"]["Min Temp"])\n' - ' \n' - 'for weather in weather_reports: \n' - ' if ("Blacksburg" in weather["Station"]["City"]):\n' - ' BB_max.append(weather["Data"]["Temperature"]["Max Temp"])\n' - 'plt.scatter(BB_min,BB_max)\n' - 'plt.xlabel("Trend")\n' - 'plt.ylabel("Temperatures")\n' - 'plt.title("Relationship between Minimum and Maximum Temperatures in Blacksburg")\n' - 'plt.show()\n') - all_labels_1 = all_labels_present() - ret = dict_out_of_loop(keys) - self.assertFalse(all_labels_1, "false negative") - all_labels_2 = all_labels_present() - self.assertFalse(ret, "...") - self.assertTrue(all_labels_1 == all_labels_2, "Side effects aren't undoing themselves") - - def test_wrong_keys(self): - # TODO: Check output string - keys = ['Date', "Temperature", "Wind", "Min Temp", "Max Temp", "Avg Temp", "Direction", "Speed", "Month", - "Year", - "Week of", "Full", "State", "Code", "City", "Location"] - self.to_source("total = 0\n" - "for reports in weather_reports:\n" - " total = total + reports['Temperature']\n" - "print(total)\n") - ret = wrong_keys(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source("temperature = 'Temperature'\n" - "total = 0\n" - "for reports in weather_reports:\n" - " total = total + reports[temperature]\n" - "print(total)\n") - ret = wrong_keys(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - self.assertTrue("Temperature" in ret, "Message '{}' didn't include correct key".format(ret)) - - self.to_source("total = 0\n" - "for reports in weather_reports:\n" - " total = total + reports['Precipitation']\n" - "print(total)\n") - ret = wrong_keys(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source("precip = 'Precipitation'\n" - "total = 0\n" - "for reports in weather_reports:\n" - " total = total + reports[precip]\n" - "print(total)\n") - ret = wrong_keys(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_dict_access_not_in_loop(self): - self.to_source('weatherPrecipitation = weather_reports["Precipitation"]\n' - 'for report in weather_reports:\n' - ' total_precipitation = weatherPrecipitation + total_precipitation\n' - 'print(total_precipitation)\n') - ret = dict_access_not_in_loop() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('for weather_report in weather_reports:\n' - ' total = total + precipitations[Precipitation]\n' - 'print(total)\n') - ret = dict_access_not_in_loop() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('for weather_report in weather_reports:\n' - ' total = total + precipitations["Precipitation"]\n' - 'print(total)\n') - ret = dict_access_not_in_loop() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('for weather in weather_reports:\n' - ' if ("San Diego" in weather["Station"]["City"]):\n' - ' sandiego_list.append(weather["Data"]["Temperature"]["Avg Temp"])\n' - 'for weather in weather_reports:\n' - ' if ("Blacksburg" in weather["Station"]["City"]):\n' - ' blacksburg_list.append(weather["Data"]["Temperature"]["Avg Temp"])\n' - 'for temp in sandiego_list:\n' - ' sandiego_temp = sandiego_temp + 1\n' - ' sandiego_number = sandiego_number + temp\n' - 'sandiego_average = sandiego_number / sandiego_temp\n' - 'for temp in blacksburg_list:\n' - ' blacksburg_temp = blacksburg_temp + 1\n' - ' blacksburg_number = blacksburg_number + temp\n' - 'blacksburg_average = blacksburg_number / blacksburg_temp\n' - 'plt.scatter(BB_min,BB_max)\n' - 'plt.xlabel("Trend")\n' - 'plt.ylabel("Temperatures")\n' - 'plt.title("Relationship between Minimum and Maximum Temperatures in Blacksburg")\n' - 'plt.show()\n') - ret = dict_access_not_in_loop() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - ret2 = all_labels_present() - self.assertFalse(ret2, "Expected False, got message instead") - - def test_hard_coded_list(self): - val_list = [0.0, 1.37, 1.86, 0.5, 0.0, 0.23] - self.to_source('total_rain = 0\n' - 'weather_reports = [0.0,1.37,1.86,0.5,0.0,0.23]\n' - 'for rain in weather_reports:\n' - ' total_rain = rain + total_rain\n' - 'print(total_rain)\n') - ret = hard_coded_list(val_list) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_rain = 0\n' - 'weather_reports = {}\n' - 'for rain in weather_reports:\n' - ' total_rain = rain + total_rain\n' - 'print(total_rain)\n'.format(self._dict_str)) - ret = hard_coded_list(val_list) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_iter_as_key(self): - # TODO: Check output string - keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", - "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] - self.to_source('total_precipitation = 0\n' - 'for Precipitation in weather_reports:\n' - ' total_precipitation = total_precipitation + "Precipitation"\n' - 'print(total_precipitation)\n') - ret = iter_as_key(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for Precipitation in weather_reports:\n' - ' total_precipitation = total_precipitation + Precipitation["Precipitation"]\n' - 'print(total_precipitation)\n') - ret = iter_as_key(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip\n' - 'print(total_precipitation)\n') - ret = iter_as_key(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_dict_acc_as_lis_var(self): - keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", - "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] - self.to_source('precipitation_total=0\n' - 'precipitation_list=[]\n' - 'for precipitation in ["Precipitation"]:\n' - ' precipitation_list.append("Precipitation")\n' - 'for precipitation in precipitation_list:\n' - ' precipitation_total=precipitation_total + precipitation\n' - 'print(precipitation_total)\n') - ret = list_str_as_list_var(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip\n' - 'print(total_precipitation)\n') - ret = list_str_as_list_var(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_append_and_sum(self): - self.to_source('precipitation_total=0\n' - 'precipitation_list=[]\n' - 'for precipitation in weather_reports["Precipitation"]:\n' - ' precipitation_list.append("Precipitation")\n' - 'for precipitation in precipitation_list:\n' - ' precipitation_total= precipitation_total + 1\n' - 'print(precipitation_total)\n') - ret = append_and_sum() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip\n' - 'print(total_precipitation)\n') - ret = append_and_sum() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_iter_prop_dict_acc(self): - self.to_source('for weather_reports["Precipitation"] in weather_reports:\n' - ' total = weather_reports[Precipitation] + total\n' - 'print(total)\n') - ret = iter_prop_dict_acc() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip\n' - 'print(total_precipitation)\n') - ret = iter_prop_dict_acc() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_list_str_dict(self): - # TODO: Check output string - keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", - "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] - self.to_source('total=0\n' - 'number=0\n' - 'for precipitation1 in "Precipitation":\n' - ' total= total+ precipitation1["Precipitation"]\n' - ' number= number + 1\n' - 'average= total/ total\n' - 'print(average)\n') - ret = list_str_dict(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip\n' - 'print(total_precipitation)\n') - ret = list_str_dict(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_missing_key(self): - # TODO: Check output string - keys = ["Precipitation", "Data"] - self.to_source('total=0\n' - 'number=0\n' - 'for precipitation1 in "Precipitation":\n' - ' total= total+ precipitation1["Precipitation"]\n' - ' number= number + 1\n' - 'average= total/ total\n' - 'print(average)\n') - ret = missing_key(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' - 'print(total_precipitation)\n') - ret = missing_key(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_blank_key(self): - keys = ["distance", "time"] - self.to_source('distance_in_kilometers = trip_data["____"]/1000\n' - 'trip_data = {"distance":123000.0, "time":14000.0}\n' - 'print(average_speed_in_mph) \n' - 'average_speed_in_mph = ____ / time_in_hours\n' - 'time_in_hours = trip_data["____"]/____\n' - '____ = distance_in_kilometers / 1.6\n') - ret = blank_key(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('trip_data = {"distance":123000.0, "time":14000.0}\n' - 'distance_in_kilometers = trip_data["distance"]/1000\n' - 'distance_in_miles = distance_in_kilometers / 1.6\n' - 'time_in_hours = trip_data["time"]/3600\n' - 'average_speed_in_mph = distance_in_miles / time_in_hours\n' - 'print(average_speed_in_mph) \n') - ret = blank_key(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_dict_parens_brack(self): - # TODO: Check output string - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for rain in weather_reports:\n' - ' sum = sum + weather_reports(["Data"]["Precipitation"])\n' - 'print(sum)\n') - ret = dict_parens_brack() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('book = {"number_of_pages":285, "price":99.23, "discount":0.1}\n' - 'print(["price"])') - ret = dict_parens_brack() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' - 'print(total_precipitation)\n') - ret = dict_parens_brack() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_comma_dict_acc(self): - # TODO: Check output string - self.to_source("import weather\n" - "weather_reports = weather.get_weather()\n" - "total = 0\n" - "for report in weather_reports:\n" - " total = total + report['Data'],['Precipitation']\n" - "print(total)\n") - ret = comma_dict_acc() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' - 'print(total_precipitation)\n') - ret = comma_dict_acc() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_no_dict_in_loop(self): - # TODO: Check output values - self.to_source("import weather\n" - "weather_reports = weather.get_weather()\n" - "total = 0\n" - "for precip in weather_reports:\n" - " total = total + precip\n" - "print(total)\n") - ret = no_dict_in_loop() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip2["Data"]["Precipitation"]\n' - 'print(total_precipitation)\n') - ret = no_dict_in_loop() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' - 'print(total_precipitation)\n') - ret = no_dict_in_loop() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'key = "Precipitation"\n' - 'for city in weather_reports:\n' - ' total_precipitation = total_precipitation + city[key]\n' - 'print(total_precipitation)\n') - ret = no_dict_in_loop() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def func_filter(self): - keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", - "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'total_precipitation = 0\n' - 'for report in weather_reports:\n' - ' total_pecipitation = total_precipitation + weather.get_weather("Data")\n' - 'print(total_precipitation)\n') - ret = func_filter(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'precipitation_total=0\n' - 'weather_reports = weather.get_weather("Precipitation")\n' - 'for report in weather_reports:\n' - ' precipitation_total = precipitation_total + 1\n') - ret = func_filter(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' - 'print(total_precipitation)\n') - ret = func_filter(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source("report_list = classics.get_books(test=True)") - ret = func_filter(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_str_list(self): - # TODO: check output values - keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", - "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'totalPrecip = 0\n' - 'for weather in weather_reports:\n' - ' totalPrecip = totalPrecip + ["Precipitation"]\n' - 'print(totalPrecip)\n') - ret = str_list(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' - 'print(total_precipitation)\n') - ret = str_list(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_list_var_dict_acc(self): - # TODO: Check output values - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'pt = 0\n' - 'for precipitation in weather_reports["Precipitation"]:\n' - ' pt = pt + precipiation\n' - 'print(pt)\n') - ret = list_var_dict_acc() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('total_precipitation = 0\n' - 'for precip in weather_reports:\n' - ' total_precipitation = total_precipitation + precip["Data"]["Precipitation"]\n' - 'print(total_precipitation)\n') - ret = list_var_dict_acc() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_key_comp(self): - # TODO: Check output values - keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", - "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' if weather_instance["Data"] == "Precipitation":\n' - ' sum = sum + weather_instance["Data"]\n' - 'print(sum)\n') - ret = key_comp(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'data = "Data"\n' - 'precip = "Precipitation"\n' - 'for weather_instance in weather_reports:\n' - ' if weather_instance[data] == precip:\n' - ' sum = sum + weather_instance[data]\n' - 'print(sum)\n') - ret = key_comp(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' if weather_instance["Station"]["City"] == "Chicago":\n' - ' sum = sum + weather_instance["Data"]["Precipitation"]\n' - 'print(sum)\n') - ret = key_comp(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'loc1 = "Station"\n' - 'loc2 = "City"\n' - 'data = "Data"\n' - 'precip = "Precipitation"\n' - 'for weather_instance in weather_reports:\n' - ' if weather_instance[loc1][loc2] == "Chicago":\n' - ' sum = sum + weather_instance[data][precip]\n' - 'print(sum)\n') - ret = key_comp(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'data = "Data"\n' - 'precip = "Precipitation"\n' - 'for weather_instance in weather_reports:\n' - ' loc1 = weather_instance["Station"]["City"]\n' - ' if loc1 == "Chicago":\n' - ' sum = sum + weather_instance[data][precip]\n' - 'print(sum)\n') - ret = key_comp(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'precip = "Precipitation"\n' - 'for weather_instance in weather_reports:\n' - ' data = weather_instance["Data"]\n' - ' if data == precip:\n' - ' sum = sum + weather_instance[data]\n' - 'print(sum)\n') - ret = key_comp(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('for reports in weather_reports:\n' - ' if report["Station"]["City"] == "Chicago":\n' - ' trend.append(reports["Data"]["Precipitation"])') - ret = key_comp(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source("weather_reports = weather.get_weather()\n" - "for report in weather_reports:\n" - " City = report['Station']['City']\n" - " if City == 'Blacksburg':\n" - " pass\n") - ret = key_comp(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source("weather_reports = weather.get_weather()\n" - "for report in weather_reports:\n" - " City = report['Station']\n" - " if City == 'City':\n" - " pass\n") - ret = key_comp(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - def test_col_dict(self): - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'precipitation = 0\n' - 'for weather in weather_reports:\n' - ' preciptation = precipitaion + weather["Data":"Precipitation"]\n' - 'print(precipitation)\n') - ret = col_dict() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' if weather_instance["Station"]["City"] == "Chicago":\n' - ' sum = sum + weather_instance["Data"]["Precipitation"]\n' - 'print(sum)\n') - ret = col_dict() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_var_key(self): - # TODO: Check output value - keys = ['Data', 'Date', "Station", "Temperature", "Precipitation", "Wind", "Min Temp", "Max Temp", "Avg Temp", - "Direction", "Speed", "Month", "Year", "Week of", "Full", "State", "Code", "City", "Location"] - self.to_source("import weather\n" - "weather_reports = weather.get_weather()\n" - "sum = 0\n" - "for rain in weather_reports:\n" - " if rain[Station][City] == Chicago:\n" - " sum = sum + rain[Data][Precipitation]\n" - "print(sum)\n") - ret = var_key(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' if weather_instance["Station"]["City"] == "Chicago":\n' - ' sum = sum + weather_instance["Data"]["Precipitation"]\n' - 'print(sum)\n') - ret = var_key(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('import weather\n' - 'Station = "Station"\n' - 'City = "City"\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' if weather_instance[Station][City] == "Chicago":\n' - ' sum = sum + weather_instance["Data"]["Precipitation"]\n' - 'print(sum)\n') - ret = var_key(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_key_order(self): - keys1 = ["Station", "City"] - keys2 = ["Data", "Precipitation"] - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather in weather_reports:\n' - ' if weather["Station"]["City"] == "Chicago":\n' - ' sum = sum + weather_instance["Chicago"]["Precipitation"]\n' - 'print(sum)\n') - ret = key_order(keys1) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - ret = key_order(keys2) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' if weather_instance["Station"]["City"] == "Chicago":\n' - ' sum = sum + weather_instance["Data"]["Precipitation"]\n' - 'print(sum)\n') - ret = key_order(keys1) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - ret = key_order(keys2) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' station = weather_instance["Station"]\n' - ' city = station["City"]\n' - ' if city == "Chicago":\n' - ' data = weather_instance["Data"]\n' - ' precipitation = data["Precipitation"]\n' - ' sum = sum + precipitation\n' - 'print(sum)\n') - ret = key_order(keys1) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - ret = key_order(keys2) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_key_order_unchained(self): - keys1 = ["Station", "City"] - keys2 = ["Data", "Precipitation"] - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' station = weather_instance["City"]\n' - ' city = station["Station"]\n' - ' if city == "Chicago":\n' - ' data = weather_instance["Precipitation"]\n' - ' precipitation = data["Data"]\n' - ' sum = sum + precipitation\n' - 'print(sum)\n') - ret = key_order_unchained(keys1) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - ret = key_order_unchained(keys2) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' station = weather_instance["Station"]\n' - ' city = station["City"]\n' - ' if city == "Chicago":\n' - ' data = weather_instance["Data"]\n' - ' precipitation = data["Precipitation"]\n' - ' sum = sum + precipitation\n' - 'print(sum)\n') - ret = key_order_unchained(keys1) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - ret = key_order_unchained(keys2) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' if weather_instance["Station"]["City"] == "Chicago":\n' - ' sum = sum + weather_instance["Data"]["Precipitation"]\n' - 'print(sum)\n') - ret = key_order_unchained(keys1) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - ret = key_order_unchained(keys2) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_filt_key(self): - # TODO: Check output values - c_value = "Chicago" - num_slices = 3 - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'total_precipitation_Chicago = 0\n' - 'total_precipitation_Chicago\n' - 'for report in weather_reports:\n' - ' total_precipitation = total_precipitation + report["Data"]["Precipitation"]["Chicago"]\n' - 'print (total_precipitation)\n') - ret = filt_key(c_value, num_slices) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'total_precipitation_Chicago = 0\n' - 'for report in weather_reports:\n' - ' precip = report["Data"]["Precipitation"]\n' - ' chicago_precip = precip["Chicago"]\n' - ' total_precipitation = total_precipitation + chicago_recip\n' - 'print (total_precipitation)\n') - ret = filt_key(c_value, num_slices) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' if weather_instance["Station"]["City"] == "Chicago":\n' - ' sum = sum + weather_instance["Data"]["Precipitation"]\n' - 'print(sum)\n') - ret = filt_key(c_value, num_slices) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_miss_dict_acc(self): - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' if City == "Chicago":\n' - ' sum = sum + "Precipitation"\n' - 'print(sum)\n') - ret = miss_dict_acc() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'total_precipitation_Chicago = 0\n' - 'total_precipitation_Chicago\n' - 'for report in weather_reports:\n' - ' precip = report["Data"]["Precipitation"]\n' - ' chicago_precip = precip["Chicago"]\n' - ' total_precipitation = total_precipitation + chicago_recip\n' - 'print (total_precipitation)\n') - ret = miss_dict_acc() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_compare_key(self): - c_value = "Chicago" - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather_instance in weather_reports:\n' - ' if weather_instance["Station"]["City"] == ["Chicago"]:\n' - ' sum = sum + weather_instance["Data"]["Precipitation"]\n' - 'print(sum)\n') - ret = compare_key(c_value) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'total_precipitation_Chicago = 0\n' - 'total_precipitation_Chicago\n' - 'for report in weather_reports:\n' - ' precip = report["Data"]["Precipitation"]\n' - ' chicago_precip = precip["Chicago"]\n' - ' total_precipitation = total_precipitation + chicago_recip\n' - 'print (total_precipitation)\n') - ret = compare_key(c_value) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_str_equality(self): - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'sum = 0\n' - 'for weather in weather_reports:\n' - ' if("City" == "Chichago"):\n' - ' sum = sum + weather["Data"]["Precipitation"]\n' - 'print(sum)\n') - ret = str_equality() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'total_precipitation = 0\n' - 'for report in weather_reports:\n' - ' if report["Station"]["City" == "Chicago"]:\n' - ' total_precipitation = total_precipitation + report["Data"]["Precipitation"]\n' - 'print(total_precipitation)\n') - ret = str_equality() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'total_precipitation_Chicago = 0\n' - 'total_precipitation_Chicago\n' - 'for report in weather_reports:\n' - ' precip = report["Data"]["Precipitation"]\n' - ' chicago_precip = precip["Chicago"]\n' - ' total_precipitation = total_precipitation + chicago_recip\n' - 'print (total_precipitation)\n') - ret = str_equality() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_unnecessary_cast(self): - cast_list = ["float"] - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'total_precipitation_Chicago = 0\n' - 'total_precipitation_Chicago\n' - 'for report in weather_reports:\n' - ' precip = report["Data"]["Precipitation"]\n' - ' chicago_precip = int(precip["Chicago"])\n' - ' total_precipitation = total_precipitation + chicago_recip\n' - 'print (total_precipitation)\n') - ret = unnecessary_cast(cast_list) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'total_precipitation_Chicago = 0\n' - 'total_precipitation_Chicago\n' - 'for report in weather_reports:\n' - ' precip = report["Data"]["Precipitation"]\n' - ' chicago_precip = precip["Chicago"]\n' - ' total_precipitation = total_precipitation + chicago_recip\n' - 'print (total_precipitation)\n') - ret = unnecessary_cast(cast_list) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_fetch_acc_dict(self): - keys = ["Data", "Precipitation", "Station", "Chicago"] - self.to_source('import weather\n' - 'precipitation = 0\n' - 'weather_reports = weather.get_weather("Chicago")\n' - 'where = weather.get_weather["Chicago"]\n' - 'for weather in weather_reports:\n' - ' precipitation = precipitation + weather["Data"]["Precipitation"]\n' - 'print(precipitation)\n') - ret = fetch_acc_dict(keys) - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'total_precipitation_Chicago = 0\n' - 'total_precipitation_Chicago\n' - 'for report in weather_reports:\n' - ' precip = report["Data"]["Precipitation"]\n' - ' chicago_precip = precip["Chicago"]\n' - ' total_precipitation = total_precipitation + chicago_recip\n' - 'print (total_precipitation)\n') - ret = fetch_acc_dict(keys) - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_app_assign(self): - self.to_source('import weather\n' - 'import matplotlib.pyplot as plt\n' - 'weather_reports = weather.get_weather()\n' - 'sum = []\n' - 'for rain in weather_reports:\n' - ' if rain["Station"]["City"] == "Chicago":\n' - ' sum = sum.append(rain["Data"]["Precipitation"])\n' - 'plt.plot(sum)\n' - 'plt.xlabel("Years")\n' - 'plt.ylabel("Precipitation")\n' - 'plt.title("Chicago Rain")\n' - 'plt.show(sum)\n') - ret = app_assign() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'total_precipitation_Chicago = 0\n' - 'total_precipitation_Chicago\n' - 'for report in weather_reports:\n' - ' precip = report["Data"]["Precipitation"]\n' - ' chicago_precip = precip["Chicago"]\n' - ' total_precipitation = total_precipitation + chicago_recip\n' - 'print (total_precipitation)\n') - ret = app_assign() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_show_args(self): - self.to_source('import weather\n' - 'import matplotlib.pyplot as plt\n' - 'weather_reports = weather.get_weather()\n' - 'sum = []\n' - 'for rain in weather_reports:\n' - ' if rain["Station"]["City"] == "Chicago":\n' - ' sum.append(rain["Data"]["Precipitation"])\n' - 'plt.plot(sum)\n' - 'plt.xlabel("Years")\n' - 'plt.ylabel("Precipitation")\n' - 'plt.title("Chicago Rain")\n' - 'plt.show(sum)\n') - ret = show_args() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'import matplotlib.pyplot as plt\n' - 'weather_reports = weather.get_weather()\n' - 'sum = []\n' - 'for rain in weather_reports:\n' - ' if rain["Station"]["City"] == "Chicago":\n' - ' sum.append(rain["Data"]["Precipitation"])\n' - 'plt.plot(sum)\n' - 'plt.xlabel("Years")\n' - 'plt.ylabel("Precipitation")\n' - 'plt.title("Chicago Rain")\n' - 'plt.show()\n') - ret = show_args() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_dict_plot(self): - self.to_source('import weather\n' - 'import matplotlib.pyplot as plt\n' - 'weather_reports = {}\n' - 'sum = []\n' - 'for rain in weather_reports:\n' - ' if rain["Station"]["City"] == "Chicago":\n' - ' sum.append(rain["Data"]["Precipitation"])\n' - 'plt.plot(weather_reports)\n' - 'plt.xlabel("Years")\n' - 'plt.ylabel("Precipitation")\n' - 'plt.title("Chicago Rain")\n' - 'plt.show()\n'.format(self._dict_str)) - ret = dict_plot() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'import matplotlib.pyplot as plt\n' - 'weather_reports = weather.get_weather()\n' - 'cityWeather = input("Choose a city: ")\n' - 'cityPrecip = []\n' - '# add other input and variable initializations here\n' - '# Put here the code to create the list of data to be plotted.\n' - 'for weather in weather_reports:\n' - ' if weather["Station"]["City"] == cityWeather:\n' - ' cityPrecip.append(weather["Data"]["Precipitation"])\n' - '# Put here the code to display a properly labelled line plot of the list of data.\n' - 'plt.plot(cityPrecip)\n' - 'plt.title(cityWeather)\n' - 'plt.xlabel("Trend")\n' - 'plt.ylabel("Amount of Precipitation")\n' - 'plt.show()\n') - ret = dict_plot() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('import weather\n' - 'import matplotlib.pyplot as plt\n' - 'weather_reports = weather.get_weather()\n' - 'sum = []\n' - 'for rain in weather_reports:\n' - ' if rain["Station"]["City"] == "Chicago":\n' - ' sum.append(rain["Data"]["Precipitation"])\n' - 'plt.plot(sum)\n' - 'plt.xlabel("Years")\n' - 'plt.ylabel("Precipitation")\n' - 'plt.title("Chicago Rain")\n' - 'plt.show()\n') - ret = dict_plot() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - self.to_source('import classics\n' - 'report_list = classics.get_books(test=True)\n' - 'for report in report_list:\n' - ' hist = report["bibliography"]["type"]\n' - ' if hist == "Text":\n' - ' list.append("Text")\n' - 'plt.hist(list)\n' - 'plt.x("test")\n' - 'plt.y("test")\n' - 'plt.title(list)\n') - ret = dict_plot() - self.assertFalse(ret, "Didn't give message returned {} instead".format(ret)) - - def test_comp_in_dict_acc(self): - self.to_source('import weather\n' - 'weather_reports = weather.get_weather()\n' - 'total_precipitation_Chicago = 0\n' - 'for report in weather_reports:\n' - ' if report["Station"]["City" == "Chicago"]:\n' - ' total_precipitation_Chicago = total_precipitation_Chicago + ' - 'report["Data"]["Precipitation"]\n' - 'print(total_precipitation_Chicago)\n') - ret = comp_in_dict_acc() - self.assertTrue(ret, "Didn't give message, returned {} instead".format(ret)) - - self.to_source('import weather\n' - 'import matplotlib.pyplot as plt\n' - 'weather_reports = weather.get_weather()\n' - 'sum = []\n' - 'for rain in weather_reports:\n' - ' if rain["Station"]["City"] == "Chicago":\n' - ' sum.append(rain["Data"]["Precipitation"])\n' - 'plt.plot(sum)\n' - 'plt.xlabel("Years")\n' - 'plt.ylabel("Precipitation")\n' - 'plt.title("Chicago Rain")\n' - 'plt.show()\n') - ret = dict_plot() - self.assertFalse(ret, "Expected False, got {} instead".format(ret)) - - def test_general_testing(self): - self.to_source('print("fun")') - matches = find_matches("_var_") - var = matches[0]["_var_"] - self.assertTrue(var.ast_name == "Name", "is: {}".format(var.ast_name)) - - def test_group(self): - self.to_source("earthquake_report = [{'Location' : 'California', 'Magnitude' : 2.3, 'Depth' : 7.66},\n" - " {'Location' : 'Japan', 'Magnitude' : 5.3, 'Depth' : 3.34},\n" - " {'Location' : 'Burma', 'Magnitude' : 4.9, 'Depth' :97.07},\n" - " {'Location' : 'Alaska', 'Magnitude' : 4.6, 'Depth' : 35.0},\n" - " {'Location' : 'Washington', 'Magnitude' : 2.19, 'Depth' : 15.28},\n" - " {'Location' : 'China', 'Magnitude' : 4.3, 'Depth' : 10.0}\n" - " ]\n" - "total = 0\n" - "number = 0\n" - "for earthquake_report in earthquake_reports:\n" - " total = total + earthquake_report['Magnitude']\n" - " number = 1 + number\n" - "average = total / number\n" - "print(average)" - ) - target_dict = ('_quake_dict_list_ = [{"Location": "California", "Magnitude": 2.3, "Depth": 7.66},' - '{"Location": "Japan", "Magnitude": 5.3, "Depth": 3.34},' - '{"Location": "Burma", "Magnitude": 4.9, "Depth": 97.07},' - '{"Location": "Alaska", "Magnitude": 4.6, "Depth": 35.0},' - '{"Location": "Washington", "Magnitude": 2.19, "Depth": 15.28},' - '{"Location": "China", "Magnitude": 4.3, "Depth": 10.0}]') - matches = find_matches(target_dict) - if not matches: - explain_r("You need to properly define a dictionary for the abstraction first", "dict_def_err", - label="Dictionary Definition Incorrect") - - all_keys = ["Location", "Magnitude", "Depth"] - unused_keys = ["Location", "Depth"] - used_keys = ["Magnitude"] - dict_acc_group(all_keys, unused_keys, used_keys) - dict_list_group(all_keys) - - target_list = [2.3, 5.3, 4.9, 4.6, 2.19, 4.3] - ___target_avg = sum(target_list) / len(target_list) - - prevent_literal(___target_avg, str(___target_avg)) - - (success, score, category, label, - message, data, hide) = simple.resolve() - # self.assertFalse(success) - # self.assertEqual(message, 'You should always create unit tests.') - self.assertEqual(message, "The list of Dictionaries earthquake_report is not itself a dictionary. " - "To access key-value pairs of the dictionaries in the list, you need to access each " - "dictionary in the list one at a time.

    (list_dict)

    ") diff --git a/src/lib/pedal/__init__.py b/src/lib/pedal/__init__.py deleted file mode 100644 index 27602eb32e..0000000000 --- a/src/lib/pedal/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -A package for analyzing student code. -""" - -# Probably want to import useful stuff from: -# report -# source -# sandbox -# tifa -# cait -# resolver -# etc. - -from pedal.cait import (find_match, find_matches, - parse_program, - find_submatches, find_expr_sub_matches, - def_use_error, data_state, data_type, - expire_cait_cache) -from pedal.report.imperative import (suppress, explain, compliment, - give_partial, gently, set_success) -from pedal.sandbox.sandbox import run, reset -from pedal.tifa import tifa_analysis -from pedal.source import (set_source, check_section_exists, next_section, - set_source_file) diff --git a/src/lib/pedal/assertions/__init__.py b/src/lib/pedal/assertions/__init__.py deleted file mode 100644 index cbb49e6c91..0000000000 --- a/src/lib/pedal/assertions/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from pedal.report.imperative import MAIN_REPORT - -from pedal.assertions.setup import _setup_assertions, resolve_all -from pedal.assertions.assertions import * -from pedal.assertions.organizers import * - - -def set_assertion_mode(exceptions=True, report=None): - if report is None: - report = MAIN_REPORT - _setup_assertions(report) - - report['assertions']['exceptions'] = exceptions diff --git a/src/lib/pedal/assertions/assertions.py b/src/lib/pedal/assertions/assertions.py deleted file mode 100644 index 5d7d3c0c33..0000000000 --- a/src/lib/pedal/assertions/assertions.py +++ /dev/null @@ -1,625 +0,0 @@ -import string -import re - -from pedal.report.imperative import MAIN_REPORT -from pedal.sandbox.result import SandboxResult -from pedal.sandbox.exceptions import SandboxException -from pedal.sandbox.sandbox import DataSandbox -from pedal.assertions.setup import _setup_assertions, AssertionException -from pedal.assertions.tests import _normalize_string, strip_punctuation, equality_test, output_test - - -# TODO: Allow bundling of assertions to make a table - - -def iterable(obj): - return hasattr(obj, '__iter__') or hasattr(obj, '__getitem__') - - -DELTA = .001 -_MAX_LENGTH = 80 - - -def _escape_curly_braces(result): - return result.replace("{", "{{").replace("}", "}}") - - -def safe_repr(obj, short=False): - try: - result = repr(obj) - except Exception: - result = object.__repr__(obj) - if short and len(result) >= _MAX_LENGTH: - result = result[:_MAX_LENGTH] + ' [truncated]...' - result = result - return result - - -def _fail(code_message, actual_message, expected_message, - show_expected_value, modify_right, *values): - normal_values = [] - sandboxed_values = [] - sandboxed_results = [] - if modify_right and values: - values = values[:-1] + (modify_right(values[-1]),) - for value in values: - if is_sandbox_result(value): - sandboxed_results.append(value) - value = value._actual_value - sandboxed_values.append(safe_repr(value)) - else: - normal_values.append(safe_repr(value)) - if sandboxed_results: - code_message = _build_context(sandboxed_results, actual_message, - expected_message, show_expected_value) - return AssertionException(code_message.format(*(sandboxed_values + normal_values))) - - -def _build_result_from_target(target, index, quantity): - if target == "_": - if quantity == 1: - return "the result" - elif index == 0: - return "the first result" - else: - return "the second result" - return "" + target + "" - - -def _build_context(sandboxed_results, actual_message, expected_message, - show_expected_value): - context = [] - calls = [] - inputs = [] - outputs = [] - targets = [] - for result in sandboxed_results: - # Look up info - call_id = result._actual_call_id - sandbox = result._actual_sandbox - outputs.extend(sandbox.output_contexts[call_id]) - calls.extend(sandbox.call_contexts[call_id]) - inputs.extend(sandbox.input_contexts[call_id]) - targets.append(sandbox.target_contexts[call_id]) - # Actual rendering of text - if calls: - calls = [_escape_curly_braces(str(call)) for call in calls] - context.append("I ran:\n
    " + "\n".join(calls) + "
    ") - if inputs: - inputs = [_escape_curly_braces(str(inp)) for inp in inputs] - context.append("I entered as input:\n
    " + "\n".join(inputs) + "
    ") - actual_message += ":\n
    {}
    " - for i, target in enumerate(targets): - named_target = _build_result_from_target(target, i, len(targets)) - if target == '_': - context.append(named_target.capitalize() + " " + actual_message) - else: - context.append("The value of " + named_target + " " + actual_message) - expected_context = "But I expected " - if len(targets) == 2: - expected_context += _build_result_from_target(targets[0], 0, 2) - expected_context += " " + expected_message + " " - expected_context += _build_result_from_target(targets[1], 1, 2) - else: - expected_context += _build_result_from_target(targets[0], 0, 1) - expected_context += " " + expected_message - if show_expected_value: - expected_context += ":\n
    {}
    " - context.append(expected_context) - return "\n".join(context) - - -def is_sandbox_result(value): - if hasattr(value, "__actual_class__"): - if value.__actual_class__ == SandboxResult: - return True - return False - - -def _basic_assertion(left, right, operator, code_comparison_message, - hc_message, hc_message_past, message, report, contextualize, - show_expected_value=True, modify_right=None): - if report is None: - report = MAIN_REPORT - _setup_assertions(report) - context = "" - if message: - message = "\n" + message - else: - message = "" - # TODO: Handle right-side sandbox result - # if is_sandbox_result(right): - # right = right._actual_value - if isinstance(left, Exception): - return False - if isinstance(right, Exception): - return False - if not operator(left, right): - failure = _fail(code_comparison_message, hc_message, hc_message_past, - show_expected_value, modify_right, left, right) - report['assertions']['collected'].append(failure) - report.attach('Instructor Test', category='student', tool='Assertions', - mistake={'message': "Student code failed instructor test.
    \n" + - context + str(failure) + message}) - report['assertions']['failures'] += 1 - if report['assertions']['exceptions']: - raise failure - else: - return False - return True - - -PRE_VAL = "" - - -def assertEqual(left, right, score=None, message=None, report=None, - contextualize=True, exact=False, compare_lengths=None): - if compare_lengths is None: - compare_lengths = (iterable(left) and isinstance(right, (int, float))) - if _basic_assertion(left, right, - lambda l, r: - equality_test(len(l), r, exact, DELTA) if - compare_lengths else - equality_test(l, r, exact, DELTA), - "len({}) != {}" if compare_lengths else "{} != {}", - "was" + PRE_VAL, - "to have its length equal to" - if compare_lengths else "to be equal to", - message, report, contextualize): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -assert_equal = assertEqual - - -def assertNotEqual(left, right, score=None, message=None, report=None, - contextualize=True, exact=False): - if _basic_assertion(left, right, - lambda l, r: not equality_test(l, r, exact, DELTA), - "{} == {}", - "was" + PRE_VAL, - "to not be equal to", - message, report, contextualize): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def assertTrue(something, score=None, message=None, report=None, - contextualize=True): - if _basic_assertion(something, True, - lambda l, r: bool(l), - "{} is true", - "was false" + PRE_VAL, - "to be true", - message, report, contextualize, - show_expected_value=False): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def assertFalse(something, score=None, message=None, report=None, - contextualize=True): - if _basic_assertion(something, False, - lambda l, r: not bool(l), - "{} is false", - "was true" + PRE_VAL, - "to be false", - message, report, contextualize, - show_expected_value=False): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def assertIs(left, right, score=None, message=None): - pass - - -def assertIsNot(left, right, score=None, message=None): - pass - - -def _actually_is_none(l, r): - if is_sandbox_result(l): - return l._actual_value is None - return l is None - - -def assertIsNone(something, score=None, message=None, report=None, - contextualize=True): - if _basic_assertion(something, None, - _actually_is_none, - "{} is none", - "was" + PRE_VAL, - "to be none", - message, report, contextualize, - show_expected_value=False): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def _actually_is_not_none(l, r): - if is_sandbox_result(l): - return l._actual_value is not None - return l is not None - - -def assertIsNotNone(something, score=None, message=None, report=None, - contextualize=True): - if _basic_assertion(something, None, - _actually_is_not_none, - "{} is not none", - "was" + PRE_VAL, - "to not be none", - message, report, contextualize, - show_expected_value=False): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def assertIn(needle, haystack, score=None, message=None, report=None, - contextualize=True): - expected_message = "to be in" - if not is_sandbox_result(needle) and is_sandbox_result(haystack): - expected_message = "to contain" - if _basic_assertion(needle, haystack, - lambda n, h: n in h, - "{} not in {}", - "was" + PRE_VAL, - expected_message, - message, report, contextualize): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def assertNotIn(needle, haystack, score=None, message=None, report=None, - contextualize=True): - expected_message = "to not be in" - if not is_sandbox_result(needle) and is_sandbox_result(haystack): - expected_message = "to not contain" - if _basic_assertion(needle, haystack, - lambda n, h: n not in h, - "{} in {}", - "was" + PRE_VAL, - expected_message, - message, report, contextualize): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def _humanize_type(t): - if hasattr(t, '__name__'): - return t.__name__ - else: - return str(t) - - -def _humanize_types(types): - if isinstance(types, tuple): - return ', '.join(_humanize_type(t) for t in types) - return _humanize_type(types) - - -def assertIsInstance(value, types, score=None, message=None, report=None, - contextualize=True): - if _basic_assertion(value, types, - lambda v, t: isinstance(v, t), - "isinstance({}, {})", - "was" + PRE_VAL, - "to be of type", - message, report, contextualize, - modify_right=_humanize_types): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def assertNotIsInstance(value, types): - pass - - -def assertRaises(exception): - pass - - -def assertRaisesRegexp(exception): - pass - - -def assertAlmostEqual(left, right): - pass - - -def assertNotAlmostEqual(left, right): - pass - - -def assertGreater(left, right, score=None, message=None, report=None, - contextualize=True, compare_lengths=None): - if compare_lengths is None: - compare_lengths = (iterable(left) and isinstance(right, (int, float))) - if _basic_assertion(left, right, - lambda l, r: - len(l) > r if - compare_lengths else - l > r, - "len({}) <= {}" if compare_lengths else "{} <= {}", - "was" + PRE_VAL, - "to have its length greater than" - if compare_lengths else - "to be greater than", - message, report, contextualize): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def assertGreaterEqual(left, right, score=None, message=None, report=None, - contextualize=True, compare_lengths=None): - if compare_lengths is None: - compare_lengths = (iterable(left) and isinstance(right, (int, float))) - if _basic_assertion(left, right, - lambda l, r: - len(l) >= r if - compare_lengths else - l >= r, - "len({}) < {}" if compare_lengths else "{} < {}", - "was" + PRE_VAL, - "to have its length greater than or equal to" if compare_lengths else - "to be greater than or equal to", - message, report, contextualize): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def assertLess(left, right, score=None, message=None, report=None, - contextualize=True, compare_lengths=None): - if compare_lengths is None: - compare_lengths = (iterable(left) and isinstance(right, (int, float))) - if _basic_assertion(left, right, - lambda l, r: - len(l) < r if - compare_lengths else - l < r, - "len({}) >= {}" if compare_lengths else "{} >= {}", - "was" + PRE_VAL, - "to have its length less than" - if compare_lengths else - "to be less than", - message, report, contextualize): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def assertLessEqual(left, right, score=None, message=None, report=None, - contextualize=True, compare_lengths=None): - if compare_lengths is None: - compare_lengths = (iterable(left) and isinstance(right, (int, float))) - if _basic_assertion(left, right, - lambda l, r: - len(l) <= r if - compare_lengths else - l <= r, - "len({}) > {}" if compare_lengths else "{} > {}", - "was" + PRE_VAL, - "to have its length less than or equal to" if compare_lengths else - "to be less than or equal to", - message, report, contextualize): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def assertRegexpMatches(text, pattern): - pass - - -def assertNotRegexpMatches(text, pattern): - pass - - -def assertItemsEqual(left, right): - pass - - -def assertDictContainsSubset(left, right): - pass - - -def assertMultiLineEqual(left, right): - pass - - -def assertSequenceEqual(left, right): - pass - - -# TODO: assertPrintIncludes - -# Speciality Asserts -def assertPrints(result, expected_output, args=None, returns=None, - score=None, message=None, report=None, - contextualize=True, exact=False): - if not isinstance(result, SandboxResult): - return False - raise TypeError("You must pass in a SandboxResult (e.g., using `call`) to assertPrints") - if report is None: - report = MAIN_REPORT - _setup_assertions(report) - call_id = result._actual_call_id - sandbox = result._actual_sandbox - calls = sandbox.call_contexts[call_id] - inputs = sandbox.input_contexts[call_id] - actual_output = sandbox.output_contexts[call_id] - if not output_test(actual_output, expected_output, exact): - context = [] - if calls: - context.append("I ran:\n
    " +
    -                           "\n".join(map(str, calls)) +
    -                           "
    ") - if inputs: - context.append("I entered as input:\n
    " +
    -                           "\n".join(map(str, inputs)) +
    -                           "
    ") - if actual_output: - context.append("The function printed:\n
    " +
    -                           "\n".join(map(str, actual_output)) +
    -                           "
    ") - else: - context.append("The function printed nothing.") - context.append("But I expected the output:\n
    " + "\n".join(map(str, expected_output)) + "
    ") - failure = AssertionException("\n".join(context)) - report['assertions']['collected'].append(failure) - report.attach('Instructor Test', category='student', tool='Assertions', - mistake={'message': "Student code failed instructor test.
    \n" + - str(failure)}) - report['assertions']['failures'] += 1 - if report['assertions']['exceptions']: - raise failure - else: - return False - report.give_partial(score) - return True - - -def assertHasFunction(obj, function, args=None, returns=None, - score=None, message=None, report=None, - contextualize=True, exact=False): - # If object is a sandbox, will check the .data[variable] attribute - # Otherwise, check it directly - if isinstance(obj, DataSandbox): - comparison = lambda o, f: f in o.data - else: - def comparison(o, f): - try: - return f in o - except: - return hasattr(o, f) - if not _basic_assertion(obj, function, - comparison, - "Could not find function {}{}", - "was" + PRE_VAL, - "to have the function", - message, report, contextualize): - return False - if isinstance(obj, DataSandbox): - student_function = obj.data[function] - else: - try: - student_function = obj[function] - except: - student_function = getattr(obj, function) - if _basic_assertion(student_function, function, - lambda l, r: callable(l), - "The value {} is in the variable {}, and that value is not a callable function.", - "was callable" + PRE_VAL, - "to be callable", - message, report, contextualize, - show_expected_value=False): - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - return False - - -def assertHasClass(sandbox, class_name, attrs=None): - pass - - -def assertHas(obj, variable, types=None, value=None, score=None, - message=None, report=None, contextualize=True): - # If object is a sandbox, will check the .data[variable] attribute - # Otherwise, check it directly - if isinstance(obj, DataSandbox): - comparison = lambda o, v: v in o.data - else: - comparison = lambda o, v: v in hasattr(o, v) - if not _basic_assertion(obj, variable, - comparison, - "Could not find variable {}{}", - "was" + PRE_VAL, - "to have the variable", - message, report, contextualize): - return False - if isinstance(obj, DataSandbox): - student_variable = obj.data[variable] - else: - student_variable = getattr(obj, variable) - if types is not None: - if not _basic_assertion(student_variable, types, - lambda v, t: isinstance(v, t), - "isinstance({}, {})", - "was" + PRE_VAL, - "to be of type", - message, report, contextualize, - modify_right=_humanize_types): - return False - if value is not None: - if not _basic_assertion(student_variable, value, - lambda l, r: equality_test(l, r, False, DELTA), - "{} != {}", - "was" + PRE_VAL, - "to be equal to", - message, report, contextualize, - show_expected_value=False): - return False - if report is None: - report = MAIN_REPORT - report.give_partial(score) - return True - - -def assertGenerally(expression, score=None, message=None, report=None, - contextualize=True): - if report is None: - report = MAIN_REPORT - _setup_assertions(report) - if expression: - report.give_partial(score) - return True - else: - report['assertions']['failures'] += 1 - if report['assertions']['exceptions']: - raise AssertionException("General assertion") - else: - return False - -# Allow addition of new assertions -# e.g., assertGraphType, assertGraphValues diff --git a/src/lib/pedal/assertions/organizers.py b/src/lib/pedal/assertions/organizers.py deleted file mode 100644 index 84f00667c4..0000000000 --- a/src/lib/pedal/assertions/organizers.py +++ /dev/null @@ -1,167 +0,0 @@ -''' - -Sections are a way to separate the pieces of a file such that the pieces do not -interfere with each other. - -Phases are a way to chunk a collection of functions together. If one of these -functions fails, the other functions in the phase will continue to be evaluated. -However, that phase will still have failed. You can establish that one phase -comes before or after another phase; if a precondition phase fails, then the -subsequent phase will not run. - -Example: - Students are working on a text adventure game and have to implement a - function named create_world(). The grading for portion of the assignment - has three phases: - 'create_world_exists' which confirms that the function was defined - 'create_world_returns' which confirms that calling the function - produces the right result. - 'create_world_complete' which confirms that the previous phase - terminated in order to give some partial credit. - - Although the 'create_world_exists' phase is composed of one function, the - 'create_world_returns' phase is actually composed of several functions that - check the components of the function. - - @phase('create_world_exists') - - @phase('create_world_returns', after='create_world_exists') - -Phases are reset between sections. - -''' - -from pedal.report.imperative import MAIN_REPORT -from pedal.assertions.setup import (_setup_assertions, AssertionException, - _add_relationships, _add_phase) -from functools import wraps - - -def contextualize_calls(): - pass - - -class _finish_section: - def __init__(self, number, *functions): - if isinstance(number, int): - self.number = number - else: - self.number = -1 - functions = [number] + list(functions) - self.functions = functions - for function in functions: - self(function, False) - - def __call__(self, f=None, quiet=True): - if f is not None: - f() - if quiet: - print("\tNEXT SECTION") - - def __enter__(self): - pass - - def __exit__(self, x, y, z): - print("\tNEXT SECTION") - # return wrapped_f - - -def finish_section(number, *functions, next_section=False): - if len(functions) == 0: - x = _finish_section(number, *functions) - x() - else: - result = _finish_section(number, *functions) - if next_section: - print("\tNEXT SECTION") - return result - - -def section(*args): - ''' - TODO: Deprecate? - ''' - _setup_assertions(MAIN_REPORT) - - def wrap(f): - _add_phase(phase_name, _handle_entry) - MAIN_REPORT['assertions']['phases'].append((section_number, f)) - return f - - section_number = -1 - if len(args) >= 1 and callable(args[0]): - if len(args) >= 2: - section_number = args[1] - return wrap(args[0]) - elif len(args) >= 1: - section_number = args[0] - return wrap - - -def phase(phase_name, before=None, after=None): - ''' - - Args: - phase_name (str): The name of the phase this function will belong to. - before (list[str] or str): the name(s) of any phases that this phase - should be before. - after (list[str] or str): the name(s) of any phases that this phase - should be after. - ''' - _setup_assertions(MAIN_REPORT) - - def wrap(f): - @wraps(f) - def _handle_entry(*args, **kwargs): - old_exception_state = MAIN_REPORT['assertions']['exceptions'] - MAIN_REPORT['assertions']['exceptions'] = True - value = f(*args, **kwargs) - MAIN_REPORT['assertions']['exceptions'] = old_exception_state - return value - - _add_phase(phase_name, _handle_entry) - _add_relationships(phase_name, before) - _add_relationships(after, phase_name) - return _handle_entry - - return wrap - - -def stop_on_failure(f): - _setup_assertions(MAIN_REPORT) - - @wraps(f) - def wrapped(*args, **kwargs): - old_exception_state = MAIN_REPORT['assertions']['exceptions'] - MAIN_REPORT['assertions']['exceptions'] = True - value = None - try: - value = f(*args, **kwargs) - except AssertionException: - pass - MAIN_REPORT['assertions']['exceptions'] = old_exception_state - return value - - return wrapped - - -def try_all(): - _setup_assertions(MAIN_REPORT) - - @wraps(f) - def wrapped(*args, **kwargs): - old_exception_state = MAIN_REPORT['assertions']['exceptions'] - MAIN_REPORT['assertions']['exceptions'] = False - value = f(*args, **kwargs) - MAIN_REPORT['assertions']['exceptions'] = old_exception_state - return value - - return wrapped - - -def precondition(function): - pass - - -def postcondition(function): - pass diff --git a/src/lib/pedal/assertions/setup.py b/src/lib/pedal/assertions/setup.py deleted file mode 100644 index 5df3a598cc..0000000000 --- a/src/lib/pedal/assertions/setup.py +++ /dev/null @@ -1,116 +0,0 @@ -import sys - -from pedal.report.imperative import MAIN_REPORT -from pedal.sandbox.exceptions import SandboxStudentCodeException - - -class AssertionException(Exception): - def __str__(self): - return self.args[0] - - -def _topological_sort(names, orderings): - visited = set() - stack = [] - - def dfs(name): - visited.add(name) - if name in orderings: - for neighbor in orderings[name]: - if neighbor not in visited: - dfs(neighbor) - stack.insert(0, name) - - for name in names[::-1]: - if name not in visited: - dfs(name) - return stack - - -def resolve_all(set_success=False, report=None): - from pprint import pprint - if report is None: - report = MAIN_REPORT - _setup_assertions(report) - orderings = report['assertions']['relationships'] - phase_functions = report['assertions']['phase_functions'] - phase_names = report['assertions']['phases'] - phase_names = _topological_sort(phase_names, orderings) - # pprint(orderings) - phase_success = False - for phase_name in phase_names: - phase_success = True - for function in phase_functions[phase_name]: - try: - phase_success = phase_success and (function() is not False) - except AssertionException: - phase_success = False - except SandboxStudentCodeException: - phase_success = False - if not phase_success: - break - - # for f in report.feedback: - # print("\t", f, f.mistake, f.misconception) - if not report['assertions']['failures'] and phase_success and set_success: - report.set_success() - - _reset_phases(report) - - -def _add_phase(phase_name, function, report=None): - if report is None: - report = MAIN_REPORT - phase_functions = report['assertions']['phase_functions'] - phases = report['assertions']['phases'] - if phase_name not in phase_functions: - phase_functions[phase_name] = [] - phases.append(phase_name) - phase_functions[phase_name].append(function) - - -def _add_relationships(befores, afters, report=None): - if report is None: - report = MAIN_REPORT - relationships = report['assertions']['relationships'] - if None in (befores, afters): - return - if not isinstance(befores, (list, tuple)): - befores = [befores] - if not isinstance(afters, (list, tuple)): - afters = [afters] - for before in befores: - if not isinstance(before, str): - before = before.__name__ - if before not in relationships: - relationships[before] = [] - for after in afters: - if not isinstance(after, str): - after = after.__name__ - relationships[before].append(after) - - -def _reset_phases(report=None): - if report is None: - report = MAIN_REPORT - report['assertions']['relationships'].clear() - report['assertions']['phases'].clear() - report['assertions']['phase_functions'].clear() - report['assertions']['failures'] = 0 - - -def _setup_assertions(report): - if 'assertions' not in report: - report['assertions'] = { - 'phases': [], - 'phase_functions': {}, - 'relationships': {}, - 'exceptions': False, - 'failures': 0, - 'collected': [], - # Should we batch up multiple assertion failures? - # The grouping mechanism is try_all - 'tabular_output': False, - } - report.add_hook('source.next_section.before', resolve_all) - report.add_hook('pedal.resolvers.resolve', resolve_all) diff --git a/src/lib/pedal/assertions/tests.py b/src/lib/pedal/assertions/tests.py deleted file mode 100644 index aa8bfef6b5..0000000000 --- a/src/lib/pedal/assertions/tests.py +++ /dev/null @@ -1,147 +0,0 @@ -import string -import re - -# Number encapsulates bool, int, float, complex, decimal.Decimal, etc. -try: - from numbers import Number -except: - Number = (bool, int, float, complex) - -try: - bytes -except NameError: - bytes = str - -try: - frozenset() -except: - frozenset = tuple() - -try: - punctuation_table = str.maketrans(string.punctuation, ' ' * len(string.punctuation)) -except AttributeError: - punctuation_table = None - -if punctuation_table is None: - def strip_punctuation(a_string): - return ''.join(ch for ch in a_string if ch not in set(string.punctuation)) -else: - def strip_punctuation(a_string): - return a_string.translate(punctuation_table) - -SET_GENERATOR_TYPES = (type({}.keys()), type({}.values()), type({}.items())) - -LIST_GENERATOR_TYPES = (type(map(bool, [])), type(filter(bool, [])), - type(range(0)), type(reversed([])), type(zip()), - type(enumerate([]))) - - -def _normalize_string(a_string, numeric_endings=False): - # Lower case - a_string = a_string.lower() - # Remove trailing decimals (TODO: How awful!) - if numeric_endings: - a_string = re.sub(r"(\s*[0-9]+)\.[0-9]+(\s*)", r"\1\2", a_string) - # Remove punctuation - a_string = strip_punctuation(a_string) - # Split lines - lines = a_string.split("\n") - normalized = [[piece for piece in line.split()] - for line in lines] - normalized = [[piece for piece in line if piece] - for line in normalized if line] - return sorted(normalized) - - -def output_test(actual, expected, _exact_strings): - normalized_actual = [_normalize_string(line) for line in actual] - if isinstance(expected, (str, bytes)): - return _normalize_string(expected) in normalized_actual - else: - normalized_expected = [_normalize_string(line) for line in expected] - return all(each_actual in normalized_expected for each_actual in normalized_actual) - - -def equality_test(actual, expected, _exact_strings, _delta): - # Check if generators - if isinstance(expected, LIST_GENERATOR_TYPES): - expected = list(expected) - elif isinstance(expected, SET_GENERATOR_TYPES): - expected = set(expected) - if isinstance(actual, LIST_GENERATOR_TYPES): - actual = list(actual) - elif isinstance(actual, SET_GENERATOR_TYPES): - actual = set(actual) - - # Float comparison - if isinstance(expected, float) and isinstance(actual, (float, int)): - error = 10 ** (-_delta) - return abs(expected - actual) < error - # Other numerics - elif isinstance(expected, Number) and isinstance(actual, Number) and isinstance(expected, type(actual)): - return expected == actual - # String comparisons - elif ((isinstance(expected, str) and isinstance(actual, str)) or - (isinstance(expected, bytes) and isinstance(actual, bytes))): - if _exact_strings: - return expected == actual - else: - return _normalize_string(expected) == _normalize_string(actual) - # Exact Comparison - elif actual == expected: - return True - # Sequence comparisons - elif isinstance(expected, list) and isinstance(actual, list): - return _are_sequences_equal(actual, expected, _exact_strings, _delta) - elif isinstance(expected, tuple) and isinstance(actual, tuple): - return _are_sequences_equal(actual, expected, _exact_strings, _delta) - elif isinstance(expected, set) and isinstance(actual, set): - return _are_sets_equal(actual, expected, _exact_strings, _delta) - elif isinstance(expected, frozenset) and isinstance(actual, frozenset): - return _are_sets_equal(actual, expected, _exact_strings, _delta) - elif isinstance(expected, dict) and isinstance(actual, dict): - primary_keys = set(expected.keys()) - if not _are_sets_equal(primary_keys, set(actual.keys()), _exact_strings, _delta): - return False - for key in primary_keys: - if not equality_test(expected[key], actual[key], _exact_strings, _delta): - return False - return True - # Else - return False - - -def _are_sequences_equal(x, y, _exact_strings, _delta): - """ - For sequences that support __len__, __iter__, and should have the same - order. - """ - if len(x) != len(y): - return False - for x_element, y_element in zip(x, y): - if not equality_test(x_element, y_element, _exact_strings, _delta): - return False - return True - - -def _set_contains(needle, haystack, _exact_strings, _delta): - """ - Tests if the given needle is one of the elements of haystack, using - the _is_equal function. - """ - for element in haystack: - if equality_test(element, needle, _exact_strings, _delta): - return True - return False - - -def _are_sets_equal(x, y, _exact_strings, _delta): - """ - For sequences that support __len__, __iter__, but order does not matter. - """ - if len(x) != len(y): - return False - for x_element in x: - if not _set_contains(x_element, y, _exact_strings, _delta): - return False - return True diff --git a/src/lib/pedal/cait/__init__.py b/src/lib/pedal/cait/__init__.py deleted file mode 100644 index a01b744ec1..0000000000 --- a/src/lib/pedal/cait/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -A package of tools for capturing student code by matching it against patterns. -""" - -NAME = 'CAIT' -SHORT_DESCRIPTION = "Captures instructor code patterns within student code." -DESCRIPTION = ''' -''' -REQUIRES = ['Source'] -OPTIONALS = ['TIFA'] - -from pedal.cait.cait_api import (find_match, find_matches, - parse_program, - find_submatches, find_expr_sub_matches, - def_use_error, data_state, data_type, - expire_cait_cache) diff --git a/src/lib/pedal/cait/ast_helpers.py b/src/lib/pedal/cait/ast_helpers.py deleted file mode 100644 index 2adc042b31..0000000000 --- a/src/lib/pedal/cait/ast_helpers.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -A pretty-printing dump function for the ast module. The code was copied from -the ast.dump function and modified slightly to pretty-print. - -Alex Leone (acleone ~AT~ gmail.com), 2010-01-30 - -From http://alexleone.blogspot.co.uk/2010/01/python-ast-pretty-printer.html -""" - -from ast import AST, iter_fields, parse - - -def dump(node, annotate_fields=True, include_attributes=False, indent=' '): - """ - Return a formatted dump of the tree in *node*. This is mainly useful for - debugging purposes. The returned string will show the names and the values - for fields. This makes the code impossible to evaluate, so if evaluation is - wanted *annotate_fields* must be set to False. Attributes such as line - numbers and column offsets are not dumped by default. If this is wanted, - *include_attributes* can be set to True. - """ - - def _format(_node, level=0): - if isinstance(_node, AST): - fields = [(a, _format(b, level)) for a, b in iter_fields(_node)] - if include_attributes and _node._attributes: - fields.extend([(a, _format(getattr(_node, a), level)) - for a in _node._attributes]) - return ''.join([ - _node.__class__.__name__, - '(', - ', '.join(('%s=%s' % field for field in fields) - if annotate_fields else - (b for a, b in fields)), - ')']) - elif isinstance(_node, list): - lines = ['['] - lines.extend((indent * (level + 2) + _format(x, level + 2) + ',' - for x in _node)) - if len(lines) > 1: - lines.append(indent * (level + 1) + ']') - else: - lines[-1] += ']' - return '\n'.join(lines) - return repr(_node) - - if not isinstance(node, AST): - raise TypeError('expected AST, got %r' % node.__class__.__name__) - return _format(node) - - -def parseprint(code, filename="", mode="exec", **kwargs): - """Parse some code from a string and pretty-print it.""" - node = parse(code, mode=mode) # An ode to the code - print(dump(node, **kwargs)) - - -# Short name: pdp = parse, dump, print -pdp = parseprint diff --git a/src/lib/pedal/cait/ast_map.py b/src/lib/pedal/cait/ast_map.py deleted file mode 100644 index 940f54876e..0000000000 --- a/src/lib/pedal/cait/ast_map.py +++ /dev/null @@ -1,275 +0,0 @@ -from pedal.cait.cait_node import CaitNode - - -class AstSymbol: - """ - This represents an Ast symbol, whether it be a variable (name node) or a function name - for place holders used in instructor patterns - - Notes: - Also has the attributes of the relevant Name node from the ast class. - - Attributes: - id (str): the name of the variable place holder used by the instructor - ast_node (cait_node): the ast node of the variable - """ - - def __init__(self, _id="", _node=None): - self.id = _id - self.astNode = _node - self.ast_node = _node - - def __getattr__(self, attr): - return getattr(self.astNode, attr) - - def __str__(self): - # return ''.join(["id = ", self.id.__str__(), ", astNode = ", type(self.astNode).__name__]) - return self.id - - def __repr__(self): - return ''.join(["id = ", self.id.__str__(), ", astNode = ", type(self.astNode).__name__]) - - -class AstSymbolList: - """ - This class is a wrapper for a list of AstSymbols for ease of access - If accessed as a list, manipulable as a list, otherwise, acts as the first AstSymbol in the list - """ - - def __init__(self): - self.my_list = [] - - def __getitem__(self, item): - return self.my_list.__getitem__(item) - - def append(self, item): - self.my_list.append(item) - - def __getattr__(self, attr): - return getattr(self.my_list[0], attr) - - def __len__(self): - return self.my_list.__len__() - - -class AstMap: - def __init__(self): - self.mappings = {} - self.symbol_table = {} - self.exp_table = {} - self.func_table = {} - self.conflict_keys = [] - self.match_root = None - self.diagnosis = "" - - def add_func_to_sym_table(self, ins_node, std_node): - """ - Adds ins_node.name to the symbol table if it doesn't already exist, mapping it to a set of ins_node. Updates a - second dictionary that maps ins_node to an std_node, and overwrites the current std_node since there should only - be one mapping. - - Args: - ins_node: instructor node or str representing a function name - std_node: student node representing function - - Returns: - int: number of conflicts generated - - """ - if not isinstance(std_node, CaitNode): - raise TypeError - if isinstance(ins_node, str): - key = ins_node - else: - try: - if ins_node.ast_name == "FunctionDef": - key = ins_node.astNode.name - else: # TODO: Little skulpt artifact that doesn't raise Attribute Errors... - key = ins_node._id - raise AttributeError - except AttributeError: - key = ins_node.astNode._id - - try: - if std_node.ast_name == "FunctionDef": - value = AstSymbol(std_node.astNode.name, std_node) - else: # TODO: Little skulpt artifact that doesn't raise Attribute Errors... - raise AttributeError - # value = AstSymbol(std_node.astNode.name, std_node) - except AttributeError: - node = std_node - if type(node.astNode).__name__ != "Call": - node = node.parent - node._id = std_node._id - value = AstSymbol(std_node._id, node) - if key in self.func_table: - new_list = self.func_table[key] - if value not in new_list: - new_list.append(value) - if not (key in self.conflict_keys): - for other in new_list: - if value.id != other.id: - self.conflict_keys.append(key) - break - else: - new_list = AstSymbolList() - new_list.append(value) - - self.func_table[key] = new_list - return len(self.conflict_keys) - - def add_var_to_sym_table(self, ins_node, std_node): - """ - Adds ins_node._id to the symbol table if it doesn't already exist, mapping it to a set of ins_node. Updates a - second dictionary that maps ins_node to an std_node, and overwrites the current std_node since there should only - be one mapping. - - Args: - ins_node: instructor node or str representing variable - std_node: student node representing variable - - Returns: - int: number of conflicts generated - - """ - if not isinstance(std_node, CaitNode): - raise TypeError - if isinstance(ins_node, str): - key = ins_node - else: - key = ins_node.astNode._id - value = AstSymbol(std_node.astNode._id, std_node) - if key in self.symbol_table: - new_list = self.symbol_table[key] - new_list.append(value) - if not (key in self.conflict_keys): - for other in new_list: - if value._id != other._id: - self.conflict_keys.append(key) - break - else: - new_list = AstSymbolList() - new_list.append(value) - - self.symbol_table[key] = new_list - return len(self.conflict_keys) - - def add_exp_to_sym_table(self, ins_node, std_node): - """ - Adds mapping of expression symbol to student node - This function does NOT check for conflicts at the moment and probably should at some point. - TODO: Check for conflicts - Args: - ins_node: Instructor node representing an expression - std_node: student ast subtree corresponding to the symbol - - Returns: - None - """ - if not isinstance(std_node, CaitNode): - raise TypeError - self.exp_table[ins_node.astNode.id] = std_node - - def add_node_pairing(self, ins_node, std_node): - """ - Adds a mapping of instructor ast node to a specific student ast node - Args: - ins_node: instructor pattern ast node - std_node: student ast node - - Returns: - None - """ - if not isinstance(std_node, CaitNode): - raise TypeError - self.mappings[ins_node] = std_node - - def has_conflicts(self): - """ - - Returns: - bool: True if number of conflicts is greater than 0 - """ - return len(self.conflict_keys) > 0 - - def new_merged_map(self, other): - """ - Returns a newly merged map consisting of this and other - without modifying self. - Args: - other (AstMap): the other AstMap to be merged with - - Returns: - AstMap: self modified by adding the contents of other - """ - new_map = AstMap() - new_map.merge_map_with(self) - new_map.merge_map_with(other) - return new_map - - def merge_map_with(self, other): - """ - Returns a newly merged map consisting of this and other - by modifying self - Args: - other (AstMap): the other AstMap to be merged with - - Returns: - AstMap: self modified by adding the contents of other - """ - if other is None: - return - - if not isinstance(other, type(self)): - raise TypeError - - # merge all mappings - self.mappings.update(other.mappings) - - # merge all expressions - self.exp_table.update(other.exp_table) - - # merge all symbols - for key, value in other.symbol_table.items(): - for sub_value in value: - self.add_var_to_sym_table(key, sub_value.astNode) - - # merge all functions - for key, value in other.func_table.items(): - for sub_value in value: - self.add_func_to_sym_table(str(key), sub_value.astNode) - - @property - def match_lineno(self): - """ - - Returns: - int: the line number this match started on - """ - values = [v.lineno for v in self.mappings.values() - if v.lineno is not None] - if not values: - return -1 - else: - return min(values) - - def __getitem__(self, id_n): - if id_n.startswith('__'): - expression = self.exp_table[id_n] - expression.map = self - return expression - else: - if id_n in self.symbol_table: - return self.symbol_table[id_n] - else: - return self.func_table[id_n] - - def __contains__(self, id_n): - if id_n.startswith('__'): - return id_n in self.exp_table - else: - exists = id_n in self.symbol_table - if exists: - return exists - else: - return id_n in self.func_table diff --git a/src/lib/pedal/cait/cait_api.py b/src/lib/pedal/cait/cait_api.py deleted file mode 100644 index 8dff0a0308..0000000000 --- a/src/lib/pedal/cait/cait_api.py +++ /dev/null @@ -1,272 +0,0 @@ -from pedal.report import MAIN_REPORT -from pedal.cait.stretchy_tree_matching import StretchyTreeMatcher -from pedal.cait.cait_node import CaitNode -import ast - - -class CaitException(Exception): - pass - - -""" -CaitReport: - A collection of information from the latest CAIT run. - - Attrs: - ast: The CaitNode tree that was most recently parsed out. - cache[str:CaitNode]: A dictionary mapping student code (str) to - parsed representations. - success: Whether there have been any errors so far. - error: The exception that occurred, or None if no exception so far. -""" - - -def _parse_source(code, cait_report): - """ - Parses the given code and returns its Cait representation. If the parse was - unsuccessful, it attaches the error to the report. - - Args: - code (str): A string of Python code. - cait_report (dict): A Cait Report to store information in. - Returns: - AstNode: The parsed AST reprensetation, or None - """ - try: - parsed = ast.parse(code) - except SyntaxError as e: - cait_report['success'] = False - cait_report['error'] = e - return ast.parse("") - return parsed - - -def _load_cait(student_code, report): - """ - Retrieves the current report for CAIT. If there is no CAIT report, it will - generate one. If source code is given, that will be used instead of the - report's source code. - - Args: - student_code (str): The code to parse into the a CaitNode tree. If - None, then it will use the code in the report's Source tool. - report (Report): The report to attach data to. - - Returns: - dict: Returns the Cait Report - """ - if 'cait' not in report: - report['cait'] = {'success': True, 'error': None, - 'ast': None, 'cache': {}} - cait = report['cait'] - if student_code is not None: - if student_code in cait['cache']: - cait['ast'] = cait['cache'][student_code] - return cait - else: - student_ast = _parse_source(student_code, cait) - elif report['source']['success']: - student_code = report['source']['code'] - if student_code in cait['cache']: - cait['ast'] = cait['cache'][student_code] - return cait - else: - student_ast = report['source']['ast'] - else: - report.attach("Unparsable Source", tool='cait', - category='analyzer') - cait['success'] = False - cait['ast'] = CaitNode(ast.parse(""), report=report) - return cait - cait['ast'] = cait['cache'][student_code] = CaitNode(student_ast, report=report) - return cait - - -def require_tifa(self): - """ - Confirms that TIFA was run successfully, otherwise raises a - CaitException. - """ - if not self.report['tifa']['success']: - raise CaitException("TIFA was not run prior to CAIT.") - - -# noinspection PyBroadException -def parse_program(student_code=None, report=None): - """ - Parses student code and produces a CAIT representation. - - Args: - student_code (str): The student source code to parse. If None, defaults - to the code within the Source tool of the given Report. - report (Report): The report to attach data to. Defaults to MAIN_REPORT. - - Returns: - CaitNode: A CAIT-enhanced representation of the root Node. - """ - if report is None: - report = MAIN_REPORT - cait_report = _load_cait(student_code, report) - return cait_report['ast'] - - -def expire_cait_cache(report=None): - """ - Deletes the most recent CAIT run and any cached CAIT parses. - - Args: - report (Report): The report to attach data to. Defaults to MAIN_REPORT. - """ - if report is None: - report = MAIN_REPORT - report['cait']['ast'] = None - report['cait']['cache'] = {} - - -def def_use_error(node, report=None): - """ - Checks if node is a name and has a def_use_error - - Args: - node (str or AstNode or CaitNode): The Name node to look up. - report (Report): The report to attach data to. Defaults to MAIN_REPORT. - Returns: - True if the given name has a def_use_error - """ - if report is None: - report = MAIN_REPORT - if not isinstance(node, str) and node.ast_name != "Name": - raise TypeError - try: - def_use_vars = report['tifa']['issues']['Initialization Problem'] - except KeyError: - return False - if not isinstance(node, str): - node_id = node.id - else: - node_id = node - has_error = False - for issue in def_use_vars: - name = issue['name'] - if name == node_id: - has_error = True - break - return has_error - - -# noinspection PyBroadException -def data_state(node, report=None): - """ - Determines the Tifa State of the given node. - - Args: - node (str or AstNode or CaitNode): The Name node to look up in TIFA. - report (Report): The report to attach data to. Defaults to MAIN_REPORT. - Returns: - The State of the object (Tifa State) or None if it doesn't exist - """ - if report is None: - report = MAIN_REPORT - if not isinstance(node, str) and node.ast_name != "Name": - raise TypeError - if isinstance(node, str): - node_id = node - else: - node_id = node.id - try: - return report['tifa']["top_level_variables"][node_id] - except KeyError: - return None - - -def data_type(node, report=None): - """ - Looks up the type of the node using Tifa's analysis. - - Args: - node (str or AstNode or CaitNode): The Name node to look up in TIFA. - report (Report): The report to attach data to. Defaults to MAIN_REPORT. - Returns: - The type of the object (Tifa type) or None if a type doesn't exist - """ - state = data_state(node, report=report) - if state is not None: - return state.type - return None - - -def find_match(pattern, student_code=None, report=None, cut=False): - """ - Apply Tree Inclusion and return the first match of the `pattern` in the - `student_code`. - - Args: - pattern (str): The CaitExpression to match against. - student_code (str): The string of student code to check against. - Defaults to the code of the Source tool in the Report. - report (Report): The report to attach data to. - cut (bool): Set to true to trim root to first branch - Returns: - CaitNode or None: The first matching node for the given pattern, or - None if nothing was found. - """ - matches = find_matches(pattern=pattern, student_code=student_code, - report=report, cut=cut) - if matches: - return matches[0] - else: - return None - - -def find_matches(pattern, student_code=None, report=None, cut=False): - """ - Apply Tree Inclusion and return all matches of the `pattern` in the - `student_code`. - - Args: - pattern (str): The CaitExpression to match against. - student_code (str): The string of student code to check against. - report (Report): The report to attach data to. - cut (bool): Set to true to trim root to first branch - Returns: - List[CaitNode]: All matching nodes for the given pattern. - """ - if report is None: - report = MAIN_REPORT - cait_report = _load_cait(student_code, report) - if not cait_report['success']: - return [] - student_ast = cait_report['ast'] - matcher = StretchyTreeMatcher(pattern, report=report) - return matcher.find_matches(student_ast) - - -def find_submatches(pattern, student_code, is_mod=False): - """ - Incomplete. - """ - return find_expr_sub_matches(pattern, student_code, is_mod) - - -def find_expr_sub_matches(pattern, student_code, is_mod=False, report=None): - """ - Finds pattern in student_code - # TODO: Add code to make pattern accept CaitNodes - # TODO: Make this function without so much meta knowledge - Args: - pattern: the expression to find (str that MUST evaluate to a Module node with a single child or an AstNode) - student_code: student subtree - is_mod (bool): currently hack for multiline sub matches - report: defaults to MAIN_REPORT unless another one exists - Returns: - a list of matches or False if no matches found - """ - if report is None: - report = MAIN_REPORT - is_node = isinstance(pattern, CaitNode) - if not isinstance(pattern, str) and not is_node: - raise TypeError("pattern expected str or CaitNode, found {0}".format(type(pattern))) - matcher = StretchyTreeMatcher(pattern, report=report) - if (not is_node and not is_mod) and len(matcher.root_node.children) != 1: - raise ValueError("pattern does not evaluate to a singular statement") - return matcher.find_matches(student_code, check_meta=False) diff --git a/src/lib/pedal/cait/cait_node.py b/src/lib/pedal/cait/cait_node.py deleted file mode 100644 index e64509fb0d..0000000000 --- a/src/lib/pedal/cait/cait_node.py +++ /dev/null @@ -1,516 +0,0 @@ -import ast -from pedal.cait.ast_helpers import dump -from types import MethodType -from pedal.report import MAIN_REPORT - - -class CaitNode: - """ - A wrapper class for AST nodes. Linearizes access to the children of the ast - node and saves the field this AST node - originated from. - - Attributes: - ast_name (str): The name of the original AstNode (e.g., "Name" or - "FunctionDef") - - TODO: May want to just add fields and methods to the existing AST nodes and - use a production pattern instead. - """ - - def __init__(self, ast_node, my_field='', tid=0, lin_tree=None, - ancestor=None, report=None): - """ - - Args: - ast_node (ast_node): The AST node to be wrapped - my_field (str): the field of the parent node that produced this child. - tid (int): the tree id - lin_tree list of cait_node: A linear version of the tree - ancestor (cait_node): The parent of this node - report: The report associated with this particular match. - """ - if report is None: - report = MAIN_REPORT - self.report = report - self.children = [] - self.astNode = ast_node - self.field = my_field - self.tree_id = tid - self.parent = ancestor - if lin_tree is None: - self.linear_tree = [self] - else: - lin_tree.append(self) - self.linear_tree = lin_tree - - # reference to the easy node wrapping the ast_node - setattr(ast_node, 'cait_node', self) - - tid_count = tid - - my_field_generator = ast.iter_fields(self.astNode) - for item in my_field_generator: - field, value = item - # if the field doesn't have a value, no child exists - if value is None: - continue - - # If the children are not in an array, wrap it in an array for - # consistency in the code the follows - if not isinstance(value, list): - value = [value] - - # Reference ast_node_visitor.js for the original behavior and keep note of it for the purposes of handling - # the children noting the special case when the nodes of the array are actually parameters of the node - # (e.g. a load function) instead of a child node - for sub_value in value: - if isinstance(sub_value, ast.AST): - new_child = CaitNode(sub_value, my_field=field, - tid=tid_count + 1, - lin_tree=self.linear_tree, - ancestor=self, - report=self.report) - self.children.append(new_child) - tid_count = len(self.linear_tree) - 1 - - def __str__(self): - return ''.join([self.field, "\n", dump(self.astNode)]) - - def numeric_logic_check(self, mag, expr): - """ - If this node is a Compare or BoolOp node, sees if the logic in expr (a javascript string being a logical - statement) matches the logic of self. This assumes that we are only comparing numerical values to a single - variable - TODO: modify this to take multiple variables - TODO: modify to support more than +, -, *, and / BinOps - TODO: modify to support unary operators other than USub and Not - TODO: This is very finicky and buggy, try not to use it - Args: - mag (float): the order of magnitude that should be added to numbers to check logic, 1 is usually a good value, - especially when working with the set of integers. - expr (Compare or BoolOp): the "Compare" or "BoolOp" tree to check self against - - Returns: - bool: True if self (typically student node) and expr are equivalent boolean expressions - """ - - def eval_unop(unop_num, unop_node): - operand = eval_selector(unop_num, unop_node.operand) - op = unop_node.op_name - - return {"USub": -operand, - "Not": not operand}[op] - - def eval_binop(binop_num, binop_node): - left = eval_selector(binop_num, binop_node.left) - right = eval_selector(binop_num, binop_node.right) - op = binop_node.op_name - - return { - "Add": left + right, - "Sub": left - right, - "Mult": left * right, - "Div": left / right}[op] - - def eval_selector(op_num, op_expr): - op_expr = op_num if op_expr.ast_name == "Name" else op_expr - if isinstance(op_expr, (int, float)): - return op_expr - if op_expr.ast_name == "BinOp": - return eval_binop(op_num, op_expr) - if op_expr.ast_name == "UnaryOp": - return eval_unop(op_num, op_expr) - if op_expr.ast_name == "Num": - return op_expr.n - raise NotImplementedError - - def eval_bool_comp(num_list, comp_ast): - ops = comp_ast.ops_names - comps = comp_ast.comparators - results = [] - current = comp_ast.left - left = current - - for num_i in num_list: - result = True - for op, comp in zip(ops, comps): - current = eval_selector(num_i, current) - comp_p = eval_selector(num_i, comp) - - res = { - "Eq": current == comp_p, - "NotEq": current != comp_p, - "Lt": current < comp_p, - "LtE": current <= comp_p, - "Gt": current > comp_p, - "GtE": current >= comp_p, - }[op] - current = comp - result = result and res - if not result: - break - results.append(result) - current = left - return results - - def eval_boolop(num_list, boolop_ast): - boolop = boolop_ast.op_name - values = boolop_ast.values - results_c = None - is_and = boolop == "And" - for value in values: - if value.ast_name == "Compare": - results = eval_bool_comp(num_list, value) - else: # should be boolop - results = eval_boolop(num_list, value) - if results_c is None: - results_c = results - else: # compile results - new_result = [] - for result1, result2 in zip(results_c, results): - if is_and: - new_result.append(result1 and result2) - else: - new_result.append(result1 or result2) - results_c = new_result - return results_c - - try: - ins_expr = CaitNode(ast.parse(expr), report=self.report).body[0].value - ins_nums = ins_expr.find_all("Num") - std_nums = self.find_all("Num") - test_nums = [] - for num in ins_nums: - raw_num = num.n - test_nums.append(raw_num) - test_nums.append(raw_num + mag) - test_nums.append(raw_num - mag) - for num in std_nums: - raw_num = num.n - test_nums.append(raw_num) - test_nums.append(raw_num + mag) - test_nums.append(raw_num - mag) - - if self.ast_name == "Compare": - std_res = eval_bool_comp(test_nums, self) - elif self.ast_name == "BoolOp": - std_res = eval_boolop(test_nums, self) - else: - return False - - if ins_expr.ast_name == "Compare": - ins_res = eval_bool_comp(test_nums, ins_expr) - elif ins_expr.ast_name == "BoolOp": - ins_res = eval_boolop(test_nums, ins_expr) - else: - raise TypeError - return ins_res == std_res - except Exception: - return False - - def get_next_tree(self): - """Gets the next tree in the AST - This method gets the next AST node that is of equal or higher level than self. Returns None if the end of the - tree is reached - TODO: Create a get sibling method. - - Returns: - cait_node: The next tree in the AST - - """ - - # adding function to track tree ids - def visit_counter(self, node): - self.counter += 1 - self.generic_visit(node) - - node_counter = ast.NodeVisitor() - setattr(node_counter, 'counter', self.tree_id) - node_counter.visit = MethodType(visit_counter, node_counter) - - # getting ids - node_counter.visit(self.astNode) - out_of_tree = node_counter.counter >= len(self.linear_tree) # check if out of bounds - # len(self.children) > 0 and self.children[-1] == node_counter - if out_of_tree: - return None - return self.linear_tree[node_counter.counter] - - def get_child(self, node): - """ - - Args: - node: a non-CaitNode ast node - - Returns: - cait_node: the corresponding cait_node to the child - """ - if isinstance(node, ast.AST): - for child in self.children: - if child.astNode == node: - return child - elif isinstance(node, int): - return self.children(node) - return None - - @staticmethod - def get_ast_name(node): - return type(node).__name__ - - def get_clashing_attr(self, key): - if key == "value": - return self.get_value() - - def __getattr__(self, item): - key = item - """ - Non-ast node attributes based on ast_node attributes - """ - node_name = CaitNode.get_ast_name(self.astNode) - if node_name == "Assign" and key == "target": - key = "targets" - if item in AST_SINGLE_FUNCTIONS: - key = item[:-5] # strip suffix '_name' - if item in AST_ARRAYS_OF_FUNCTIONS: - key = item[:-6] # strip suffix '_names' - - """ - Non-ast node attributes - """ - if key == 'next_tree': - return self.get_next_tree() - if key == 'ast_name': - return node_name - elif key == '_name': - return self.astNode.name - elif key == 'ast_node': - return self.astNode - else: # ast node attributes or derivative attributes - if hasattr(self.astNode, key): - # noinspection PyBroadException - try: - field = self.astNode.__getattribute__(key) - except Exception: - field = None - if node_name == "Assign" and item != key: - if item == "target": - return field[0].cait_node # Get's the relevant ast node - elif item == "targets" and isinstance(field, list): - easy_array = [] - for node in field: - easy_array.append(node.cait_node) - return easy_array - else: - return field - elif item in AST_SINGLE_FUNCTIONS: - return type(field).__name__ - elif item in AST_ARRAYS_OF_FUNCTIONS: - str_ops_list = [] - for op in field: - str_ops_list.append(type(op).__name__) - return str_ops_list - elif isinstance(field, ast.AST): - return field.cait_node - elif isinstance(field, list): - try: - return [f.cait_node for f in field] - except AttributeError: - # This can only happen in NonLocals, which has a list - # of raw strings in the `names` property - return field - else: - return field - else: # get added field that may have existed for different node types - return self.get_clashing_attr(key) - - def find_matches(self, pattern, is_mod=False, check_meta=True, use_previous=True): - """ - Retrieves any patterns that match against this CaitNode. Expected to be - used for subpattern matching. - Args: - pattern (CaitNode/str): The pattern searched for in this cait_node - is_mod (bool): Assumes that this node is a module node - check_meta (bool): Checks if meta information of the nodes also matches (e.g. instead of just checking children, - also checking whether the children have the same field name in both the pattern and the - source - use_previous (bool): If True, the match will be searched while inheriting the symbol table of the parent. This - means that variable consistency must be maintained between parent and child matches. - - Returns: - :obj: 'list' of :'obj': AstMap: a list of matches - - """ - # Avoid circular import - import pedal.cait.stretchy_tree_matching as stm - is_node = isinstance(pattern, CaitNode) - if not isinstance(pattern, str) and not is_node: - raise TypeError("pattern expected str or CaitNode, found {0}".format(type(pattern))) - matcher = stm.StretchyTreeMatcher(pattern, report=self.report) - if (not is_node and not is_mod) and len(matcher.root_node.children) != 1: - raise ValueError("pattern does not evaluate to a singular statement") - prev_match = self.map if use_previous else None - return matcher.find_matches(self, check_meta=check_meta, pre_match=prev_match) - - def find_match(self, pattern, is_mod=False, check_meta=True, use_previous=True): - matches = self.find_matches(pattern, is_mod, check_meta=check_meta, use_previous=use_previous) - if len(matches) != 0: - return matches[0] - return None - - def find_all(self, node_type): - """Finds all nodes defined by string node_type - - Args: - node_type: the string representing the "type" of node to look for - - Returns: - a list of Ast Nodes (cait_nodes) of self that are of the specified type (including self if self - meets that criteria) - """ - items = [] - visitor = ast.NodeVisitor() - # setattr(visitor, "current_id", self.tree_id - 1) - setattr(visitor, "items", items) - func_name = 'visit_' + node_type - - def main_visit(self, node): - self.items.append(node.cait_node) - return self.generic_visit(node) - - func_ref = main_visit - setattr(visitor, func_name, MethodType(func_ref, visitor)) - visitor.visit(self.astNode) - return visitor.items - - def has(self, node): - """ - Determine if this node has the given `node`. - """ - if isinstance(node, (int, float)): - visitor = ast.NodeVisitor() - has_num = [] - - def visit_Num(self, potential): - has_num.append(node == potential.n) - return self.generic_visit(potential) - - visitor.visit_Num = MethodType(visit_Num, visitor) - visitor.visit(self.astNode) - return any(has_num) - elif node.ast_name != "Name": - return False - visitor = ast.NodeVisitor() - has_name = [] - - def visit_Name(self, potential): - has_name.append(node.id == potential.id) - return self.generic_visit(potential) - - visitor.visit_Name = MethodType(visit_Name, visitor) - visitor.visit(self.astNode) - return any(has_name) - - def is_before(self, other): - """ - Uses tree id to check if self node came before other. - Args: - other (cait_node): the other node to compare to - - Returns: - bool: True if self is before other - """ - try: - return self.tree_id < other.tree_id and self.linear_tree == other.linear_tree - except Exception: - raise TypeError - - def is_ast(self, ast_name): - """ - Checks self is the type of the specified ast node - Args: - ast_name (str): The name of the ast node type - - Returns: - bool: True if this node's ast name matches the specified one - """ - if not isinstance(ast_name, str): - ast_name = CaitNode.get_ast_name(ast_name.astNode) - return CaitNode.get_ast_name(self.astNode).lower() == ast_name.lower() - - def is_method(self): - """ - Checks if self is a method - - Returns: - bool: True if I'm a FunctionDef, and if any of my parents are ClassDef. - """ - # Check if I'm a FunctionDef, and if any of my parents are ClassDef. - if self.ast_name != "FunctionDef": - return False - current = self.parent - while current is not None: - if current.ast_name == "ClassDef": - return True - # Don't treat closures as methods - elif current.ast_name == "FunctionDef": - return False - current = current.parent - return False - - def get_data_state(self): - """ - Gets the data_state object of self - - Returns: - data_state or None: returns data_state if self is a name and exists, otherwise None - """ - if self.ast_name != "Name": - return None - try: - return self.report['tifa']["top_level_variables"][self.id] - except KeyError: - return None - - def get_data_type(self): - """ - - Returns: - type of the variable associated with this node if it's a name node, otherwise None. - """ - state = self.get_data_state() - if state is None: - return None - else: - return state.type - - def was_type(self, tp): - """ - - Returns: - type of the variable associated with this node if it's a name node, otherwise None. - """ - state = self.get_data_state() - if state is None: - return None - else: - return state.was_type(tp) - - def get_value(self): - """" - Returns: - Value of node if Num or Str, and get_data_state if Name - """ - value = None - if self.is_ast("Num"): - value = self.n - elif self.is_ast("Str"): - value = self.s - elif self.is_ast("Name"): - # TODO: Decide on what this should return... - value = self.id - return value - - -AST_SINGLE_FUNCTIONS = ["ctx_name", "op_name"] -AST_ARRAYS_OF_FUNCTIONS = ["ops_names"] diff --git a/src/lib/pedal/cait/stretchy_tree_matching.py b/src/lib/pedal/cait/stretchy_tree_matching.py deleted file mode 100644 index e18cae37af..0000000000 --- a/src/lib/pedal/cait/stretchy_tree_matching.py +++ /dev/null @@ -1,666 +0,0 @@ -import ast -import re -from pedal.cait.ast_map import AstMap -from pedal.cait.cait_node import CaitNode - -# "Enums" for _name_regex -_VAR = "var" -_EXP = "exp" -_WILD = "wild" -_NONE_FIELD = "none" - - -def is_primitive(item): - """ - Determines if the given item is a primitive value (either an int, float, - str, bool, or None). - - Args: - item (any): Any value - Returns: - bool: Whether the item is a primitive value. - """ - return isinstance(item, (int, float, str, bool)) or item is None - - -def _name_regex(name_id): - var_match = re.compile('^_[^_].*_$') # /regex - exp_match = re.compile('^__.*__$') # /regex - wild_card = re.compile('^___$') # /regex - return {_VAR: var_match.match(name_id), - _EXP: exp_match.match(name_id), - _WILD: wild_card.match(name_id)} - - -class StretchyTreeMatcher: - def __init__(self, ast_or_code, report, filename="__main__"): - """ - The StretchyTreeMatcher is used to compare a pattern against some - student code. It produces a set of potential mappings between them. - - Args: - ast_or_code (str or AstNode): The students' code or a valid AstNode from - `ast.parse`. If the code has invalid syntax, a SyntaxError - will be raised. - filename (str): The filename to parse with - only used for error - reporting. - report (Report): A report to obtain data from. - """ - self.report = report - if isinstance(ast_or_code, str): - ast_node = ast.parse(ast_or_code, filename) - else: - ast_node = ast_or_code - # Build up root - if ast_node is None: - self.root_node = None - elif isinstance(ast_node, CaitNode): - self.root_node = ast_node - else: - self.root_node = CaitNode(ast_node, _NONE_FIELD, report=self.report) - - def find_matches(self, ast_or_code, filename="__main__", check_meta=True, pre_match=None): - """ - Args: - ast_or_code (str or AstNode): The students' code or a valid AstNode from - `ast.parse`. If the code has invalid syntax, a SyntaxError - will be raised. - filename (str): The filename to parse with - only used for error - reporting. - check_meta (bool): Determine if the nodes came from the same AST - field. - Returns: - list[AstMap]: A list of AstMaps that are suitable matches. - """ - if isinstance(ast_or_code, str): - other_tree = CaitNode(ast.parse(ast_or_code, filename), report=self.report) - elif isinstance(ast_or_code, CaitNode): - other_tree = ast_or_code - else: - other_tree = CaitNode(ast_or_code, _NONE_FIELD, report=self.report) - explore_root = self.root_node - trim_set = ["Expr", "Module"] - explore_root_old_field = explore_root.field - if self.root_node is not None: # Trimming ins_node - while (len(explore_root.children) == 1 and - explore_root.ast_name in trim_set): - explore_root.field = explore_root_old_field - explore_root = explore_root.children[0] - explore_root_old_field = explore_root.field - explore_root.field = _NONE_FIELD - other_root = other_tree - other_root_old_field = other_root.field - if other_root is not None: # Trimming std_node - while len(other_root.children) == 1 and other_root.ast_name in trim_set: - other_root.field = other_root_old_field - other_root = other_root.children[0] - other_root_old_field = other_root.field - other_root.field = _NONE_FIELD - matches = self.any_node_match(explore_root, other_root, - check_meta=check_meta, pre_match=pre_match) - explore_root.field = explore_root_old_field - other_root.field = other_root_old_field - return matches - - def any_node_match(self, ins_node, std_node, check_meta=True, cut=False, pre_match=None): - """ - Finds whether ins_node can be matched to some node in the tree std_node - - Args: - ins_node: - std_node: - check_meta: - cut: - - Returns: - list of AstMaps: a mapping of nodes and a symbol table mapping ins_node to - some node in the tree std_node or False if such a matching does not - exist - """ - # @TODO: create a more public function that converts ins_node and std_node into CaitNodes - # TODO: Create exhaustive any_node_match - # matching: an object representing the mapping and the symbol table - matching = self.deep_find_match(ins_node, std_node, check_meta, pre_match=pre_match) - # if a direct matching is found - if matching: - for match in matching: - match.match_root = std_node - else: - matching = [] - # return matching # return it - # if not matching or exhaust: # otherwise - # try to matching ins_node to each child of std_node, recursively - for std_child in std_node.children: - matching_c = self.any_node_match(ins_node, std_child, check_meta=check_meta, cut=cut, pre_match=pre_match) - if matching_c: - for match in matching_c: - match.match_root = std_child - # return matching - matching = matching + matching_c - if len(matching) > 0: - return matching - return [] - - def deep_find_match(self, ins_node, std_node, check_meta=True, - pre_match=None): - """ - Finds whether ins_node and matches std_node and whether ins_node's children flexibly match std_node's children - in order - Args: - ins_node: The instructor ast that should be included in the student AST - std_node: The student AST that we are searching for the included tree - check_meta: Flag, if True, check whether the two nodes originated from the same ast field - pre_match: If this was part of a previous match... - - Returns: - a mapping of nodes and a symbol table mapping ins_node to std_node, or [] if no mapping was found - """ - method_name = "deep_find_match_" + type(ins_node.astNode).__name__ - target_func = getattr(self, method_name, self.deep_find_match_generic) - return target_func(ins_node, std_node, check_meta, pre_match=pre_match) - - # noinspection PyPep8Naming - def deep_find_match_Name(self, ins_node, std_node, check_meta=True, pre_match=None): - name_id = ins_node.astNode.id - match = _name_regex(name_id) - mapping = AstMap() - matched = False - meta_matched = self.metas_match(ins_node, std_node, check_meta) - if match[_VAR] and meta_matched: # if variable - if type(std_node.astNode).__name__ == "Name": - return self.deep_find_match_generic(ins_node, std_node, - check_meta=check_meta, ignores=["ctx"], pre_match=pre_match) - # could else return False, but shallow_match_generic should do this as well - elif match[_EXP]: # and meta_matched: # if expression - # terminate recursion, the whole subtree should match since expression nodes match to anything - mapping.merge_map_with(pre_match) - mapping.add_exp_to_sym_table(ins_node, std_node) - matched = True - elif match[_WILD] and meta_matched: # if wild card, don't care - # terminate the recursion, the whole subtree should match since wild cards match to anything - matched = True - - if matched: - mapping.add_node_pairing(ins_node, std_node) - return [mapping] - # else - return self.deep_find_match_generic(ins_node, std_node, - check_meta=check_meta, ignores=["ctx"], pre_match=pre_match) - - # noinspection PyPep8Naming - def deep_find_match_BinOp(self, ins_node, std_node, check_meta=True, pre_match=None): - op = ins_node.astNode.op - op = type(op).__name__ - is_generic = not (op == "Mult" or op == "Add") - if is_generic: - return self.deep_find_match_generic(ins_node, std_node, check_meta, pre_match=pre_match) - else: # this means that the node is clearly commutative - return self.deep_find_match_binflex(ins_node, std_node, False, pre_match=pre_match) - - # noinspection PyMethodMayBeStatic - def binflex_helper(self, case_left, case_right, new_mappings, base_mappings, pre_match=None): - """ - adds to new_mappings (return/modify by argument) the mappings for both the left and right subtrees as denoted by - case_left and case_right - Args: - case_left: The mappings for the left opperand - case_right: The mappings for the right opperand - new_mappings: The new set of mappings to generate - base_mappings: The original mappings of the binop node - pre_match: A mapping passed down from an initial match - Returns: - None - """ - if case_left and case_right: - for case_l in case_left: - new_map = base_mappings[0].new_merged_map(case_l).new_merged_map(pre_match) - for case_r in case_right: - both = new_map.new_merged_map(case_r) - if not both.has_conflicts(): - new_mappings.append(both) - - def deep_find_match_binflex(self, ins_node, std_node, check_meta=False, pre_match=None): - base_mappings = self.shallow_match(ins_node, std_node, check_meta) - if not base_mappings: - return [] - op_mappings = self.shallow_match(ins_node.children[1], std_node.children[1], check_meta=True) - if not op_mappings: - return [] - base_mappings = [base_mappings[0].new_merged_map(op_mappings[0])] - - if base_mappings: - ins_left = ins_node.children[0] # instructor left ast node - ins_right = ins_node.children[2] # instructor right ast node - std_left = std_node.children[0] # student left ast node - std_right = std_node.children[2] # student right ast node - new_mappings = [] - # case 1: ins_left->std_left and ins_right->std_right - case_left = self.deep_find_match(ins_left, std_left, False) - case_right = self.deep_find_match(ins_right, std_right, False) - self.binflex_helper(case_left, case_right, new_mappings, base_mappings, pre_match=pre_match) - # case 2: ins_left->std_right and ins_right->std_left - case_left = self.deep_find_match(ins_left, std_right, False) - case_right = self.deep_find_match(ins_right, std_left, False) - self.binflex_helper(case_left, case_right, new_mappings, base_mappings, pre_match=pre_match) - if len(new_mappings) == 0: - return [] - return new_mappings - return [] - - def deep_find_match_Expr(self, ins_node, std_node, check_meta=True, pre_match=None): - """ - An Expression node (not to be confused with expressions denoted by the instructor nodes in Name ast nodes) - checks whether it should be generic, or not - Args: - ins_node: Instructor ast to find in the student ast - std_node: Student AST to search for the instructor ast in - check_meta: flag to check whether the fields of the instructor node and the student node should match - pre_match: An AstMap from a previous matching run - - Returns: - AstMap: a mapping between the instructor and student asts, or False if such a mapping doesn't exist - """ - # if check_meta and ins_node.field != std_node.field: - if not self.metas_match(ins_node, std_node, check_meta): - return [] - mapping = AstMap() if pre_match is None else pre_match - value = ins_node.value - ast_type = type(value.astNode).__name__ - if ast_type == "Name": - name_id = value.astNode.id - exp_match = re.compile('^__.*__$') # /regex - wild_card = re.compile('^___$') # /regex - matched = False - meta_matched = self.metas_match(ins_node, std_node, check_meta) - if exp_match.match(name_id): # and meta_matched: # if expression - # terminate recursion, the whole subtree should match since expression nodes match to anything - mapping.add_exp_to_sym_table(value, std_node) - matched = True - elif wild_card.match(name_id) and meta_matched: # if wild card, don't care - # terminate the recursion, the whole subtree should match since wild cards match to anything - matched = True - if matched: - mapping.add_node_pairing(ins_node, std_node) - return [mapping] - return self.deep_find_match_generic(ins_node, std_node, check_meta) - - def deep_find_match_generic(self, ins_node, std_node, check_meta=True, ignores=None, pre_match=None): - """ - This first uses shallow match to find a base map (match) from which to - build off. The algorithm then tracks all the possible mappings that - match a given child node in the instructor AST, keeping track of which - siblings have been visited. - - For each instructor child, when all children of the student node have - been iterated through recursively, a helper function is called. This - helper function determines which possible children validly can extend - the base match to create a set of new base maps through use of the - indicies of the sibilings. - - The process repeats itself until no matches can be grown or until each - instructor child node has been visited - - Args: - ins_node: Instructor ast to find in the student ast - std_node: Student AST to search for the instructor ast in - check_meta: flag to check whether the fields of the instructor node and the student node should match - ignores: List of fields to ignore in the field match - pre_match: a map from a previous match - - Returns: - a mapping between the isntructor and student asts, or [] if such a mapping doesn't exist - """ - if ignores is None: - ignores = [] - base_mappings = self.shallow_match(ins_node, std_node, check_meta) - if base_mappings: - for mapping in base_mappings: - mapping.merge_map_with(pre_match) - # base case this runs 0 times because no children - # find each child of ins_node that matches IN ORDER - base_sibs = [-1] - youngest_sib = 0 - # for each child - for i, insChild in enumerate(ins_node.children): - # make a new set of maps - running_maps = [] - running_sibs = [] - if insChild.field in ignores: - continue - # accumulate all potential matches for current child - for j, std_child in enumerate(std_node.children[youngest_sib:], youngest_sib): - std_child = std_node.children[j] - new_mapping = self.deep_find_match(insChild, std_child, check_meta) - if new_mapping: - running_maps.append(new_mapping) - running_sibs.append(j) - map_update = self.map_merge(base_mappings, base_sibs, running_maps, running_sibs) - if map_update is None: - return [] - base_mappings = map_update['new_maps'] - base_sibs = map_update['new_sibs'] - youngest_sib = map_update['youngest_sib'] + 1 - return base_mappings - return [] - - # noinspection PyMethodMayBeStatic - def map_merge(self, base_maps, base_sibs, run_maps, run_sibs): - """ - Merges base_maps with the current possible maps. Helper method to deep_find_match_generic. checks whether each - mapping in run_maps can extend the match to any possible mapping in base_maps. - - Args: - base_maps: The original mappings - base_sibs: The corresponding siblings for each mapping in base_maps - run_maps: The set of maps to merge into the current base_maps - run_sibs: The corresponding siblings for each mapping in run_maps - - Returns: - A new set of maps for all valid extensions of base_maps with running maps - """ - # no matching nodes were found - if len(run_maps) == 0: - return None - new_maps = [] - new_sibs = [] - youngest_sib = run_sibs[0] - for baseMap, base_sib in zip(base_maps, base_sibs): - for run_map, runSib in zip(run_maps, run_sibs): - if runSib > base_sib: - for run_mapsub in run_map: - new_map = baseMap.new_merged_map(run_mapsub) - if not new_map.has_conflicts(): # if it's a valid mapping - new_maps.append(new_map) - new_sibs.append(runSib) - if len(new_maps) == 0: - return None - return { - 'new_maps': new_maps, - 'new_sibs': new_sibs, - 'youngest_sib': youngest_sib - } - - # noinspection PyMethodMayBeStatic,PyPep8Naming,PyUnusedLocal - def shallow_match_Module(self, ins_node, std_node, check_meta=True): - """ - Flexibly matches a module node to a module or a body - Args: - ins_node: - std_node: - check_meta: - - Returns: - a mapping of ins_node to std_node, or False if doesn't match - """ - if type(std_node.astNode).__name__ == "Module" or std_node.field == "body": - mapping = AstMap() - mapping.add_node_pairing(ins_node, std_node) - return [mapping] - return [] - - def shallow_symbol_handler(self, ins_node, std_node, id_val, check_meta=True): - """ - TODO: Make this handle the func field to handle functions - Matches ins_node to std_node for different cases of encountering a name node in ins_node - case 1: _var_ matches if std_node is a name node and automatically returns a mapping and symbol table - case 2: __exp__ matches to any subtree and automatically returns a mapping and symbol table - case 3: ___ matches to any subtree and automatically returns a mapping - case 4: matches only if the exact names are the same (falls through to shallow_match_generic) - Args: - ins_node: - std_node: - id_val: - check_meta: - - Returns: - list of AstMap: a mapping of ins_node to std_node and possibly a symbol_table, or False if it doesn't match - """ - name_id = ins_node.astNode.__getattribute__(id_val) - match = _name_regex(name_id) - mapping = AstMap() - matched = False - # TODO: add functionality to add function references to func_table? - meta_matched = self.metas_match(ins_node, std_node, check_meta) - if match[_VAR] and meta_matched: # variable - if type(std_node.astNode).__name__ == "Name" or id_val in ["attr", "arg"]: - if id_val in ["attr", "arg"]: - std_node.astNode._id = std_node.astNode.__getattribute__(id_val) - if std_node.field == "func" and ins_node.field != _NONE_FIELD: - # TODO: This 'ins_node.field != _NONE_FIELD' code is for an obscure edge case where the - # instructor code is only _var_ - std_node.astNode._id = std_node.astNode.__getattribute__(id_val) - mapping.add_func_to_sym_table(ins_node, std_node) - else: - std_node.astNode._id = std_node.astNode.__getattribute__(id_val) - mapping.add_var_to_sym_table(ins_node, std_node) # TODO: Capture result? - matched = True - # could else return False, but shallow_match_generic should do this as well - elif match[_EXP] and meta_matched: - mapping.add_exp_to_sym_table(ins_node, std_node) - matched = True - elif match[_WILD] and meta_matched: - matched = True - - if matched: - mapping.add_node_pairing(ins_node, std_node) - return [mapping] - # else - return self.shallow_match_main(ins_node, std_node, check_meta=check_meta, ignores=["ctx"]) - - # noinspection PyPep8Naming,PyMethodMayBeStatic - def shallow_match_arg(self, ins_node, std_node, check_meta=True): - ins_node.astNode._id = ins_node.arg - # TODO: annotations are currently ignored because shallow_symbol_handler doesn't handle them, feature? or - # should we fix this. Although this should actually be toggleable? - return self.shallow_symbol_handler(ins_node, std_node, "arg", check_meta=check_meta) - - def shallow_match_arguments(self, ins_node, std_node, check_meta=True): - # TODO: do we ignore default values? Currently not ignored - return self.shallow_match_generic(ins_node, std_node, check_meta=check_meta) - - # noinspection PyPep8Naming,PyMethodMayBeStatic - def shallow_func_handle(self, ins_node, std_node, check_meta=True): - if ins_node.field == "func" and std_node.field == "func": - ins_node.astNode._id = ins_node.astNode.attr - return self.shallow_symbol_handler(ins_node, std_node, "attr", check_meta) - return self.shallow_match_generic(ins_node, std_node, check_meta) - - def shallow_match_Attribute(self, ins_node, std_node, check_meta=True): - if ins_node.field == "func" and std_node.ast_name == "Attribute": - return self.shallow_func_handle(ins_node, std_node, check_meta) - elif std_node.ast_name == "Attribute": - ins_node.astNode._id = ins_node.attr # TODO: Fix this hack more gracefully - # add_var_to_sym_table in ast_map needs the id attribute to make the map - return self.shallow_symbol_handler(ins_node, std_node, "attr", check_meta) - else: - return self.shallow_match_generic(ins_node, std_node, check_meta) - - # noinspection PyPep8Naming - def shallow_match_Name(self, ins_node, std_node, check_meta=True): - """ - TODO: Make this handle the func field to handle functions - Matches ins_node to std_node for different cases of encountering a name node in ins_node - case 1: _var_ matches if std_node is a name node and automatically returns a mapping and symbol table - case 2: __exp__ matches to any subtree and automatically returns a mapping and symbol table - case 3: ___ matches to any subtree and automatically returns a mapping - case 4: matches only if the exact names are the same (falls through to shallow_match_generic) - Args: - ins_node: - std_node: - check_meta: - - Returns: - list of AstMap: a mapping of ins_node to std_node and possibly a symbol_table, or False if it doesn't match - """ - ins_node.ast_node._id = ins_node.id - return self.shallow_symbol_handler(ins_node, std_node, "id", check_meta) - - # noinspection PyPep8Naming,PyMethodMayBeStatic - def shallow_match_Pass(self, ins_node, std_node, check_meta=True): - """ - An empty body should match to anything - Args: - ins_node: Instructor ast to find in the student ast - std_node: Student AST to search for the instructor ast in - check_meta: flag to check whether the fields of the instructor node and the student node should match - - Returns: - list of AstMap: a mapping between the isntructor and student asts, or False if such a mapping doesn't exist - """ - # if check_meta and ins_node.field != std_node.field: - if not self.metas_match(ins_node, std_node, check_meta): - return [] - mapping = AstMap() - mapping.add_node_pairing(ins_node, std_node) - return [mapping] - - # noinspection PyPep8Naming,PyMethodMayBeStatic - def shallow_match_Expr(self, ins_node, std_node, check_meta=True): - """ - An Expression node (not to be confused with expressions denoted by the instructor nodes in Name ast nodes) - should match to anything - Args: - ins_node: Instructor ast to find in the student ast - std_node: Instructor ast to find in the student ast - check_meta: flag to check whether the fields of the instructor node and the student node should match - - Returns: - a mapping between the instructor and student asts, or False if such a mapping doesn't exist - """ - # if check_meta and ins_node.field != std_node.field: - if not self.metas_match(ins_node, std_node, check_meta): - return [] - mapping = AstMap() - mapping.add_node_pairing(ins_node, std_node) - return [mapping] - - def shallow_match_Call(self, ins_node, std_node, check_meta=True): - return self.shallow_match_main(ins_node, std_node, check_meta, ignores=None) - # matches = self.shallow_match_main(ins_node, std_node, check_meta, ignores=["func"]) - # if matches: - # pass - # return None - # TODO: Make this handle Calls more intelligently - - # noinspection PyPep8Naming - def shallow_match_FunctionDef(self, ins_node, std_node, check_meta=True): - ins = ins_node.astNode - std = std_node.astNode - meta_matched = self.metas_match(ins_node, std_node, check_meta) - is_match = type(ins).__name__ == type(std).__name__ and meta_matched - mapping = self.shallow_match_main(ins_node, std_node, check_meta, ignores=['name', 'args']) - matched = False - if is_match and mapping: - name = ins.name - match = _name_regex(name) - if match[_VAR] and meta_matched: # variable - ins._id = name - std._id = std.name - mapping[0].add_func_to_sym_table(ins_node, std_node) # TODO: Capture result? - matched = True - elif match[_WILD] and meta_matched: - matched = True - elif name == std.name and meta_matched: - matched = True - if matched: - return mapping - else: - return [] - - # noinspection PyMethodMayBeStatic - def shallow_match_generic(self, ins_node, std_node, check_meta=True): - """ - Checks that all non astNode attributes are equal between ins_node and std_node - Args: - ins_node: Instructor ast root node - std_node: Student AST root node - check_meta: flag to check whether the fields of the instructor node and the student node should match - - Returns: - list of AstMap: a mapping between the instructor and student root nodes (potentially empty) - """ - return self.shallow_match_main(ins_node, std_node, check_meta=check_meta) - - def shallow_match_main(self, ins_node, std_node, check_meta=True, ignores=None): - """ - Checks that all non astNode attributes are equal between ins_node and std_node - Args: - ins_node: Instructor ast root node - std_node: Student AST root node - check_meta: flag to check whether the fields of the instructor node and the student node should match - ignores: a mapping between the instructor and student root nodes, or False if such a mapping doesn't exist - - Returns: - - """ - if ignores is None: - ignores = [] - ignores.append("_id") # special exception for symbols in lookup tables - ins = ins_node.astNode - std = std_node.astNode - ins_field_list = list(ast.iter_fields(ins)) - std_field_list = list(ast.iter_fields(std)) - meta_matched = self.metas_match(ins_node, std_node, check_meta) - is_match = len(ins_field_list) == len(std_field_list) and type(ins).__name__ == type( - std).__name__ and meta_matched - for insTup, stdTup in zip(ins_field_list, std_field_list): - if not is_match: - break - - ins_field = insTup[0] - ins_value = insTup[1] - std_field = stdTup[0] - std_value = stdTup[1] - - if ins_value is None: - continue - - ignore_field = ins_field in ignores - - is_match = (ins_field == std_field) or ignore_field - - if not isinstance(ins_value, list): - ins_value = [ins_value] - - if not isinstance(std_value, list): - std_value = [std_value] - - # is_match = len(ins_value) == len(std_value)# for stretchy matching this isn't True - # Reference ast_node_visitor.js for the original behavior and keep note of it for the purposes of handling - # the children noting the special case when the nodes of the array are actually parameters of the node - # (e.g. a load function) instead of a child node - if not ignore_field: - for inssub_value, stdsub_value in zip(ins_value, std_value): - if not is_match: - break - # TODO: make this a smarter comparison, maybe handle dictionaries, f-strings, tuples, etc. - if is_primitive(inssub_value): - is_match = inssub_value == stdsub_value - if is_match: - mapping = AstMap() # return MAPPING - mapping.add_node_pairing(ins_node, std_node) - return [mapping] - else: - return [] - - # filter function for various types of nodes - def shallow_match(self, ins_node, std_node, check_meta=True): - method_name = 'shallow_match_' + type(ins_node.astNode).__name__ - target_func = getattr(self, method_name, self.shallow_match_generic) - return target_func(ins_node, std_node, check_meta=check_meta) - - @staticmethod - def metas_match(ins_node, std_node, check_meta=True): - """ - Args: - ins_node: - std_node: - check_meta: - - Returns: - - """ - return ((check_meta and ins_node.field == std_node.field) or - not check_meta - # or std_node.field == _NONE_FIELD - or ins_node.field == _NONE_FIELD) diff --git a/src/lib/pedal/mistakes/__init__.py b/src/lib/pedal/mistakes/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/lib/pedal/mistakes/instructor_append.py b/src/lib/pedal/mistakes/instructor_append.py deleted file mode 100644 index 21e0288e53..0000000000 --- a/src/lib/pedal/mistakes/instructor_append.py +++ /dev/null @@ -1,136 +0,0 @@ -from pedal.cait.cait_api import find_matches, find_expr_sub_matches, data_state -from pedal.report.imperative import gently_r, explain_r - - -def append_group_on_change(): - wrong_not_append_to_list() - - -def append_group(): - missing_append_in_iteration() - missing_append_list_initialization() - wrong_append_list_initialization() - wrong_not_append_to_list() - append_list_wrong_slot() - # TODO: add app_assign on next iteration of experiment! - # app_assign() - - -def find_append_in(node): - append_list = [] - calls = node.find_all("Call") - for node in calls: - if node.func.attr == "append": - append_list.append(node) - return append_list - - -""" -def missing_append_in_iteration(): - std_ast = parse_program() - for_loops = std_ast.find_all("For") - for loop in for_loops: - if len(find_append_in(loop)): - return False - explain("You must construct a list by appending values one at a time to the list.

    (app_in_iter)
    ") - return True -""" - - -def missing_append_in_iteration(): - message = "You must construct a list by appending values one at a time to the list." - code = "app_in_iter" - tldr = "For Loop Append Not Found" - matches = find_matches("for ___ in ___:\n" - " __expr__") - if matches: - for match in matches: - __expr__ = match["__expr__"] - submatch = __expr__.find_matches("___.append(___)") - if submatch: - return False - return explain_r(message, code, label=tldr) - return False - - -def wrong_not_append_to_list(): - message = ("Values can only be appended to a list. The variable {0!s} is either not initialized, " - "not initialized correctly, or is confused with another variable.") - code = "app_not_list" - tldr = "Not Appending to List" - matches = find_matches("for ___ in ___:\n" - " __expr__") - for match in matches: - __expr__ = match["__expr__"] - submatches = __expr__.find_matches("_target_.append(___)") - for submatch in submatches: - _target_ = submatch["_target_"] - if not data_state(_target_).was_type('list'): - return explain_r(message.format(_target_), code, label=tldr) - return False - - -def missing_append_list_initialization(): - message = "The list variable {0!s} must be initialized." - code = "no_app_list_init" - tldr = "List Not Initialized" - matches = find_matches("for ___ in ___:\n" - " __expr__") - for match in matches: - __expr__ = match["__expr__"] - submatches = __expr__.find_matches("_new_list_.append(___)", ) - for submatch in submatches: - _new_list_ = submatch["_new_list_"].astNode - # TODO: In theory revisit this by merging matches - matches02 = find_matches("{} = []\n" - "for ___ in ___:\n" - " __expr__".format(_new_list_.id)) - if not matches02: - return explain_r(message.format(_new_list_.id), code, label=tldr) - return False - - -def wrong_append_list_initialization(): - message = ("The list variable {0!s} is either not initialized " - "correctly or mistaken for another variable. " - "The list you append to should be initialized to an empty list.") - code = "app_list_init" - tldr = "Incorrect Initialization or Usage of Empty List" - matches = find_matches("_list_ = __expr1__\n" - "for ___ in ___:\n" - " __expr2__") - for match in matches: - _list_ = match["_list_"].astNode - __expr1__ = match["__expr1__"] - __expr2__ = match["__expr2__"] - submatch = __expr2__.find_matches("_list_.append(___)") - if submatch and (__expr1__.ast_name == "List" and - len(__expr1__.elts) != 0 or - __expr1__.ast_name != "List"): - return explain_r(message.format(_list_.id), code, label=tldr) - return False - - -def append_list_wrong_slot(): - message = "You should not append a list ({0!s}) to {1!s}." - code = "app_list_slot" - tldr = "Appending List Error" - matches = find_matches("_target_.append(_item_)") - if matches: - for match in matches: - _item_ = match["_item_"].astNode - _target_ = match["_target_"].astNode - if data_state(_item_).was_type('list'): - return explain_r(message.format(_item_.id, _target_.id), code, label=tldr) - return False - - -def app_assign(): - message = ("Appending modifies the list, so unlike addition," - " an assignment statement is not needed when using append.") - code = "app_asgn" - - matches = find_matches("_sum_ = _sum_.append(__exp__)") - if matches: - return explain_r(message, code) - return False diff --git a/src/lib/pedal/mistakes/instructor_filter.py b/src/lib/pedal/mistakes/instructor_filter.py deleted file mode 100644 index 7362ae24ef..0000000000 --- a/src/lib/pedal/mistakes/instructor_filter.py +++ /dev/null @@ -1,53 +0,0 @@ -from pedal.cait.cait_api import find_match, find_matches -from pedal.report.imperative import gently_r, explain_r - - -def filter_group(): - missing_if_in_for() - append_not_in_if() - - -def missing_if_in_for(): - """ - Name: missing_if_in_for - Pattern: - missing - for in ___ : - if ... ... : - - Feedback: The arrangement of decision and iteration is not correct for the filter pattern. - Returns: - - """ - message = ("The arrangement of decision and iteration is not correct for the filter pattern. " - "You need to evaluate the decision for each element of the list.") - code = "missing_if_in_for" - tldr = "Missing if In For" - matches = find_matches("for _item_ in ___:\n" - " if __expr__:\n" - " pass") - if not matches: - return explain_r(message, code, label=tldr) - return False - - -def append_not_in_if(): - """ - Name: append_not_in_if - Pattern: - missing - if ... : - ___.append(___) - - Feedback: Only items satisfying some condition should be appended to the list. - - Returns: - """ - message = "Only items satisfying some condition should be appended to the list." - code = "app_not_in_if" - tldr = "Append not in if" - match = find_match("if ___:\n" - " ___.append(___)") - if not match: - return explain_r(message, code, label=tldr) - return False diff --git a/src/lib/pedal/mistakes/instructor_histogram.py b/src/lib/pedal/mistakes/instructor_histogram.py deleted file mode 100644 index 7893bee929..0000000000 --- a/src/lib/pedal/mistakes/instructor_histogram.py +++ /dev/null @@ -1,124 +0,0 @@ -from pedal.cait.cait_api import find_match, find_matches, data_state -from pedal.report.imperative import gently_r, explain_r - - -def histogram_group(): - histogram_argument_not_list() - histogram_wrong_list() - histogram_missing() - plot_show_missing() - - -def histogram_missing(): - """ - Name: histogram_missing - Pattern: - - Missing - plt.hist(___) - - Feedback: The program should display a histogram. - - Returns: - """ - message = "The program should display a histogram." - code = "histo_missing" - tldr = "Missing Histogram" - match = find_match("plt.hist(___)") - if not match: - return explain_r(message, code, label=tldr) - return False - - -def plot_show_missing(): - """ - Name: plot_show_missing - Pattern: - Missing - plt.show() - - Feedback: The plot must be explicitly shown to appear in the Printer area. - - Returns: - """ - message = "The plot must be explicitly shown to appear in the Printer area." - code = "plot_show_missing" - tldr = "No Plot Shown" - match = find_match("plt.show()") - if not match: - return explain_r(message, code, label=tldr) - return False - - -def histogram_argument_not_list(): - """ - - Name: histogram_argument_not_list - Pattern: - plt.hist() - Where type() is not "list" - - Feedback: Making a histogram requires a list; is not a list. - - - Returns: - """ - message = "Making a histogram requires a list; {0!s} is not a list." - code = "hist_arg_not_list" - tldr = "Making Histogram from Non-list" - matches = find_matches("plt.hist(_argument_)") - if matches: - for match in matches: - _argument_ = match["_argument_"].astNode - if not _argument_.get_data_state() or not _argument_.get_data_state().was_type('list'): - return explain_r(message.format(_argument_.id), code, label=tldr) - return False - - -def histogram_wrong_list(): - """ - - Name: histogram_wrong_list - Pattern: - - for ___ in ___: - .append(___) - plt.hist() - - where name() != name() - - Feedback: The list created in the iteration is not the list being used to create the histogram. - - Returns: - """ - message = "The list created in the iteration is not the list being used to create the histogram." - code = "histo_wrong_list" - tldr = "Plotting Wrong List" - matches = find_matches("for ___ in ___:\n" - " __expr__\n" - "plt.hist(_list_)") - if matches: - for match in matches: - _list_ = match["_list_"].astNode - __expr__ = match["__expr__"] - submatches = __expr__.find_matches("_list_.append(___)") - if submatches: - return False - return explain_r(message, code, label=tldr) - return False - - -def histogram_wrong_placement(): - message = "The histogram should be plotted only once, after the new list has been created" - code = "histo_wrong_place" - tldr = "Histogram Plot Placed Incorrectly" - matches = find_matches("for ___ in ___:\n" - " pass\n") - if matches: - matches02 = find_matches("plt.hist(___)") - for match in matches: - if matches02: - for match02 in matches02: - if match02.match_lineno > match.match_lineno: - return False - return explain_r(message, code, label=tldr) diff --git a/src/lib/pedal/mistakes/instructor_iteration.py b/src/lib/pedal/mistakes/instructor_iteration.py deleted file mode 100644 index 4b1817eb57..0000000000 --- a/src/lib/pedal/mistakes/instructor_iteration.py +++ /dev/null @@ -1,153 +0,0 @@ -from pedal.cait.cait_api import (parse_program, find_match, find_matches, - find_expr_sub_matches, data_state, - def_use_error) -from pedal.report.imperative import gently_r, explain_r - - -def iteration_group(): - list_initialization_misplaced() - wrong_target_is_list() - wrong_list_repeated_in_for() - missing_iterator_initialization() - list_not_initialized_on_run() - wrong_iterator_not_list() - missing_target_slot_empty() - missing_for_slot_empty() - wrong_target_reassigned() - - -def iteration_group_on_change(): - wrong_target_is_list() - wrong_list_repeated_in_for() - wrong_iterator_not_list() - - -def all_for_loops(): - std_ast = parse_program() - return std_ast.find_all("For") - - -# this conflics with list_repeated_in_for -def wrong_target_is_list(): - message = ('The variable {0!s} is a list and ' - 'should not be placed in the iteration variable slot of the "for" block') - code = "target_is_list" - tldr = "Iteration Variable Overwriting List" - match = find_match("for _item_ in ___:\n pass") - if match: - _item_ = match["_item_"].astNode - if data_state(_item_).was_type('list'): - return explain_r(message.format(_item_.id), code, label=tldr) - return False - - -# this conflicts with list_in_wrong_slot_in_for -def wrong_list_repeated_in_for(): - message = 'The {0!s} variable can only appear once in the "for" block.' - code = "list_repeat" - tldr = "Duplicate Iteration Variable" - match = find_match("for _item_ in _item_:\n pass") - if match: - _item_ = match["_item_"].astNode - if data_state(_item_).was_type('list'): - return explain_r(message.format(_item_.id), code, label=tldr) - return False - - -# this isn't consistent with the pattern you wrote TODO: Fix this -def missing_iterator_initialization(): - message1 = "The slot to hold a list in the iteration is empty." - code1 = "no_iter_init-blank" - tldr1 = "Iteration Variable is Blank" - - message2 = "The variable {0!s} is in the list slot of the iteration but is not a list." - code2 = "no_iter_init" - tldr2 = "Iteration Variable is Not a List" - - match = find_match("for ___ in _list_:\n pass") - if match: - _list_ = match["_list_"].astNode - if _list_.id == "___": - return explain_r(message1, code1, label=tldr1) - elif not data_state(_list_).was_type('list'): - return explain_r(message2.format(_list_.id), code2, label=tldr2) - return False - - -# TODO: We need to cover the different cases for these -def wrong_iterator_not_list(): - message = ("The variable {0!s} has been set to something that is not a list but is placed " - "in the iteration block that must be a list.") - code = "iter_not_list" - tldr = "Iteration List is not list" - - match = find_match("for ___ in _item_:\n pass") - if match: - _item_ = match["_item_"].astNode - if not data_state(_item_).was_type('list'): - return explain_r(message.format(_item_.id), code, label=tldr) - return False - - -def missing_target_slot_empty(): - message = "You must fill in the empty slot in the iteration." - code = "target_empty" - tldr = "Missing Iteration Variable" - match = find_match("for _item_ in ___:\n pass") - if match: - _item_ = match["_item_"].astNode - if _item_.id == "___": - return explain_r(message, code, label=tldr) - return False - - -def list_not_initialized_on_run(): - message = "The list in your for loop has not been initialized." - code = "no_list_init" - tldr = "List Variable Uninitialized" - match = find_match("for ___ in _item_:\n pass") - if match: - _item_ = match["_item_"][0].astNode - if def_use_error(_item_): - return explain_r(message, code, label=tldr) - return False - - -def list_initialization_misplaced(): - message = "Initialization of {0!s} is a list but either in the wrong place or redefined" - code = "list_init_misplaced" - tldr = "Iterating over Non-list" - match = find_match("for ___ in _item_:\n pass") - if match: - _item_ = match["_item_"][0].astNode - if data_state(_item_).was_type('list') and def_use_error(_item_): - return explain_r(message.format(_item_.id), code, label=tldr) - return False - - -def missing_for_slot_empty(): - message = "You must fill in the empty slot in the iteration." - code = "for_incomplete" - tldr = "Iteration Incomplete" - match = find_match("for _item_ in _list_:\n pass") - if match: - _item_ = match["_item_"][0].astNode - _list_ = match["_list_"][0].astNode - if _item_.id == "___" or _list_.id == "___": - return explain_r(message, code, label=tldr) - return False - - -def wrong_target_reassigned(): - message = "The variable {0!s} has been reassigned. The iteration variable shouldn't be reassigned" - code = "target_reassign" - tldr = "Iteration Variable has been Reassigned" - matches = find_matches("for _item_ in ___:\n" - " __expr__") - for match in matches: - __expr__ = match["__expr__"] - _item_ = match["_item_"][0] - submatches = __expr__.find_matches("_item_ = ___") - if submatches: - return explain_r(message.format(_item_), code, label=tldr) - return False diff --git a/src/lib/pedal/mistakes/iteration_context.py b/src/lib/pedal/mistakes/iteration_context.py deleted file mode 100644 index 4df295854b..0000000000 --- a/src/lib/pedal/mistakes/iteration_context.py +++ /dev/null @@ -1,1194 +0,0 @@ -from pedal.cait.cait_api import (parse_program, - find_matches, find_match, - find_expr_sub_matches) -from pedal.report.imperative import explain, gently -import pedal.mistakes.instructor_append as append_api -from pedal.toolkit.utilities import * -from pedal.sandbox.compatibility import get_output, get_plots -from pedal.report.imperative import gently_r, explain_r - - -# ################8.2 Start####################### -def wrong_list_length_8_2(): - message = "You must have at least three pieces" - code = "list length_8.2" - tldr = "List too short" - matches = find_matches("_list_ = __expr__") - if matches: - for match in matches: - __expr__ = match["__expr__"] - if __expr__.ast_name == "List" and len(__expr__.elts) < 3: - return explain_r(message, code, label=tldr) - return False - - -def missing_list_initialization_8_2(): - message = ('You must set the variable shopping_cart' - 'to a list containing the prices of items in the shopping cart.') - code = "missing_list_init_8.2" - tldr = "Missing list initialization" - matches = find_matches("shopping_cart = __expr__") - for match in matches: - __expr__ = match["__expr__"] - if __expr__.ast_name == "List": - return False - return explain_r(message, code, label=tldr) - - -def wrong_list_is_constant_8_2(): - message = 'You must set shoppping_cart to a list of values not to a single number.' - code = "list_is_const_8.2" - tldr = "Shopping Cart not set to list" - matches = find_matches("shopping_cart = __expr__") - for match in matches: - __expr__ = match["__expr__"] - if __expr__.ast_name == "Num": - return explain_r(message, code, label=tldr) - return False - - -def list_all_zeros_8_2(): - message = 'Try seeing what happens when you change the numbers in the list.' - code = 'default_list_8.2' - tldr = 'Use different numbers' - matches = find_matches("_var_ = [__list__]") - for match in matches: - __list__ = match['__list__'] - list_node = __list__.parent - all_num = list_node.find_all("Num") - all_zeros = True - for num in all_num: - if num.n != 0: - all_zeros = False - break - if all_zeros: - return explain_r(message, code, label=tldr) - return False - - -# ################8.2 End####################### - - -# ################8.3 Start####################### -def wrong_list_initialization_placement_8_3(): - message = ('The list of episode lengths (episode_length_list)' - ' must be initialized before the iteration which uses this list.') - code = "init_place_8.3" - tldr = "Wrong Initialization Placement" - for_matches = find_matches("for ___ in ___:\n" - " pass") - init_matches = find_matches("episode_length_list = ___") - if init_matches and for_matches: - for for_match in for_matches: - for_lineno = for_match.match_lineno - for init_match in init_matches: - if init_match.match_lineno > for_lineno: - return explain_r(message, code, label=tldr) - return False - - -def wrong_accumulator_initialization_placement_8_3(): - message = ('The variable to hold the sum of the episode lengths (sum_length) ' - 'must be initialized before the iteration which uses this variable.') - code = "accu_init_place_8.3" - tldr = "Accumulator initialization misplaced" - for_matches = find_matches("for ___ in ___:" - " pass") - init_matches = find_matches("sum_length = 0") - if init_matches and for_matches: - for for_match in for_matches: - for_lineno = for_match.match_lineno - for init_match in init_matches: - if init_match.match_lineno > for_lineno: - return explain_r(message, code, label=tldr) - return False - - -def wrong_iteration_body_8_3(): - message = "The addition of each episode length to the total length is not in the correct place." - code = "iter_body_8.3" - tldr = "Accumulation Misplaced" - match = find_match("for _item_ in _list_:\n" - " sum_length = ___ + ___\n") - if not match: - return explain_r(message, code, label=tldr) - return False - - -def wrong_print_8_3(): - message = ('The output of the total length of time is not in the correct place. The total length of time should be' - ' output only once after the total length of time has been computed.') - code = "print_8.3" - tldr = "Print statement misplaced" - match = find_match("for _item_ in _list_:\n" - " pass\n" - "print(_total_)") - if not match: - return explain_r(message, code, label=tldr) - return False - - -# ################8.3 End####################### - - -# ################8.4 Start####################### -def missing_target_slot_empty_8_4(): - message = 'You must fill in the empty slot in the iteration.' - code = 'target_empty_8.4' - tldr = "Iteration Variable Empty" - matches = find_matches("for _item_ in pages_count_list:\n" - " pass") - if matches: - for match in matches: - _item_ = match["_item_"][0] - if _item_.id == "___": - return explain_r(message, code, tldr) - return False - - -def missing_addition_slot_empty_8_4(): - message = "You must fill in the empty slot in the addition." - code = "add_empty_8.4" - tldr = "Addition Blank" - matches = find_matches("sum_pages + _item_") - if matches: - for match in matches: - _item_ = match["_item_"][0] - if _item_.id == "___": - return explain_r(message, code, label=tldr) - return False - - -def wrong_names_not_agree_8_4(): - message = "Each value of {0!s} must be added to {1!s}." - code = "name_agree_8.4" - tldr = "Iteration Variable and Accumulation Mismatch" - matches = find_matches("for _item1_ in pages_count_list:\n" - " sum_pages = sum_pages + _item2_") - if matches: - for match in matches: - # in theory, these will always be different? should test in test_cait - _item1_ = match["_item1_"][0] - _item2_ = match["_item2_"][0] - if _item1_.id != _item2_.id: - return explain_r(message.format(_item1_.id, _item2_.id), code, label=tldr) - return False - - -# ################8.4 End####################### -def wrong_modifying_list_8_5(): - """ - - # old code for record keeping because significantly different semantics - std_ast = parse_program() - list_init = std_ast.find_all('List') - true_sum = 0 - if len(list_init) != 0: - for value in list_init[0].elts: - true_sum = value.n + true_sum - if true_sum != sum([20473, 27630, 17849, 19032, 16378]) or len(list_init) == 0: - explain('Don\'t modify the list

    (mod_list_8.5)
    ') - return True - return False - - Returns: - """ - message = "Don't modify the list" - code = "mod_list_8.5" - match = find_match("[20473, 27630, 17849, 19032, 16378]") - if not match: - return explain_r(message, code) - return False - - -def wrong_modifying_list_8_6(): - """ - std_ast = parse_program() - list_init = std_ast.find_all('List') - true_sum = 0 - for value in list_init[0].elts: - true_sum = value.n + true_sum - if true_sum != sum([2.9, 1.5, 2.3, 6.1]): - explain('Don\'t modify the list

    (mod_list_8.6)
    ') - Returns: - """ - message = "Don't modify the list" - code = "mod_list_8.6" - match = find_match("_list_ = [2.9, 1.5, 2.3, 6.1]") - if not match: - return explain_r(message, code) - return False - - -def wrong_should_be_counting(): - """ - std_ast = parse_program() - for_loops = std_ast.find_all('For') - for loop in for_loops: - iter_prop = loop.target - assignments = loop.find_all('Assign') - for assignment in assignments: - binops = assignment.find_all('BinOp') - for binop in binops: - if binop.has(iter_prop) and binop.op == 'Add': - explain('This problem asks for the number of items in the list not the total of all the values in ' - 'the list.

    (not_count)
    ') - Returns: - """ - message = "This problem asks for the number of items in the list not the total of all the values in the list." - code = "not_count" - tldr = "Summing instead of counting" - matches = find_matches("for _item_ in ___:\n" - " __expr__") - if matches: - for match in matches: - _item_ = match["_item_"][0] - __expr__ = match["__expr__"] - submatches = __expr__.find_matches("___ = ___ + _item_") - if submatches: - return explain_r(message, code, label=tldr) - return False - - -def wrong_should_be_summing(): - """ - std_ast = parse_program() - for_loops = std_ast.find_all('For') - for loop in for_loops: - assignments = loop.find_all('Assign') - for assignment in assignments: - binops = assignment.find_all('BinOp') - for binop in binops: - if binop.has(1) and binop.op == 'Add': - explain('This problem asks for the total of all the values in the list not the number of items in ' - 'the list.

    (not_sum)
    ') - """ - message = "This problem asks for the total of all the values in the list not the number of items in the list." - code = "not_sum" - tldr = "Counting instead of summing" - matches = find_matches("for _item_ in ___:\n" - " __expr__") - if matches: - for match in matches: - __expr__ = match["__expr__"] - submatches = __expr__.find_matches("___ = 1 + ___", ) - if submatches: - return explain_r(message, code, label=tldr) - return False - - -def missing_addition_slot_empty(): - """ - std_ast = parse_program() - assignments = std_ast.find_all('Assign') - for assignment in assignments: - # left = assignment.target - right = assignment.value - binOp = right.find_all('BinOp') - if len(binOp) == 1: - binOp = binOp[0] - if binOp.op == 'Add': - if binOp.left.ast_name == 'Name' and binOp.right.ast_name == 'Name': - if binOp.left.id == '___' or binOp.right.id == '___': - explain('You must fill in the empty slot in the addition.

    (add_empty)
    ') - return True - return False - Returns: - """ - message = "You must fill in the empty slot in the addition." - code = "add_empty" - tldr = "Addition Blank" - matches = find_matches("___ + _item_") - if matches: - for match in matches: - _item_ = match["_item_"][0] - if _item_.id == "___": - return explain_r(message, code, tldr) - return False - - -def wrong_cannot_sum_list(): - """ - - std_ast = parse_program() - for_loops = std_ast.find_all('For') - for loop in for_loops: - list_prop = loop.iter - assignments = loop.find_all('Assign') - for assignment in assignments: - binops = assignment.find_all('BinOp') - for binop in binops: - if binop.has(list_prop) and binop.op == 'Add': - explain('Addition can only be done with a single value at a time, not with an entire list at one' - ' time.

    (sum_list)
    ') - Returns: - """ - message = 'Addition can only be done with a single value at a time, not with an entire list at one' - code = "sum_list" - tldr = "Cannot Sum a List" - matches = find_matches("for ___ in _list_ :\n" - " __expr__") - if matches: - for match in matches: - _list_ = match["_list_"][0] - __expr__ = match["__expr__"] - # submatches = __expr__.find_matches("___ = ___ + {}".format(_list_.id), ) - submatches = __expr__.find_matches("___ = ___ + _list_") - if submatches: - return explain_r(message, code, label=tldr) - return False - - -def missing_no_print(): - message = "Program does not output anything." - code = "no_print" - tldr = "Missing Output" - prints = find_match('print(___)', cut=True) - if not prints: - return explain_r(message, code, label=tldr) - return False - - -def missing_counting_list(): - """ - std_ast = parse_program() - has_count = False - for_loops = std_ast.find_all('For') - if len(for_loops) > 0: - for loop in for_loops: - assignments = loop.find_all('Assign') - if len(assignments) < 1: - continue - for assignment in assignments: - binops = assignment.find_all('BinOp') - if len(binops) < 1: - continue - lhs = assignment.target - for binop in binops: - if binop.has(lhs) and binop.has(1) and binop.op == 'Add': - has_count = True - if not has_count: - explain('Count the total number of items in the list using iteration.

    (miss_count_list)
    ') - Returns: - """ - message = 'Count the total number of items in the list using iteration.' - code = "miss_count_list" - tldr = "Missing Count in Iteration" - matches = find_matches("for _item_ in ___:\n" - " __expr__") - if matches: - for match in matches: - __expr__ = match["__expr__"] - submatches = __expr__.find_matches("_sum_ = _sum_ + 1", ) - if submatches: - return False - return explain_r(message, code, label=tldr) - - -def missing_summing_list(): - """ - std_ast = parse_program() - has_total = False - for_loops = std_ast.find_all('For') - if len(for_loops) > 0: - for loop in for_loops: - assignments = loop.find_all('Assign') - if len(assignments) < 1: - continue - iter_prop = loop.target - for assignment in assignments: - binops = assignment.find_all('BinOp') - if len(binops) < 1: - continue - lhs = assignment.target - for binop in binops: - if binop.has(lhs) and binop.has(iter_prop) and binop.op == 'Add': - has_total = True - if not has_total: - explain('Sum the total of all list elements using iteration.

    (miss_sum_list)
    ') - Returns: - """ - message = 'Sum the total of all list elements using iteration.' - code = "miss_sum_list" - tldr = "Missing Sum in Iteration" - matches = find_matches("for _item_ in ___:\n" - " __expr__") - if matches: - for match in matches: - _item_ = match["_item_"][0] - __expr__ = match["__expr__"] - submatches = __expr__.find_matches("_sum_ = _sum_ + _item_") - if submatches: - return False - return explain_r(message, code, label=tldr) - - -def missing_zero_initialization(): - """ - - std_ast = parse_program() - for_loops = std_ast.find_all('For') - accumulator = None - loop_acu = None - for loop in for_loops: - assignments = loop.find_all('Assign') - for assignment in assignments: - binops = assignment.find_all('BinOp') - if len(binops) > 0: - lhs = assignment.target - for binop in binops: - if binop.has(lhs) and binop.op == 'Add': - accumulator = lhs - loop_acu = loop - accu_init = False - if accumulator is not None: - assignments = std_ast.find_all('Assign') - for assignment in assignments: - if loop_acu.lineno > assignment.lineno: - lhs = assignment.target - if lhs.id == accumulator.id and assignment.has(0): - accu_init = True - break - if not accu_init and accumulator is not None: - explain('The addition on the first iteration step is not correct because either the variable ' - '{0!s} has not been initialized to an appropriate initial value or it has not been placed' - ' in an appropriate location

    (miss_zero_init)
    '.format(accumulator.id)) - return False - return True - Returns: - """ - - message = ('The addition on the first iteration step is not correct because either the variable {0!s} ' - 'has not been initialized to an appropriate initial value ' - 'or it has not been placed in an appropriate location') - code = "miss_zero_init" - tldr = "Missing Initialization for Accumulator" - matches01 = find_matches("for ___ in ___:\n" - " __expr__") - if matches01: - for match01 in matches01: - __expr__ = match01["__expr__"] - submatches01 = __expr__.find_matches("_sum_ = _sum_ + ___", ) - if submatches01: - for submatch01 in submatches01: - _sum_ = submatch01["_sum_"][0] - matches02 = find_matches(("{} = 0\n" - "for ___ in ___:\n" - " __expr__").format(_sum_.id)) - if not matches02: - return explain_r(message.format(_sum_.id), code, label=tldr) - return False - - -def wrong_printing_list(): - message = 'You should be printing a single value.' - code = "list_print" - tldr = "Printing in Iteration" - matches = find_matches("for ___ in ___:\n" - " __expr__") - if matches: - for match in matches: - __expr__ = match["__expr__"] - if __expr__.find_matches("print(___)", ): - return explain_r(message, code, label=tldr) - return False - - -# TODO: This might be reason to rethink letting instructor symbols map to multiple items -def missing_average(): - message = "An average value is not computed.<" - code = "no_avg" - tldr = "Missing Computation" - matches_missing = find_matches("for ___ in ___:\n" - " pass\n" - "__expr__") - matches = [] - if matches_missing: - for match in matches_missing: - __expr__ = match["__expr__"] - sub_matches = __expr__.find_matches("_total_/_count_", ) - if sub_matches: - for sub_match in sub_matches: - _total_ = sub_match["_total_"][0] - _count_ = sub_match["_count_"][0] - if _total_.id != _count_.id: - matches.append(match) - if not len(matches) > 0: - return explain_r(message, code, label=tldr) - return False - - -def warning_average_in_iteration(): - message = ('An average value is best computed after the properties name {0!s}(total) and ' - '{1!s} are completely known rather than recomputing the average on each iteration.') - code = "avg_in_iter" - tldr = "Redundant Average Calculation" - matches = find_matches("for ___ in ___:\n" - " __expr__\n") - if matches: - for match in matches: - __expr__ = match["__expr__"] - submatches = __expr__.find_matches("_average_ = _total_/_count_", ) - if submatches: - for submatch in submatches: - _total_ = submatch["_total_"][0] - _count_ = submatch["_count_"][0] - _average_ = submatch["_average_"][0] - if _total_.id != _count_.id != _average_.id and _total_.id != _average_.id: - return explain_r(message.format(_total_.id, _count_.id), code, label=tldr) - - return False - - -def wrong_average_denominator(): - message = "The average is not calculated correctly." - code = "avg_denom" - tldr = "Incorrect Average Calculation" - matches = find_matches("for ___ in ___:\n" - " __expr__\n" # where expr contains _count_ = _count_ + 1 - "__expr2__") # where expr2 contains ___/_value_ - # where _value_.id != _count_.id - if matches: - for match in matches: - __expr__ = match["__expr__"] - __expr2__ = match["__expr2__"] - # _value_ = match["_value_"][0] - submatches = __expr__.find_matches("_count_ = _count_ + 1", ) - submatches02 = find_expr_sub_matches("___/_value_", __expr2__) - if submatches and submatches02: - for submatch in submatches: - for submatch02 in submatches02: - _count_ = submatch["_count_"][0] - _value_ = submatch02["_value_"][0] - if _count_.id != _value_.id: - return explain_r(message, code, label=tldr) - return False - - -def wrong_average_numerator(): - message = "The average is not calculated correctly." - code = "avg_numer" - tldr = "Incorrect Average Calculation" - matches = find_matches("for _item_ in ___:\n" - " __expr__\n" # where expr contains _total_ = _total_ + 1 - "__expr2__") # where expr2 contains _value_/___ - if matches: - for match in matches: - __expr__ = match["__expr__"] - __expr2__ = match["__expr2__"] - _item_ = match["_item_"][0] - # TODO: In theory, we could merge these matches to match variables... - submatches = __expr__.find_matches("_total_ = _total_ + _item_") - # submatches02 = find_expr_sub_matches("_value_/___", __expr2__) - submatches02 = __expr2__.find_matches("_value_/___") - if submatches and submatches02: - for submatch in submatches: - for submatch02 in submatches02: - _value_ = submatch02["_value_"][0] - _total_ = submatch["_total_"][0] - if _total_.id != _value_.id: - return explain_r(message, code, label=tldr) - return False - - -# #######################AVERAGE END########################### -def wrong_compare_list(): - message = "Each item in the list {0!s} must be compared one item at a time." - code = "comp_list" - tldr = "Not Comparing Each Item" - matches = find_matches("for ___ in _list_:\n" - " if __expr__:\n" - " pass") - if matches: - for match in matches: - _list_ = match["_list_"][0] - __expr__ = match["__expr__"] - if __expr__.has(_list_.astNode): - return explain_r(message.format(_list_.id), code, label=tldr) - return False - - -def wrong_for_inside_if(): - message = "The iteration should not be inside the decision block." - code = "for_in_if" - tldr = "For inside if" - match = find_match("if ___:\n" - " for ___ in ___:\n" - " pass") - if match: - return explain_r(message, code, label=tldr) - return False - - -def iterator_is_function(): - message = "You should make a variable for the list instead of using a function call for the list" - code = "iter_is_func" - tldr = "Using Function Call instead of List" - std_ast = parse_program() - for_loops = std_ast.find_all('For') - # noinspection PyBroadException - try: - for loop in for_loops: - list_prop = loop.iter - if list_prop.ast_name == 'Call': - return explain_r(message, code, label=tldr) - except Exception: - return False - return False - - -# ##########################9.1 START############################ -def wrong_list_initialization_9_1(): - message = "The list of rainfall amounts (rainfall_list) is not initialized properly." - code = "list_init_9.1" - tldr = "Incorrect List Initialization" - match = find_match('rainfall_list = weather.get("Data.Precipitation","Station.Location","Blacksburg, VA")') - if not match: - return explain_r(message, code, label=tldr) - return False - - -def wrong_accumulator_initialization_9_1(): - message = ("The variable to hold the total value of the rainfall amounts (rainfall_sum) " - "is not initialized properly.") - code = "accu_init_9.1" - tldr = "Incorrect Accumulation Variable initialization" - match = find_match("rainfall_sum = 0") - if not match: - return explain_r(message, code, label=tldr) - return False - - -def wrong_accumulation_9_1(): - message = "The addition of each rainfall amount to rainfall_sum is not correct." - code = "accu_9.1" - tldr = "Incorrect Accumulation Statement" - matches = find_matches("rainfall_sum = _item_ + rainfall") - if matches: - for match in matches: - _item_ = match["_item_"][0] - if _item_.id != "rainfall_sum": - return explain_r(message, code, label=tldr) - return False - - -def wrong_list_initialization_placement_9_1(): - message = ("The list of rainfall amount (rainfall_list) " - "must be initialized before the iteration that uses this list.") - code = "list_init_place_9.1" - tldr = "List initialization Misplaced or Missing" - match = find_match("rainfall_list = ___\n" - "for _item_ in _list_:\n" - " pass") - if not match: - return explain_r(message, code, label=tldr) - return False - - -def wrong_accumulator_initialization_placement_9_1(): - message = ("The variable for the sum of all the rainfall amounts (rainfall_sum) " - "must be initialized before the iteration which uses this variable.") - code = "accu_init_place_9.1" - tldr = "Accumulator Initialization Misplaced or missing" - matches = find_matches("rainfall_sum = ___\n" - "for _item_ in _list_:\n" - " pass") - if not matches: - return explain_r(message, code, label=tldr) - return False - - -def wrong_iteration_body_9_1(): - message = "The addition of each rainfall amount to the total rainfall is not in the correct place." - code = "iter_body_9.1" - tldr = "Accumulation Statement Misplaced or Missing" - matches = find_matches("for _item_ in _list_:\n" - " rainfall_sum = ___") - if not matches: - return explain_r(message, code, label=tldr) - return False - - -def wrong_print_9_1(): - """ - Returns: - """ - message = ('The output of the total rainfall amount is not in the correct place. The total rainfall should be ' - 'output only once after the total rainfall has been computed.') - code = "print_9.1" - tldr = "Print Statement Misplaced or Missing" - match = find_match("for _item_ in _list_:\n" - " pass\n" - "print(_total_)") - if not match: - return explain_r(message, code, label=tldr) - return False - - -# ##########################9.1 END############################ - - -# ##########################9.2 START############################ -def wrong_list_initialization_9_2(): - message = "The list of rainfall amounts (rainfall_list) is not initialized properly." - code = "list_init_9.2" - tldr = "Incorrect List Initialization" - matches = find_matches('rainfall_list = weather.get("Data.Precipitation","Station.Location","Blacksburg, VA")') - if not matches: - return explain_r(message, code, label=tldr) - return False - - -def wrong_accumulator_initialization_9_2(): - message = ("The variable to hold the total value of the rainfall amounts " - "(rainfall_count) is not initialized properly.") - code = "accu_init_9.2" - tldr = "Incorrect Initialization" - if not find_matches("rainfall_count = 0"): - return explain_r(message, code, label=tldr) - return False - - -def wrong_accumulation_9_2(): - message = ('The adding of another day with rainfall to the total ' - 'count of days with rainfall (rainfall_count) is not correct.') - code = "accu_9.2" - tldr = "Accumulation Statement Incorrect" - matches = find_matches("rainfall_count = _item_ + 1") - if matches: - for match in matches: - _item_ = match["_item_"][0] - if _item_.id != "rainfall_count": - return explain_r(message, code, label=tldr) - return False - - -def wrong_list_initialization_placement_9_2(): - message = ("The list of rainfall amount (rainfall_list) " - "must be initialized before the iteration that uses this list.") - code = "list_init_place_9.2" - tldr = "Incorrect List Initialization Placement" - matches = find_matches("rainfall_list = ___\n" - "for _item_ in _list_:\n" - " pass") - if not matches: - return explain_r(message, code, label=tldr) - return False - - -def wrong_accumulator_initialization_placement_9_2(): - message = ("The variable for the count of the number of days having rain (rainfall_count) " - "must be initialized before the iteration which uses this variable.") - code = "accu_init_place_9.2" - tldr = "Accumulator Initialization Misplaced" - matches = find_matches("rainfall_count = ___\n" - "for _item_ in _list_:\n" - " pass") - if not matches: - return explain_r(message, code, label=tldr) - return False - - -def wrong_iteration_body_9_2(): - message = ("The test (if) to determine if a given amount " - "of rainfall is greater than (>) zero is not in the correct place.") - code = "iter_body_9.2" - tldr = "If statement misplaced" - matches = find_matches("for _item_ in _list_:\n" - " if __expr__:\n" - " pass") - if matches: - for match in matches: - __expr__ = match["__expr__"] - if __expr__.numeric_logic_check(1, 'var > 0'): - return False - return explain_r(message, code, label=tldr) - - -def wrong_decision_body_9_2(): - message = ("The increase by 1 in the number of days having rainfall " - "(rainfall_count) is not in the correct place.") - code = "dec_body_9.2" - tldr = "Accumulation Statement Misplaced" - matches = find_matches("if __expr__:\n" - " rainfall_count = rainfall_count + 1") - if matches: - for match in matches: - __expr__ = match["__expr__"] - if __expr__.numeric_logic_check(1, 'var > 0'): - return False - return explain_r(message, code, label=tldr) - - -def wrong_print_9_2(): - message = ("The output of the total number of days with rainfall is not in the correct place. The total number of " - "days should be output only once after the total number of days has been computed.") - code = "print_9.2" - tldr = "Misplaced Print Statement" - match = find_match("for _item_ in _list_:\n" - " pass\n" - "print(_total_)") - if not match: - return explain_r(message, code, label=tldr) - return False - - -# ##########################9.2 END############################ - - -# ##########################9.6 START############################ -def wrong_comparison_9_6(): - message = "In this problem you should be finding temperatures above 80 degrees." - code = "comp_9.6" - tldr = "Incorrect Comparison Statement" - matches = find_matches("if __comp__:\n" - " pass") - if matches: - for match in matches: - __comp__ = match["__comp__"] - if not __comp__.numeric_logic_check(1, 'var > 80'): - return explain_r(message, code, label=tldr) - return False - - -# ##########################9.6 END############################ - - -# ##########################10.2 START############################ -def wrong_conversion_10_2(): - """ - '''missing - for _target_ in ____ : - _target_ * 0.4 - ''' - Returns: - """ - message = "The conversion of {0!s} to inches is either missing, incorrect, or misplaced." - code = "conv_10.2" - tldr = "Incorrect/Missing Conversion" - matches = find_matches("for _target_ in ___:\n" - " __expr__") - for match in matches: - # code version 1 start - _target_ = match["_target_"][0] - __expr__ = match["__expr__"] - matches02 = __expr__.find_matches("_target_*0.04".format(_target_.id)) - if matches02: - return False - return explain_r(message.format(_target_.id), code, label=tldr) - return False - - -# ##########################10.2 END############################ - - -# ##########################10.3 START############################ -def wrong_filter_condition_10_3(): - message = "The condition used to filter the year when artists died is not correct." - code = "filt_10.3" - tldr = "Incorrect Condition" - matches = find_matches("if __expr__:\n" - " pass") - if matches: - for match in matches: - __expr__ = match["__expr__"] - if __expr__.numeric_logic_check(1, "var > 0") or __expr__.numeric_logic_check(1, "var != 0"): - return False - return explain_r(message, code, label=tldr) - return False - - -# ##########################10.3 END############################ - - -# ##########################10.4 START############################ -def wrong_and_filter_condition_10_4(): - message = ("The condition used to filter the temperatures " - "into the specified range of temperatures is not correct.") - code = "filt_and_10.4" - tldr = "Incorrect Condition Statement" - matches = find_matches("for _temp_ in _list_:\n" - " if __expr__:\n" - " pass") - if matches: - for match in matches: - _temp_ = match["_temp_"][0] - __expr__ = match["__expr__"] - if (__expr__.has(_temp_.astNode) and - not __expr__.numeric_logic_check(1, "32 <= temp <= 50")): - return explain_r(message, code, label=tldr) - return False - - -def wrong_nested_filter_condition_10_4(): - message = ("The decisions used to filter the temperatures into " - "the specified range of temperatures is not correct.") - code = "nest_filt_10.4" - tldr = "Incorrect Set of Decisions" - matches = find_matches("for _temp_ in _list_:\n" - " if __cond1__:\n" - " if __cond2__:\n" - " pass") - if matches: - for match in matches: - _temp_ = match["_temp_"][0].astNode - __cond1__ = match["__cond1__"] - __cond2__ = match["__cond2__"] - if not (__cond1__.has(_temp_) and __cond2__.has(_temp_) and ( - __cond1__.numeric_logic_check(1, "32 <= temp") and __cond2__.numeric_logic_check(1, "temp <= 50") or - __cond2__.numeric_logic_check(1, "32 <= temp") and - __cond1__.numeric_logic_check(1, "temp <= 50"))): - return explain_r(message, code, label=tldr) - return False - - -# ##########################10.4 END############################ - - -# ########################10.5 START############################### -def wrong_conversion_problem_10_5(): - message = "The conversion from kilometers to miles is not correct." - code = "conv_10.5" - tldr = "Incorrect Conversion" - matches = find_matches("for _item_ in ___:\n" - " __expr__") - if matches: - for match in matches: - _item_ = match["_item_"][0] - __expr__ = match["__expr__"] - matches02 = __expr__.find_matches("_item_*0.62") - if matches02: - return False - return explain_r(message, code, label=tldr) - return False - - -def wrong_filter_problem_atl1_10_5(): - """ - find pattern where expression is equal to _item_*0.62 and - where the condition is not equivalent to _expr_ > 10 - Returns: - """ - message = "You are not correctly filtering out values from the list." - code = "filt_alt1_10.5" - tldr = "Incorrect Filter Statement" - matches = find_matches("for _item_ in ___:\n" - " if __cond__:\n" - " _list_.append(__expr__)") - if matches: - for match in matches: - _item_ = match["_item_"][0].astNode - __cond__ = match["__cond__"] - __expr__ = match["__expr__"] - # matches02 = __expr__.find_matches("{0!s}*0.62".format(_item_.id)) - matches02 = __expr__.find_matches("_item_*0.62") - if matches02: - for match02 in matches02: - if (__cond__.has(_item_) and - not __cond__.numeric_logic_check(0.1, "item > 16.1290322580645")): - return explain_r(message, code, label=tldr) - return False - - -def wrong_filter_problem_atl2_10_5(): - message = "You are not correctly filtering out values from the list." - code = "filt_alt2_10.5" - tldr = "Incorrect Filter Statement" - matches = find_matches("for _item_ in ___:\n" - " _miles_ = __expr__\n" - " if __cond__:\n" - " _list_.append(_miles_)") - if matches: - for match in matches: - __expr__ = match["__expr__"] - __cond__ = match["__cond__"] - _item_ = match["_item_"][0].astNode - _miles_ = match["_miles_"][0].astNode - matches02 = __expr__.find_matches("_item_*0.62") - for _ in matches02: - if not (__cond__.has(_miles_) and - __cond__.numeric_logic_check(1, "_item_ > 10")): - return explain_r(message, code, label=tldr) - return False - - -def wrong_append_problem_atl1_10_5(): - message = "You are not appending the correct values.

    (app_alt1_10.5" - code = "app_alt1_10.5" - tldr = "Incorrect Value Appended" - matches = find_matches("for _item_ in ___:\n" - " if __cond__:\n" - " _list_.append(__expr__)") - if matches: - for match in matches: - _item_ = match["_item_"][0].astNode - __cond__ = match["__cond__"] - __expr__ = match["__expr__"] - if (__cond__.numeric_logic_check(0.1, "item > 16.1290322580645") and - __cond__.has(_item_)): - # new_code = "{}*0.62".format(_item_.id) - new_code = "_item_*0.62" - matches02 = __expr__.find_matches(new_code) - if not matches02: - return explain_r(message, code, label=tldr) - return False - - -def wrong_append_problem_atl2_10_5(): - message = "You are not appending the correct values." - code = "app_alt2_10.5" - tldr = "Incorrect Value Appended" - matches = find_matches("for _item_ in ___:\n" - " _miles_ = _item_ * 0.62\n" - " if __cond__:\n" - " _list_.append(_var_)") - for match in matches: - __cond__ = match["__cond__"] - _miles_ = match["_miles_"][0] - _var_ = match["_var_"][0] - if __cond__.has(_miles_) and __cond__.numeric_logic_check(1, "_miles_ > 10"): - if _var_.id != _miles_.id: - return explain_r(message, code, label=tldr) - return False - - -# ########################10.5 END############################### -def wrong_debug_10_6(): - """ - Should be on change feedback as opposed to on-run - Returns: - """ - message = "This is not one of the two changes needed. Undo the change and try again." - code = "debug_10.6" - tldr = "At least one unnecessary change" - matches = find_matches('quakes = earthquakes.get("location.depth","(None)","")\n' - 'quakes_in_miles = []\n' - 'for quake in _list1_:\n' - ' _list2_.append(quake * 0.62)\n' - 'plt.hist(quakes_in_miles)\n' - 'plt.xlabel("Depth in Miles")\n' - 'plt.ylabel("Number of Earthquakes")\n' - 'plt.title("Distribution of Depth in Miles of Earthquakes")\n' - 'plt.show()') - for match in matches: - name1 = match["_list1_"][0].ast_node.id - name2 = match["_list2_"][0].ast_node.id - master_list = ["quake", "quakes", "quakes_in_miles"] - if (name1 in master_list and name2 in master_list and - name1 != "quakes_in_miles" and name2 != "quakes" and - (name1 != "quake" or name2 != "quake")): - return False - return explain_r(message, code, label=tldr) - - -def wrong_debug_10_7(): - message = "This is not the change needed. Undo the change and try again." - code = "debug_10.7" - tldr = "At least one unnecessary change" - match = find_match("filtered_sentence_counts = []\n" - "book_sentence_counts = classics.get('metrics.statistics.sentences','(None)','')\n" - "for book in book_sentence_counts:\n" - " if book >= 5000:\n" - " filtered_sentence_counts.append(book)\n" - "plt.hist(filtered_sentence_counts)\n" - "plt.title('Distribution of Number of Sentences in Long Books')\n" - "plt.xlabel('Number of Sentences')\n" - "plt.ylabel('Number of Long Books')\n" - "plt.show()\n") - - if not match: - return explain_r(message, code, label=tldr) - return False - - -# ########################.....############################### -def wrong_initialization_in_iteration(): - message = ("You only need to initialize {0!s} once. " - "Remember that statements in an iteration block happens multiple times") - code = "wrong_init_in_iter" - tldr = "Initialization in Iteration" - matches = find_matches("for ___ in ___:\n" - " __expr__") - if matches: - for match in matches: - __expr__ = match["__expr__"] - submatches = __expr__.find_matches("_assign_ = __expr__", ) - if submatches: - for submatch in submatches: - __expr__sub = submatch["__expr__"] - _assign_ = submatch["_assign_"][0].astNode - if len(__expr__sub.find_all("Name")) == 0: - return explain_r(message.format(_assign_.id), code, label=tldr) - return False - - -def wrong_duplicate_var_in_add(): - message = "You are adding the same variable twice; you need two different variables in your addition." - code = "dup_var" - tldr = "Duplicate Division" - match = find_match("_item_ + _item_") - if match: - return explain_r(message, code, label=tldr) - return False - - -# ########################PLOTTING############################### -def plot_group_error(output=None, plots=None): - if output is None: - output = get_output() - if plots is None: - plots = get_plots() - if len(plots) > 1: - explain_r('You should only be plotting one thing!', "print_one", "Multiple Calls to plot") - return True - elif len(plots) == 0: - explain_r('The algorithm is plotting an empty list. Check your logic.', 'blank_plot', "Blank Plot") - return True - elif output: - explain('You should be plotting, not printing!', 'printing', "Printing instead of Plotting") - return True - elif len(plots[0]['data']) != 1: - explain('You should only be plotting one thing!', 'one_plot', "Too Many Plots") - return True - - -def all_labels_present(): # TODO: make sure it's before the show, maybe check for default values - """ - plt.title("Distribution of Number of Sentences in Long Books") - plt.xlabel("Number of Sentences") - plt.ylabel("Number of Long Books") - plt.show() - Returns: - """ - message = "Make sure you supply labels to all your axes and provide a title and then call show" - code = "labels_present" - tldr = "Missing Label(s)" - match = find_match("plt.title(___)\nplt.show()") - match02 = find_match("plt.xlabel(___)\nplt.show()") - match03 = find_match("plt.ylabel(___)\nplt.show()") - - if (not match) or (not match02) or (not match03): - return gently_r(message, code, label=tldr) - return False - - -def show_parens(): - message = "Make sure you add parenthesis to plt.show" - code = "show_parens" - tldr = "Incorrect Show" - if not find_match("plt.show"): - return gently_r() - return False - - -def hard_code_8_5(): # TODO: This one's weird - message = "Use iteration to calculate the sum." - code = "hard_code_8.5" - tldr = "Hard Coded Answer" - match = find_matches("print(__num__)") - if match: - for m in match: - __num__ = m["__num__"] - if len(__num__.find_all("Num")) > 0: - return explain_r(message, code, label=tldr) - return False diff --git a/src/lib/pedal/plugins/__init__.py b/src/lib/pedal/plugins/__init__.py deleted file mode 100644 index cc1889972d..0000000000 --- a/src/lib/pedal/plugins/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -''' -def default_pipeline(tifa=False, cait=True, sandbox=True): - next_section() - results = [] - if tifa: - results.append(tifa_analysis()) - if cait: - results.append(parse_program()) - if sandbox: - results.append(execute()) - return tuple(results) -''' diff --git a/src/lib/pedal/plugins/blockpy_compatibility.py b/src/lib/pedal/plugins/blockpy_compatibility.py deleted file mode 100644 index 660a3d84c6..0000000000 --- a/src/lib/pedal/plugins/blockpy_compatibility.py +++ /dev/null @@ -1,106 +0,0 @@ -class GracefulExit(Exception): - pass - - -class StudentData: - def __init__(self): - pass - - def get_names_by_type(self, type, exclude_builtins): - pass - - def get_values_by_type(self, type, exclude_builtins): - pass - - -student = StudentData() - - -def get_output(): - pass - - -def reset_output(): - pass - - -def queue_input(*inputs): - pass - - -def get_program(): - pass - - -def parse_program(): - pass - - -def had_execution_time_error(): - pass - - -def limit_execution_time(): - pass - - -def unlimit_execution_time(): - pass - - -def analyze_program(): - pass - - -def def_use_error(AstNode): - pass - - -class CorruptedAstNode: - def __init__(self): - pass - - -def find_match(instructor_code): - pass - - -def find_matches(instructor_code): - pass - - -class ASTMap: - def __init__(self, JSAstMap): - pass - - def get_std_name(self, id): - pass - - def get_std_exp(self, id): - pass - - -class AstNode: - def __init__(self, id): - pass - - def __eq__(self, other): - pass - - def numeric_logic_check(self, mag, expr): - pass - - def __str__(self): - pass - - def __repr__(self): - pass - - def __getattr__(self, key): - pass - - def has(self, AstNode): - pass - - def find_all(self, type): - pass diff --git a/src/lib/pedal/plugins/cmd_line.py b/src/lib/pedal/plugins/cmd_line.py deleted file mode 100644 index 65d7ef1a1e..0000000000 --- a/src/lib/pedal/plugins/cmd_line.py +++ /dev/null @@ -1,129 +0,0 @@ -from pedal.cait.cait_api import * -from pedal.report import MAIN_REPORT -from pedal.source import set_source -from pedal.tifa import tifa_analysis -from pedal.sandbox.compatibility import * -import importlib.util -import numpy as np -import pandas as pd - -import sys -import os -import re - - -def setup(student_code, input_vals): - """ - Clears MAIN_REPORT, sets source, and runs TIFA - Args: - student_code: String of student code - input_vals: list of inputs to be queued. - Returns: - None - """ - MAIN_REPORT.clear() - set_source(student_code) - tifa_analysis() - if len(input_vals) != 0: - queue_input(*input_vals) - run_student(True) - return get_sandbox() - - -def process(file, module, ins_code, report): - student_code1 = file.read() - setup(student_code1, inputs) # setup returns a sandbox object - module.loader.exec_module(ins_code) - feedback = report.feedback - return feedback - - -p2Flag = True -secrets = False -assignment_id = -1 -if __name__ == "__main__": - # processing args - feedback_code = sys.argv[1] - code_dir = sys.argv[2] - flag = sys.argv[3] - if flag == "-p2": - p2Flag = True - inputs = sys.argv[4:] - elif flag == "-secrets": - p2Flag = True - secrets = True - inputs = sys.argv[4:] - else: - inputs = sys.argv[3:] -else: - # feedback_suffix = "prequiz.py" - # assignment_id = 409 - feedback_suffix = "postquiz1.py" - assignment_id = 410 # Pass Count = 1 - # feedback_suffix = "postquiz2-1.py" - # assignment_id = 411 # Pass Count = 2 - # feedback_suffix = "postquiz2-2.py" - # assignment_id = 412 - # feedback_code = ("C:/Users/User/Documents/Luke_Stuff/Research/ComputationalThinking/DictionaryUnit/test_cmd/" - # "ins_script.py") - feedback_code = ("C:/Users/User/Documents/Luke_Stuff/Research/ComputationalThinking/" - "DictionaryUnit/ID/Assessments/") - feedback_code += feedback_suffix - - code_dir = ("C:/Users/User/Documents/Luke_Stuff/Research/ComputationalThinking/ResearchData/" - "ComputationalThinking/Tests/results/") - code_dir += "Spring2019/DictionaryData/cs1014_spr2019_log-v1/" - # code_dir += "Fall2018/DictionaryData/exported-f18/" - p2Flag = True - secrets = True - inputs = [] - -# Grabbing instructor feedback code -ins_mod = re.match("(?:.*/)(.*).py", feedback_code)[1] -my_spec = importlib.util.spec_from_file_location(ins_mod, feedback_code) -foo = importlib.util.module_from_spec(my_spec) - -# preparing to process - - -# Grabbing student files -if p2Flag: - student_feedback = [] - pass_count = 0 - main_table = "MainTable" - if secrets: - main_table += "-2" - main_table += ".csv" - df = pd.read_csv(code_dir + main_table) - code_states = code_dir + "CodeStates/" - for index, row in df.iterrows(): - scan = True - if assignment_id >= 0: - if secrets: - if int(row["AssignmentID"]) != assignment_id: - scan = False - if scan: - code_f = code_states + str(int(row['CodeStateID'])) + "/__main__.py" - # check assignment and find corresponding answer key in DictionaryUnit/ID/Assessments/... - with open(code_f) as code: - feedback_result = process(code, my_spec, foo, MAIN_REPORT) - # df.at[index, 'InterventionMessage'] = feedback_result - student_feedback.append(feedback_result) - score = 0.0 - if not feedback_result: - score = 1.0 - pass_count += 1 - df.at[index, 'Score'] = score - df.to_csv(code_dir + "processed.csv", index=False) -else: - student_feedback = [] - print(os.getcwd()) - student_files_base = os.listdir(code_dir) - student_files = [] - for code_name in student_files_base: - student_files.append(code_dir + code_name) - for code_name in student_files: - with open(code_name) as code_f: - student_feedback.append(process(code_f, my_spec, foo, MAIN_REPORT)) - if __name__ == "__main__": - print(student_feedback) diff --git a/src/lib/pedal/plugins/grade_magic.py b/src/lib/pedal/plugins/grade_magic.py deleted file mode 100644 index 695f45f72e..0000000000 --- a/src/lib/pedal/plugins/grade_magic.py +++ /dev/null @@ -1,389 +0,0 @@ -# Built-in imports -import json -import requests - -# IPython imports -from IPython.core.magic import (Magics, magics_class, line_magic, cell_magic, line_cell_magic) -from IPython.display import Javascript, display -from IPython.utils.io import capture_output, CapturedIO - -# Logging imports -import os -import sys -from warnings import warn -# from traitlets import Bool -import time - -# TODO: Opportunity here to add in requests-cache. This would allow us to avoid -# the repeated trip. However, you'll need to handle expiring the cache in a -# smart way. One option is to write a command line script to just wipe as -# necessary. Simply deleting the cache file would be pretty easy, assuming it -# installs per user. - -# This really should come in as a configuration setting somewhere. -BLOCKPY_URL = 'https://think.cs.vt.edu/blockpy/blockpy/load_assignment_give_feedback' - - -def get_response_error(response): - """ - Transform a Response object into a friendlier string. - - Args: - response (requests.Response): A Requests reponse object to parse for - some kind of error. - Returns: - str: A string representation of the URL response. - """ - return "{} {}: {}".format(response.status_code, response.reason, - response.text) - - -def download_on_run(assignment_id): - """ - Download the on_run (give_feedback) code to use to test their solution. - - Args: - assignment_id (int OR str): The ID of the assignment to get the - on_run code for. - Returns: - bool: Whether or not the request was successful. - str: If unsuccesful, a message to display to the user. Otherwise, it'll - be the on_run code. - """ - data = {'assignment_id': assignment_id} - try: - response = requests.get(BLOCKPY_URL, data=data) - except Exception as error: - return False, str(error) - try: - result = response.json() - except ValueError: - # Failed to parse the JSON; perhaps it was some text data? - return False, get_response_error(response) - if result['success']: - return True, result['give_feedback'] - else: - return False, result['message'] - - -PEDAL_PIPELINE = ''' -from pedal.report import * -from pedal.report.imperative import * -clear_report() -from pedal.source import set_source -set_source({student_code}) -from pedal.tifa import tifa_analysis -tifa_analysis(True) -from pedal.sandbox.compatibility import * -queue_input({inputs}) -run_student(True) -student = get_sandbox() -from pedal.cait.cait_api import parse_program -{on_run} -from pedal.resolvers import simple -SUCCESS, SCORE, CATEGORY, LABEL, MESSAGE, DATA, HIDE = simple.resolve() -''' - - -def blockpy_grade(assignment_id, student_code, inputs): - """ - Helper function to capture the request from the server. - - Args: - assignment_id (int): The assignment ID to look up and use the on_run - code for. - student_code (str): The code that was written by the student. - - inputs (str): The inputs to queue into the assignment - - Returns: - str: The HTML formatted feedback for the student. - """ - successful_download, on_run = download_on_run(assignment_id) - # If it failed, let's display some information about why. - if not successful_download: - return on_run - return execute_on_run_code(on_run, student_code, inputs) - - -def execute_on_run_code(on_run, student_code, inputs): - """ - Actually execute the on_run code for the given student code. - """ - # Even though the student code is a string, we need to escape it to prevent - # any weirdness from being in the instructor code. - escaped_student_code = json.dumps(student_code) - instructor_code = PEDAL_PIPELINE.format(on_run=on_run, - student_code=escaped_student_code, - # inputs=','.join(inputs)) - inputs=inputs) - # Execute the instructor code in a new environment - global_variables = globals() - compiled_code = compile(instructor_code, 'instructor_code.py', 'exec') - exec(compiled_code, global_variables) - category = global_variables['CATEGORY'] - label = global_variables['LABEL'] - message = global_variables['MESSAGE'] - # In some cases, we might want to override how the text is rendered. - if category.lower() == 'instructor' and label.lower() == 'explain': - category = "Instructor Feedback" - label = '' - # Return the result as HTML - return '''{}: {}
    {}'''.format(category, label, message) - - -# The following string literals are used to create the JavaScript code that -# creates the Python code that will execute the instructor's feedback code -# using the student's Python code. - -# Extract out the student code, embed the result -EXTRACT_STUDENT_CODE = r""" -// Convert Notebook cells to a string of Python code -var makePython = function(cell) { - if (cell.cell_type == "code") { - // Code is embedded unchanged, unless it is magic - var source = cell.get_text(); - if (source.startsWith('%')) { - // Skip magic - return ''; - } else { - return source; - } - } else if (cell.cell_type == "markdown" || - cell.cell_type == "raw") { - // Markdown and text is wrapped in a string. - var escaped_text = cell.get_text().replace(/'''/g, "\\'\\'\\'"); - return "'''"+escaped_text+"'''"; - } -} -var isUsable = function(cell) { - return cell.cell_type == "code" || - cell.cell_type == "markdown" || - cell.cell_type == "raw"; -} -var cells = Jupyter.notebook.get_cells(); -var source_code = cells.filter(isUsable).map(makePython).join("\n"); -source_code = JSON.stringify(source_code); -console.log(source_code); -// Start constructing the feedback code (which will be Python). -var on_run_code = []; -on_run_code.push("student_code="+source_code); -""" - -# Retrieve the last cell, and also recolor it a little for style -ANIMATE_LAST_CELL = r""" -// While we are accessing the server, recolor the last cell a little. -var last = null; -if (cells.length > 0) { - last = cells[cells.length-1]; - $(last.element).animate({"background-color": "#E0E6FF"}, 1000); -} -""" - -# If the %grade magic is used, we run the code directly. -LOCAL_GRADE = r''' -on_run_code.push("from pedal.plugins.grade_magic import execute_on_run_code"); -on_run_code.push('print(execute_on_run_code({on_run_code}, student_code, {inputs}))'); -''' - -# If the %grade_blockpy magic is used, we need to get the on_run from blockpy. -BLOCKPY_GRADE = r''' -on_run_code.push("from pedal.plugins.grade_magic import blockpy_grade"); -on_run_code.push('import json') -on_run_code.push('inputs = {inputs}') -console.log('inputs = {inputs}') -on_run_code.push("print(blockpy_grade({assignment}, student_code, inputs))"); -''' - -# This chunk actually performs the on_run code execution using the kernel. -EXECUTE_CODE = r''' -on_run_code = on_run_code.join("\n"); -console.log(on_run_code); -var kernel = IPython.notebook.kernel; -if (kernel !== null) { - var t = kernel.execute(on_run_code, { 'iopub' : {'output' : function(x) { - if (x.msg_type == "error") { - // If this was an error, show the traceback properly. - if (last !== null) { - last.output_area.append_error(x.content); - console.error(x); - } else { - console.error("Could not append to final cell.", x); - } - } else if (!x.content.data && x.content.text) { - // If it was valid data, we show it as HTML. - console.log(x); - element.html(x.content.text.replace(/\n/g, "
    ")); - } else { - // I'm not sure what it is - better dump it on the console. - console.log(x); - } - // Decolor the last cell if it was there. - if (last !== null) { - last = cells[cells.length-1]; - $(last.element).animate({"background-color": "white"}, 1000); - } - }}}); -}''' - - -@magics_class -class GradeMagic(Magics): - """ - This class holds the magic for the %grade and %grade_blockpy - """ - - @line_magic - def grade_logstart(self, line=""): - # ######Logging - ts = time.time() - logger = self.shell.logger # logging - old_logfile = self.shell.logfile # logging - directory = os.path.expanduser("log_folder{}~/".format(line)) - logfname = os.path.expanduser("log_folder{}~/log_{}.py~".format(line, ts)) - self.shell.logfile = logfname - loghead = u'# IPython log file\n\n' - try: - os.makedirs(directory, exist_ok=True) - logger.logstart(logfname, loghead, 'rotate', True, True, - True) - except BaseException: - self.shell.logfile = old_logfile - warn("Couldn't start log: %s" % sys.exc_info()[1]) - self.shell.run_code("input = __builtins__.input") - self.shell.run_code("print = __builtins__.print") - self.shell.run_code("sum = __builtins__.sum") - self.shell.run_code("len = __builtins__.len") - - @line_magic - def grade_logstop(self, line=""): - self.shell.logger.logstop() - - def logging(self): - # ######Logging - ts = time.time() - logger = self.shell.logger # logging - old_logfile = self.shell.logfile # logging - logfname = os.path.expanduser("log_folder~/log_{}.py~".format(ts)) - self.shell.logfile = logfname - loghead = u'# IPython log file\n\n' - try: - logger.logstart(logfname, loghead, 'rotate', False, True, - True) - except BaseException: - self.shell.logfile = old_logfile - warn("Couldn't start log: %s" % sys.exc_info()[1]) - logger.timestamp = False - input_hist = self.shell.history_manager.input_hist_raw - logger.log_write(u'\n'.join(input_hist[1:])) - logger.log_write(u'\n') - logger.timestamp = True - self.shell.logger.logstop() - # ######Logging - - # noinspection PyMethodMayBeStatic - def grade_parser(self, line, cell=None): - if ',' in line: - if cell is None: - assignment, line = line.split(",", maxsplit=1) - else: - assignment = None - inputs = json.dumps(line.split(",")) - inputs = "\\'" + inputs[1:len(inputs) - 1] + "\\'" - else: - if cell is None: - assignment, inputs = line, "" - else: - inputs = line - assignment = "" - inputs = json.dumps(inputs) - return {"inputs": inputs, "assignment": assignment} - - # noinspection PyMethodMayBeStatic - def unified_helper(self, local_code, **kwargs): - code = EXTRACT_STUDENT_CODE - code += ANIMATE_LAST_CELL - code += local_code.format(**kwargs) - code += EXECUTE_CODE - return code - - @cell_magic - def grade(self, line="", cell=""): - dump = self.grade_parser(line, cell) - code = self.unified_helper(LOCAL_GRADE, on_run_code="INSTRUCTOR_CODE", inputs=dump['inputs']) - cell = cell.replace("\\", "\\\\") - cell = cell.replace("\n", "\\n") - cell = cell.replace("'", "\\'") - cell = cell.replace('"', '\\"') - # Runs this code in the kernel as python code - # Can also run compiled code - self.shell.run_code("INSTRUCTOR_CODE = " + '"' + cell + '"') - # TODO: This was the easier way for me to get this to work - # This might be worth using in more depth to have less translation - # to and from javascript. See usage_examples - return display(Javascript(code)) - - @line_cell_magic - def usage_examples(self, line="", cell="print('running cell')\nprint('running cell2')"): - # Runs code in the kernel's context - self.shell.run_code("print('fun')") - - # Runs code in kernel's context using compiled code - sample = compile(cell, "usage_examples.py", "exec") - self.shell.run_code(sample) - - # runs javascript code - self.shell.run_cell_magic("javascript", "", "console.log('I do JAVASCRIPT');\n") - # Maybe can use javascript execution to pass things around...not sure though...can't get it to work - # You can pass values, but it doesn't seem to work unless you run it again. - # https://michhar.github.io/javascript-and-python-have-a-party/ - - self.shell.run_cell_magic( - "javascript", "", - # js_code = Javascript( - """var callbacks = { iopub : { output: function(out_data){ console.log(out_data) } } };\n""" - """var code = "fun = 12";\n""" - """IPython.notebook.kernel.execute(code);\n""") - # handle = display(js_code, display_id="usage_examples") - # handle.update(handle) - self.shell.run_cell_magic("javascript", "", "console.log('I do JAVASCRIPT TOO!!');\n") - # captures standard output, standard error, etc. and stops or not stops it - # class IPython.utils.capture.capture_output(stdout=True, stderr=True, display=True) - # Note that Tracebacks aren't put in standard error? - with capture_output(True, False, False) as captured: - print(dir(self)) - self.shell.run_code("print(fun)") - sys.stderr.write("spam\n") - print("I captured stdout") - print(captured.stdout) - print("I captured stderr") - print(captured.stderr) - - @line_magic - def grade_blockpy(self, line=""): - dump = self.grade_parser(line) - code = self.unified_helper(BLOCKPY_GRADE, assignment=dump["assignment"], inputs=dump["inputs"]) - return display(Javascript(code)) - - -def load_ipython_extension(ipython): - """ - Register this plugin with Jupyter Notebooks. Although it is allegedly - necessary in order to make this a plugin, we do not actually use it. - """ - ipython.register_magics(GradeMagic) - - -""" -DEPRECATED: The following lines of code do not seem to be necessary to - register this plugin with Jupyter. -def _jupyter_server_extension_paths(): - return [{ - "module": "pedal.plugins.grade_magic" - }] - -# jupyter serverextension enable --py pedal.plugins.grade_magic -def load_jupyter_server_extension(nbapp): - from IPython import get_ipython - get_ipython().register_magics(GradeMagic) -""" diff --git a/src/lib/pedal/plugins/test_reference_solution.py b/src/lib/pedal/plugins/test_reference_solution.py deleted file mode 100644 index 1cbb7bd432..0000000000 --- a/src/lib/pedal/plugins/test_reference_solution.py +++ /dev/null @@ -1,142 +0,0 @@ -''' -Tool for running a Grading script through a series of student reference -solutions. - -python -m pedal.plugins.test_reference_solution -''' - -# Runner -from pedal.report.imperative import clear_report, MAIN_REPORT -from pedal.cait import parse_program -import sys -import os -from io import StringIO -from contextlib import redirect_stdout -import unittest -from unittest.mock import patch, mock_open -import argparse - -# Arguments -DEFAULT_REFERENCE_SOLUTIONS_DIR = "reference_solutions/" - - -class TestReferenceSolutions(unittest.TestCase): - maxDiff = None - - -def substitute_args(arg, student_path, seed): - if arg == "$_STUDENT_MAIN": - return student_path - elif arg == "$_STUDENT_NAME": - return seed - return arg - - -def add_test(class_, name, python_file, - expected_output_path, expected_output, - grader_code, grader_path, grader_args, student_path): - seed = find_seed(python_file) - grader_args = [substitute_args(arg, student_path, seed) for arg in grader_args] - - def _inner_test(self): - captured_output = StringIO() - with redirect_stdout(captured_output): - # TODO: mock_open will only work if we are not anticipating - # the student or instructor to open files... - with patch('builtins.open', mock_open(read_data=python_file), - create=True): - with patch.object(sys, 'argv', grader_args): - clear_report() - grader_exec = compile(grader_code, grader_path, 'exec') - exec(grader_exec, globals()) - # print(repr(MAIN_REPORT.feedback[0].mistake['error'])) - actual_output = captured_output.getvalue() - if expected_output is None: - print("File not found:", expected_output_path) - with open(expected_output_path, 'w') as out: - out.write(actual_output) - print("\tCreated missing file with current output") - else: - self.assertEqual(actual_output, expected_output) - - setattr(class_, 'test_' + name, _inner_test) - - -def find_seed(python_code): - try: - ast = parse_program(python_code) - for assign in ast.find_all("Assign"): - if assign.targets[0].ast_name != "Name": - continue - if assign.targets[0].id == "__STUDENT_SEED__": - if assign.value.ast_name == "Str": - return assign.value.s - elif assign.value.ast_name == "Num": - return assign.value.n - elif assign.value.ast_name == "List": - return [e.n for e in assign.value.elts] - except SyntaxError: - return 0 - return 0 - - -# Load reference solutions -def add_all_tests(grader_path, reference_solutions_dir, grader_args, limit): - # Load grader file - with open(grader_path, 'r') as grader_file: - grader_code = grader_file.read() - for filename in os.listdir(reference_solutions_dir): - if limit is not None and limit != filename: - continue - path = os.path.join(reference_solutions_dir, filename) - if path.endswith(".py"): - text_path = path[:-2] + "txt" - with open(path, 'r') as python_file: - python = python_file.read() - if os.path.exists(text_path): - with open(text_path, 'r') as output_file: - output = output_file.read() - else: - output = None - add_test(TestReferenceSolutions, filename[:-3], python, - text_path, output, - grader_code, grader_path, grader_args, path) - - -def run_tests(): - unittest.main(argv=['first-arg-is-ignored']) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Run instructor grading script on a collection of reference solutions') - parser.add_argument('grader', help='The path to the instructor grading script.') - parser.add_argument('--path', '-p', - help='The path to the student reference files. If not given, assumed to be in the same folder ' - 'as the instructor grading script.', - default=DEFAULT_REFERENCE_SOLUTIONS_DIR) - parser.add_argument('--args', '-a', - help='Pass in arguments that the grading script will use. ' - 'Variable substitutions include "$_STUDENT_MAIN".', - default='test_reference_solution.py,$_STUDENT_MAIN,$_STUDENT_NAME') - parser.add_argument('--limit', '-l', help='Limit to a specific file.', default=None) - args = parser.parse_args() - - # Turn the reference solutions path into an absolute filename - if os.path.isabs(args.path): - reference_solutions_path = args.path - else: - reference_solutions_path = os.path.join(os.path.dirname(args.grader), args.path) - - # If no reference solutions folder, let's make it - if not os.path.exists(reference_solutions_path): - os.mkdir(reference_solutions_path) - - # Fix up the passed in args - grader_args = args.args.split(",") - - # Check that we actually have some files to try out - if not os.listdir(reference_solutions_path): - print("No reference solutions found") - else: - add_all_tests(args.grader, reference_solutions_path, grader_args, args.limit) - run_tests() diff --git a/src/lib/pedal/plugins/vpl.py b/src/lib/pedal/plugins/vpl.py deleted file mode 100644 index d1b61a5cd6..0000000000 --- a/src/lib/pedal/plugins/vpl.py +++ /dev/null @@ -1,148 +0,0 @@ -from pedal.plugins.vpl_unittest import UnitTestedAssignment - -""" -Some kind of function to break up the sections -""" -import re -import sys -from html.parser import HTMLParser - -from pedal.report import MAIN_REPORT -from pedal import source -from pedal.resolvers import sectional -from pedal.cait.cait_api import expire_cait_cache - - -class VPLStyler(HTMLParser): - HEADERS = ("h1", "h2", "h3", "h4", "h5") - - def __init__(self): - super().__init__() - self.reset() - self.fed = [] - self.inside_pre = False - - def convert(self, html): - self.feed(html) - return self.get_data() - - @property - def text(self): - return ''.join(self.fed) - - def get_data(self): - return self.text - - def force_new_line(self): - if self.text and self.text[-1] not in ("\n", "\r"): - self.fed.append("\n") - - def handle_starttag(self, tag, attrs): - if tag in self.HEADERS: - self.force_new_line() - self.fed.append("-") - elif tag in ("pre",): - self.force_new_line() - self.fed.append(">") - self.inside_pre = True - - def handle_data(self, data): - if self.inside_pre: - # Need to prepend ">" to the start of new lines. - self.fed.append(data.replace("\n", "\n>")) - else: - self.fed.append(data) - - def handle_endtag(self, tag): - if tag in self.HEADERS: - self.fed.append("") - elif tag in ("pre",): - self.fed.append("") - self.inside_pre = False - - -def strip_tags(html): - return VPLStyler().convert(html) - - -def set_maximum_score(number, cap=True, report=None): - if report is None: - report = MAIN_REPORT - report['vpl']['score_maximum'] = number - report['vpl']['score_cap'] = cap - - -def resolve(report=None, custom_success_message=None): - if report is None: - report = MAIN_REPORT - print("<|--") - success, score, hc, messages_by_group = sectional.resolve(report) - last_group = 0 - for group, messages in sorted(messages_by_group.items()): - if group != last_group: - for intermediate_section in range(last_group, group, 2): - print("-" + report['source']['sections'][1 + intermediate_section]) - printed_first_bad = False - for message in messages: - if message['priority'] in ('positive', 'instructions'): - print(strip_tags(message['message'])) - elif not printed_first_bad: - print(strip_tags(message['message'])) - printed_first_bad = True - last_group = group - print("-Overall") - if success: - if custom_success_message is None: - print("Complete! Great job!") - else: - print(custom_success_message) - else: - print("Incomplete") - print("--|>") - print("Grade :=>>", round(score)) - - -class SectionalAssignment: - max_points = 1 - sections = None - - def __init__(self, filename=None, max_points=None, report=None): - self.report = MAIN_REPORT if report is None else report - find_file(filename if filename else self.filename, - sections=True, report=report) - set_maximum_score(self.max_points - if max_points is None else max_points) - source.check_section_exists(self.sections) - - def pre_test(self): - source.next_section() - verified = source.verify_section() - expire_cait_cache() - return verified - - def post_test(self): - return True - - def resolve(self): - checks = ((self.pre_test() and - getattr(self, attr)() and - self.post_test()) - for attr in dir(self) - if attr.startswith('test_') and - callable(getattr(self, attr))) - if all(checks): - self.report.set_success() - resolve(report=self.report) - - -from pedal.plugins.vpl_unittest import UnitTestedAssignment - - -def unittest_resolver(phases, report=None, custom_success_message=None): - success = True - for title, phase in phases: - outcome = phase()._run_all_tests() - if not outcome: - break - success = success and outcome - resolve(custom_success_message=custom_success_message) diff --git a/src/lib/pedal/plugins/vpl_safe_runner.py b/src/lib/pedal/plugins/vpl_safe_runner.py deleted file mode 100644 index 7bba8e6e44..0000000000 --- a/src/lib/pedal/plugins/vpl_safe_runner.py +++ /dev/null @@ -1,10 +0,0 @@ -from pedal import run -from pedal import set_source_file -import sys - -if __name__ == "__main__": - set_source_file(sys.argv[1] if len(sys.argv) > 1 else 'main.py') - student = run(context=False) - print(student.raw_output) - if student.exception: - print(student.exception_formatted, file=sys.stderr) diff --git a/src/lib/pedal/plugins/vpl_unittest.py b/src/lib/pedal/plugins/vpl_unittest.py deleted file mode 100644 index 09e4c08119..0000000000 --- a/src/lib/pedal/plugins/vpl_unittest.py +++ /dev/null @@ -1,112 +0,0 @@ -from unittest.util import safe_repr -from pedal import gently -from pedal.assertions.assertions import _normalize_string - - -class UnitTestedAssignment: - DELTA = .001 - - class AssertionException(Exception): - def __init__(self, message): - self.message = message - - def __init__(self): - pass - - def setUp(self): - pass - - def tearDown(self): - pass - - def _run_all_tests(self): - methods = [func for func in dir(self) - if callable(getattr(self, func)) and - func.startswith('test_')] - all_passed = True - for method in methods: - self.setUp() - try: - getattr(self, method)() - except UnitTestedAssignment.AssertionException as e: - gently(e.message) - all_passed = False - self.tearDown() - return all_passed - - def assertSimilarStrings(self, first, second, msg): - if _normalize_string(first) != _normalize_string(second): - return self.assertEqual(first, second, msg, exact=True) - - def assertNotSimilarStrings(self, first, second, msg): - if _normalize_string(first) == _normalize_string(second): - return self.assertEqual(first, second, msg, exact=True) - - def assertLessEqual(self, val1, val2, msg=None): - if not (val1 <= val2): - self.fail(msg, "{} is not less than or equal to {}".format(safe_repr(val1), safe_repr(val2))) - - def assertGreaterEqual(self, val1, val2, msg=None): - if not (val1 >= val2): - self.fail(msg, "{} is not greater than or equal to {}".format(safe_repr(val1), safe_repr(val2))) - - def assertNotEqual(self, val1, val2, msg=None, exact=False): - if val1 != val2: - return - if not exact and isinstance(val1, str) and isinstance(val2, str): - self.assertNotSimilarStrings(val1, val2, msg) - elif (not exact and isinstance(val1, (int, float)) and - isinstance(val2, (int, float))): - if abs(val2 - val1) > UnitTestedAssignment.DELTA: - return - standardMsg = "{} == {}".format(safe_repr(val1), safe_repr(val2)) - self.fail(msg, standardMsg) - - def assertEqual(self, val1, val2, msg=None, exact=False): - if val1 == val2: - return - if not exact and isinstance(val1, str) and isinstance(val2, str): - self.assertSimilarStrings(val1, val2, msg) - elif (not exact and isinstance(val1, (int, float)) and - isinstance(val2, (int, float))): - if abs(val2 - val1) <= UnitTestedAssignment.DELTA: - return - standardMsg = "{} != {}".format(safe_repr(val1), safe_repr(val2)) - self.fail(msg, standardMsg) - - def assertIn(self, member, container, msg=None): - if member not in container: - standardMsg = "{} not found in {}".format(safe_repr(member), - safe_repr(container)) - self.fail(msg, standardMsg) - - def assertNotIn(self, member, container, msg=None): - if member in container: - standardMsg = "{} found in {}".format(safe_repr(member), - safe_repr(container)) - self.fail(msg, standardMsg) - - def assertTrue(self, value, msg=None): - if not value: - self.fail(msg, "{} is not true".format(value)) - - def assertFalse(self, value, msg=None): - if value: - self.fail(msg, "{} is not false".format(value)) - - def assertSandbox(self, sandbox, msg=None): - if sandbox.exception is not None: - self.fail(msg, sandbox.format_exception()) - - def assertIsInstance(self, value, parent, msg=None): - if not isinstance(value, parent): - self.fail(msg, "{} is not an instance of {}".format(safe_repr(value), safe_repr(parent))) - - def assertHasAttr(self, object, attr, msg=None): - if not hasattr(object, attr): - self.fail(msg, "{} does not have an attribute named {}".format(safe_repr(object), safe_repr(attr))) - - def fail(self, message, standardMsg): - if message is None: - message = standardMsg - raise UnitTestedAssignment.AssertionException(message) diff --git a/src/lib/pedal/questions/__init__.py b/src/lib/pedal/questions/__init__.py deleted file mode 100644 index ce0380282b..0000000000 --- a/src/lib/pedal/questions/__init__.py +++ /dev/null @@ -1,123 +0,0 @@ -""" -A tool for providing dynamic questions to learners. -""" - -NAME = 'Questions' -SHORT_DESCRIPTION = "Provides dynamic questions to learners" -DESCRIPTION = ''' -''' -REQUIRES = [] -OPTIONALS = [] -CATEGORY = 'Instructions' - -__all__ = ['NAME', 'DESCRIPTION', 'SHORT_DESCRIPTION', 'REQUIRES', 'OPTIONALS', - 'Question', 'Pool', 'set_seed'] - -from pedal.report.imperative import MAIN_REPORT, gently -from pedal.questions.setup import _setup_questions, set_seed, _name_hash -from pedal.questions.loader import load_question, SETTING_SHOW_CASE_DETAILS - - -class QuestionGrader: - def _get_functions_with_filter(self, filter='grade_'): - return [getattr(self, method_name) for method_name in dir(self) - if method_name.startswith(filter) and - callable(getattr(self, method_name))] - - def _test(self, question): - methods = self._get_functions_with_filter() - for method in methods: - method(question) - - -class Question: - def __init__(self, name, instructions, tests, seed=None, report=None): - self.name = name - self.instructions = instructions - self.tests = tests - self.seed = seed - if report is None: - report = MAIN_REPORT - self.report = report - self.answered = False - - def answer(self): - self.answered = True - - def ask(self): - if isinstance(self.tests, QuestionGrader): - self.tests._test(self) - else: - for test in self.tests: - test(self) - if not self.answered: - show_question(self.instructions, self.report) - - -def show_question(instructions, report=None): - if report is None: - report = MAIN_REPORT - report.attach('Question', category='Instructions', tool='Questions', - group=report.group, priority='instructions', hint=instructions) - - -class Pool: - _POOL_TRACKER = 0 - - def __init__(self, name, choices, seed=None, report=None, position=None): - self.name = name - self.choices = choices - self.seed = seed - if report is None: - report = MAIN_REPORT - self.report = report - if position is None: - position = Pool._POOL_TRACKER - Pool._POOL_TRACKER += 1 - self.position = position - - def choose(self, force=None): - _setup_questions(self.report) - if force is None: - if self.seed is None: - force = self.report['questions']['seed'] - if isinstance(force, str): - force = _name_hash(force + self.name) - # Assume iterable; could be check that throws better error - if not isinstance(force, int): - force = force[self.position] - else: - force = self.seed - return self.choices[force % len(self.choices)] - - @property - def answered(self): - for choice in self.choices: - if choice.answered: - return True - return False - - -def check_pool_exam(name, questions, force=None, seed=None): - _setup_questions(MAIN_REPORT) - # Choose a question - if force is None: - if seed is None: - force = MAIN_REPORT['questions']['seed'] - if isinstance(force, str): - force = _name_hash(force + name) - else: - force = seed - elif isinstance(force, str): - force = _name_hash(force + name) - question = questions[force % len(questions)] - # Ask it - show_question(question['instructions']) - # Check if they're done - if 'settings' not in question: - question['settings'] = {} - question['settings'][SETTING_SHOW_CASE_DETAILS] = False - results = list(load_question(question)) - if results: - message, label = results[0] - gently(message, label=label) diff --git a/src/lib/pedal/questions/design.md b/src/lib/pedal/questions/design.md deleted file mode 100644 index b38978ff92..0000000000 --- a/src/lib/pedal/questions/design.md +++ /dev/null @@ -1,92 +0,0 @@ -# Questions Tool - -The questions model flips the script of Feedback generation to also generate -instructions. It is assumed that an environment would provide some initial -meta-instruction, and that initial evaluation would generate some new question -specific instructions. - -Taxonomy: -* Question: A bundle of instructions and tests that can be delivered to a - student, not as feedback, but as a narrowing/modification of the - original problem. -* Pool: A collection of Questions that can be drawn from randomly to - individualize the student experiment. -* Instructions: The (HTML) text rendered to the learner to prepare them for - a question. The default is to assume that this would be static, - but more interesting possibilities could occur. -* Tests: The collection of feedback control logic that is bundled for this - question. This speaks to the idea of better encapsulation for - the control logic - perhaps it is time for the Organizers from - Assertions to be promoted to their own Tool? -* Seed: A value (expected to be constant for a given user) that can act as - an "offset" for selecting problems. This allows users to - deterministically receive feedback from a feedback engine. - Numeric seeds allow specifically selecting questions from the pool, - while String seeds are hashed to "random" indexes. An example is to - use student usernames, emails, or internal IDs (probably better to - treat numeric IDs as strings). - -# Delivery - -By default, new question text is delivered by the Resolver as feedback -that appears at the top of all the other feedback without preventing any -subsequent feedback, similar to the way Compliments do not prevent actual -feedback. - -However, Resolvers probably want to override this. For example, BlockPy would -probably want to modify the problem instructions area. VPL would probably -want to isolate the instructions to their own group or to provide a header with -them. - -# Timing - -Here are a few different models of the timing of questions: -1. The student requests initial feedback, and all questions appear. -2. The student indicates in some way which question they want to appear, - and that question's Instructions appear. -3. Students are given a single initial question, and when they complete it, - the text of a new question appears. -4. Instead of a single question appearing, the students are presented with - a choice of questions (see CYOA). - -# Random Pool - -Frequently, instructors need to be able to draw a question from a pool. - -A design principle based on research is that questions should be as equal -in difficulty and learning objectives as possible. Granted - there are -pedagogical design decisions that could justify breaking that guideline. -We should encourage question equivalency but allow the instructor to have -wildly different questions. - -In theory, question selection doesn't have to be random. Subclasses -could be created that draw on data sources about the user - these could -be local data sources ("You struggled a lot on the last question, so let's -try another one that's similar") or more exotic ("My records indicate that you -haven't really mastered IF statements, so let's do some practice with that.") - -# Templated Questions - -Being able to generate questions based on a template or some rules appears -to be a popular request. In the Random Pool model, we had a static set of -Questions. But now we have a series of rules for generating questions. - -TemplateQuestion is a class for creating questions from finite sets of terms. -You provide a set of variables, some templated text (Python Format? Jinja2?), -and the set of values for variables. From this, questions could be automatically -generated. - -DynamicQuestion is a more general-purpose class for true dynamic generation of -problems. The model here would be to subclass and redefine components -by overriding methods. - -# CYOA - -One of the more interesting ideas is to support Choose-Your-Own-Adventure -style chains of questions. In this model, completing a question could unlock -multiple paths to move forward on. - -Open Questions: -* How do students indicate a choice along the path? -* How do we elegantly connect Decision A with Paths B, C, and D; keeping - in mind that game flow is a DAG or possibly even a graph. diff --git a/src/lib/pedal/questions/graders.py b/src/lib/pedal/questions/graders.py deleted file mode 100644 index 608b550cdd..0000000000 --- a/src/lib/pedal/questions/graders.py +++ /dev/null @@ -1,106 +0,0 @@ -from pedal.questions import QuestionGrader - -from pedal import run, compliment, explain, gently -from pedal.report.imperative import MAIN_REPORT -from pedal.assertions.assertions import * -from pedal.toolkit.functions import * - - -class FunctionGrader(QuestionGrader): - MAX_POINTS = 10 - DEFINITION_POINTS = 3 - COMPONENTS_POINTS = 1 - MAX_COMPONENTS_POINTS = 2 - UNIT_TEST_TYPE_POINTS = None - UNIT_TEST_VALUE_POINTS = None - UNIT_TEST_TOTAL_POINTS = 5 - UNIT_TEST_TYPE_RATIO = .5 - UNIT_TEST_COMPLETION_POINTS = 2 - - def __init__(self, function_name, signature, tests): - super().__init__() - self.function_name = function_name - self.signature = signature - self.tests = tests - self.points = 0 - - def _test(self, question): - defined = self.grade_definition(question) - - if not defined: - return self.report_status(question) - - self.grade_components(question) - - passed_tests = self.grade_unit_tests(question) - if not passed_tests: - return self.report_status(question) - - self.report_success(question) - - def report_status(self, question): - pass - - def report_success(self, question): - question.answer() - - def grade_definition(self, question): - self.student = run(report_exceptions=True, context=False) - self.student.report_exceptions_mode = False - - self.definition = match_signature_muted(self.function_name, *self.signature) - if not assertGenerally(self.definition): - gently("Function not defined") - return False - - if self.student.exception: - return False - if not assertHasFunction(self.student, self.function_name): - gently("Function defined incorrectly") - return False - - self.points += self.DEFINITION_POINTS - return True - - def grade_components(self, question): - self.component_points = 0 - components = self._get_functions_with_filter('grade_component_') - for component in components: - component(question) - self.component_points = min(self.component_points, self.MAX_COMPONENTS_POINTS) - self.points += self.component_points - - def assertEqual(self, *parameters): - return assertEqual(*parameters) - - def grade_unit_tests(self, question): - all_good = True - if self.UNIT_TEST_TOTAL_POINTS is None: - TYPE_POINT_ADD = self.UNIT_TEST_TYPE_POINTS - VALUE_POINT_ADD = self.UNIT_TEST_VALUE_POINTS - else: - ratio = self.UNIT_TEST_TYPE_RATIO - TYPE_POINT_ADD = (self.UNIT_TEST_TOTAL_POINTS / len(self.tests) * (ratio)) - VALUE_POINT_ADD = (self.UNIT_TEST_TOTAL_POINTS / len(self.tests) * (1 - ratio)) - for arguments, expected in self.tests: - # import sys - # print(repr(arguments), file=sys.stderr) - result = self.student.call(self.function_name, *arguments, context=False) - # print(repr(self.student.exception), file=sys.stderr) - if self.student.exception: - all_good = False - continue - if assertIsInstance(result, type(expected)): - self.points += TYPE_POINT_ADD - else: - all_good = False - continue - if self.assertEqual(result, expected): - self.points += VALUE_POINT_ADD - else: - all_good = False - if all_good: - self.points += self.UNIT_TEST_COMPLETION_POINTS - else: - gently("Failing instructor unit tests") - return all_good diff --git a/src/lib/pedal/questions/loader.py b/src/lib/pedal/questions/loader.py deleted file mode 100644 index 603c1de2f1..0000000000 --- a/src/lib/pedal/questions/loader.py +++ /dev/null @@ -1,496 +0,0 @@ -""" -instructions: blah blah blah - -settings: - tifa: - enabled: True - unit test by function (bool): Whether to test each function entirely before moving onto the - next one, or to first check that all functions have been defined, and then - checking their parameters, etc. Defaults to True. - show case details (bool): Whether to show the specific args/inputs that caused a test case - to fail. -rubric: - functions: - total: 100 - definition: 10 - signature: 10 - cases: 80 -global: - variables: - name: - type: - value: - inputs: - prints: -# Sandbox, type checking -functions: - documentation: "any" or "google" - coverage: 100% - tests: int - name: do_complicated_stuff - arity: int - signature: int, int -> float - signature: int, int, list[int], (int->str), dict[str:list[int]] -> list[int] - parameters: - name: banana - exactly: - regex: - includes: - within: - type: int - cases: - - arguments (list): 5, 4 - inputs (list): - returns (Any): - equals: 27.3 - is: - is not: _1 - name (str): Meaningful name for tracking purposes? Or possibly separate into label/id/code - hint (str): Message to display to user - prints: - exactly: - regex: - startswith: - endswith: - plots: -# Cait -syntax: - prevent: - ___ + ___ -# Override any of our default feedback messages -messages: - FUNCTION_NOT_DEFINED: "Oops you missed a function" -""" -from pedal.report.imperative import set_success, give_partial - -from pedal.sandbox.compatibility import _check_sandbox -from pedal.toolkit.printing import * -from pedal.toolkit.utilities import * -from pedal.toolkit.functions import * -from pedal.assertions.tests import equality_test - -SETTING_SHOW_CASE_DETAILS = "show case details" -DEFAULT_SETTINGS = { - SETTING_SHOW_CASE_DETAILS: True -} - -EXAMPLE_DATA = { - 'functions': [{ - 'name': 'do_complicated_stuff', - 'signature': 'int, int, [int] -> list[int]', - 'cases': [ - {'arguments': "5, 4, 3", 'returns': "12"}, - ] - }] -} - - -class FeedbackException(Exception): - def __init__(self, category, label, **fields): - self.category = category - self.label = label - self.fields = fields - - def as_message(self): - return FEEDBACK_MESSAGES[self.category][self.label].format(**self.fields) - - -def check_function_defined(function, function_definitions, settings=None): - # 1. Is the function defined syntactically? - # 1.1. With the right name? - function_name = function['name'] - if function_name not in function_definitions: - raise FeedbackException('toolkit', 'missing_function', function_name=function_name) - definition = function_definitions[function_name] - return definition - - -def check_function_signature(function, definition, settings=None): - function_name = function['name'] - # 1.2. With the right parameters and return type? - # 1.2.1 'arity' style - simply checks number of parameters - if 'arity' in function or 'parameters' in function: - expected_arity = function['arity'] if 'arity' in function else len(function['parameters']) - actual_arity = len(definition.args.args) - if actual_arity < expected_arity: - raise FeedbackException('toolkit', 'insufficient_args', - function_name=function_name, expected_arity=expected_arity, - actual_arity=actual_arity) - elif actual_arity > expected_arity: - raise FeedbackException('toolkit', 'excessive_args', - function_name=function_name, expected_arity=expected_arity, - actual_arity=actual_arity) - # 1.2.2 'parameters' style - checks each parameter's name and type - if 'parameters' in function: - expected_parameters = function['parameters'] - actual_parameters = definition.args.args - for expected_parameter, actual_parameter in zip(expected_parameters, actual_parameters): - actual_parameter_name = get_arg_name(actual_parameter) - if 'name' in expected_parameter: - if actual_parameter_name != expected_parameter['name']: - raise FeedbackException('toolkit', 'wrong_parameter_name', - function_name=function_name, - expected_parameter_name=expected_parameter['name'], - actual_parameter_name=actual_parameter_name - ) - if 'type' in expected_parameter: - actual_parameter_type = parse_type(actual_parameter) - # TODO: Handle non-string expected_parameter types (dict) - expected_parameter_type = parse_type_value(expected_parameter['type'], True) - if not type_check(expected_parameter_type, actual_parameter_type): - raise FeedbackException('toolkit', 'wrong_parameter_type', - function_name=function_name, - parameter_name=actual_parameter_name, - expected_parameter_type=expected_parameter_type, - actual_parameter_type=actual_parameter_type) - # 1.2.3. 'returns' style - checks the return type explicitly - if 'returns' in function: - expected_returns = parse_type_value(function['returns'], True) - actual_returns = parse_type(definition.returns) - if actual_returns != "None": - if not type_check(expected_returns, actual_returns): - raise FeedbackException("toolkit", "wrong_returns", - function_name=function_name, expected_returns=expected_returns, - actual_returns=actual_returns) - elif expected_returns != "None": - raise FeedbackException("toolkit", "missing_returns", - function_name=function_name, expected_returns=expected_returns) - # 1.2.4. 'signature' style - shortcut for specifying the types - if 'signature' in function: - expected_signature = function['signature'] - actual_returns = parse_type(definition.returns) - actual_parameters = ", ".join(parse_type(actual_parameter.annotation) - for actual_parameter in definition.args.args) - actual_signature = "{} -> {}".format(actual_parameters, actual_returns) - if not type_check(expected_signature, actual_signature): - raise FeedbackException("toolkit", "wrong_signature", - function_name=function_name, expected_signature=expected_signature, - actual_signature=actual_signature) - # All good here! - return True - - -def check_function_value(function, values, settings): - """ - 2. Does the function exist in the data? - - :param function: - :param values: - :param settings: - :return: - """ - function_name = function['name'] - # 2.1. Does the name exist in the values? - if function_name not in values: - raise FeedbackException("toolkit", "function_not_available", function_name=function_name) - function_value = values[function_name] - # 2.2. Is the name bound to a callable? - if not callable(function_value): - raise FeedbackException("toolkit", "name_is_not_function", function_name=function_name) - # All good here - return function_value - - -class TestCase: - CASE_COUNT = 0 - - def __init__(self, function_name, case_name): - self.function_name = function_name - if case_name is None: - self.case_name = str(TestCase.CASE_COUNT) - TestCase.CASE_COUNT += 1 - else: - self.case_name = case_name - self.arguments, self.has_arguments = [], False - self.inputs, self.has_inputs = [], False - self.error, self.has_error = None, False - self.message, self.has_message = None, False - self.expected_prints, self.has_expected_prints = None, False - self.expected_returns, self.has_expected_returns = None, False - self.prints = [] - self.returns = None - self.success = True - - def add_message(self, message): - self.message = message - self.has_message = True - - def add_inputs(self, inputs): - if not isinstance(inputs, list): - inputs = [inputs] - self.inputs = inputs - self.has_inputs = True - - def add_arguments(self, arguments): - if not isinstance(arguments, list): - arguments = [arguments] - self.arguments = arguments - self.has_arguments = True - - def add_error(self, error): - self.error = error - self.has_error = True - self.success = False - - def add_expected_prints(self, prints): - self.expected_prints = prints - self.has_expected_prints = True - - def add_expected_returns(self, returns): - self.expected_returns = returns - self.has_expected_returns = True - - def add_prints_returns(self, prints, returns): - self.prints = prints - self.returns = returns - - def fail(self): - self.success = False - - -def check_case(function, case, student_function): - """ - - :param function: - :param case: - :param student_function: - :return: status, arg, input, error, output, return, message - """ - function_name = function['name'] - test_case = TestCase(function_name, case.get('name')) - # Get callable - sandbox = _check_sandbox(MAIN_REPORT) - sandbox.set_output(None) - # Potential bonus message - if 'message' in case: - test_case.add_message(case['message']) - # Queue up the the inputs - if 'inputs' in case: - test_case.add_inputs(case['inputs']) - sandbox.set_input(test_case.inputs) - else: - sandbox.set_input(None) - # Pass in the arguments and call the function - if 'arguments' in case: - test_case.add_arguments(case['arguments']) - result = sandbox.call(function_name, *test_case.arguments, - report_exceptions=False, context=False) - # Store actual values - test_case.add_prints_returns(sandbox.output, result) - # Check for errors - if sandbox.exception: - test_case.add_error(sandbox.exception) - # 4. Check out the output - if 'prints' in case: - test_case.add_expected_prints(case['prints']) - if not output_test(sandbox.output, case['prints'], False, .0001): - test_case.fail() - # 5. Check the return value - if 'returns' in case: - test_case.add_expected_returns(case['returns']) - if not equality_test(result, case['returns'], True, .0001): - test_case.fail() - # TODO: Check the plots - # Return results - return test_case - - -# TODO: blockpy-feedback-unit => pedal-test-cases in BlockPy Client -TEST_TABLE_TEMPLATE = """ - - - - - - - {body} -
    ArgumentsExpectedReturned
    """ -TEST_TABLE_FOOTER = "" -TEST_TABLE_ROW_HEADER = "" -TEST_TABLE_ROW_NORMAL = "" -TEST_TABLE_ROW_FOOTER = "" -TEST_TABLE_ROW_INFO = "" -GREEN_CHECK = " ✔" -RED_X = " ❌" -CODE_CELL = " {}" -COLUMN_TITLES = ["", "Arguments", "Inputs", "Errors", "Expected", "Expected", "Returned", "Printed"] - - -def make_table(cases): - body = [] - for case in cases: - body.append(" ") - body.append(GREEN_CHECK if case.success else RED_X) - body.append(CODE_CELL.format(", ".join(repr(arg) for arg in case.arguments))) - if case.has_error: - body.append(" Error: {}".format(str(case.error))) - else: - body.append(CODE_CELL.format(repr(case.expected_returns))) - body.append(CODE_CELL.format(repr(case.returns))) - if not case.success and case.has_message: - body.append(" {}".format(case.message)) - body.append(" ") - body = "\n".join(body) - return TEST_TABLE_TEMPLATE.format(body=body) - # if ((any(args) and any(inputs)) or - # (any(expected_outputs) and any(expected_returns)) or - # (any(actual_outputs) and any(actual_returns))): - # # Complex cells - # pass - # else: - # Simple table - # Make header - - # row_mask = [True, any(args), any(inputs), False, - # any("returns" in reason for reason in reasons), - # any("prints" in reason for reason in reasons), - # any("returns" in reason for reason in reasons), - # any("prints" in reason for reason in reasons)] - # header_cells = "".join("{}".format(title) for use, title in zip(row_mask, COLUMN_TITLES) if use) - # body = [TEST_TABLE_ROW_HEADER.format(header_cells)] - # for case in zip( - # statuses, args, inputs, errors, actual_outputs, actual_returns, - # expected_outputs, expected_returns): - # status, case = case[0], case[1:] - # print(row_mask[1:], case) - # def make_code(values): - # if values == None: - # return "None" - # elif isinstance(values, int): - # return "{!r}".format(values) - # else: - # return ", ".join("{}".format(repr(value)) for value in values) - # body.append( - # TEST_TABLE_ROW_NORMAL+ - # (GREEN_CHECK if case[0] else RED_X)+ - # "\n".join(" {}".format(make_code(values)) - # for use, values in zip(row_mask[1:], case) if use)+ - # "\n" - # ) - # # Make each row - # table = "{}\n{}\n{}".format(TEST_TABLE_HEADER, "\n ".join(body), TEST_TABLE_FOOTER) - # return table - - -def check_cases(function, student_function, settings): - function_name = function['name'] - if 'cases' in function: - cases = function['cases'] - test_cases = [check_case(function, case, student_function) for case in cases] - success_cases = sum(test.success for test in test_cases) - if success_cases < len(cases): - if settings[SETTING_SHOW_CASE_DETAILS]: - table = make_table(test_cases) - raise FeedbackException("toolkit", "failed_test_cases", - function_name=function_name, - cases_count=len(cases), failure_count=len(cases) - success_cases, - table=table) - else: - raise FeedbackException("toolkit", "failed_test_cases_count", - function_name=function_name, - cases_count=len(cases), failure_count=len(cases) - success_cases) - - -def get_arg_name(node): - name = node.id - if name is None: - return node.arg - else: - return name - - -def load_question(data): - """ - - :param data: - :return: - """ - ast = parse_program() - student = compatibility.get_student_data() - # Check that there aren't any invalid syntactical structures - # Get all of the function ASTs in a dictionary - function_definitions = {definition._name: definition - for definition in ast.find_all("FunctionDef")} - settings = DEFAULT_SETTINGS.copy() - settings.update(data.get('settings', {})) - rubric = settings.get('rubric', {}) - function_points = 0 - if 'functions' in data: - function_rubric = rubric.get('functions', {}) - successes = [] - for function in data['functions']: - success = False - try: - definition = check_function_defined(function, function_definitions, settings) - function_points += function_rubric.get('definition', 10) - check_function_signature(function, definition, settings) - function_points += function_rubric.get('signature', 10) - student_function = check_function_value(function, student.data, settings) - function_points += function_rubric.get('value', 0) - except FeedbackException as fe: - yield fe.as_message(), fe.label - else: - try: - check_cases(function, student_function, settings) - except FeedbackException as fe: - success_ratio = (1.0 - fe.fields['failure_count'] / fe.fields['cases_count']) - function_points += function_rubric.get('cases', 80 * success_ratio) - yield fe.as_message(), fe.label - else: - function_points += function_rubric.get('cases', 80) - success = True - successes.append(success) - function_points /= len(data['functions']) - if all(successes): - set_success() - else: - give_partial(function_points) - - -def check_question(data): - results = list(load_question(data)) - if results: - message, label = results[0] - gently(message, label=label) - - -def check_pool(questions): - pass - - -def load_file(filename): - pass - - -FEEDBACK_MESSAGES = { - "toolkit": { - "missing_function": "No function named `{function_name}` was found.", - "insufficient_args": ("The function named `{function_name}` " - "has fewer parameters ({actual_arity}) " - "than expected ({expected_arity})."), - "excessive_args": ("The function named `{function_name}` " - "has more parameters ({actual_arity}) " - "than expected ({expected_arity})."), - # TODO: missing_parameter that checks if parameter name exists, but is in the wrong place - "wrong_parameter_name": ("Error in definition of `{function_name}`. " - "Expected a parameter named `{expected_parameter_name}`, " - "instead found `{actual_parameter_name}`."), - "wrong_parameter_type": ("Error in definition of function `{function_name}` " - "parameter `{parameter_name}`. Expected `{expected_parameter_type}`, " - "instead found `{actual_parameter_type}`."), - "missing_returns": ("Error in definition of function `{function_name}` return type. " - "Expected `{expected_returns}`, but there was no return type specified."), - "wrong_returns": ("Error in definition of function `{function_name}` return type. " - "Expected `{expected_returns}`, instead found `{actual_returns}`."), - "wrong_signature": ("Error in definition of function `{function_name}` signature. " - "Expected `{expected_signature}`, instead found `{actual_signature}`."), - "name_is_not_function": "You defined `{function_name}`, but did not define it as a function.", - "function_not_available": ("You defined `{function_name}` somewhere in your code, " - "but it was not available in the top-level scope to be called. " - "Perhaps you defined it inside another function or scope?"), - "failed_test_cases": ("I ran your function {function_name} on my own test cases. " - "It failed {failure_count}/{cases_count} of my tests.\n{table}"), - "failed_test_cases_count": ("I ran your function {function_name} on my own test cases. " - "It failed {failure_count}/{cases_count} of my tests."), - } -} diff --git a/src/lib/pedal/questions/setup.py b/src/lib/pedal/questions/setup.py deleted file mode 100644 index d5308872a9..0000000000 --- a/src/lib/pedal/questions/setup.py +++ /dev/null @@ -1,42 +0,0 @@ -from pedal.report.imperative import MAIN_REPORT - -import hashlib - - -def _name_hash(name): - return hashlib.md5(name.encode('utf8')).digest()[0] - - -def _setup_questions(report): - ''' - Initialize any necessary fields for the report's question tool. - - Args: - report (Report): The report object to store data and feedback in. - ''' - if 'questions' not in report: - report['questions'] = { - 'seed': 0 - } - - -def set_seed(seed_value, report=None): - ''' - Sets the seed that will be used in selecting questions. - - Args: - seed_value (int or str or iterable[int]): The value to use when - selecting questions, deterministically. If int, the same index - will be used for all questions. If an iterable of ints, each - one will serve as the index for the corresponding problem (throws - an exception if the iterable isn't long enough). If a string, - it will be hashed to a value (the hash is deterministic across - platforms) that will be modulo'd to be in the right range for the - pool. Presently, hashing generates values from [0, 256) so you - need to limit your questions to 256. - report (Report): The report object to store data and feedback in. If - left None, defaults to the global MAIN_REPORT. - ''' - if report is None: - report = MAIN_REPORT - report['questions']['seed'] = seed_value diff --git a/src/lib/pedal/report/__init__.py b/src/lib/pedal/report/__init__.py deleted file mode 100644 index 250ba0da0c..0000000000 --- a/src/lib/pedal/report/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -The collection of classes and functions used to store the fundamental Report -and Feedback objects. -""" - -from pedal.report.report import Report -from pedal.report.feedback import Feedback -from pedal.report.imperative import * diff --git a/src/lib/pedal/report/feedback.py b/src/lib/pedal/report/feedback.py deleted file mode 100644 index 9ccb53aaef..0000000000 --- a/src/lib/pedal/report/feedback.py +++ /dev/null @@ -1,102 +0,0 @@ -""" -Simple data classes for storing feedback to present to learners. -""" - -__all__ = ['Feedback'] - - -class Feedback: - """ - A class for storing raw feedback. - - Attributes: - label (str): An internal name for this specific piece of feedback. - tool (str): An internal name for indicating the tool that created - this feedback. - category (str): A human-presentable name showable to the learner. - More than one Feedback will be in a category, most - likely. - priority (str): An indication of how important this feedback is. - Might be "high/medium/low" or the name of a - category (tool?) to supersede. Exactly how this gets - used is up to the resolver. A special kind of priority - is "positive" - which indicates that this feedback is - positive, and the information is good to convey to the - student. - group (int or str): The group that this piece of feedback should be - associated with. Some resolvers want to group feedback using this - identifier. - result (bool): Whether or not this feedback is associated with the - learner completing the task ("Success!"). - performance (float): A relative amount that this feedback contributes - to the students' performance (think in terms of - partial credit, like "Triggering this feedback - is worth 20% (.2)"). - misconceptions (Message): A description of the misconception that - is believed to be in the student's mind, - or perhaps the relevant concept from the - material that should be associated with - this. ("Variables must be initialized - before they are used"). - mistakes (Message): A description of the error or bug that the - student has created ("NameError on line 5: sum - has not been defined"). - hints (Message): A suggestion for what the student can do - ("Initialize the sum variable on line 2"). - constraints (Message): A description of the task requirements or - task type that the student has violated - ("You used a for loop, but this question - expected you to use recursion."). - metacognitives (Message): A suggestion for more regulative - strategies ("You have been working for - 5 hours, perhaps it is time to take - a break?"). - """ - MESSAGE_TYPES = ['hint', 'mistake', 'misconception', - 'constraint', 'metacognitive'] - - def __init__(self, label, tool='instructor', - category='Instructor feedback', priority=None, group=None, - result=None, performance=None, misconception=None, - mistake=None, hint=None, constraint=None, - metacognitive=None): - # Metadata - self.label = label - self.tool = tool - self.category = category - self.priority = priority - self.group = group - # Data - self.result = result - self.performance = performance - self.misconception = misconception - self.mistake = mistake - self.hint = hint - self.constraint = constraint - self.metacognitive = metacognitive - - def __str__(self): - return "".format(self.label) - - def __repr__(self): - metadata = "" - if self.tool is not None: - metadata += ", tool=" + self.tool - if self.category is not None: - metadata += ", category=" + self.category - if self.priority is not None: - metadata += ", priority=" + self.priority - if self.group is not None: - metadata += ", group=" + str(self.group) - data = "" - return "Feedback({}{}{})".format(self.label, metadata, data) - - -""" -A Message is one of: - str - Dict with a `message` field and any other suitable fields, such as: - html_message: An HTML message instead of a plaintext message. - line: The line number to highlight - error: The error message to render -""" diff --git a/src/lib/pedal/report/imperative.py b/src/lib/pedal/report/imperative.py deleted file mode 100644 index ef8cb76839..0000000000 --- a/src/lib/pedal/report/imperative.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -Imperative style commands for constructing feedback in a convenient way. -Uses a global report object (MAIN_REPORT). -""" - -__all__ = ['set_success', 'compliment', 'give_partial', 'explain', 'explain_r', - 'gently', 'gently_r', 'hide_correctness', 'suppress', 'log', 'debug', - 'clear_report', 'get_all_feedback', 'MAIN_REPORT', 'guidance'] - -from pedal.report.report import Report - -#: The global Report object. Meant to be used as a default singleton -#: for any tool, so that instructors do not have to create their own Report. -#: Of course, all APIs are expected to work with a given Report, and only -#: default to this Report when no others are given. -MAIN_REPORT = Report() - - -def set_success(): - """ - Creates Successful feedback for the user, indicating that the entire - assignment is done. - """ - MAIN_REPORT.set_success() - - -def compliment(message, line=None): - """ - Create a positive feedback for the user, potentially on a specific line of - code. - - Args: - message (str): The message to display to the user. - line (int): The relevant line of code to reference. - """ - MAIN_REPORT.compliment(message, line) - - -def give_partial(value, message=None): - """ - Increases the user's current score by the `value`. Optionally display - a positive message too. - - Args: - value (number): The number to increase the user's score by. - message (str): The message to display to the user. - """ - MAIN_REPORT.give_partial(value, message) - - -def explain(message, priority='medium', line=None, label='explain'): - MAIN_REPORT.explain(message, priority, line, label=label) - - -def guidance(message, priority='medium', line=None, label='Guidance'): - MAIN_REPORT.guidance(message, priority, line, label=label) - - -def gently(message, line=None, label='explain'): - MAIN_REPORT.gently(message, line, label=label) - - -def gently_r(message, code, line=None, label="explain"): - gently(message + "

    ({})

    ".format(code), line, label=label) - return message - - -def explain_r(message, code, priority='medium', line=None, label="explain"): - explain(message + "

    ({})

    ".format(code), priority, line, label=label) - return message - - -def hide_correctness(): - MAIN_REPORT.hide_correctness() - - -def suppress(category, label=True): - MAIN_REPORT.suppress(category, label) - - -def log(message): - MAIN_REPORT.log(message) - - -def debug(message): - MAIN_REPORT.debug(message) - - -def clear_report(): - MAIN_REPORT.clear() - - -def get_all_feedback(): - return MAIN_REPORT.feedback diff --git a/src/lib/pedal/report/report.py b/src/lib/pedal/report/report.py deleted file mode 100644 index 2b14bd30df..0000000000 --- a/src/lib/pedal/report/report.py +++ /dev/null @@ -1,164 +0,0 @@ -from pedal.report.feedback import Feedback - -__all__ = ['Report'] - - -class Report: - """ - A class for storing Feedback generated by Tools, along with any auxiliary - data that the Tool might want to provide for other tools. - - Attributes: - feedback (list of Feedback): The raw feedback generated for this Report - so far. - suppressions (list of tuple(str, str)): The categories and labels that - have been suppressed so far. - group (int or str): The label for the current group. Feedback given - by a Tool will automatically receive the current `group`. This - is used by the Source tool, for example, in order to group feedback - by sections. - group_names (dict[group:str]): A printable, student-facing name for the - group. When a group needs to be rendered out to the user, this - will override whatever label was going to be presented instead. - group_order (sequence or callable or None): The mechanism to use to - order groups. If a sequence, the order will be inferred based on - the order of elements in the sequence. If a callable, the callable - will be used as a key function for `sort`. If `None`, then defaults - to the natural ordering of the groups. Defaults to `None`. - hooks (dict[str: list[callable]): A dictionary mapping events to - a list of callable functions. Tools can register functions on - hooks to have them executed when the event is triggered by another - tool. For example, the Assertions tool has hooks on the Source tool - to trigger assertion resolutions before advancing to next sections. - _results (dict of str => any): Maps tool names to their data. The - namespace for a tool can be used to - store whatever they want, but will - probably be in a dictionary itself. - """ - group_order = None - - def __init__(self): - """ - Creates a new Report instance. - """ - self.clear() - - def clear(self): - self.feedback = [] - self.suppressions = {} - self._results = {} - self.group = None - self.group_names = {} - self.hooks = {} - - def set_success(self, group=None): - """ - Creates Successful feedback for the user, indicating that the entire - assignment is done. - """ - if group is None: - group = self.group - self.feedback.append(Feedback('set_success', priority='positive', - result=True, group=group)) - - def give_partial(self, value, message=None, group=None): - if value is None: - return False - if group is None: - group = self.group - self.feedback.append(Feedback('give_partial', performance=value, - priority='positive', - group=group, - mistake=message)) - return True - - def hide_correctness(self): - self.suppressions['success'] = [] - - def explain(self, message, priority='medium', line=None, group=None, - label='explain'): - misconception = {'message': message} - if line is not None: - misconception['line'] = line - if group is None: - group = self.group - self.attach(label, priority=priority, category='instructor', - group=group, misconception=misconception) - - def gently(self, message, line=None, group=None, label='explain'): - self.explain(message, priority='student', line=line, group=group, - label=label) - - def guidance(self, message, line=None, group=None, label='guidance'): - hint = {'message': message} - if line is not None: - hint['line'] = line - if group is None: - group = self.group - self.attach(label, priority='instructions', category='instructions', group=group, hint=hint) - - def compliment(self, message, line=None, group=None, label='explain'): - self.explain(message, priority='positive', line=line, group=group, - label=label) - - def attach(self, label, **kwargs): - self.feedback.append(Feedback(label, **kwargs)) - - def log(self, message): - pass - - def debug(self, message): - pass - - def suppress(self, category, label=True, where=True): - """ - Args: - category (str): The category of feedback to suppress. - label (str): A specific label to match against and suppress. - where (bool or group): Which group of report to localize the - suppression to. If instead `True` is passed, the suppression - occurs in every group globally. - TODO: Currently, only global suppression is supported. - """ - category = category.lower() - if isinstance(label, str): - label = label.lower() - if category not in self.suppressions: - self.suppressions[category] = [] - self.suppressions[category].append(label) - - def add_hook(self, event, function): - """ - Register the `function` to be executed when the given `event` is - triggered. - - Args: - event (str): An event name. Multiple functions can be triggered for - the same `event`. The format is as follows: - "pedal.module.function.extra" - - The `".extra"` component is optional to add further nuance, but - the general idea is that you are referring to functions that, - when called, should trigger other functions to be called first. - function (callable): A callable function. This function should - accept a keyword parameter named `report`, which will - """ - if event not in self.hooks: - self.hooks[event] = [] - self.hooks[event].append(function) - - def execute_hooks(self, event): - if event in self.hooks: - for function in self.hooks[event]: - function(report=self) - - def __getitem__(self, key): - if key not in self._results: - self._results[key] = {} - return self._results[key] - - def __setitem__(self, key, value): - self._results[key] = value - - def __contains__(self, key): - return key in self._results diff --git a/src/lib/pedal/resolvers/__init__.py b/src/lib/pedal/resolvers/__init__.py deleted file mode 100644 index 0bbf9c8536..0000000000 --- a/src/lib/pedal/resolvers/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -""" - -Resolver Types - -Does there need to be some kind of hook for Tools to wrap up their business? - -Simple - Find the highest priority feedback and show that, along with any positive feedback. - -Sectional - Find the highest priority feedback for each section, and show that along with any positive feedback. - -Full - Report all feedback, grouped by tool/category/priority/time. - -Full Summary - Report all feedback but divided into frequencies of labels grouped by tool/category/priority/time. - -""" diff --git a/src/lib/pedal/resolvers/core.py b/src/lib/pedal/resolvers/core.py deleted file mode 100644 index 07a0b696e2..0000000000 --- a/src/lib/pedal/resolvers/core.py +++ /dev/null @@ -1,22 +0,0 @@ -from pedal.report.imperative import MAIN_REPORT - - -def make_resolver(func, report=None): - ''' - Decorates the given function as a Resolver. This means that when the - function is executed, the `"pedal.resolver.resolve"` event will be - triggered. - - Args: - func (callable): The function to decorate. - report (Report): The Report to trigger the event on. If None, then use - the `MAIN_REPORT`. - ''' - if report is None: - report = MAIN_REPORT - - def resolver_wrapper(): - report.execute_hooks("pedal.resolvers.resolve") - return func() - - return resolver_wrapper diff --git a/src/lib/pedal/resolvers/readme.md b/src/lib/pedal/resolvers/readme.md deleted file mode 100644 index 8b244293ae..0000000000 --- a/src/lib/pedal/resolvers/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# Resolvers - -A tool for selecting and managing reported data from other tools, in order to select a relevant piece of feedback. \ No newline at end of file diff --git a/src/lib/pedal/resolvers/sectional.py b/src/lib/pedal/resolvers/sectional.py deleted file mode 100644 index c3fae486d2..0000000000 --- a/src/lib/pedal/resolvers/sectional.py +++ /dev/null @@ -1,77 +0,0 @@ -import sys - -from pedal.resolvers import simple -from pedal.report import MAIN_REPORT - - -def resolve(report=None, priority_key=None): - """ - Args: - report (Report): The report object to resolve down. Defaults to the - global MAIN_REPORT - - Returns - str: A string of HTML feedback to be delivered - """ - if report is None: - report = MAIN_REPORT - if priority_key is None: - priority_key = simple.by_priority - # Prepare feedbacks - feedbacks = report.feedback - feedbacks.sort(key=lambda f: (f.group or 0, priority_key(f))) - suppressions = report.suppressions - # Process - final_success = False - final_score = 0 - finals = {} - found_failure = False - for feedback in feedbacks: - group = feedback.group or 0 - category = feedback.category.lower() - if category in suppressions: - if True in suppressions[category]: - continue - elif feedback.label.lower() in suppressions[category]: - continue - success, partial, message, data = simple.parse_feedback(feedback) - final_success = success or final_success - final_score += partial - if message is not None: - # print("RESETING GROUP", group, message[:20], found_failure, feedback.priority) - if group not in finals: - finals[group] = [] - found_failure = False - if feedback.priority not in ('positive', 'instructions'): - if found_failure: - continue - found_failure = True - entry = {'label': feedback.label, - 'message': message, - 'category': feedback.category, - 'priority': feedback.priority, - 'data': data} - if feedback.priority == 'instructions': - # Find end of instructions - index = 0 - for feedback in finals[group]: - if feedback['priority'] != 'instructions': - break - index += 1 - finals[group].insert(index, entry) - elif feedback.priority != 'positive': - finals[group].insert(0, entry) - else: - finals[group].append(entry) - # from pprint import pprint - # pprint(finals) - final_hide_correctness = suppressions.get('success', False) - if not finals: - finals[0] = [{ - 'label': 'No errors', - 'category': 'Instructor', - 'data': [], - 'priority': 'medium', - 'message': "No errors reported." - }] - return (final_success, final_score, final_hide_correctness, finals) diff --git a/src/lib/pedal/resolvers/simple.py b/src/lib/pedal/resolvers/simple.py deleted file mode 100644 index 59222f2cea..0000000000 --- a/src/lib/pedal/resolvers/simple.py +++ /dev/null @@ -1,156 +0,0 @@ -from pedal.report import MAIN_REPORT, Feedback -from pedal.resolvers.core import make_resolver - -DEFAULT_CATEGORY_PRIORITY = [ - 'syntax', - 'mistakes', - 'instructor', - 'analyzer', - 'runtime', - 'student', - 'positive', - 'instructions', - 'uncategorized', -] - -# For compatibility with the old feedback API -LEGACY_CATEGORIZATIONS = { - # 'student': 'runtime', - 'parser': 'syntax', - 'verifier': 'syntax', - 'instructor': 'instructor' -} - - -def by_priority(feedback): - """ - Converts a feedback into a numeric representation for sorting. - - Args: - feedback (Feedback): The feedback object to convert - Returns: - float: A decimal number representing the feedback's relative priority. - """ - category = 'uncategorized' - if feedback.category is not None: - category = feedback.category.lower() - priority = 'medium' - if feedback.priority is not None: - priority = feedback.priority.lower() - priority = LEGACY_CATEGORIZATIONS.get(priority, priority) - if category in DEFAULT_CATEGORY_PRIORITY: - value = DEFAULT_CATEGORY_PRIORITY.index(category) - else: - value = len(DEFAULT_CATEGORY_PRIORITY) - offset = .5 - if priority == 'low': - offset = .7 - elif priority == 'high': - offset = .3 - elif priority not in ('low', 'medium', 'high'): - if priority in DEFAULT_CATEGORY_PRIORITY: - value = DEFAULT_CATEGORY_PRIORITY.index(priority) - offset = .1 - return value + offset - - -def parse_message(component): - if isinstance(component, str): - return component - elif isinstance(component, list): - return '
    \n'.join(parse_message(c) for c in component) - elif isinstance(component, dict): - if "html" in component: - return component["html"] - elif "message" in component: - return component["message"] - else: - raise ValueError("Component has no message field: " + str(component)) - else: - raise ValueError("Invalid component type: " + str(type(component))) - - -def parse_data(component): - if isinstance(component, str): - return [{'message': component}] - elif isinstance(component, list): - return component - elif isinstance(component, dict): - return [component] - - -def parse_feedback(feedback): - # Default returns - success = False - performance = 0 - message = None - data = [] - # Actual processing - for feedback_type in Feedback.MESSAGE_TYPES: - feedback_value = getattr(feedback, feedback_type) - if feedback_value is not None: - data.extend(parse_data(feedback_value)) - parsed_message = parse_message(feedback_value) - if parsed_message is not None: - message = parsed_message - if feedback.result is not None: - success = feedback.result - if feedback.performance is not None: - performance = feedback.performance - return success, performance, message, data - - -@make_resolver -def resolve(report=None, priority_key=None): - """ - Args: - report (Report): The report object to resolve down. Defaults to the - global MAIN_REPORT - - Returns - str: A string of HTML feedback to be delivered - """ - if report is None: - report = MAIN_REPORT - if priority_key is None: - priority_key = by_priority - # Prepare feedbacks - feedbacks = report.feedback - feedbacks.sort(key=priority_key) - suppressions = report.suppressions - # Process - final_success = False - final_score = 0 - final_message = None - final_category = 'Instructor' - final_label = 'No errors' - final_data = [] - for feedback in feedbacks: - category = feedback.category.lower() - if category in suppressions: - if True in suppressions[category]: - continue - elif feedback.label.lower() in suppressions[category]: - continue - success, partial, message, data = parse_feedback(feedback) - final_success = success or final_success - final_score += partial - if (message is not None and - final_message is None and - feedback.priority != 'positive'): - final_message = message - final_category = feedback.category - final_label = feedback.label - final_data = data - if final_message is None: - final_message = "No errors reported." - final_hide_correctness = suppressions.get('success', False) - if (not final_hide_correctness and final_success and - final_label == 'No errors' and - final_category == 'Instructor'): - final_category = 'Complete' - final_label = 'Complete' - final_message = "Great work!" - return (final_success, final_score, final_category, - final_label, final_message, final_data, - final_hide_correctness) diff --git a/src/lib/pedal/sandbox/__init__.py b/src/lib/pedal/sandbox/__init__.py deleted file mode 100644 index 212322d314..0000000000 --- a/src/lib/pedal/sandbox/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -from pedal.report import MAIN_REPORT -from pedal.sandbox.sandbox import Sandbox, DataSandbox - -# Compatibility API -''' -run_student -queue_input -reset_output -get_output -''' - - -def reset(report=None): - if report is None: - report = MAIN_REPORT - report['sandbox']['run'] = Sandbox(filename=report['source']['filename']) - - -def run(raise_exceptions=True, report=None, coverage=False, threaded=False, inputs=None): - if report is None: - report = MAIN_REPORT - if 'run' not in report['sandbox']: - report['sandbox']['run'] = Sandbox(filename=report['source']['filename'], threaded=threaded) - sandbox = report['sandbox']['run'] - source_code = report['source']['code'] - sandbox.record_coverage = coverage - sandbox.run(source_code, _as_filename=report['source']['filename'], _inputs=inputs) - if raise_exceptions and sandbox.exception is not None: - name = str(sandbox.exception.__class__)[8:-2] - report.attach(name, category='Runtime', tool='Sandbox', - section=report['source']['section'], - mistakes={'message': sandbox.format_exception(), - 'error': sandbox.exception}) - return sandbox diff --git a/src/lib/pedal/sandbox/compatibility.py b/src/lib/pedal/sandbox/compatibility.py deleted file mode 100644 index a7c1a07b31..0000000000 --- a/src/lib/pedal/sandbox/compatibility.py +++ /dev/null @@ -1,124 +0,0 @@ -import sys - -from pedal.sandbox.sandbox import Sandbox -from pedal.sandbox.messages import EXTENDED_ERROR_EXPLANATION - -from pedal.report import MAIN_REPORT, Feedback - - -def _check_sandbox(report): - if 'run' not in report['sandbox']: - report['sandbox']['run'] = Sandbox() - return report['sandbox']['run'] - - -def run_student(raise_exceptions=False, report=None, old_style_messages=False): - if report is None: - report = MAIN_REPORT - sandbox = _check_sandbox(report) - source_code = report['source']['code'] - filename = report['source']['filename'] - sandbox.run(source_code, as_filename=filename, report_exceptions=not raise_exceptions) - if raise_exceptions: - raise_exception(sandbox.exception, sandbox.exception_position, - report=report, message=None if old_style_messages else sandbox.exception_formatted) - return sandbox.exception - - -def queue_input(*inputs, **kwargs): - if 'report' not in kwargs: - report = MAIN_REPORT - else: - report = kwargs['report'] - sandbox = _check_sandbox(report) - sandbox.set_input(inputs) - - -def reset_output(report=None): - if report is None: - report = MAIN_REPORT - sandbox = _check_sandbox(report) - sandbox.set_output(None) - - -def get_output(report=None): - if report is None: - report = MAIN_REPORT - sandbox = _check_sandbox(report) - return sandbox.output - - -def get_plots(report=None): - if report is None: - report = MAIN_REPORT - sandbox = _check_sandbox(report) - if 'matplotlib.pyplot' in sandbox.modules: - mock_plt = sandbox.modules['matplotlib.pyplot'] - if hasattr(mock_plt, 'plots'): - return mock_plt.plots - return [] - - -def capture_output(function, *args, **kwargs): - if 'report' in kwargs: - report = kwargs['report'] - else: - report = MAIN_REPORT - sandbox = _check_sandbox(report) - sandbox.set_output(None) - sandbox.call(function.__name__, *args) - return sandbox.output - - -def get_sandbox(report=None): - if report is None: - report = MAIN_REPORT - sandbox = _check_sandbox(report) - return sandbox - - -def raise_exception(exception, position=None, report=None, message=None): - if report is None: - report = MAIN_REPORT - sandbox = _check_sandbox(report) - if exception is None: - return - extended = EXTENDED_ERROR_EXPLANATION.get(exception.__class__, "") - if message is None: - message = "
    {}
    \n{}".format(str(exception), extended) - # Skulpt compatible name lookup - name = str(exception.__class__)[8:-2] - report.attach(name, category='Runtime', tool='Sandbox', - mistake={'message': message, - 'error': exception, - 'position': position, - 'traceback': None}) - sandbox.exception = exception - - -def get_student_data(report=None): - if report is None: - report = MAIN_REPORT - sandbox = _check_sandbox(report) - return sandbox - - -def set_sandbox(sandbox, report=None): - """ - Update the sandbox to hold the new sandbox instance. Particularly useful - for Skulpt, which needs to set the sandbox in an unusual way. - """ - if report is None: - report = MAIN_REPORT - report['sandbox']['run'] = sandbox - return sandbox - - -def trace_lines(report=None): - if report is None: - report = MAIN_REPORT - sandbox = _check_sandbox(report) - if sandbox.tracer_style == 'coverage': - return sandbox.trace.lines - sandbox.trace.missing - else: - return [] diff --git a/src/lib/pedal/sandbox/exceptions.py b/src/lib/pedal/sandbox/exceptions.py deleted file mode 100644 index ccae0bc6cc..0000000000 --- a/src/lib/pedal/sandbox/exceptions.py +++ /dev/null @@ -1,191 +0,0 @@ -import traceback -import os -import sys - -try: - TimeoutError -except NameError: - class TimeoutError(Exception): - pass - - -class SandboxException(Exception): - """ - Generic base exception for sandbox errors. - """ - - -class SandboxStudentCodeException(SandboxException): - """ - Caused by an error in student code - """ - - def __init__(self, actual): - self.actual = actual - - -class SandboxPreventModule(Exception): - """ - Caused by student attempting to load a module that they shouldn't. - """ - - -class SandboxHasNoFunction(SandboxException): - """ - Caused by attempting to access a function that the student hasn't created. - """ - - -class SandboxHasNoVariable(SandboxException): - """ - Caused by attempting to access a variable that the student hasn't created. - """ - - -class SandboxNoMoreInputsException(Exception): - """ - Caused by the student calling `input` when the instructor hasn't provided - enough inputs. Typically, the student has an infinite loop around their - `input` function. - """ - - -BuiltinKeyError = KeyError - - -class KeyError(BuiltinKeyError): - """ - A version of KeyError that replaces the built-in with one small - modification: when printing an explanatory message, the message is not - rendered as a tuple. Because that's stupid and the fact that it made it - into CPython is just rude. - - See Also: - https://github.com/python/cpython/blob/master/Objects/exceptions.c#L1556 - """ - __module__ = "builtins" - - def __init__(self, original, message): - for field in ['__cause__', '__traceback__', '__context__']: - if hasattr(original, field): - setattr(self, field, getattr(original, field)) - else: - setattr(self, field, None) - self.message = message - - def __str__(self): - return self.message - - -def _add_context_to_error(e, message): - if isinstance(e, BuiltinKeyError): - new_args = repr(e.args[0]) + message - e = KeyError(e, new_args) - e.args = tuple([new_args]) - elif isinstance(e, OSError): - # TODO: Investigate OSError, since they have so many args. - # Might be weird. - e.args = tuple([e.args[0] + message]) - return e - elif hasattr(e, 'args') and e.args: - e.args = tuple([e.args[0] + message]) - return e - - -x = sys.stdout - - -class SandboxTraceback: - """ - Class for reformatting tracebacks to have more pertinent information. - """ - - def __init__(self, exception, exc_info, full_traceback, - instructor_filename, line_offset, student_filename, - original_code_lines): - """ - Args: - exception (Exception): The exception that was raised. - exc_info (ExcInfo): The result of sys.exc_info() when the exception - was raised. - full_traceback (bool): Whether or not to provide the full traceback - or just the parts relevant to students. - instructor_filename (str): The name of the instructor file, which - can be used to avoid reporting instructor code in the - traceback. - """ - self.line_offset = line_offset - self.exception = exception - self.exc_info = exc_info - self.full_traceback = full_traceback - self.instructor_filename = instructor_filename - self.student_filename = student_filename - self.line_number = traceback.extract_tb(exc_info[2])[-1][1] - self.original_code_lines = original_code_lines - - def _clean_traceback_line(self, line): - return line.replace(', in ', '', 1) - - def format_exception(self, preamble=""): - if not self.exception: - return "" - if isinstance(self.exception, TimeoutError): - return str(self.exception) - cl, exc, tb = self.exc_info - while tb and self._is_relevant_tb_level(tb): - tb = tb.tb_next - length = self._count_relevant_tb_levels(tb) - tb_e = traceback.TracebackException(cl, self.exception, tb, limit=length, - capture_locals=False) - # print(list(), file=x) - for frame in tb_e.stack: - if frame.filename == os.path.basename(self.student_filename): - frame.lineno += self.line_offset - if frame.lineno - 1 < len(self.original_code_lines): - frame._line = self.original_code_lines[frame.lineno - 1] - else: - frame._line = "*line missing*" - lines = [self._clean_traceback_line(line) - for line in tb_e.format()] - lines[0] = "Traceback:\n" - return preamble + ''.join(lines) - - def _count_relevant_tb_levels(self, tb): - length = 0 - while tb and not self._is_relevant_tb_level(tb): - length += 1 - tb = tb.tb_next - return length - - def _is_relevant_tb_level(self, tb): - """ - Determines if the give part of the traceback is relevant to the user. - - Returns: - boolean: True means it is NOT relevant - """ - # Are in verbose mode? - if self.full_traceback: - return False - filename, a_, b_, _ = traceback.extract_tb(tb, limit=1)[0] - # Is the error in the student file? - if filename == self.student_filename: - return False - # Is the error in the instructor file? - if filename == self.instructor_filename: - return True - # Is the error in this test directory? - current_directory = os.path.dirname(os.path.realpath(__file__)) - if filename.startswith(current_directory): - return True - # Is the error related to a file in the parent directory? - parent_directory = os.path.dirname(current_directory) - # Currently we don't refer to this? - # Is the error in a local file? - if filename.startswith('.'): - return False - # Is the error in an absolute path? - if not os.path.isabs(filename): - return False - # Okay, it's not a student related file - return True diff --git a/src/lib/pedal/sandbox/messages.py b/src/lib/pedal/sandbox/messages.py deleted file mode 100644 index 350d8e43e3..0000000000 --- a/src/lib/pedal/sandbox/messages.py +++ /dev/null @@ -1,61 +0,0 @@ -# Skulpt has weird errors, and is missing some errors. Compatibility. -try: - ParseError -except NameError: - class ParseError(Exception): - pass -try: - SyntaxError -except NameError: - class SyntaxError(Exception): - pass -try: - ReferenceError -except NameError: - class ReferenceError(Exception): - pass -try: - EOFError -except NameError: - class EOFError(Exception): - pass -try: - MemoryError -except NameError: - class MemoryError(Exception): - pass -try: - OSError -except NameError: - class OSError(Exception): - pass -try: - TokenError -except NameError: - class TokenError(Exception): - pass -try: - TimeLimitError -except NameError: - class TimeLimitError(Exception): - pass - -EXTENDED_ERROR_EXPLANATION = { - ParseError: "A parse error means that Python does not understand the syntax on the line the error message points out. Common examples are forgetting commas beteween arguments or forgetting a : (colon) on a for statement.
    Suggestion: To fix a parse error you just need to look carefully at the line with the error and possibly the line before it. Make sure it conforms to all of Python's rules.", - TypeError: "Type errors most often occur when an expression tries to combine two objects with types that should not be combined. Like using + to add a number to a list instead of .append, or dividing a string by a number.
    Suggestion: To fix a type error you will most likely need to trace through your code and make sure the variables have the types you expect them to have.", - SyntaxError: "This message indicates that Python can't figure out the syntax of a particular statement. Some examples are assigning to a literal, or a function call.
    Suggestion: Check your assignment statements and make sure that the left hand side of the assignment is a variable, not a literal (e.g., 7 or \"hello\") or a function.", - NameError: "A name error almost always means that you have used a variable before it has a value. Often this may be a simple typo, so check the spelling carefully.
    Suggestion: Check the right hand side of assignment statements and your function calls, this is the most likely place for a NameError to be found. It really helps to step through your code, one line at a time, mentally keeping track of your variables.", - ValueError: "A ValueError most often occurs when you pass a parameter to a built-in function, and the function is expecting one type and you pass something different. For instance, if you try to convert a non-numeric string to an int, you will get a ValueError:
      int(\"Corgi\") # ValueError: invalid literal for int() with base 10

    Suggestion: The error message gives you a pretty good hint about the name of the function as well as the value that is incorrect. Look at the error message closely and then trace back to the variable containing the problematic value. }", - AttributeError: "This happens when you try to do SOMETHING.WHATEVER and either SOMETHING wasn't declared or WHATEVER isn't an attribute of SOMETHING. This error message is telling you that the object on the left hand side of the dot, does not have the attribute or method on the right hand side.
    Suggestion: You were probably trying to either get access to some data (weather.get) or append (a_list.append). If it's the first one, you should make sure the module is imported and that you are called its function correctly. If it's the second one, you should make sure you spelled \"append\" right and that you are using a variable with a list for a value.", - TokenError: "Most of the time this error indicates that you have forgotten a right parenthesis or have forgotten to close a pair of quotes.
    Suggestion: Check each line of your program and make sure that your parenthesis are balanced.", - IndexError: "This message means that you are trying to index past the end of a string or a list. For example, if your list has 3 things in it and you try to access the item at position 5.
    Suggestion: Remember that the first item in a list or string is at index position 0, quite often this message comes about because you are off by one. Remember in a list of length 3 the last legal index is 2.
    favorite_colors = [\"red\", \"blue\", \"green\"]\nfavorite_colors[2] # prints green favorite_color[3] # raises an IndexError
    ", - ImportError: "This error message indicates that you are trying to import a module that does not exist, or is not in the same directory as your python script.
    Suggestion: One problem may simply be that you have a typo - remember, you must not capitalize the module name. Another common problem is that you have placed the module in a different directory. Finally, if you're using a dataset module, then it might not be imported. Use the \"Import Datasets\" button below!", - ReferenceError: "This is a really hard error to get, so I'm not entirely sure what you did.
    Suggestion: Bring this code to the instructor. ", - ZeroDivisionError: "This tells you that you are trying to divide by 0. Typically this is because the value of the variable in the denominator of a division expression has the value 0.
    Suggestion: Are you sure you are dividing by the right variable? Are you sure that that variable has the value you expect - is it possible that you counted the number of elements in an empty list, for instance?", - IndentationError: "This error occurs when you have not indented your code properly. This is most likely to happen as part of an if, for, while or def statement.
    Suggestion: Check your if, def, for, and while statements to be sure the lines are properly indented beneath them (seriously, this happens ALL the time). Another source of this error comes from copying and pasting code where you have accidentally left some bits of code lying around that don't belong there anymore. Finally, a very sinister but unlikely possibility is that you have some tab characters in your code, which look identical to four spaces. Never, ever use tabs, and carefully check code from the internet to make sure it doesn't have tabs.", - EOFError: "If you are using input() or raw_input() commands, then this error happens when they don't get the right ending.
    Suggestion: It's hard to protect against users. However, if you're using input(), you might be able to use raw_input() instead to avoid this problem. ", - IOError: "This is a very easy error to get. The most common reason is that you were trying to open a file and it wasn't in the right place.
    Suggestion: Make sure that the file is in the right place - print out the file path, and then check that it's definitely on your computer at that location. If you need help doing file processing, you should probably check with an instructor.", - KeyError: "A dictionary has a bunch of keys that you can use to get data. This error is caused by you trying to refer to a key that does not exist.
    Suggestion: The most common reason you get this exception is that you have a typo in your dictionary access. Check your spelling. Also double check that the key definitely exists.", - MemoryError: "Somehow, you have run out of memory.
    Suggestion: Make sure you are filtering your dataset! Alternatively, bring your code to an instructor.", - OSError: "It's hard to say what an OSError is without deep checking. Many things can cause it.
    Suggestion: Bring your code to an instructor. ", - TimeoutError: "A TimeLimit error means that BlockPy wasn't able to process your program fast enough. Typically, this means that you're iterating through too many elements."} diff --git a/src/lib/pedal/sandbox/mocked.py b/src/lib/pedal/sandbox/mocked.py deleted file mode 100644 index da841dd62b..0000000000 --- a/src/lib/pedal/sandbox/mocked.py +++ /dev/null @@ -1,336 +0,0 @@ -""" -Mocked functions that can be used to prevent malicious or accidental `eval` -behavior. -""" -import re -import types - -from pedal.sandbox.exceptions import (SandboxNoMoreInputsException, - SandboxPreventModule) - - -def _disabled_compile(source, filename, mode, flags=0, dont_inherit=False): - """ - A version of the built-in `compile` method that fails with a runtime - error. - """ - raise RuntimeError("You are not allowed to call 'compile'.") - - -def _disabled_eval(object, globals=globals(), locals=None): - """ - A version of the built-in `eval` method that fails with a runtime - error. - """ - raise RuntimeError("You are not allowed to call 'eval'.") - - -# ------------------------------------------------------------- - - -def _disabled_exec(object, globals=globals(), locals=None): - """ - A version of the built-in `exec` method that fails with a runtime - error. - """ - raise RuntimeError("You are not allowed to call 'exec'.") - - -# ------------------------------------------------------------- - - -def _disabled_globals(): - """ - A version of the built-in `globals` method that fails with a runtime - error. - """ - raise RuntimeError("You are not allowed to call 'globals'.") - - -class FunctionNotAllowed(Exception): - pass - - -def disabled_builtin(name): - def _disabled_version(*args, **kwargs): - raise FunctionNotAllowed("You are not allowed to call '{}'.".format(name)) - - return _disabled_version - - -_OPEN_FORBIDDEN_NAMES = re.compile(r"(^[./])|(\.py$)") -_OPEN_FORBIDDEN_MODES = re.compile(r"[wa+]") - - -# TODO: Turn this into a function that lets us more elegantly specify valid and -# invalid filenames/paths - - -def _restricted_open(name, mode='r', buffering=-1): - if _OPEN_FORBIDDEN_NAMES.search(name): - raise RuntimeError("The filename you passed to 'open' is restricted.") - elif _OPEN_FORBIDDEN_MODES.search(mode): - raise RuntimeError("You are not allowed to 'open' files for writing.") - else: - return _original_builtins['open'](name, mode, buffering) - - -# TODO: Allow this to be flexible - - -def _restricted_import(name, globals=None, locals=None, fromlist=(), level=0): - if name == 'pedal' or name.startswith('pedal.'): - raise RuntimeError("You cannot import pedal!") - return _original_builtins['__import__'](name, globals, locals, fromlist, level) - - -try: - __builtins__ -except NameError: - _default_builtins = {'globals': globals, - 'locals': locals, - 'open': open, - 'input': input, - '__import__': __import__} -else: - if isinstance(__builtins__, types.ModuleType): - _default_builtins = __builtins__.__dict__ - else: - _default_builtins = __builtins__ - -_original_builtins = { - 'globals': _default_builtins['globals'], - 'locals': _default_builtins['locals'], - 'open': _default_builtins['open'], - 'input': _default_builtins['input'], - 'exec': _default_builtins.get('exec', _disabled_exec), - 'eval': _default_builtins.get('eval', _disabled_eval), - 'compile': _default_builtins.get('compile', _disabled_compile), - '__import__': _default_builtins['__import__'] -} - - -def make_inputs(input_list, repeat=None): - """ - Helper function for creating mock user input. - - Params: - input_list (list of str): The list of inputs to be returned - Returns: - function (str=>str): The mock input function that is returned, which - will return the next element of input_list each - time it is called. - """ - generator = iter(input_list) - - def mock_input(prompt=''): - print(prompt) - try: - return next(generator) - except StopIteration as SI: - if repeat is None: - # TODO: Make this a custom exception - raise SandboxNoMoreInputsException("User had no more input to give.") - else: - return repeat - - return mock_input - - -_sys_modules = {} - - -def _override_builtins(namespace, custom_builtins): - """ - Add the custom builtins to the `namespace` (and the original `__builtins__`) - suitable for `exec`. - """ - # Obtain the dictionary of built-in methods, which might not exist in - # some python versions (e.g., Skulpt) - - # Create a shallow copy of the dictionary of built-in methods. Then, - # we'll take specific ones that are unsafe and replace them. - namespace["__builtins__"] = _default_builtins.copy() - for name, function in custom_builtins.items(): - namespace["__builtins__"][name] = function - - -def create_module(module_name): - submodule_names = module_name.split(".") - modules = {} - root = types.ModuleType(submodule_names[0]) - modules[submodule_names[0]] = root - reconstructed_path = submodule_names[0] - for submodule_name in submodule_names[1:]: - reconstructed_path += "." + submodule_name - new_submodule = types.ModuleType(reconstructed_path) - setattr(root, submodule_name, new_submodule) - modules[reconstructed_path] = new_submodule - return root, modules - - -class MockModule: - def _generate_patches(self): - return {k: v for k, v in vars(self).items() - if not k.startswith('_')} - - def _add_to_module(self, module): - for name, value in self._generate_patches().items(): - setattr(module, name, value) - - -class BlockedModule(MockModule): - MODULE_NAME = "this module" - - def _generate_patches(self): - return {'__getattr__': self.prevent_module} - - def prevent_module(self, **kwargs): - raise SandboxPreventModule("You cannot import {module_name} from student code.".format( - module_name=self.MODULE_NAME - )) - - -class MockPedal(BlockedModule): - MODULE_NAME = "pedal" - - -class MockTurtle(MockModule): - """ - Mock Turtle Module that can be used to trace turtle calls. - - Attributes: - calls (list of dict): The traced list of calls - # TODO: it'd be awesome to have a way to construct a representation - # of the drawing result that we could autograde! - """ - - def __init__(self): - super().__init__() - - def _reset_turtles(self): - self.calls = [] - - def __repr__(self): - return repr(self.plots) - - ''' - def _generate_patches(self): - def dummy(**kwargs): - pass - - return dict(Canvas=dummy, Pen=dummy, RawPen=dummy, RawTurtle=dummy, Screen=dummy, ScrolledCanvas=dummy, - Shape=dummy, TK=dummy, TNavigator=dummy, TPen=dummy, Tbuffer=dummy, Terminator=dummy, - Turtle=dummy, TurtleGraphicsError=dummy, TurtleScreen=dummy, TurtleScreenBase=dummy, Vec2D=dummy, - addshape=dummy, back=dummy, backward=dummy, begin_fill=dummy, begin_poly=dummy, bgcolor=dummy, - bgpic=dummy, bk=dummy, bye=dummy, circle=dummy, clear=dummy, clearscreen=dummy, clearstamp=dummy, - clearstamps=dummy, clone=dummy, color=dummy, colormode=dummy, config_dict=dummy, deepcopy=dummy, - degrees=dummy, delay=dummy, distance=dummy, done=dummy, dot=dummy, down=dummy, end_fill=dummy, - end_poly=dummy, exitonclick=dummy, fd=dummy, fillcolor=dummy, filling=dummy, forward=dummy, - get_poly=dummy, get_shapepoly=dummy, getcanvas=dummy, getmethparlist=dummy, getpen=dummy, - getscreen=dummy, getshapes=dummy, getturtle=dummy, goto=dummy, heading=dummy, hideturtle=dummy, - home=dummy, ht=dummy, inspect=dummy, isdown=dummy, isfile=dummy, isvisible=dummy, join=dummy, - left=dummy, listen=dummy, lt=dummy, mainloop=dummy, math=dummy, mode=dummy, numinput=dummy, - onclick=dummy, ondrag=dummy, onkey=dummy, onkeypress=dummy, onkeyrelease=dummy, onrelease=dummy, - onscreenclick=dummy, ontimer=dummy, pd=dummy, pen=dummy, pencolor=dummy, pendown=dummy, - pensize=dummy, penup=dummy, pos=dummy, position=dummy, pu=dummy, radians=dummy, - read_docstrings=dummy, readconfig=dummy, register_shape=dummy, reset=dummy, resetscreen=dummy, - resizemode=dummy, right=dummy, rt=dummy, screensize=dummy, seth=dummy, setheading=dummy, - setpos=dummy, setposition=dummy, settiltangle=dummy, setundobuffer=dummy, setup=dummy, - setworldcoordinates=dummy, setx=dummy, sety=dummy, shape=dummy, shapesize=dummy, - shapetransform=dummy, shearfactor=dummy, showturtle=dummy, simpledialog=dummy, speed=dummy, - split=dummy, st=dummy, stamp=dummy, sys=dummy, textinput=dummy, tilt=dummy, tiltangle=dummy, - time=dummy, title=dummy, towards=dummy, tracer=dummy, turtles=dummy, turtlesize=dummy, types=dummy, - undo=dummy, undobufferentries=dummy, up=dummy, update=dummy, width=dummy, window_height=dummy, - window_width=dummy, write=dummy, write_docstringdict=dummy, xcor=dummy, ycor=dummy) - ''' - - -class MockPlt(MockModule): - """ - Mock MatPlotLib library that can be used to capture plot data. - - Attributes: - plots (list of dict): The internal list of plot dictionaries. - """ - - def __init__(self): - super().__init__() - self._reset_plots() - - def show(self, **kwargs): - self.plots.append(self.active_plot) - self._reset_plot() - - def unshown_plots(self): - return self.active_plot['data'] - - def __repr__(self): - return repr(self.plots) - - def __str__(self): - return str(self.plots) - - def _reset_plots(self): - self.plots = [] - self._reset_plot() - - def _reset_plot(self): - self.active_plot = {'data': [], - 'xlabel': None, 'ylabel': None, - 'title': None, 'legend': False} - - def hist(self, data, **kwargs): - label = kwargs.get('label', None) - self.active_plot['data'].append({'type': 'hist', 'values': data, - 'label': label}) - - def plot(self, xs, ys=None, **kwargs): - label = kwargs.get('label', None) - if ys is None: - self.active_plot['data'].append({'type': 'line', - 'x': list(range(len(xs))), - 'y': xs, 'label': label}) - else: - self.active_plot['data'].append({'type': 'line', 'x': xs, - 'y': ys, 'label': label}) - - def scatter(self, xs, ys, **kwargs): - label = kwargs.get('label', None) - self.active_plot['data'].append({'type': 'scatter', 'x': xs, - 'y': ys, 'label': label}) - - def xlabel(self, label, **kwargs): - self.active_plot['xlabel'] = label - - def title(self, label, **kwargs): - self.active_plot['title'] = label - - def suptitle(self, label, **kwargs): - self.title(label, **kwargs) - - def ylabel(self, label, **kwargs): - self.active_plot['ylabel'] = label - - def legend(self, **kwargs): - self.active_plot['legend'] = True - - def _generate_patches(self): - def dummy(**kwargs): - pass - - return dict(hist=self.hist, plot=self.plot, - scatter=self.scatter, show=self.show, - xlabel=self.xlabel, ylabel=self.ylabel, - title=self.title, legend=self.legend, - xticks=dummy, yticks=dummy, - autoscale=dummy, axhline=dummy, - axhspan=dummy, axvline=dummy, - axvspan=dummy, clf=dummy, - cla=dummy, close=dummy, - figlegend=dummy, figimage=dummy, - suptitle=self.suptitle, text=dummy, - tick_params=dummy, ticklabel_format=dummy, - tight_layout=dummy, xkcd=dummy, - xlim=dummy, ylim=dummy, - xscale=dummy, yscale=dummy) diff --git a/src/lib/pedal/sandbox/result.py b/src/lib/pedal/sandbox/result.py deleted file mode 100644 index e7d269d9f3..0000000000 --- a/src/lib/pedal/sandbox/result.py +++ /dev/null @@ -1,369 +0,0 @@ -class SandboxResult: - """ - Proxy class for wrapping results from executing student code. Attempts - to perfectly emulate the underlying data value, so that users will never - realize they have a proxy. The advantage is that special information is - available in the corresponding Sandbox about this result that can give - more context. - - Attributes: - value (any): The actual data stored in this class that we are proxying. - If the underlying proxy object has a field called `value`, then - you can use either `_actual_value` to access the proxied object. - _actual_call_id (int): The call that was used to generate this result. - _actual_sandbox (Sandbox): The sandbox that was used to generate this - result. If None, then the sandbox was lost. - - """ - ASSIGNABLE_ATTRS = ['value', '_actual_call_id', '_actual_sandbox', - '_clone_this_result'] - - def __init__(self, value, call_id=None, sandbox=None): - """ - Args: - value (any): Literally any type of data. - call_id (int): The unique call ID that generated this result. If - None, then the SandboxResult was generated by manipulating an earlier - result. - TODO: We could actually remember the operations applied to this - instance and use them to reconstruct the transformations... - sandbox (Sandbox): The sandbox that was used to generate this - result. If None, then the sandbox was lost. - """ - self.value = value - self._actual_call_id = call_id - self._actual_sandbox = sandbox - - def __getattribute__(self, name): - """ - Get the attribute with the given `name`. This allows us to pass - most attributes along to the underlying `value`, while still - maintaining access to the proxy's attributes. - """ - v = object.__getattribute__(self, "value") - if name == "__class__": - return v.__class__ - elif name == "__actual_class__": - return object.__getattribute__(self, "__class__") - elif name == "_actual_value": - return v - elif name in SandboxResult.ASSIGNABLE_ATTRS: - return object.__getattribute__(self, name) - elif name == "value" and not hasattr(v, "value"): - return v - else: - return SandboxResult(object.__getattribute__(v, name), - object.__getattribute__(self, "_actual_call_id"), - object.__getattribute__(self, "_actual_sandbox")) - - def __setattr__(self, name, value): - if name in SandboxResult.ASSIGNABLE_ATTRS: - object.__setattr__(self, name, value) - else: - setattr(self.value, name, value) - - def __delattr__(self, name): - if name in SandboxResult.ASSIGNABLE_ATTRS: - object.__delattr__(self, name, value) - else: - delattr(self.value, name, value) - - def _clone_this_result(self, new_value): - """ - Create a new SandboxResult based on this current one. Copies over the - `call_id` and `sandbox`. - - Args: - new_value (any): The new value to be proxying. - Returns: - SandboxResult - """ - return SandboxResult(new_value, - call_id=self._actual_call_id, - sandbox=self._actual_sandbox) - - def __repr__(self): - """ - Returns the representation of the proxied object. - - Returns: - str: The `repr` of the proxied object. - """ - return repr(self.value) - - def __str__(self): - """ - Returns the string representation of the proxied object. - - Returns: - str: The `str` of the proxied object. - """ - return str(self.value) - - def __bytes__(self): - return bytes(self.value) - - def __format__(self, format_spec): - return format(self.value, format_spec) - - def __call__(self, *args): - """ - Returns the result of calling the proxied object with the args. - - Returns: - SandboxResult: A proxy of the Sandbox object. - """ - return self._clone_this_result(self.value(*args)) - - def __hash__(self): - return hash(self.value) - - def __bool__(self): - return bool(self.value) - - def __dir__(self): - return dir(self.value) - - def __instancecheck__(self, instance): - return isinstance(self.value, instance) - - def __subclasscheck__(self, subclass): - return issubclass(self.value, subclass) - - def __len__(self): - ''' - Fun fact: cpython DEMANDS that __len__ return an integer. Not something - that looks like an integer, but a true, honest-to-god integer that - can fit into a slot. - https://stackoverflow.com/questions/42521449/how-does-python-ensure-the-return-value-of-len-is-an-integer-when-len-is-cal - ''' - return len(self.value) - - def __getitem__(self, key): - return self._clone_this_result(self.value[key]) - - def __setitem__(self, key, value): - self.value[key] = value - - def __delitem__(self, key): - del self.value[key] - - def __missing__(self, key): - return self.value.__missing__(key) - - def __iter__(self): - return iter(self.value) - - def __reversed__(self): - return reversed(self.value) - - def __contains__(self, item): - return self.value.__contains__(item) - - def __eq__(self, other): - """ - Test if the proxied object is equal to the given `other`. - - Args: - other (any): The other object. - - Returns: - bool or any: Returns whatever the proxy object's __eq__ returns. - """ - if isinstance(other, SandboxResult): - return self.value == other.value - return self.value == other - - def __lt__(self, other): - if isinstance(other, SandboxResult): - return self.value < other.value - return self.value < other - - def __le__(self, other): - if isinstance(other, SandboxResult): - return self.value <= other.value - return self.value <= other - - def __gt__(self, other): - if isinstance(other, SandboxResult): - return self.value > other.value - return self.value > other - - def __ge__(self, other): - if isinstance(other, SandboxResult): - return self.value >= other.value - return self.value >= other - - def __ne__(self, other): - if isinstance(other, SandboxResult): - return self.value != other.value - return self.value != other - - ## Numeric Operations - - def __add__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value + other.value) - return self._clone_this_result(self.value + other) - - def __sub__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value - other.value) - return self._clone_this_result(self.value - other) - - def __mul__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value * other.value) - return self._clone_this_result(self.value * other) - - def __matmul__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value.__matmul__(other.value)) - return self._clone_this_result(self.value.__matmul__(other)) - - def __truediv__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value.__truediv__(other.value)) - return self._clone_this_result(self.value.__truediv__(other)) - - def __floordiv__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value.__floordiv__(other.value)) - return self._clone_this_result(self.value.__floordiv__(other)) - - def __mod__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value.__mod__(other.value)) - return self._clone_this_result(self.value.__mod__(other)) - - def __divmod__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value.__divmod__(other.value)) - return self._clone_this_result(self.value.__divmod__(other)) - - def __pow__(self, other, *modulo): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value.__pow__(other.value, *modulo)) - return self._clone_this_result(self.value.__pow__(other, *modulo)) - - def __lshift__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value.__lshift__(other.value)) - return self._clone_this_result(self.value.__lshift__(other)) - - def __rshift__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value.__rshift__(other.value)) - return self._clone_this_result(self.value.__rshift__(other)) - - def __and__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value.__and__(other.value)) - return self._clone_this_result(self.value.__and__(other)) - - def __xor__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value.__xor__(other.value)) - return self._clone_this_result(self.value.__xor__(other)) - - def __or__(self, other): - if isinstance(other, SandboxResult): - return self._clone_this_result(self.value.__or__(other.value)) - return self._clone_this_result(self.value.__or__(other)) - - def __radd__(self, other): - if isinstance(self.value, str): - return self._clone_this_result(self.value.__add__(other)) - return self._clone_this_result(self.value.__radd__(other)) - - def __rsub__(self, other): - return self._clone_this_result(self.value.__rsub__(other)) - - def __rmul__(self, other): - return self._clone_this_result(self.value.__rmul__(other)) - - def __rmatmul__(self, other): - return self._clone_this_result(self.value.__rmatmul__(other)) - - def __rtruediv__(self, other): - return self._clone_this_result(self.value.__rtruediv__(other)) - - def __rfloordiv__(self, other): - return self._clone_this_result(self.value.__rfloordiv__(other)) - - def __rmod__(self, other): - return self._clone_this_result(self.value.__rmod__(other)) - - def __rdivmod__(self, other): - return self._clone_this_result(self.value.__rdivmod__(other)) - - def __rpow__(self, other): - return self._clone_this_result(self.value.__rpow__(other)) - - def __rlshift__(self, other): - return self._clone_this_result(self.value.__rlshift__(other)) - - def __rand__(self, other): - return self._clone_this_result(self.value.__rand__(other)) - - def __rxor__(self, other): - return self._clone_this_result(self.value.__rxor__(other)) - - def __ror__(self, other): - return self._clone_this_result(self.value.__ror__(other)) - - ## TODO: __iadd__ and other in-place assignment operators? - - def __neg__(self): - return self._clone_this_result(self.value.__neg__()) - - def __pos__(self): - return self._clone_this_result(self.value.__pos__()) - - def __abs__(self): - return self._clone_this_result(self.value.__abs__()) - - def __invert__(self): - return self._clone_this_result(self.value.__invert__()) - - def __complex__(self): - return self._clone_this_result(self.value.__complex__()) - - def __int__(self): - return self._clone_this_result(self.value.__int__()) - - def __float__(self): - return self._clone_this_result(self.value.__float__()) - - def __round__(self, *ndigits): - return self._clone_this_result(self.value.__round__(*ndigits)) - - def __trunc__(self): - return self._clone_this_result(self.value.__trunc__()) - - def __floor__(self): - return self._clone_this_result(self.value.__floor__()) - - def __ceil__(self): - return self._clone_this_result(self.value.__ceil__()) - - def __enter__(self): - return self.value.__enter__() - - def __exit__(self, exc_type, exc_value, traceback): - return self.value.__exit__(exc_type, exc_value, traceback) - - def __await__(self): - return self.value.__await__() - - def __aiter__(self): - return self.value.__aiter__() - - def __anext__(self): - return self.value.__anext__() - - def __aenter__(self): - return self.value.__aenter__() - - def __aexit__(self, exc_type, exc_value, traceback): - return self.value.__aexit__(exc_type, exc_value, traceback) diff --git a/src/lib/pedal/sandbox/sandbox.py b/src/lib/pedal/sandbox/sandbox.py deleted file mode 100644 index 156890bbe3..0000000000 --- a/src/lib/pedal/sandbox/sandbox.py +++ /dev/null @@ -1,726 +0,0 @@ -from pprint import pprint -import ast -import re -import sys -import io -import os -import string -from unittest.mock import patch - -from pedal.report import MAIN_REPORT -from pedal.sandbox import mocked -from pedal.sandbox.exceptions import (SandboxTraceback, SandboxHasNoFunction, - SandboxStudentCodeException, - SandboxHasNoVariable, _add_context_to_error) -from pedal.sandbox.timeout import timeout -from pedal.sandbox.messages import EXTENDED_ERROR_EXPLANATION -from pedal.sandbox.result import SandboxResult -from pedal.sandbox.tracer import (SandboxCallTracer, SandboxCoverageTracer, - SandboxBasicTracer) - - -def _dict_extends(d1, d2): - """ - Helper function to create a new dictionary with the contents of the two - given dictionaries. Does not modify either dictionary, and the values are - copied shallowly. If there are repeats, the second dictionary wins ties. - - The function is written to ensure Skulpt compatibility. - - Args: - d1 (dict): The first dictionary - d2 (dict): The second dictionary - Returns: - dict: The new dictionary - """ - d3 = {} - for key, value in d1.items(): - d3[key] = value - for key, value in d2.items(): - d3[key] = value - return d3 - - -class SandboxVariable: - def __init__(self, name, value): - self.name = name - self.value = value - - -class DataSandbox: - """ - Simplistic Mixin class that contains the functions for accessing a - self-contained student data namespace. - """ - - def __init__(self): - super().__init__() - self.data = {} - - def get_names_by_type(self, type, exclude_builtins=True): - result = [] - for name, value in self.data.items(): - if isinstance(value, type): - if exclude_builtins and name.startswith('__'): - continue - result.append(name) - return result - - def get_values_by_type(self, type, exclude_builtins=True): - names = self.get_names_by_type(type, exclude_builtins) - return [self.data[name] for name in names] - - def get_variables_by_type(self, type, exclude_builtins=True): - names = self.get_names_by_type(type, exclude_builtins) - return [(name, self.data[name]) for name in names] - - @property - def functions(self): - """ - Retrieve a list of all the callable names in the students' namespace. - In other words, get a list of all the functions the student defined. - - Returns: - list of callables - """ - return {k: v for k, v in self.data.items() if callable(v)} - - @property - def var(self): - return {k: SandboxVariable(k, v) for k, v in self.data.items()} - - def __repr__(self): - return "" - - -class Sandbox(DataSandbox): - """ - - The Sandbox is a container that can safely execute student code and store - the result. - - Attributes: - data: The namespace produced by the students' code. This is basically - a dictionary mapping valid python names to their values. - raw_output (str): The exact literal results of all the `print` calls - made so far, including the "\n" characters. - output (list of str): The current lines of output, broken up by - distinct print calls (not "\n" characters). Note that this will - not have any "\n" characters unless you explicitly printed them. - output_contexts (dict[str:list[str]]): The output for each call context. - call_id (int): The current call_id of the most recent call. Is - initially 0, indicating the original sandbox creation. - modules: A dictionary of the mocked modules (accessible by their - imported names). - context: A list of strings representing the code previously run through - this sandbox via .call. - contextualize (bool): Whether or not to contextualize stack frames. - """ - - CONTEXT_MESSAGE = ( - "\n\nThe error above occurred when I ran:
    \n
    {context}
    " - ) - FILE_CONTEXT_MESSAGE = ( - "\n\nThe error above occurred when I ran your file: {filename}" - ) - INPUT_CONTEXT_MESSAGE = ( - "And entered the inputs:\n```\n{inputs}\n```" - ) - TRACER_STYLES = { - 'coverage': SandboxCoverageTracer, - 'calls': SandboxCallTracer, - 'none': SandboxBasicTracer, - } - - def __init__(self, initial_data=None, - initial_raw_output=None, - initial_exception=None, - modules=None, full_traceback=False, - tracer_style='none', - threaded=False, report=None, - context=None, result_proxy=SandboxResult, - instructor_filename="instructor_tests.py", - allowed_functions=None): - """ - Args: - initial_data (dict[str:Any]): An initial namespace to provide when - executing the students' code. The keys must be strings and - should be valid Python names. Defaults to None, which will be - an empty namespace. - initial_exception (Exception): An initial exception to load into - the Sandbox. Usually you will let the students' code generate - its own exceptions, but if you're constructing a sandbox you - might need to specify one. Defaults to None. - modules: A dictionary of strings (valid python package names) that - map to either the value True (if we provide a default - implementation) or a user-created MockedModule. By default, - we mock out the following modules: - * matplotlib - * pedal - context (False, None, or list[str]): How to contextualize calls by - default in this Sandbox. False means no contextualization. - None (default) means contextualize automatically. If you give - a list[str], then it assumes you want to contextualize - automatically but starting off with the given strings. - initial_raw_output (str): The initial printed output for the - sandbox. Usually defaults to None to indicate a blank printed - area. - instructor_filename (str): The filename to display in tracebacks, - when executing student code in instructor tests. Although you - can specify something else, defaults to "instructor_tests.py". - """ - super().__init__() - if initial_data is None: - initial_data = {} - self.data = initial_data - - # Context - self.call_id = 0 - self.target_contexts = {self.call_id: []} - self.call_contexts = {self.call_id: []} - self.input_contexts = {self.call_id: []} - self.context = context - self.keep_context = False - # Update outputs - self.set_output(initial_raw_output) - # filename - self.instructor_filename = instructor_filename - # Temporary data - self._temporaries = set() - self._backups = {} - # Exception - self.exception = initial_exception - self.exception_position = None - self.exception_formatted = None - self.report_exceptions_mode = False - self.raise_exceptions_mode = False - # Input - self.set_input(None) - self._input_tracker = self._track_inputs() - # Modules - if modules is None: - modules = {'matplotlib': True, - 'pedal': mocked.MockPedal() - } - self.mocked_modules = {} - self.modules = {} - self.add_mocks(modules) - self.mocked_functions = { - 'compile': mocked._disabled_compile, - 'eval': mocked._disabled_eval, - 'exec': mocked._disabled_exec, - 'globals': mocked._disabled_globals, - 'open': mocked._restricted_open, - '__import__': mocked._restricted_import, - } - if allowed_functions is not None: - for function_name in allowed_functions: - if function_name in self.mocked_functions: - del self.mocked_functions[function_name] - # Patching - self._current_patches = [] - # Settings - self.full_traceback = full_traceback - self.MAXIMUM_VALUE_LENGTH = 120 - # Tracer Styles - self.tracer_style = tracer_style - # Proxying results - self.result_proxy = result_proxy - # report - if report is None: - report = MAIN_REPORT - self.report = report - # Threading - self.threaded = threaded - self.allowed_time = 3 - - def _set_tracer_style(self, tracer_style): - self._tracer_style = tracer_style.lower() - self.trace = self.TRACER_STYLES[tracer_style.lower()]() - - def _get_tracer_style(self): - return self._tracer_style - - tracer_style = property(_get_tracer_style, _set_tracer_style) - - def add_mocks(self, modules): - """ - :param modules: Keyword listing of modules and their contents - (MockedModules) or True (if its one that we have a - default implementation for). - :type modules: dict - """ - for module_name, module_data in modules.items(): - self._add_mock(module_name, module_data) - - def _add_mock(self, module_name, module_data): - # MatPlotLib's PyPlot - if module_name == 'matplotlib': - matplotlib, modules = mocked.create_module('matplotlib.pyplot') - self.mocked_modules.update(modules) - if module_data is True: - mock_plt = mocked.MockPlt() - mock_plt._add_to_module(matplotlib.pyplot) - self.modules['matplotlib.pyplot'] = mock_plt - else: - module_data._add_to_module(matplotlib.pyplot) - else: - root, modules = mocked.create_module(module_name) - self.mocked_modules.update(modules) - self.modules[module_name] = module_data - module_data._add_to_module(root) - - def set_output(self, raw_output): - """ - Change the current printed output for the sandbox to the given value. - If None is given, then clears all the given output (empty list for - `output` and empty string for `raw_output`). - - Args: - raw_output (str): The new raw_output for the sandbox. To compute - the `output` attribute, the system splits and rstrips at - newlines. - """ - if raw_output is None: - self.raw_output = "" - self.output = [] - self.output_contexts = {self.call_id: list(self.output)} - else: - self.raw_output = raw_output - lines = raw_output.rstrip().split("\n") - self.output = [line.rstrip() for line in lines] - self.output_contexts[self.call_id] = list(self.output) - - def append_output(self, raw_output): - """ - Adds the string of `raw_output` to the current `raw_output` attribute. - The added string will be split on newlines and rstripped to append - to the `output` attribute. - - Args: - raw_output (str): The new raw_output for the sandbox. To compute - the `output` attribute, the system splits and rstrips at - newlines. - """ - self.raw_output += raw_output - lines = raw_output.rstrip().split("\n") - lines = [line.rstrip() for line in lines] - if self.raw_output: - self.output.extend(lines) - self.output_contexts[self.call_id].extend(lines) - - def set_input(self, inputs, clear=True): - """ - Queues the given value as the next arguments to the `input` function. - """ - if inputs is None: - self.inputs = [] - if clear: - self.inputs.clear() - if isinstance(inputs, str): - self.inputs.append(inputs) - elif isinstance(inputs, (list, tuple)): - self.inputs.extend(inputs) - elif inputs is not None: - # TODO: intelligently handle custom generator - self.inputs = inputs - - def _track_inputs(self): - """ - Wraps an input function with a tracker. - """ - - def _input_tracker(*args, **kwargs): - if args: - prompt = args[0] - else: - prompt = "" - print(prompt) - if self.inputs: - value_entered = self.inputs.pop(0) - else: - # TODO: Make this smarter, more elegant in choosing IF we should repeat 0 - value_entered = '0' - self.input_contexts[self.call_id].append(value_entered) - return value_entered - - return _input_tracker - - def _purge_temporaries(self): - """ - Delete any variables in the namespace that have been made as - temporaries. This happens automatically after you execute code. - """ - for key in self._temporaries: - if key in self._backups: - self.data[key] = self.backups[key] - else: - del self.data[key] - self._temporaries = set() - - def _is_long_value(self, value): - return len(repr(value)) > 25 - - def _make_temporary(self, category, name, value, context): - """ - Create a temporary variable in the namespace for the given - category/name. This is used to load arguments into the namespace to - be used in function calls. Temporaries are only created if the value's - repr length is too long, as defined by _is_long_value. - - Args: - category (str): A categorical division for the temporary variable - that can help keep the namespace distinctive - there are a - few different kinds of categories (e.g., for regular positional - args, star args, kwargs). - name (str): A distinctive ID for this variable. The final variable - name will be "_temporary__". - value: The value for this argument. - Returns: - str: The new name for the temporary variable. - """ - if isinstance(value, SandboxVariable): - return value.name - if not self._is_long_value(value): - return repr(value) - key = '_temporary_{}_{}'.format(category, name) - if key in self.data: - self._backups[key] = self.data[key] - self._temporaries.add(key) - self.data[key] = value - if context is None: - self.call_contexts[self.call_id].append("{} = {}".format(key, value)) - return key - - def run_file(self, filename, as_filename=None, modules=None, inputs=None, - threaded=None, context=None, report_exceptions=None, - raise_exceptions=None): - """ - Load the given filename and execute it within the current namespace. - - Args: - context (False, None, or list[str]): The context to give any - exceptions. If None, then the recorded context will be used. If - a string, tracebacks will be shown with the given context. If - False, no context will be given. - """ - if as_filename is None: - as_filename = filename - with open(filename, 'r') as code_file: - code = code_file.read() + '\n' - self.run(code, as_filename, modules, inputs, threaded, - context, report_exceptions, raise_exceptions) - - def list(self, *args): - pass - - def call(self, function, *args, **kwargs): - """ - Args: - function (str): The name of the function to call that was defined - by the user. - as_filename (str): The filename to use when calling this function. - Defaults to the instructor filename, since you are calling - code on the student's behalf. - target (str): The new variable in the namespace to assign to. By - default this will be "_". If you use None, then no variable - will be assigned to. Note that this could overwrite a variable - in the user namespace. - TODO: Add a feature to prevent user namespace overwriting. - input (list of str): The strings to send in to calls to input. - You can also pass in a generator to construct strings - dynamically. - threaded (bool): Whether or not the function execution should be - executed in a separate thread. Defaults to True. This prevents - timeouts from occuring in the students' code (a TimeOutError - will be thrown after 3 seconds). - context (False, None, or list[str]): The context to give any - exceptions. If None, then the recorded context will be used. If - a string, tracebacks will be shown with the given context. If - False, no context will be given. - keep_context (bool): Whether or not to stay in the current context, - or to start a new one. Defaults to False. - Returns: - If the call was successful, returns the result of executing the - code. Otherwise, it will return an Exception relevant to the - failure (might be a SandboxException, might be a user-space - exception). - """ - # Confirm that the function_name exists - if function not in self.functions: - if function not in self.data: - self.exception = SandboxHasNoVariable( - "The function {function} does not exist.".format(function=function) - ) - else: - self.exception = SandboxHasNoFunction( - "The variable {function} is not a function.".format(function=function) - ) - return self.exception - # Parse kwargs for any special arguments. - as_filename = kwargs.pop('as_filename', self.instructor_filename) - target = kwargs.pop('target', '_') - modules = kwargs.pop('modules', {}) - inputs = kwargs.pop('inputs', None) - threaded = kwargs.pop('threaded', self.threaded) - context = kwargs.pop('context', self.context) - keep_context = kwargs.pop('keep_context', self.keep_context) - report_exceptions = kwargs.pop('report_exceptions', self.report_exceptions_mode) - raise_exceptions = kwargs.pop('raise_exceptions', self.raise_exceptions_mode) - # Create the actual arguments and call - if not keep_context or not self.call_id: - self.call_id += 1 - self.output_contexts[self.call_id] = [] - self.call_contexts[self.call_id] = [] - self.input_contexts[self.call_id] = [] - # Always update the target context to be most recent - self.target_contexts[self.call_id] = target - actual, student = self._construct_call(function, args, kwargs, target, - context) - if context is None: - context = student - # if context is None: - # self.call_contexts[self.call_id].append(student_call) - # if context is not False: - # self.call_contexts[self.call_id] = context - self.run(actual, as_filename=as_filename, modules=modules, - inputs=inputs, threaded=threaded, - context=context, keep_context=keep_context, - report_exceptions=report_exceptions, - raise_exceptions=raise_exceptions) - self._purge_temporaries() - if self.exception is None: - self._ = self.data[target] - if self.result_proxy is not None: - self._ = self.result_proxy(self._, call_id=self.call_id, - sandbox=self) - return self._ - else: - # TODO: Might need to wrap this in case the student was supposed - # to return an exception - weird circumstance though - return self.exception - - def make_safe_variable(self, name): - """ - Tries to construct a safe variable name in the current namespace, based - off the given one. This is accomplished by appending a "_" and a number - of increasing value until no comparable name exists in the namespace. - This is particularly useful when you want to create a variable name to - assign to, but you are concerned that the user might have a variable - with that name already, which their code relies on. - - Args: - name (str): A desired target name. - Returns: - str: A safe target name, based off the given one. - """ - current_addition = "" - attempt_index = 2 - while name + current_addition in self.data: - current_addition = "_{}".format(attempt_index) - attempt_index += 1 - return name + current_addition - - def _construct_call(self, function, args, kwargs, target, context): - str_args = [self._make_temporary('arg', index, value, context) - for index, value in enumerate(args)] - str_kwargs = ["{}={}".format(key, - self._make_temporary('kwarg', key, value, context)) - for key, value in kwargs.items()] - arguments = ", ".join(str_args + str_kwargs) - call = "{}({})".format(function, arguments) - if target is None: - actual = call - else: - actual = "{} = {}".format(target, call) - student_call = call if target is "_" else actual - return actual, student_call - - def _start_patches(self, *patches): - self._current_patches.append(patches) - for patch in patches: - patch.start() - - def _stop_patches(self): - if not self._current_patches: - return - patches = self._current_patches.pop() - for patch in patches: - patch.stop() - - def _capture_exception(self, exception, exc_info, report_exceptions, - raise_exceptions, context, keep_context, - as_filename="", code=""): - self.exception = exception - if context is not False: - if context is None or keep_context: - contexts = self.call_contexts[self.call_id] - if context is not None: - contexts.append(context) - context = '\n'.join(contexts) # [1:]) - if context.strip(): - context = self.CONTEXT_MESSAGE.format(context=context) - inputs = self.input_contexts[self.call_id] - if inputs is not None and inputs: - inputs = "\n".join(inputs) - context += "\n" + self.INPUT_CONTEXT_MESSAGE.format(inputs=inputs) - else: - context = self.FILE_CONTEXT_MESSAGE.format(filename=self.report['source']['filename']) - self.exception = _add_context_to_error(self.exception, context) - line_offset = self.report['source'].get('line_offset', 0) - student_filename = self.report['source'].get('filename', as_filename) - if 'lines' in self.report['source']: - lines = self.report['source']['lines'] - else: - lines = code.split("\n") - traceback = SandboxTraceback(self.exception, exc_info, - self.full_traceback, - self.instructor_filename, - line_offset, student_filename, - lines) - self.exception_position = {'line': traceback.line_number} - self.exception_formatted = traceback.format_exception() - self.exception_name = str(self.exception.__class__)[8:-2] - # Do we add the exception to the report? - if report_exceptions is False: - return True - if report_exceptions is None and not self.report_exceptions_mode: - return True - self.report.attach(self.exception_name, - group=self.report.group, - category='Runtime', tool='Sandbox', - mistake={'message': self.exception_formatted, - 'error': self.exception}) - if raise_exceptions is True: - raise SandboxStudentCodeException(self.exception) - return False - - def run(self, code, as_filename=None, modules=None, inputs=None, - threaded=None, report_exceptions=True, raise_exceptions=False, - context=False, keep_context=False): - """ - Execute the given string of code in this sandbox. - - Args: - code (str): The string of code to be executed. - as_filename (str): The filename to use when executing the code - - this is cosmetic, technically speaking, it has no relation - to anything on disk. It will be present in tracebacks. - Defaults to Source's filename. - modules (dict[str:Module]): Modules to mock. - inputs (list[str]): The inputs to give from STDIN, as a list of - strings. You can also give a function that emulates the - input function; e.g., consuming a prompt (str) and producing - strings. This could be used to make a more interactive input - system. - context (str): The context to give any exceptions. - If None, then the recorded context will be used. If a string, - tracebacks will be shown with the given context. If False, - no context will be given (the default). - threaded (bool): whether or not to run this code in a separate - thread. Defaults to :attribute:`Sandbox.threaded`. - report_exceptions (bool): Whether or not to capture exceptions. - """ - # Handle any threading if necessary - if threaded is None: - threaded = self.threaded - if threaded: - try: - return timeout(self.allowed_time, self.run, code, as_filename, - modules, inputs, False, - report_exceptions, raise_exceptions, - context, keep_context) - except TimeoutError as timeout_exception: - self._stop_patches() - self._capture_exception(timeout_exception, sys.exc_info(), - report_exceptions, raise_exceptions, - context, keep_context, as_filename, - code) - return self - - if as_filename is None: - as_filename = os.path.basename(self.report['source']['filename']) - # todo: earlier version of inputs being made? - if inputs is not None: - self.set_input(inputs) - # Override builtins and mock stuff out - mocked_functions = self.mocked_functions.copy() - mocked_functions['input'] = self._input_tracker - mocked_functions['raw_input'] = self._input_tracker - mocked_functions['sys'] = sys - mocked_functions['os'] = os - mocked._override_builtins(self.data, mocked_functions) - - self.exception = None - self.exception_position = None - self.exception_formatted = None - - # Patch in dangerous built-ins - x = sys.stdout - capture_stdout = io.StringIO() - self._start_patches( - patch.dict('sys.modules', self.mocked_modules), - patch('sys.stdout', capture_stdout), - patch('time.sleep', return_value=None), - ) - # TODO: Hack, add more flexibile way to specify unusable modules - for module in list(sys.modules.keys()): - if module.startswith('pedal.'): - del sys.modules[module] - try: - compiled_code = compile(code, as_filename, 'exec') - with self.trace._as_filename(as_filename, code): - exec(compiled_code, self.data) - except Exception as user_exception: - self._stop_patches() - info = sys.exc_info() - self._capture_exception(user_exception, info, - report_exceptions, raise_exceptions, - context, keep_context, as_filename, - code) - else: - self._stop_patches() - finally: - self.append_output(capture_stdout.getvalue()) - if context is None: - self.call_contexts[self.call_id].append(code) - elif isinstance(context, str): - self.call_contexts[self.call_id].append(context) - elif context is not False: - self.call_contexts[self.call_id] = context - return self - - -def run(initial_data=None, initial_raw_output=None, initial_exception=None, - allowed_functions=None, - modules=None, inputs=None, report_exceptions=True, raise_exceptions=False, - context=None, - full_traceback=False, tracer_style='none', threaded=False, - result_proxy=SandboxResult, - instructor_filename="instructor_tests.py", - code=None, as_filename=None, report=None): - if report is None: - report = MAIN_REPORT - if 'run' not in report['sandbox']: - report['sandbox']['settings'] = [ - initial_data, initial_raw_output, initial_exception, modules, - full_traceback, tracer_style, threaded, report, context, - result_proxy, instructor_filename, allowed_functions - ] - report['sandbox']['run'] = Sandbox(*report['sandbox']['settings']) - - sandbox = report['sandbox']['run'] - if code is None: - code = report['source']['code'] - sandbox.run(code, as_filename, modules, inputs, threaded, - report_exceptions, raise_exceptions, context=context, keep_context=False) - return sandbox - - -def reset(report=None): - if report is None: - report = MAIN_REPORT - if 'settings' in report['sandbox']: - report['sandbox']['run'] = Sandbox(*report['sandbox']['settings']) - else: - run(report=report) diff --git a/src/lib/pedal/sandbox/timeout.py b/src/lib/pedal/sandbox/timeout.py deleted file mode 100644 index cb88f6de05..0000000000 --- a/src/lib/pedal/sandbox/timeout.py +++ /dev/null @@ -1,2 +0,0 @@ -def timeout(delay, func, *args, **kwargs): - return func(*args, **kwargs) \ No newline at end of file diff --git a/src/lib/pedal/sandbox/tracer.py b/src/lib/pedal/sandbox/tracer.py deleted file mode 100644 index f63d306306..0000000000 --- a/src/lib/pedal/sandbox/tracer.py +++ /dev/null @@ -1,108 +0,0 @@ -import sys -import os - -try: - import coverage -except ImportError: - coverage = None - -try: - from bdb import Bdb, BdbQuit -except Exception: - class Bdb: - pass - - - class BdbQuit: - pass - - -class SandboxBasicTracer: - def __init__(self): - super().__init__() - self.filename = "student.py" - - def _as_filename(self, filename, code): - if os.path.isabs(filename): - self.filename = filename - else: - self.filename = os.path.abspath(filename) - self.code = code - return self - - def __enter__(self): - pass - - def __exit__(self, exc_type, exc_val, traceback): - pass - - -class SandboxCoverageTracer(SandboxBasicTracer): - def __init__(self): - super().__init__() - if coverage is None: - raise ImportError("The coverage package is not available.") - self.n_missing = None - self.n_statements = None - self.pc_covered = None - self.missing = set() - self.lines = set() - # self.s = sys.stdout - - def __enter__(self): - # Force coverage to accept the code - self.original = coverage.python.get_python_source - - def _get_source_correctly(reading_filename): - print(reading_filename, file=self.s) - if reading_filename == self.filename: - return self.code - else: - return self.original(reading_filename) - - coverage.python.get_python_source = _get_source_correctly - self.coverage = coverage.Coverage() - self.coverage.start() - - def __exit__(self, exc_type, exc_val, traceback): - self.coverage.stop() - self.coverage.save() - # Restore the get_python_source reader - coverage.python.get_python_source = self.original - self.original = None - # Actually analyze the data, attach some data - analysis = self.coverage._analyze(self.filename) - # print(vars(self.coverage._analyze(self.filename)), file=self.s) - self.n_missing = analysis.numbers.n_missing - self.n_statements = analysis.numbers.n_statements - self.pc_covered = analysis.numbers.pc_covered - self.missing = analysis.missing - self.lines = analysis.statements - analysis.missing - - @property - def percent_covered(self): - return self.pc_covered - - -class SandboxCallTracer(SandboxBasicTracer, Bdb): - def __init__(self): - super().__init__() - self.calls = {} - - def user_call(self, frame, argument_list): - code = frame.f_code - name = code.co_name - if name not in self.calls: - self.calls[name] = [] - self.calls[name].append(code) - - def __enter__(self): - self.reset() - self._old_trace = sys.gettrace() - sys.settrace(self.trace_dispatch) - - def __exit__(self, exc_type, exc_val, traceback): - sys.settrace(self._old_trace) - self.quitting = True - # Return true to suppress exception (if it is a BdbQuit) - return isinstance(exc_type, BdbQuit) diff --git a/src/lib/pedal/sk_mod_instructor_list.txt b/src/lib/pedal/sk_mod_instructor_list.txt deleted file mode 100644 index 5203e8c5d9..0000000000 --- a/src/lib/pedal/sk_mod_instructor_list.txt +++ /dev/null @@ -1,40 +0,0 @@ -GracefulExit - -StudentData (instance is `student`) - __init__(self) - get_names_by_type(self, type, exclude_builtins) - get_values_by_type(self, type, exclude_builtins) -get_output() -reset_output() -queue_input(*inputs) -get_program() -run_student() - -parse_program() -had_execution_time_error() -limit_execution_time() -unlimit_execution_time() -analyze_program() - -def_use_error(AstNode) - -CorruptedAstNode - __init__(self) -find_match(instructor_code) -find_matches(instructor_code) - -ASTMap - __init__(self, JSAstMap) - get_std_name(self, id) - get_std_exp(self, id) - -AstNode - __init__(self, id) - __eq__(self, other) - numeric_logic_check(self, mag, expr) - __str__(self) - __repr__(self) - __getattr__(self, key) - has(self, AstNode) - find_all(self, type) - \ No newline at end of file diff --git a/src/lib/pedal/source/__init__.py b/src/lib/pedal/source/__init__.py deleted file mode 100644 index 4033ae3e05..0000000000 --- a/src/lib/pedal/source/__init__.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -A package for verifying source code. -""" - -from pedal.source.sections import * -from pedal.report import MAIN_REPORT -import re -import ast - -NAME = 'Source' -SHORT_DESCRIPTION = "Verifies source code and attaches it to the report" -DESCRIPTION = ''' -''' -REQUIRES = [] -OPTIONALS = [] -CATEGORY = 'Syntax' - -__all__ = ['NAME', 'DESCRIPTION', 'SHORT_DESCRIPTION', 'REQUIRES', 'OPTIONALS', - 'set_source', 'check_section_exists', 'next_section', 'verify_section', - 'set_source_file'] -DEFAULT_PATTERN = r'^(##### Part .+)$' - - -def set_source(code, filename='__main__.py', sections=False, independent=False, - report=None): - """ - Sets the contents of the Source to be the given code. Can also be - optionally given a filename. - - Args: - code (str): The contents of the source file. - filename (str): The filename of the students' code. Defaults to - __main__.py. - sections (str or bool): Whether or not the file should be divided into - sections. If a str, then it should be a - Python regular expression for how the sections - are separated. If False, there will be no - sections. If True, then the default pattern - will be used: '^##### Part (\\d+)$' - report (Report): The report object to store data and feedback in. If - left None, defaults to the global MAIN_REPORT. - """ - if report is None: - report = MAIN_REPORT - report['source']['code'] = code - report['source']['full'] = code - report['source']['lines'] = code.split("\n") - report['source']['filename'] = filename - report['source']['independent'] = independent - report['source']['success'] = True - if not sections: - report['source']['sections'] = None - report['source']['section'] = None - _check_issues(code, report) - else: - if sections: - pattern = DEFAULT_PATTERN - else: - pattern = sections - report.group = 0 - report['source']['section_pattern'] = pattern - report['source']['section'] = 0 - report['source']['line_offset'] = 0 - report['source']['sections'] = re.split(pattern, code, - flags=re.MULTILINE) - report['source']['code'] = report['source']['sections'][0] - - -def _check_issues(code, report): - if code.strip() == '': - report.attach('Blank source', category='Syntax', tool=NAME, - group=report['source']['section'], - mistake="Source code file is blank.") - report['source']['success'] = False - try: - parsed = ast.parse(code, report['source']['filename']) - report['source']['ast'] = parsed - except SyntaxError as e: - report.attach('Syntax error', category='Syntax', tool='Source', - group=report['source']['section'], - mistake={'message': "Invalid syntax on line " - + str(e.lineno), - 'error': e, - 'position': {"line": e.lineno}}) - report['source']['success'] = False - report['source']['ast'] = ast.parse("") - - -def get_program(report=None): - if report is None: - report = MAIN_REPORT - return report['source']['code'] - - -def set_source_file(filename, sections=False, independent=False, report=None): - if report is None: - report = MAIN_REPORT - try: - with open(filename, 'r') as student_file: - set_source(student_file.read(), filename=filename, - sections=sections, independent=independent, - report=report) - except IOError: - message = ("The given filename ('{filename}') was either not found" - " or could not be opened. Please make sure the file is" - " available.").format(filename=filename) - report.attach('Source File Not Found', category='Syntax', tool='Source', - group=0 if sections else None, - mistake={'message': message}) - report['source']['success'] = False diff --git a/src/lib/pedal/source/sections.py b/src/lib/pedal/source/sections.py deleted file mode 100644 index a4b9a4bbf2..0000000000 --- a/src/lib/pedal/source/sections.py +++ /dev/null @@ -1,134 +0,0 @@ -from pedal.report import MAIN_REPORT -import ast - - -# def move_to_section(section_number, name, report=None): -# pass - -def _calculate_section_number(section_index): - return int((section_index + 1) / 2) - - -def next_section(name="", report=None): - if report is None: - report = MAIN_REPORT - report.execute_hooks('source.next_section.before') - source = report['source'] - # if not report['source']['success']: - # return False - source['section'] += 2 - section_index = source['section'] - section_number = _calculate_section_number(section_index) - sections = source['sections'] - found = len(source['sections']) - if section_index < found: - if source['independent']: - source['code'] = ''.join(sections[section_index]) - old_code = ''.join(sections[:section_index]) - source['line_offset'] = len(old_code.split("\n")) - 1 - else: - source['code'] = ''.join(sections[:section_index + 1]) - report.group = section_index - else: - report.attach('Syntax error', category='Syntax', tool='Source', - mistake=("Tried to advance to next section but the " - "section was not found. Tried to load section " - "{count}, but there were only {found} sections." - ).format(count=section_number, found=found)) - - -def check_section_exists(section_number, report=None): - """ - Checks that the right number of sections exist. The prologue before the - first section is 0, while subsequent ones are 1, 2, 3, etc. - So if you have 3 sections in your code plus the prologue, - you should pass in 3 and not 4 to verify that all of them exist. - """ - if report is None: - report = MAIN_REPORT - if not report['source']['success']: - return False - found = int((len(report['source']['sections']) - 1) / 2) - if section_number > found: - report.attach('Syntax error', category='Syntax', tool='Source', - group=report['source']['section'], - mistake=("Incorrect number of sections in your file. " - "Expected {count}, but only found {found}" - ).format(count=section_number, found=found)) - - -def verify_section(report=None): - if report is None: - report = MAIN_REPORT - source = report['source'] - # if not source['success']: - # return False - code = source['code'] - try: - parsed = ast.parse(code, source['filename']) - source['ast'] = parsed - except SyntaxError as e: - report.attach('Syntax error', category='Syntax', tool='Source', - group=source['section'], - mistake={'message': "Invalid syntax on line " - + str(e.lineno + source['line_offset']) + "\n", - 'error': e, - 'position': {"line": e.lineno}}) - source['success'] = False - if 'ast' in source: - del source['ast'] - return source['success'] - - -class _finish_section: - def __init__(self, number, *functions): - if isinstance(number, int): - self.number = number - else: - self.number = -1 - functions = [number] + list(functions) - self.functions = functions - for function in functions: - self(function, False) - - def __call__(self, f=None, quiet=True): - if f is not None: - f() - if quiet: - print("\tNEXT SECTION") - - def __enter__(self): - pass - - def __exit__(self, x, y, z): - print("\tNEXT SECTION") - # return wrapped_f - - -def finish_section(number, *functions, **kwargs): - if 'next_section' in kwargs: - next_section = kwargs['next_section'] - else: - next_section = False - if len(functions) == 0: - x = _finish_section(number, *functions) - x() - else: - result = _finish_section(number, *functions) - if next_section: - print("\tNEXT SECTION") - return result - - -def section(number): - """ - """ - pass - - -def precondition(function): - pass - - -def postcondition(function): - pass diff --git a/src/lib/pedal/tifa/.gitignore b/src/lib/pedal/tifa/.gitignore deleted file mode 100644 index 401fcfe58a..0000000000 --- a/src/lib/pedal/tifa/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_temp_tifa.py \ No newline at end of file diff --git a/src/lib/pedal/tifa/__init__.py b/src/lib/pedal/tifa/__init__.py deleted file mode 100644 index f0fe320220..0000000000 --- a/src/lib/pedal/tifa/__init__.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -Python Type Inferencer and Flow Analyzer (TIFA) - -TIFA uses a number of simplifications of the Python language. - * Variables cannot change type - * Variables cannot be deleted - * Complex types have to be homogenous - * No introspection or reflective characteristics - * No dunder methods - * No closures (maybe?) - * You cannot write a variable out of scope - * You cannot read a mutable variable out of scope - * No multiple inheritance - -Additionally, it reads the following as issues: - * Cannot read a variable without having first written to it. - * Cannot rewrite a variable unless it has been read. - -Important concepts: - -.. glossary:: - - Issue - A problematic situation in the submitted code that will be reported - but may not stop the execution. However, when an Issue occurs, - any results may be invalid. - - Error - A situation in execution that terminates the program. - - Name - A name of a variable - - Scope - The context of a function, with its own namespaces. Represented - internally using numeric IDs (Scope IDs). - - Scope Chain - A stack of scopes, with the innermost scope on top. - - Fully Qualified Name - A string representation of a variable and its scope - chain, written using "/". For example: 0/1/4/my_variable_name - - Path - A single path of execution through the control flow; every program - has at least one sequential path, but IFs, FORs, WHILEs, etc. can - cause multiple paths. Paths are represented using numeric IDs (Path - IDs). - - State - Information about a Name that indicates things like the variable's - current type and whether that name has been read, set, or - overwritten. - - Identifier - A wrapper around variables, used to hold their potential - non-existence (which is an Issue but not an Error). - - Type - A symbolic representation of the variable's type. - - Literal - Sometimes, we need a specialized representation of a literal value - to be passed around. This is particularly important for accessing - elements in an tuples. - - Name Map - (Path x Fully Qualified Names) => States -""" - -from pedal.tifa.tifa import Tifa -from pedal.report import MAIN_REPORT - -NAME = 'TIFA' -SHORT_DESCRIPTION = "Finds common issues caused by students." -DESCRIPTION = '''Python Type Inferencer and Flow Analyzer (TIFA) - -Tifa traverses an AST to detect common issues made by students. -''' -REQUIRES = ['Source'] -OPTIONALS = [] - - -def tifa_analysis(python_3=True, report=None): - """ - Perform the TIFA analysis and attach the results to the Report. - - Args: - python_3 (bool): Whether to expect a Python3 formated file, or Python - 2. This has slight nuance on certain AST elements. - report (:class:`Report`): The Report object to attach results to. - Defaults to :data:`MAIN_REPORT`. - """ - if report is None: - report = MAIN_REPORT - t = Tifa(python_3=python_3, report=report) - t.process_code(report['source']['code']) - return t - - -__all__ = ['NAME', 'DESCRIPTION', 'SHORT_DESCRIPTION', - 'REQUIRES', 'OPTIONALS', - 'tifa_analysis', 'Tifa'] diff --git a/src/lib/pedal/tifa/builtin_definitions.py b/src/lib/pedal/tifa/builtin_definitions.py deleted file mode 100644 index 9ffe0d6742..0000000000 --- a/src/lib/pedal/tifa/builtin_definitions.py +++ /dev/null @@ -1,212 +0,0 @@ -from pedal.tifa.type_definitions import (UnknownType, FunctionType, - NumType, NoneType, BoolType, - TupleType, ListType, StrType, - FileType, DictType, ModuleType, - SetType, DayType, TimeType, ClassType, - LiteralNum) - - -def get_builtin_module(name): - if name == 'matplotlib': - return ModuleType('matplotlib', - submodules={ - 'pyplot': ModuleType('pyplot', fields={ - 'plot': FunctionType(name='plot', returns=NoneType()), - 'hist': FunctionType(name='hist', returns=NoneType()), - 'scatter': FunctionType(name='scatter', returns=NoneType()), - 'show': FunctionType(name='show', returns=NoneType()), - 'xlabel': FunctionType(name='xlabel', returns=NoneType()), - 'ylabel': FunctionType(name='ylabel', returns=NoneType()), - 'title': FunctionType(name='title', returns=NoneType()), - }) - }) - elif name == 'pprint': - return ModuleType('pprint', - fields={ - 'pprint': FunctionType(name='pprint', returns=NoneType()) - }) - elif name == 'random': - return ModuleType('random', - fields={ - 'randint': FunctionType(name='randint', returns=NumType()) - }) - elif name == 'string': - return ModuleType('string', - fields={ - 'letters': StrType(empty=False), - 'digits': StrType(empty=False), - 'ascii_letters': StrType(empty=False), - 'punctuation': StrType(empty=False), - 'printable': StrType(empty=False), - 'whitespace': StrType(empty=False), - 'ascii_uppercase': StrType(empty=False), - 'ascii_lowercase': StrType(empty=False), - 'hexdigits': StrType(empty=False), - 'octdigits': StrType(empty=False), - }) - elif name == 'turtle': - return ModuleType('turtle', - fields={ - 'forward': FunctionType(name='forward', returns=NoneType()), - 'backward': FunctionType(name='backward', returns=NoneType()), - 'color': FunctionType(name='color', returns=NoneType()), - 'right': FunctionType(name='right', returns=NoneType()), - 'left': FunctionType(name='left', returns=NoneType()), - }) - elif name == 'parking': - return ModuleType('parking', - fields={ - 'Time': FunctionType(name='Time', returns=TimeType()), - 'now': FunctionType(name='now', returns=TimeType()), - 'Day': FunctionType(name='Day', returns=DayType()), - 'today': FunctionType(name='today', returns=DayType()), - }), - elif name == 'math': - return ModuleType('math', - fields={ - 'ceil': FunctionType(name='ceil', returns=NumType()), - 'copysign': FunctionType(name='copysign', returns=NumType()), - 'fabs': FunctionType(name='fabs', returns=NumType()), - 'factorial': FunctionType(name='factorial', returns=NumType()), - 'floor': FunctionType(name='floor', returns=NumType()), - 'fmod': FunctionType(name='fmod', returns=NumType()), - 'frexp': FunctionType(name='frexp', returns=NumType()), - 'fsum': FunctionType(name='fsum', returns=NumType()), - 'gcd': FunctionType(name='gcd', returns=NumType()), - 'isclose': FunctionType(name='isclose', returns=BoolType()), - 'isfinite': FunctionType(name='isfinite', returns=BoolType()), - 'isinf': FunctionType(name='isinf', returns=BoolType()), - 'isnan': FunctionType(name='isnan', returns=BoolType()), - 'ldexp': FunctionType(name='ldexp', returns=NumType()), - 'modf': FunctionType(name='modf', returns=NumType()), - 'trunc': FunctionType(name='trunc', returns=NumType()), - 'log': FunctionType(name='log', returns=NumType()), - 'log1p': FunctionType(name='log1p', returns=NumType()), - 'log2': FunctionType(name='log2', returns=NumType()), - 'log10': FunctionType(name='log10', returns=NumType()), - 'pow': FunctionType(name='pow', returns=NumType()), - 'sqrt': FunctionType(name='sqrt', returns=NumType()), - 'sin': FunctionType(name='sin', returns=NumType()), - 'cos': FunctionType(name='cos', returns=NumType()), - 'tan': FunctionType(name='tan', returns=NumType()), - 'asin': FunctionType(name='asin', returns=NumType()), - 'acos': FunctionType(name='acos', returns=NumType()), - 'atan': FunctionType(name='atan', returns=NumType()), - 'atan2': FunctionType(name='atan2', returns=NumType()), - 'hypot': FunctionType(name='hypot', returns=NumType()), - 'degrees': FunctionType(name='degrees', returns=NumType()), - 'radians': FunctionType(name='radians', returns=NumType()), - 'sinh': FunctionType(name='sinh', returns=NumType()), - 'cosh': FunctionType(name='cosh', returns=NumType()), - 'tanh': FunctionType(name='tanh', returns=NumType()), - 'asinh': FunctionType(name='asinh', returns=NumType()), - 'acosh': FunctionType(name='acosh', returns=NumType()), - 'atanh': FunctionType(name='atanh', returns=NumType()), - 'erf': FunctionType(name='erf', returns=NumType()), - 'erfc': FunctionType(name='erfc', returns=NumType()), - 'gamma': FunctionType(name='gamma', returns=NumType()), - 'lgamma': FunctionType(name='lgamma', returns=NumType()), - 'pi': NumType(), - 'e': NumType(), - 'tau': NumType(), - 'inf': NumType(), - 'nan': NumType(), - }) - - -def _builtin_sequence_constructor(sequence_type): - """ - Helper function for creating constructors for the Set and List types. - These constructors use the subtype of the arguments. - - Args: - sequence_type (Type): A function for creating new sequence types. - """ - - def sequence_call(tifa, function_type, callee, args, position): - # TODO: Should inherit the emptiness too - return_type = sequence_type(empty=True) - if args: - return_type.subtype = args[0].index(LiteralNum(0)) - return_type.empty = False - return return_type - - return sequence_call - - -def _builtin_zip(tifa, function_type, callee, args, position): - """ - Definition of the built-in zip function, which consumes a series of - sequences and returns a list of tuples, with each tuple composed of the - elements of the sequence paired (or rather, tupled) together. - """ - if args: - tupled_types = TupleType(subtypes=[]) - for arg in args: - tupled_types.append(arg.index(0)) - return ListType(tupled_types, empty=False) - return ListType(empty=True) - - -# TODO: Exceptions - -def get_builtin_function(name): - # Void Functions - if name == "print": - return FunctionType(name="print", returns=NoneType()) - # Math Functions - elif name in ("int", "abs", "float", "len", "ord", "pow", "round", "sum"): - return FunctionType(name=name, returns=NumType()) - # Boolean Functions - elif name in ("bool", "all", "any", "isinstance"): - return FunctionType(name=name, returns=BoolType()) - # String Functions - elif name in ("str", 'chr', 'bin', 'repr', 'input'): - return FunctionType(name=name, returns=StrType()) - # File Functions - elif name == "open": - return FunctionType(name="open", returns=FileType()) - # List Functions - elif name == "map": - return FunctionType(name="map", returns=ListType(empty=False)) - elif name == "list": - return FunctionType(name="list", - definition=_builtin_sequence_constructor(ListType)) - # Set Functions - elif name == "set": - return FunctionType(name="set", - definition=_builtin_sequence_constructor(SetType)) - # Dict Functions - elif name == "dict": - return FunctionType(name="dict", returns=DictType()) - # Pass through - elif name == "sorted": - return FunctionType(name="sorted", returns='identity') - elif name == "reversed": - return FunctionType(name="reversed", returns='identity') - elif name == "filter": - return FunctionType(name="filter", returns='identity') - # Special Functions - elif name == "type": - return FunctionType(name="type", returns=UnknownType()) - elif name == "range": - return FunctionType(name="range", returns=ListType(NumType(), empty=False)) - elif name == "dir": - return FunctionType(name="dir", returns=ListType(StrType(), empty=False)) - elif name == "max": - return FunctionType(name="max", returns='element') - elif name == "min": - return FunctionType(name="min", returns='element') - elif name == "zip": - return FunctionType(name="zip", returns=_builtin_zip) - elif name == "__import__": - return FunctionType(name="__import__", returns=ModuleType()) - elif name == "globals": - return FunctionType(name="globals", - returns=DictType(keys=StrType(), - values=UnknownType(), - empty=False)) - elif name in ("classmethod", "staticmethod"): - return FunctionType(name=name, returns='identity') - elif name in ("__name__",): - return StrType() diff --git a/src/lib/pedal/tifa/identifier.py b/src/lib/pedal/tifa/identifier.py deleted file mode 100644 index f250069edb..0000000000 --- a/src/lib/pedal/tifa/identifier.py +++ /dev/null @@ -1,24 +0,0 @@ -class Identifier: - """ - A representation of an Identifier, encapsulating its current level of - existence, scope and State. - - Attributes: - exists (bool): Whether or not the variable actually is defined anywhere. - It is possible that a variable was retrieved that does - not actually exist yet, which indicates it might need to - be created. - in_scope (bool): Whether or not the variable exists in the current - scope. Used to detect the presence of certain kinds - of errors where the user is using a variable from - a different scope. - scoped_name (str): The fully qualified name of the variable, including - its scope chain. - state (State): The current state of the variable. - """ - - def __init__(self, exists, in_scope=False, scoped_name="UNKNOWN", state=""): - self.exists = exists - self.in_scope = in_scope - self.scoped_name = scoped_name - self.state = state diff --git a/src/lib/pedal/tifa/messages.py b/src/lib/pedal/tifa/messages.py deleted file mode 100644 index 8bc2cde2c7..0000000000 --- a/src/lib/pedal/tifa/messages.py +++ /dev/null @@ -1,167 +0,0 @@ -import ast - -OPERATION_DESCRIPTION = { - ast.Pow: "an exponent", - ast.Add: "an addition", - ast.Mult: "a multiplication", - ast.Sub: "a subtraction", - ast.Div: "a division", - ast.FloorDiv: "a division", - ast.Mod: "a modulo", - ast.LShift: "a left shift", - ast.RShift: "a right shift", - ast.BitOr: "a bit or", - ast.BitAnd: "a bit and", - ast.BitXor: "a bit xor", - ast.And: "an and", - ast.Or: "an or", - ast.Eq: "an ==", - ast.NotEq: "a !=", - ast.Lt: "a <", - ast.LtE: "a <=", - ast.Gt: "a >", - ast.GtE: "a >=", - ast.Is: "an is", - ast.IsNot: "an is not", - ast.In: "an in", - ast.NotIn: "an not in", -} - - -def _format_message(issue, data): - if issue == 'Action after return': - # A path had a statement after a return. - return ("You performed an action after already returning from a " - "function, on line {line}. You can only return on a path " - "once.").format(line=data['position']['line']) - elif issue == 'Return outside function': - # Attempted to return outside of a function - return ("You attempted to return outside of a function on line {line}." - " But you can only return from within a function." - ).format(line=data['position']['line']) - elif issue == "Multiple Return Types": - return ("Your function returned {actual} on line {line}, even though you defined it to" - " return {expected}. Your function should return values consistently." - ).format(expected=data['expected'], actual=data['actual'], line=data['position']['line']) - elif issue == 'Write out of scope': - # DEPRECATED - # Attempted to modify a variable in a higher scope - return False - return ("You attempted to write a variable from a higher scope " - "(outside the function) on line {line}. You should only " - "use variables inside the function they were declared in." - ).format(line=data['position']['line']) - elif issue == 'Unconnected blocks': - # Any names with ____ - return ("It looks like you have unconnected blocks on line {line}. " - "Before you run your program, you must make sure that all " - "of your blocks are connected that there are no unfilled " - "holes.").format(line=data['position']['line']) - elif issue == 'Iteration Problem': - # Iteration list is the iteration variable - return ("The variable {name} was iterated on line " - "{line} but you used the same variable as the iteration " - "variable. You should choose a different variable name " - "for the iteration variable. Usually, the iteration variable " - "is the singular form of the iteration list (e.g., " - "for a_dog in dogs:).").format( - line=data['position']['line'], - name=data['name']) - elif issue == 'Initialization Problem': - # A variable was read before it was defined - return ("The variable {name} was used on line {line}, " - "but it was not given a value on a previous line. " - "You cannot use a variable until it has been given a value." - ).format(line=data['position']['line'], name=data['name']) - elif issue == 'Possible Initialization Problem': - # A variable was read but was not defined in every branch - if data['name'] == '*return': - return False - return ("The variable {name} was used on line {line}, " - "but it was possibly not given a value on a previous " - "line. You cannot use a variable until it has been given " - "a value. Check to make sure that this variable was " - "declared in all of the branches of your decision." - ).format(line=data['position']['line'], name=data['name']) - elif issue == 'Unused Variable': - # A variable was not read after it was defined - name = data['name'] - if data['type'].is_equal('function'): - kind = 'function' - body = 'definition' - else: - kind = 'variable' - body = 'value' - return ("The {kind} {name} was given a {body}, but " - "was never used after that." - ).format(name=name, kind=kind, body=body) - elif issue == 'Overwritten Variable': - return ("The variable {name} was given a value, but " - "{name} was changed on line {line} before it " - "was used. One of the times that you gave {name} " - "a value was incorrect." - ).format(line=data['position']['line'], name=data['name']) - elif issue == 'Iterating over Non-list': - if 'name' not in data or data['name'] is None: - expression = "expression" - else: - expression = "variable {}".format(data['name']) - return ("The {expression} is not a list, but you used " - "it in the iteration on line {line}. You should only iterate " - "over sequences like lists." - ).format(line=data['position']['line'], expression=expression) - elif issue == 'Iterating over empty list': - if 'name' not in data or data['name'] is None: - expression = "expression" - else: - expression = "variable {}".format(data['name']) - return ("The {expression} was set as an empty list, " - "and then you attempted to use it in an iteration on line " - "{line}. You should only iterate over non-empty lists." - ).format(line=data['position']['line'], expression=expression) - elif issue == 'Incompatible types': - op = OPERATION_DESCRIPTION.get(data['operation'].__class__, - str(data['operation'])) - left = data['left'].singular_name - right = data['right'].singular_name - line = data['position']['line'] - return ("You used {op} operation with {left} and {right} on line " - "{line}. But you can't do that with that operator. Make " - "sure both sides of the operator are the right type." - ).format(op=op, left=left, right=right, line=line) - elif issue == "Parameter Type Mismatch": - name = data['parameter_name'] - parameter = data['parameter'].singular_name - argument = data['argument'].singular_name - line = data['position']['line'] - return ("You defined the parameter {name} on line {line} " - "as {parameter}. However, the argument passed to that parameter " - "was {argument}. The formal parameter type must match the argument's type." - ).format(name=name, argument=argument, parameter=parameter, line=line) - elif issue == 'Read out of scope': - return ("You attempted to read a variable from a different scope on " - "line {line}. You should only use variables inside the " - "function they were declared in." - ).format(line=data['position']['line']) - return False - - -''' -TODO: Finish these checks -"Empty Body": [], # Any use of pass on its own -"Malformed Conditional": [], # An if/else with empty else or if -"Unnecessary Pass": [], # Any use of pass -"Append to non-list": [], # Attempted to use the append method on a non-list -"Used iteration list": [], # -"Unused iteration variable": [], # -"Type changes": [], # -"Unknown functions": [], # -"Not a function": [], # Attempt to call non-function as function -"Recursive Call": [], -"Incorrect Arity": [], -"Aliased built-in": [], # -"Method not in Type": [], # A method was used that didn't exist for that type -"Submodule not found": [], -"Module not found": [], -"Else on loop body": [], # Used an Else on a For or While -''' diff --git a/src/lib/pedal/tifa/readme.md b/src/lib/pedal/tifa/readme.md deleted file mode 100644 index fd100ad20c..0000000000 --- a/src/lib/pedal/tifa/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -PyTIFA is the Python Type Inferencer and Flow Analyzer, also called just Tifa. - -Tifa is meant to be used on simple programs written in learning situations, in order to provide type information and detect certain common issues. It makes some very strong assumptions and doesn't support all language features. - -Tifa is supported by Skulpt. This means that Tifa is a Python library that can be passed in a string of Python source code in order to traverse its AST using underlying JavaScript libraries. If that isn't confusing, then we invite you to make pull requests. \ No newline at end of file diff --git a/src/lib/pedal/tifa/state.py b/src/lib/pedal/tifa/state.py deleted file mode 100644 index fe75ccd428..0000000000 --- a/src/lib/pedal/tifa/state.py +++ /dev/null @@ -1,77 +0,0 @@ -def check_trace(state): - past_types = [state.type] - for past_state in state.trace: - past_types.extend(check_trace(past_state)) - return past_types - - -class State: - """ - A representation of a variable at a particular point in time of the program. - - Attributes: - name (str): The name of the variable, without its scope chain - trace (list of State): A recursive definition of previous States for - this State. - type (Type): The current type of this variable. - method (str): One of 'store', 'read', (TODO). Indicates the change that - occurred to this variable at this State. - position (dict): A Position dictionary indicating where this State - change occurred in the source code. - read (str): One of 'yes', 'no', or 'maybe'. Indicates if this variable - has been read since it was last changed. If merged from a - diverging path, it is possible that it was "maybe" read. - set (str): One of 'yes', 'no', or 'maybe'. Indicates if this variable - has been set since it was last read. If merged from a - diverging path, it is possible that it was "maybe" changed. - over (str): One of 'yes', 'no', or 'maybe'. Indicates if this variable - has been overwritten since it was last set. If merged from a - diverging path, it is possible that it was "maybe" changed. - over_position (dict): A Position indicating where the State was - previously set versus when it was overwritten. - """ - - def __init__(self, name, trace, type, method, position, - read='maybe', set='maybe', over='maybe', over_position=None): - self.name = name - self.trace = trace - self.type = type - self.method = method - self.position = position - self.over_position = over_position - self.read = read - self.set = set - self.over = over - - def copy(self, method, position): - """ - Make a copy of this State, copying this state into the new State's trace - """ - return State(self.name, [self], self.type, method, position, - self.read, self.set, self.over, self.over_position) - - def __str__(self): - """ - Create a string representation of this State. - """ - return "{method}(r:{read},s:{set},o:{over},{type})".format( - method=self.method, - read=self.read[0], - set=self.set[0], - over=self.over[0], - type=self.type.__class__.__name__ - ) - - def __repr__(self): - """ - Create a string representation of this State. - """ - return str(self) - - def was_type(self, a_type): - """ - Retrieve all the types that this variable took on over its entire - trace. - """ - past_types = check_trace(self) - return any(past_type.is_equal(a_type) for past_type in past_types) diff --git a/src/lib/pedal/tifa/tifa.py b/src/lib/pedal/tifa/tifa.py deleted file mode 100644 index 8fadd48c0c..0000000000 --- a/src/lib/pedal/tifa/tifa.py +++ /dev/null @@ -1,1293 +0,0 @@ -import ast -from pprint import pprint - -from pedal.report import MAIN_REPORT - -from pedal.tifa.type_definitions import (UnknownType, RecursedType, - FunctionType, ClassType, InstanceType, - NumType, NoneType, BoolType, TupleType, - ListType, StrType, GeneratorType, - DictType, ModuleType, SetType, - # FileType, DayType, TimeType, - type_from_json, type_to_literal, get_tifa_type, - LiteralNum, LiteralBool, - LiteralNone, LiteralStr, - LiteralTuple) -from pedal.tifa.builtin_definitions import (get_builtin_module, get_builtin_function) -from pedal.tifa.type_operations import (merge_types, are_types_equal, - VALID_UNARYOP_TYPES, VALID_BINOP_TYPES, - ORDERABLE_TYPES, INDEXABLE_TYPES) -from pedal.tifa.identifier import Identifier -from pedal.tifa.state import State -from pedal.tifa.messages import _format_message - -__all__ = ['Tifa'] - - -class Tifa(ast.NodeVisitor): - """ - TIFA Class for traversing an AST and finding common issues. - - Args: - python_3 (bool): Whether to parse the code in regular PYTHON_3 mode or - the modified AST that Skulpt uses. - report (Report): The report object to store data and feedback in. If - left None, defaults to the global MAIN_REPORT. - """ - - def __init__(self, python_3=True, report=None): - if report is None: - report = MAIN_REPORT - self.report = report - self._initialize_report() - - def _initialize_report(self): - """ - Initialize a successful report with possible set of issues. - """ - self.report['tifa'] = { - 'success': True, - 'variables': {}, - 'top_level_variables': {}, - 'issues': {} - } - - def report_issue(self, issue, data=None): - """ - Report the given issue with associated metadata, including the position - if not explicitly included. - """ - if data is None: - data = {} - if 'position' not in data: - data['position'] = self.locate() - data['message'] = _format_message(issue, data) - if issue not in self.report['tifa']['issues']: - self.report['tifa']['issues'][issue] = [] - self.report['tifa']['issues'][issue].append(data) - if data['message']: - self.report.attach(issue, category='Analyzer', tool='TIFA', - mistake=data) - - def locate(self, node=None): - """ - Return a dictionary representing the current location within the - AST. - - Returns: - Position dict: A dictionary with the fields 'column' and 'line', - indicating the current position in the source code. - """ - if node is None: - if self.node_chain: - node = self.node_chain[-1] - else: - node = self.final_node - return {'column': node.col_offset, 'line': node.lineno} - - def process_code(self, code, filename="__main__"): - """ - Processes the AST of the given source code to generate a report. - - Args: - code (str): The Python source code - filename (str): The filename of the source code (defaults to __main__) - Returns: - Report: The successful or successful report object - """ - # Code - self.source = code.split("\n") if code else [] - filename = filename - - # Attempt parsing - might fail! - try: - ast_tree = ast.parse(code, filename) - except Exception as error: - self.report['tifa']['success'] = False - self.report['tifa']['error'] = error - self.report.attach('tifa_error', category='Analyzer', tool='TIFA', - mistake={ - 'message': "Could not parse code", - 'error': error - }) - return self.report['tifa'] - try: - return self.process_ast(ast_tree) - except Exception as error: - self.report['tifa']['success'] = False - self.report['tifa']['error'] = error - self.report.attach('tifa_error', category='Analyzer', tool='TIFA', - mistake={ - 'message': "Could not process code: " + str(error), - 'error': error - }) - return self.report['tifa'] - - def process_ast(self, ast_tree): - """ - Given an AST, actually performs the type and flow analyses to return a - report. - - Args: - ast_tree (Ast): The AST object - Returns: - Report: The final report object created (also available as a field). - """ - self._reset() - # Traverse every node - self.visit(ast_tree) - - # Check afterwards - self.report['tifa']['variables'] = self.name_map - self._finish_scope() - - # Collect top level variables - self._collect_top_level_variables() - # print(self.report['variables']) - - return self.report['tifa'] - - def _collect_top_level_variables(self): - """ - Walk through the variables and add any at the top level to the - top_level_variables field of the report. - """ - top_level_variables = self.report['tifa']['top_level_variables'] - main_path_vars = self.name_map[self.path_chain[0]] - for full_name in main_path_vars: - split_name = full_name.split("/") - if len(split_name) == 2 and split_name[0] == str(self.scope_chain[0]): - name = split_name[1] - top_level_variables[name] = main_path_vars[full_name] - - def _reset(self): - """ - Reinitialize fields for maintaining the system - """ - # Unique Global IDs - self.path_id = 0 - self.scope_id = 0 - self.ast_id = 0 - - # Human readable names - self.path_names = ['*Module'] - self.scope_names = ['*Module'] - self.node_chain = [] - - # Complete record of all Names - self.scope_chain = [self.scope_id] - self.path_chain = [self.path_id] - self.name_map = {} - self.name_map[self.path_id] = {} - self.definition_chain = [] - self.path_parents = {} - self.final_node = None - self.class_scopes = {} - - def find_variable_scope(self, name): - """ - Walk through this scope and all enclosing scopes, finding the relevant - identifier given by `name`. - - Args: - name (str): The name of the variable - Returns: - Identifier: An Identifier for the variable, which could potentially - not exist. - """ - for scope_level, scope in enumerate(self.scope_chain): - for path_id in self.path_chain: - path = self.name_map[path_id] - full_name = "/".join(map(str, self.scope_chain[scope_level:])) + "/" + name - if full_name in path: - is_root_scope = (scope_level == 0) - return Identifier(True, is_root_scope, - full_name, path[full_name]) - - return Identifier(False) - - def find_variable_out_of_scope(self, name): - """ - Walk through every scope and determine if this variable can be found - elsewhere (which would be an issue). - - Args: - name (str): The name of the variable - Returns: - Identifier: An Identifier for the variable, which could potentially - not exist. - """ - for path in self.name_map.values(): - for full_name in path: - unscoped_name = full_name.split("/")[-1] - if name == unscoped_name: - return Identifier(True, False, unscoped_name, path[full_name]) - return Identifier(False) - - def find_path_parent(self, path_id, name): - if name in self.name_map[path_id]: - return Identifier(True, state=self.name_map[path_id][name]) - else: - path_parent = self.path_parents.get(path_id) - if path_parent is None: - return Identifier(False) - else: - return self.find_path_parent(path_parent, name) - - def _finish_scope(self): - """ - Walk through all the variables present in this scope and ensure that - they have been read and not overwritten. - """ - path_id = self.path_chain[0] - for name in self.name_map[path_id]: - if Tifa.in_scope(name, self.scope_chain): - state = self.name_map[path_id][name] - if state.over == 'yes': - position = state.over_position - self.report_issue('Overwritten Variable', - {'name': state.name, 'position': position}) - if state.read == 'no': - self.report_issue('Unused Variable', - {'name': state.name, 'type': state.type, - 'position': state.position}) - - def visit(self, node): - """ - Process this node by calling its appropriate visit_* - - Args: - node (AST): The node to visit - Returns: - Type: The type calculated during the visit. - """ - # Start processing the node - self.node_chain.append(node) - self.ast_id += 1 - - # Actions after return? - if len(self.scope_chain) > 1: - return_state = self.find_variable_scope("*return") - if return_state.exists and return_state.in_scope: - if return_state.state.set == "yes": - self.report_issue("Action after return") - - # No? All good, let's enter the node - self.final_node = node - result = ast.NodeVisitor.visit(self, node) - - # Pop the node out of the chain - self.ast_id -= 1 - self.node_chain.pop() - - # If a node failed to return something, return the UNKNOWN TYPE - if result is None: - return UnknownType() - else: - return result - - def _visit_nodes(self, nodes): - """ - Visit all the nodes in the given list. - - Args: - nodes (list): A list of values, of which any AST nodes will be - visited. - """ - for node in nodes: - if isinstance(node, ast.AST): - self.visit(node) - - def walk_targets(self, targets, type, walker): - """ - Iterate through the targets and call the given function on each one. - - Args: - targets (list of Ast nodes): A list of potential targets to be - traversed. - type (Type): The given type to be unraveled and applied to the - targets. - walker (Ast Node, Type -> None): A function that will process - each target and unravel the type. - """ - for target in targets: - walker(target, type) - - def _walk_target(self, target, type): - """ - Recursively apply the type to the target - - Args: - target (Ast): The current AST node to process - type (Type): The type to apply to this node - """ - if isinstance(target, ast.Name): - self.store_iter_variable(target.id, type, self.locate(target)) - return target.id - elif isinstance(target, (ast.Tuple, ast.List)): - result = None - for i, elt in enumerate(target.elts): - elt_type = type.index(LiteralNum(i)) - potential_name = self._walk_target(elt, elt_type) - if potential_name is not None and result is None: - result = potential_name - return result - - def visit_AnnAssign(self, node): - """ - TODO: Implement! - """ - pass - - def visit_Assign(self, node): - """ - Simple assignment statement: - __targets__ = __value__ - - Args: - node (AST): An Assign node - Returns: - None - """ - # Handle value - value_type = self.visit(node.value) - # Handle targets - self._visit_nodes(node.targets) - - self.walk_targets(node.targets, value_type, self.assign_target) - - # TODO: Properly handle assignments with subscripts - def assign_target(self, target, type): - if isinstance(target, ast.Name): - self.store_variable(target.id, type) - elif isinstance(target, (ast.Tuple, ast.List)): - for i, elt in enumerate(target.elts): - eltType = type.index(LiteralNum(i)) - self.assign_target(elt, eltType) - elif isinstance(target, ast.Subscript): - left_hand_type = self.visit(target.value) - if isinstance(left_hand_type, ListType): - # TODO: Handle updating value in list - pass - elif isinstance(left_hand_type, DictType): - if not isinstance(target.slice, ast.Index): - # TODO: Can't subscript a dictionary assignment - return None - literal = self.get_literal(target.slice.value) - if not literal: - key_type = self.visit(target.slice.value) - left_hand_type.empty = False - left_hand_type.keys = key_type.clone() - left_hand_type.values = type.clone() - elif left_hand_type.literals: - original_type = left_hand_type.has_literal(literal) - if not original_type: - left_hand_type.update_key(literal, type.clone()) - elif not are_types_equal(original_type, type): - # TODO: Fix "Dictionary" to be the name of the variable - self.report_issue("Type changes", - {'name': "Dictionary", 'old': original_type, - 'new': type}) - elif isinstance(target, ast.Attribute): - left_hand_type = self.visit(target.value) - if isinstance(left_hand_type, InstanceType): - left_hand_type.add_attr(target.attr, type) - # TODO: Otherwise we attempted to assign to a non-instance - # TODO: Handle minor type changes (e.g., appending to an inner list) - - def visit_AugAssign(self, node): - # Handle value - right = self.visit(node.value) - # Handle target - left = self.visit(node.target) - # Target is always a Name, Subscript, or Attribute - name = self.identify_caller(node.target) - - # Handle operation - self.load_variable(name) - if isinstance(left, UnknownType) or isinstance(right, UnknownType): - return UnknownType() - elif type(node.op) in VALID_BINOP_TYPES: - op_lookup = VALID_BINOP_TYPES[type(node.op)] - if type(left) in op_lookup: - op_lookup = op_lookup[type(left)] - if type(right) in op_lookup: - op_lookup = op_lookup[type(right)] - result_type = op_lookup(left, right) - self.assign_target(node.target, result_type) - return result_type - - self.report_issue("Incompatible types", - {"left": left, "right": right, - "operation": node.op}) - - def visit_Attribute(self, node): - # Handle value - value_type = self.visit(node.value) - # Handle ctx - # TODO: Handling contexts - # Handle attr - return value_type.load_attr(node.attr, self, node.value, self.locate()) - - def visit_BinOp(self, node): - # Handle left and right - left = self.visit(node.left) - right = self.visit(node.right) - - # Handle operation - if isinstance(left, UnknownType) or isinstance(right, UnknownType): - return UnknownType() - elif type(node.op) in VALID_BINOP_TYPES: - op_lookup = VALID_BINOP_TYPES[type(node.op)] - if type(left) in op_lookup: - op_lookup = op_lookup[type(left)] - if type(right) in op_lookup: - op_lookup = op_lookup[type(right)] - return op_lookup(left, right) - - self.report_issue("Incompatible types", - {"left": left, "right": right, - "operation": node.op}) - return UnknownType() - - def visit_Bool(self, node): - return BoolType() - - def visit_BoolOp(self, node): - # Handle left and right - values = [] - for value in node.values: - values.append(self.visit(value)) - - # TODO: Truthiness is not supported! Probably need a Union type - # TODO: Literals used as truthy value - - # Handle operation - return BoolType() - - def visit_Call(self, node): - # Handle func part (Name or Attribute) - function_type = self.visit(node.func) - # TODO: Need to grab the actual type in some situations - callee = self.identify_caller(node) - - # Handle args - arguments = [self.visit(arg) for arg in node.args] if node.args else [] - - # TODO: Handle keywords - # TODO: Handle starargs - # TODO: Handle kwargs - if isinstance(function_type, FunctionType): - # Test if we have called this definition before - if function_type.definition not in self.definition_chain: - self.definition_chain.append(function_type.definition) - # Function invocation - result = function_type.definition(self, function_type, callee, - arguments, self.locate()) - self.definition_chain.pop() - return result - else: - self.report_issue("Recursive Call", {"name": callee}) - elif isinstance(function_type, ClassType): - constructor = function_type.get_constructor().definition - self.definition_chain.append(constructor) - result = constructor(self, constructor, callee, arguments, self.locate()) - self.definition_chain.pop() - if '__init__' in function_type.fields: - initializer = function_type.fields['__init__'] - if isinstance(initializer, FunctionType): - self.definition_chain.append(initializer) - initializer.definition(self, initializer, result, [result] + arguments, self.locate()) - self.definition_chain.pop() - return result - else: - self.report_issue("Not a function", {"name": callee}) - return UnknownType() - - def visit_ClassDef(self, node): - class_name = node.name - new_class_type = ClassType(class_name) - self.store_variable(class_name, new_class_type) - # TODO: Define a new scope definition that executes the body - # TODO: find __init__, execute that - definitions_scope = self.scope_chain[:] - class_scope = Tifa.NewScope(self, definitions_scope, class_type=new_class_type) - with class_scope: - self.generic_visit(node) - - def visit_Compare(self, node): - # Handle left and right - left = self.visit(node.left) - comparators = [self.visit(compare) for compare in node.comparators] - - # Handle ops - for op, right in zip(node.ops, comparators): - if isinstance(op, (ast.Eq, ast.NotEq, ast.Is, ast.IsNot)): - continue - elif isinstance(op, (ast.Lt, ast.LtE, ast.GtE, ast.Gt)): - if are_types_equal(left, right): - if isinstance(left, ORDERABLE_TYPES): - continue - elif isinstance(op, (ast.In, ast.NotIn)): - if isinstance(right, INDEXABLE_TYPES): - continue - self.report_issue("Incompatible types", - {"left": left, "right": right, - "operation": op}) - return BoolType() - - def _visit_collection_loop(self, node): - # Handle the iteration list - iter = node.iter - iter_list_name = None - if isinstance(iter, ast.Name): - iter_list_name = iter.id - if iter_list_name == "___": - self.report_issue("Unconnected blocks", - {"position": self.locate(iter)}) - state = self.iterate_variable(iter_list_name, self.locate(iter)) - iter_type = state.type - else: - iter_type = self.visit(iter) - - if iter_type.is_empty(): - # TODO: It should check if its ONLY ever iterating over an empty list. - # For now, only reports if we are NOT in a function - if len(self.scope_chain) == 1: - self.report_issue("Iterating over empty list", - {"name": iter_list_name, - "position": self.locate(iter)}) - - if not isinstance(iter_type, INDEXABLE_TYPES): - self.report_issue("Iterating over Non-list", - {"name": iter_list_name, - "position": self.locate(iter)}) - - iter_subtype = iter_type.index(LiteralNum(0)) - - # Handle the iteration variable - iter_variable_name = self._walk_target(node.target, iter_subtype) - - if iter_variable_name and iter_list_name: - if iter_variable_name == iter_list_name: - self.report_issue("Iteration Problem", - {"name": iter_variable_name, - "position": self.locate(node.target)}) - - def visit_comprehension(self, node): - self._visit_collection_loop(node) - # Handle ifs, unless they're blank (None in Skulpt :) - if node.ifs: - self.visit_statements(node.ifs) - - def visit_Dict(self, node): - """ - Three types of dictionaries - - empty - - uniform type - - record - TODO: Handle records appropriately - """ - type = DictType() - if not node.keys: - type.empty = True - else: - type.empty = False - all_literals = True - keys, values, literals = [], [], [] - for key, value in zip(node.keys, node.values): - literal = self.get_literal(key) - key, value = self.visit(key), self.visit(value) - values.append(value) - keys.append(key) - if literal is not None: - literals.append(literal) - else: - all_literals = False - - if all_literals: - type.literals = literals - type.values = values - else: - type.keys = key - type.values = value - return type - - def visit_DictComp(self, node): - # TODO: Handle comprehension scope - for generator in node.generators: - self.visit(generator) - keys = self.visit(node.key) - values = self.visit(node.value) - return DictType(keys=keys, values=values) - - def visit_For(self, node): - self._visit_collection_loop(node) - # Handle the bodies - self.visit_statements(node.body) - self.visit_statements(node.orelse) - - def visit_FunctionDef(self, node): - # Name - function_name = node.name - position = self.locate() - definitions_scope = self.scope_chain[:] - - def definition(tifa, call_type, call_name, parameters, call_position): - function_scope = Tifa.NewScope(self, definitions_scope) - with function_scope: - # Process arguments - args = node.args.args - if len(args) != len(parameters): - self.report_issue('Incorrect Arity', {"position": position}) - # TODO: Handle special types of parameters - for arg, parameter in zip(args, parameters): - name = arg.arg - if arg.annotation: - self.visit(arg.annotation) - annotation = get_tifa_type(arg.annotation, self) - # TODO: Use parameter information to "fill in" empty lists - if isinstance(parameter, ListType) and isinstance(annotation, ListType): - if isinstance(parameter.subtype, UnknownType): - parameter.subtype = annotation.subtype - # TODO: Check that arg.type and parameter type match! - if not are_types_equal(annotation, parameter, True): - self.report_issue("Parameter Type Mismatch", - {"parameter": annotation, "parameter_name": name, - "argument": parameter}) - if parameter is not None: - parameter = parameter.clone_mutably() - self.store_variable(name, parameter, position) - if len(args) < len(parameters): - for undefined_parameter in parameters[len(args):]: - self.store_variable(name, UnknownType(), position) - self.visit_statements(node.body) - return_state = self.find_variable_scope("*return") - return_value = NoneType() - # TODO: Figure out if we are not returning something when we should - # If the pseudo variable exists, we load it and get its type - if return_state.exists and return_state.in_scope: - return_state = self.load_variable("*return", call_position) - return_value = return_state.type - if node.returns: - # self.visit(node.returns) - returns = get_tifa_type(node.returns, self) - if not are_types_equal(return_value, returns, True): - self.report_issue("Multiple Return Types", - {"expected": returns.precise_description(), - "actual": return_value.precise_description(), - "position": return_state.position}) - return return_value - - function = FunctionType(definition=definition, name=function_name) - self.store_variable(function_name, function) - return function - - def visit_GeneratorExp(self, node): - # TODO: Handle comprehension scope - for generator in node.generators: - self.visit(generator) - return GeneratorType(self.visit(node.elt)) - - def visit_If(self, node): - # Visit the conditional - self.visit(node.test) - - if len(node.orelse) == 1 and isinstance(node.orelse[0], ast.Pass): - self.report_issue("Malformed Conditional") - elif len(node.body) == 1 and isinstance(node.body[0], ast.Pass): - if node.orelse: - self.report_issue("Malformed Conditional") - - # Visit the bodies - this_path_id = self.path_chain[0] - if_path = Tifa.NewPath(self, this_path_id, "i") - with if_path: - for statement in node.body: - self.visit(statement) - else_path = Tifa.NewPath(self, this_path_id, "e") - with else_path: - for statement in node.orelse: - self.visit(statement) - - # Combine two paths into one - # Check for any names that are on the IF path - self.merge_paths(this_path_id, if_path.id, else_path.id) - - def visit_IfExp(self, node): - # Visit the conditional - self.visit(node.test) - - # Visit the body - body = self.visit(node.body) - - # Visit the orelse - orelse = self.visit(node.orelse) - - if are_types_equal(body, orelse): - return body - - # TODO: Union type? - return UnknownType() - - def visit_Import(self, node): - # Handle names - for alias in node.names: - asname = alias.asname or alias.name - module_type = self.load_module(alias.name) - self.store_variable(asname, module_type) - - def visit_ImportFrom(self, node): - # Handle names - for alias in node.names: - if node.module is None: - asname = alias.asname or alias.name - module_type = self.load_module(alias.name) - else: - module_name = node.module - asname = alias.asname or alias.name - module_type = self.load_module(module_name) - name_type = module_type.load_attr(alias.name, self, - callee_position=self.locate()) - self.store_variable(asname, name_type) - - def visit_Lambda(self, node): - # Name - position = self.locate() - definitions_scope = self.scope_chain[:] - - def definition(tifa, call_type, call_name, parameters, call_position): - function_scope = Tifa.NewScope(self, definitions_scope) - with function_scope: - # Process arguments - args = node.args.args - if len(args) != len(parameters): - self.report_issue('Incorrect Arity', {"position": position}) - # TODO: Handle special types of parameters - for arg, parameter in zip(args, parameters): - name = arg.arg - if parameter is not None: - parameter = parameter.clone_mutably() - self.store_variable(name, parameter, position) - if len(args) < len(parameters): - for undefined_parameter in parameters[len(args):]: - self.store_variable(name, UnknownType(), position) - return_value = self.visit(node.body) - return return_value - - return FunctionType(definition=definition) - - def visit_List(self, node): - type = ListType() - if node.elts: - type.empty = False - # TODO: confirm homogenous subtype - for elt in node.elts: - type.subtype = self.visit(elt) - else: - type.empty = True - return type - - def visit_ListComp(self, node): - # TODO: Handle comprehension scope - for generator in node.generators: - self.visit(generator) - return ListType(self.visit(node.elt)) - - def visit_NameConstant(self, node): - value = node.value - if isinstance(value, bool): - return BoolType() - else: - return NoneType() - - def visit_Name(self, node): - name = node.id - if name == "___": - self.report_issue("Unconnected blocks") - if isinstance(node.ctx, ast.Load): - if name == "True" or name == "False": - return BoolType() - elif name == "None": - return NoneType() - else: - variable = self.find_variable_scope(name) - builtin = get_builtin_function(name) - if not variable.exists and builtin: - return builtin - else: - state = self.load_variable(name) - return state.type - else: - variable = self.find_variable_scope(name) - if variable.exists: - return variable.state.type - else: - return UnknownType() - - def visit_Num(self, node): - return NumType() - - def visit_Return(self, node): - if len(self.scope_chain) == 1: - self.report_issue("Return outside function") - # TODO: Unconditional return inside loop - if node.value is not None: - self.return_variable(self.visit(node.value)) - else: - self.return_variable(NoneType()) - - def visit_SetComp(self, node): - # TODO: Handle comprehension scope - for generator in node.generators: - self.visit(generator) - return SetType(self.visit(node.elt)) - - def visit_statements(self, nodes): - # TODO: Check for pass in the middle of a series of statement - if any(isinstance(node, ast.Pass) for node in nodes): - pass - return [self.visit(statement) for statement in nodes] - - def visit_Str(self, node): - if node.s == "": - return StrType(True) - else: - return StrType(False) - - def visit_Subscript(self, node): - # Handle value - value_type = self.visit(node.value) - # Handle slice - if isinstance(node.slice, ast.Index): - literal = self.get_literal(node.slice.value) - if literal is None: - dynamic_literal = type_to_literal(self.visit(node.slice.value)) - return value_type.index(dynamic_literal) - else: - return value_type.index(literal) - elif isinstance(node.slice, ast.Slice): - if node.slice.lower is not None: - self.visit(node.slice.lower) - if node.slice.upper is not None: - self.visit(node.slice.upper) - if node.slice.step is not None: - self.visit(node.slice.step) - return value_type - - def visit_Tuple(self, node): - type = TupleType() - if not node.elts: - type.empty = True - type.subtypes = [] - else: - type.empty = False - # TODO: confirm homogenous subtype - type.subtypes = [self.visit(elt) for elt in node.elts] - return type - - def visit_UnaryOp(self, node): - # Handle operand - operand = self.visit(node.operand) - - if isinstance(node.op, ast.Not): - return BoolType() - elif isinstance(operand, UnknownType): - return UnknownType() - elif type(node.op) in VALID_UNARYOP_TYPES: - op_lookup = VALID_UNARYOP_TYPES[type(node.op)] - if type(operand) in op_lookup: - return op_lookup[type(operand)]() - return UnknownType() - - def visit_While(self, node): - # Visit conditional - self.visit(node.test) - - # Visit the bodies - this_path_id = self.path_id - # One path is that we never enter the body - empty_path = Tifa.NewPath(self, this_path_id, "e") - with empty_path: - pass - # Another path is that we loop through the body and check the test again - body_path = Tifa.NewPath(self, this_path_id, "w") - with body_path: - for statement in node.body: - self.visit(statement) - # Revisit conditional - self.visit(node.test) - # If there's else bodies (WEIRD) then we should check them afterwards - if node.orelse: - self.report_issue("Else on loop body") - for statement in node.orelse: - self.visit(statement) - - # Combine two paths into one - # Check for any names that are on the IF path - self.merge_paths(this_path_id, body_path.id, empty_path.id) - - def visit_With(self, node): - for item in node.items: - type_value = self.visit(item.context_expr) - self.visit(item.optional_vars) - self._walk_target(item.optional_vars, type_value) - # Handle the bodies - self.visit_statements(node.body) - - def _scope_chain_str(self, name=None): - """ - Convert the current scope chain to a string representation (divided - by "/"). - - Returns: - str: String representation of the scope chain. - """ - if name: - return "/".join(map(str, self.scope_chain)) + "/" + name - else: - return "/".join(map(str, self.scope_chain)) - - def identify_caller(self, node): - """ - Figures out the variable that was used to kick off this call, - which is almost always the relevant Name to track as being updated. - If the origin wasn't a Name, nothing will need to be updated so None - is returned instead. - - TODO: Is this sufficient? - - Args: - node (AST): An AST node - Returns: - str or None: The name of the variable or None if no origin could - be found. - """ - if isinstance(node, ast.Name): - return node.id - elif isinstance(node, ast.Call): - return self.identify_caller(node.func) - elif isinstance(node, (ast.Attribute, ast.Subscript)): - return self.identify_caller(node.value) - return None - - def iterate_variable(self, name, position=None): - """ - Update the variable by iterating through it - this doesn't do anything - fancy yet. - """ - return self.load_variable(name, position) - - def store_iter_variable(self, name, type, position=None): - state = self.store_variable(name, type, position) - state.read = 'yes' - return state - - def return_variable(self, type): - - return self.store_variable("*return", type) - - def append_variable(self, name, type, position=None): - return self.store_variable(name, type, position) - - def store_variable(self, name, type, position=None): - """ - Update the variable with the given name to now have the new type. - - Args: - name (str): The unqualified name of the variable. The variable will - be assumed to be in the current scope. - type (Type): The new type of this variable. - Returns: - State: The new state of the variable. - """ - if position is None: - position = self.locate() - full_name = self._scope_chain_str(name) - current_path = self.path_chain[0] - variable = self.find_variable_scope(name) - if not variable.exists: - # Create a new instance of the variable on the current path - new_state = State(name, [], type, 'store', position, - read='no', set='yes', over='no') - self.name_map[current_path][full_name] = new_state - else: - new_state = self.trace_state(variable.state, "store", position) - if not variable.in_scope: - self.report_issue("Write out of scope", {'name': name}) - # Type change? - if not are_types_equal(type, variable.state.type): - self.report_issue("Type changes", - {'name': name, 'old': variable.state.type, - 'new': type, 'position': position}) - new_state.type = type - # Overwritten? - if variable.state.set == 'yes' and variable.state.read == 'no': - new_state.over_position = position - new_state.over = 'yes' - else: - new_state.set = 'yes' - new_state.read = 'no' - self.name_map[current_path][full_name] = new_state - # If this is a class scope... - current_scope = self.scope_chain[0] - if current_scope in self.class_scopes: - self.class_scopes[current_scope].add_attr(name, new_state.type) - return new_state - - def load_variable(self, name, position=None): - """ - Retrieve the variable with the given name. - - Args: - name (str): The unqualified name of the variable. If the variable is - not found in the current scope or an enclosing sope, all - other scopes will be searched to see if it was read out - of scope. - Returns: - State: The current state of the variable. - """ - full_name = self._scope_chain_str(name) - current_path = self.path_chain[0] - variable = self.find_variable_scope(name) - if position is None: - position = self.locate() - if not variable.exists: - out_of_scope_var = self.find_variable_out_of_scope(name) - # Create a new instance of the variable on the current path - if out_of_scope_var.exists: - self.report_issue("Read out of scope", {'name': name}) - else: - self.report_issue("Initialization Problem", {'name': name}) - new_state = State(name, [], UnknownType(), 'load', position, - read='yes', set='no', over='no') - self.name_map[current_path][full_name] = new_state - else: - new_state = self.trace_state(variable.state, "load", position) - if variable.state.set == 'no': - self.report_issue("Initialization Problem", {'name': name}) - if variable.state.set == 'maybe': - self.report_issue("Possible Initialization Problem", {'name': name}) - new_state.read = 'yes' - if not variable.in_scope: - self.name_map[current_path][variable.scoped_name] = new_state - else: - self.name_map[current_path][full_name] = new_state - return new_state - - def load_module(self, chain): - """ - Finds the module in the set of available modules. - - Args: - chain (str): A chain of module imports (e.g., "matplotlib.pyplot") - Returns: - ModuleType: The specific module with its members, or an empty - module type. - """ - module_names = chain.split('.') - potential_module = get_builtin_module(module_names[0]) - if potential_module is not None: - base_module = potential_module - for module in module_names: - if (isinstance(base_module, ModuleType) and - module in base_module.submodules): - base_module = base_module.submodules[module] - else: - self.report_issue("Module not found", {"name": chain}) - return base_module - else: - try: - actual_module = __import__(chain, globals(), {}, - ['_tifa_definitions']) - definitions = actual_module._tifa_definitions() - return type_from_json(definitions) - except Exception as e: - self.report_issue("Module not found", - {"name": chain, "error": str(e)}) - return ModuleType() - - def combine_states(self, left, right): - state = State(left.name, [left], left.type, 'branch', self.locate(), - read=left.read, set=left.set, over=left.over, - over_position=left.over_position) - if right is None: - state.read = 'no' if left.read == 'no' else 'maybe' - state.set = 'no' if left.set == 'no' else 'maybe' - state.over = 'no' if left.over == 'no' else 'maybe' - else: - if not are_types_equal(left.type, right.type): - self.report_issue("Type changes", {'name': left.name, - 'old': left.type, - 'new': right.type}) - state.read = Tifa.match_rso(left.read, right.read) - state.set = Tifa.match_rso(left.set, right.set) - state.over = Tifa.match_rso(left.over, right.over) - if left.over == 'no': - state.over_position = right.over_position - state.trace.append(right) - return state - - def merge_paths(self, parent_path_id, left_path_id, right_path_id): - """ - Combines any variables on the left and right path into the parent - name space. - - Args: - parent_path_id (int): The parent path of the left and right branches - left_path_id (int): One of the two paths - right_path_id (int): The other of the two paths. - """ - # Combine two paths into one - # Check for any names that are on the IF path - for left_name in self.name_map[left_path_id]: - left_state = self.name_map[left_path_id][left_name] - right_identifier = self.find_path_parent(right_path_id, left_name) - if right_identifier.exists: - # Was on both IF and ELSE path - right_state = right_identifier.state - else: - # Was only on IF path, potentially on the parent path - right_state = self.name_map[parent_path_id].get(left_name) - combined = self.combine_states(left_state, right_state) - self.name_map[parent_path_id][left_name] = combined - # Check for names that are on the ELSE path but not the IF path - for right_name in self.name_map[right_path_id]: - if right_name not in self.name_map[left_path_id]: - right_state = self.name_map[right_path_id][right_name] - # Potentially on the parent path - parent_state = self.name_map[parent_path_id].get(right_name) - combined = self.combine_states(right_state, parent_state) - self.name_map[parent_path_id][right_name] = combined - - def trace_state(self, state, method, position): - """ - Makes a copy of the given state with the given method type. - - Args: - state (State): The state to copy (as in, we trace a copy of it!) - method (str): The operation being applied to the state. - Returns: - State: The new State - """ - return state.copy(method, position) - - @staticmethod - def in_scope(full_name, scope_chain): - """ - Determine if the fully qualified variable name is in the given scope - chain. - - Args: - full_name (str): A fully qualified variable name - scope_chain (list): A representation of a scope chain. - Returns: - bool: Whether the variable lives in this scope - """ - # Get this entity's full scope chain - name_scopes = full_name.split("/")[:-1] - # against the reverse scope chain - checking_scopes = [str(s) for s in scope_chain[::-1]] - return name_scopes == checking_scopes - - @staticmethod - def match_rso(left, right): - if left == right: - return left - else: - return "maybe" - - def get_literal(self, node): - if isinstance(node, ast.Num): - return LiteralNum(node.n) - elif isinstance(node, ast.Str): - return LiteralStr(node.s) - elif isinstance(node, ast.Tuple): - values = [] - for elt in node.elts: - subvalue = self.get_literal(elt) - if subvalue is not None: - values.append(subvalue) - else: - return None - return LiteralTuple(values) - elif isinstance(node, ast.Name): - if node.id == "None": - return LiteralNone() - elif node.id == "False": - return LiteralBool(False) - elif node.id == "True": - return LiteralBool(True) - return None - - class NewPath: - """ - Context manager for entering and leaving execution paths (e.g., if - statements).) - - Args: - tifa (Tifa): The tifa instance, so we can modify some of its - properties that track variables and paths. - origin_path (int): The path ID parent to this one. - name (str): The symbolic name of this path, typically 'i' for an IF - body and 'e' for ELSE body. - - Fields: - id (int): The path ID of this path - """ - - def __init__(self, tifa, origin_path, name): - self.tifa = tifa - self.name = name - self.origin_path = origin_path - self.id = None - - def __enter__(self): - self.tifa.path_id += 1 - self.id = self.tifa.path_id - self.tifa.path_names.append(str(self.id) + self.name) - self.tifa.path_chain.insert(0, self.id) - self.tifa.name_map[self.id] = {} - self.tifa.path_parents[self.id] = self.origin_path - - def __exit__(self, type, value, traceback): - self.tifa.path_names.pop() - self.tifa.path_chain.pop(0) - - class NewScope: - """ - Context manager for entering and leaving scopes (e.g., inside of - function calls). - - Args: - tifa (Tifa): The tifa instance, so we can modify some of its - properties that track variables and paths. - definitions_scope_chain (list of int): The scope chain of the - definition - """ - - def __init__(self, tifa, definitions_scope_chain, class_type=None): - self.tifa = tifa - self.definitions_scope_chain = definitions_scope_chain - self.class_type = class_type - - def __enter__(self): - # Manage scope - self.old_scope = self.tifa.scope_chain[:] - # Move to the definition's scope chain - self.tifa.scope_chain = self.definitions_scope_chain[:] - # And then enter its body's new scope - self.tifa.scope_id += 1 - self.tifa.scope_chain.insert(0, self.tifa.scope_id) - # Register as class potentially - if self.class_type is not None: - self.class_type.scope_id = self.tifa.scope_id - self.tifa.class_scopes[self.tifa.scope_id] = self.class_type - - def __exit__(self, type, value, traceback): - # Finish up the scope - self.tifa._finish_scope() - # Leave the body - self.tifa.scope_chain.pop(0) - # Restore the scope - self.tifa.scope_chain = self.old_scope diff --git a/src/lib/pedal/tifa/type_definitions.py b/src/lib/pedal/tifa/type_definitions.py deleted file mode 100644 index 8c6d8ce2f6..0000000000 --- a/src/lib/pedal/tifa/type_definitions.py +++ /dev/null @@ -1,599 +0,0 @@ -import ast - - -def are_literals_equal(first, second): - if first is None or second is None: - return False - elif not isinstance(first, type(second)): - return False - else: - if isinstance(first, LiteralTuple): - if len(first.value) != len(second.value): - return False - for l, s in zip(first.value, second.value): - if not are_literals_equal(l, s): - return False - return True - elif not isinstance(first, LiteralValue): - return True - else: - return first.value == second.value - - -class LiteralValue: - """ - A special literal representation of a value, used to represent access on - certain container types. - """ - - def __init__(self, value): - self.value = value - - -class LiteralNum(LiteralValue): - """ - Used to capture indexes of containers. - """ - - def type(self): - return NumType() - - -class LiteralBool(LiteralValue): - def type(self): - return BoolType() - - -class LiteralStr(LiteralValue): - def type(self): - return StrType() - - -class LiteralTuple(LiteralValue): - def type(self): - return TupleType(self.value) - - -class LiteralNone(LiteralValue): - def type(self): - return LiteralNone() - - -def literal_from_json(val): - if val['type'] == 'LiteralStr': - return LiteralStr(val['value']) - elif val['type'] == 'LiteralNum': - return LiteralNum(val['value']) - elif val['type'] == 'LiteralBool': - return LiteralBool(val['value']) - - -def _dict_extends(d1, d2): - """ - Helper function to create a new dictionary with the contents of the two - given dictionaries. Does not modify either dictionary, and the values are - copied shallowly. If there are repeates, the second dictionary wins ties. - - The function is written to ensure Skulpt compatibility. - - Args: - d1 (dict): The first dictionary - d2 (dict): The second dictionary - """ - d3 = {} - for key, value in d1.items(): - d3[key] = value - for key, value in d2.items(): - d3[key] = value - return d3 - - -class Type: - """ - Parent class for all other types, used to provide a common interface. - - TODO: Handle more complicated object-oriented types and custom types - (classes). - """ - fields = {} - immutable = False - singular_name = 'a type' - - def clone(self): - return self.__class__() - - def __str__(self): - return str(self.__class__.__name__) - - def precise_description(self): - return self.singular_name - - def clone_mutably(self): - if self.immutable: - return self.clone() - else: - return self - - def index(self, i): - return self.clone() - - def load_attr(self, attr, tifa, callee=None, callee_position=None): - if attr in self.fields: - return self.fields[attr] - # TODO: Handle more kinds of common mistakes - if attr == "append": - tifa.report_issue('Append to non-list', - {'name': tifa.identify_caller(callee), - 'position': callee_position, 'type': self}) - return UnknownType() - - def is_empty(self): - return True - - def is_equal(self, other): - # TODO: Make this more sophisticated! - if type(self) not in TYPE_LOOKUPS: - return False - return other in TYPE_LOOKUPS[type(self)] - - def is_instance(self, other): - # TODO: Implement this correctly - return self.is_equal(other) - - -class UnknownType(Type): - """ - A special type used to indicate an unknowable type. - """ - - -class RecursedType(Type): - """ - A special type used as a placeholder for the result of a - recursive call that we have already process. This type will - be dominated by any actual types, but will not cause an issue. - """ - - -class FunctionType(Type): - """ - - Special values for `returns`: - identity: Returns the first argument's type - element: Returns the first argument's first element's type - void: Returns the NoneType - """ - singular_name = 'a function' - - def __init__(self, definition=None, name="*Anonymous", returns=None): - if returns is not None and definition is None: - if returns == 'identity': - def definition(ti, ty, na, args, ca): - if args: - return args[0].clone() - return UnknownType() - elif returns == 'element': - def definition(ti, ty, na, args, ca): - if args: - return args[0].index(0) - return UnknownType() - elif returns == 'void': - def definition(ti, ty, na, args, ca): - return NoneType() - else: - def definition(ti, ty, na, args, ca): - return returns.clone() - self.definition = definition - self.name = name - - -class ClassType(Type): - singular_name = 'a class' - - def __init__(self, name): - self.name = name - self.fields = {} - self.scope_id = None - - def add_attr(self, name, type): - self.fields[name] = type - - def get_constructor(self): - i = InstanceType(self) - return FunctionType(name='__init__', returns=i) - - def clone(self): - return ClassType(self.name) - - -class InstanceType(Type): - def __init__(self, parent): - self.parent = parent - self.fields = parent.fields - - def __str__(self): - return "InstanceTypeOf" + str(self.parent.name) - - def clone(self): - return InstanceType(self.parent) - - def add_attr(self, name, type): - # TODO: What if this is a type change? - self.parent.add_attr(name, type) - - -class NumType(Type): - singular_name = 'a number' - immutable = True - - def index(self, i): - return UnknownType() - - -class NoneType(Type): - singular_name = 'a None' - immutable = True - - -class BoolType(Type): - singular_name = 'a boolean' - immutable = True - - -class TupleType(Type): - """ - """ - singular_name = 'a tuple' - - def __init__(self, subtypes=None): - if subtypes is None: - subtypes = [] - self.subtypes = subtypes - - def index(self, i): - if isinstance(i, LiteralNum): - return self.subtypes[i.value].clone() - else: - return self.subtypes[i].clone() - - def clone(self): - return TupleType([t.clone() for t in self.subtypes]) - - immutable = True - - -class ListType(Type): - singular_name = 'a list' - - def __init__(self, subtype=None, empty=True): - if subtype is None: - subtype = UnknownType() - self.subtype = subtype - self.empty = empty - - def index(self, i): - return self.subtype.clone() - - def clone(self): - return ListType(self.subtype.clone(), self.empty) - - def load_attr(self, attr, tifa, callee=None, callee_position=None): - if attr == 'append': - def _append(tifa, function_type, callee, args, position): - if args: - cloned_type = ListType(subtype=args[0].clone(), - empty=False) - if callee: - tifa.append_variable(callee, cloned_type, position) - self.empty = False - self.subtype = args[0] - - return FunctionType(_append, 'append') - return Type.load_attr(self, attr, tifa, callee, callee_position) - - def is_empty(self): - return self.empty - - -class StrType(Type): - singular_name = 'a string' - - def __init__(self, empty=False): - self.empty = empty - - def index(self, i): - return StrType() - - def is_empty(self): - return self.empty - - fields = _dict_extends(Type.fields, {}) - immutable = True - - -StrType.fields.update({ - # Methods that return strings - "capitalize": FunctionType(name='capitalize', returns=StrType()), - "center": FunctionType(name='center', returns=StrType()), - "expandtabs": FunctionType(name='expandtabs', returns=StrType()), - "join": FunctionType(name='join', returns=StrType()), - "ljust": FunctionType(name='ljust', returns=StrType()), - "lower": FunctionType(name='lower', returns=StrType()), - "lstrip": FunctionType(name='lstrip', returns=StrType()), - "replace": FunctionType(name='replace', returns=StrType()), - "rjust": FunctionType(name='rjust', returns=StrType()), - "rstrip": FunctionType(name='rstrip', returns=StrType()), - "strip": FunctionType(name='strip', returns=StrType()), - "swapcase": FunctionType(name='swapcase', returns=StrType()), - "title": FunctionType(name='title', returns=StrType()), - "translate": FunctionType(name='translate', returns=StrType()), - "upper": FunctionType(name='upper', returns=StrType()), - "zfill": FunctionType(name='zfill', returns=StrType()), - # Methods that return numbers - "count": FunctionType(name='count', returns=NumType()), - "find": FunctionType(name='find', returns=NumType()), - "rfind": FunctionType(name='rfind', returns=NumType()), - "index": FunctionType(name='index', returns=NumType()), - "rindex": FunctionType(name='rindex', returns=NumType()), - # Methods that return booleans - "startswith": FunctionType(name='startswith', returns=BoolType()), - "endswith": FunctionType(name='endswith', returns=BoolType()), - "isalnum": FunctionType(name='isalnum', returns=BoolType()), - "isalpha": FunctionType(name='isalpha', returns=BoolType()), - "isdigit": FunctionType(name='isdigit', returns=BoolType()), - "islower": FunctionType(name='islower', returns=BoolType()), - "isspace": FunctionType(name='isspace', returns=BoolType()), - "istitle": FunctionType(name='istitle', returns=BoolType()), - "isupper": FunctionType(name='isupper', returns=BoolType()), - # Methods that return List of Strings - "rsplit": FunctionType(name='rsplit', returns=ListType(StrType(), empty=False)), - "split": FunctionType(name='split', returns=ListType(StrType(), empty=False)), - "splitlines": FunctionType(name='splitlines', returns=ListType(StrType(), empty=False)) -}) - - -class FileType(Type): - singular_name = 'a file' - - def index(self, i): - return StrType() - - fields = _dict_extends(Type.fields, { - 'close': FunctionType(name='close', returns='void'), - 'read': FunctionType(name='read', returns=StrType()), - 'readlines': FunctionType(name='readlines', returns=ListType(StrType(), False)) - }) - - def is_empty(self): - return False - - -class DictType(Type): - singular_name = 'a dictionary' - - def precise_description(self): - base = "a dictionary" - if self.literals: - base += " mapping " - # TODO: Handle recursive precise names more correctly - base += ", ".join("{!r} to {}".format(l.value, r.precise_description()) - for l, r in zip(self.literals, self.values)) - elif self.keys: - keys = self.keys[0] if isinstance(self.keys, list) else self.keys - values = self.values[0] if isinstance(self.values, list) else self.values - base += " mapping {}".format(keys.precise_description()) - base += " to {}".format(values.precise_description()) - return base - - def __init__(self, empty=False, literals=None, keys=None, values=None): - self.empty = empty - self.literals = literals - self.values = values - self.keys = keys - - def clone(self): - return DictType(self.empty, self.literals, self.keys, self.values) - - def is_empty(self): - return self.empty - - def has_literal(self, l): - for literal, value in zip(self.literals, self.values): - if are_literals_equal(literal, l): - return value - return None - - def index(self, i): - if self.empty: - return UnknownType() - elif self.literals is not None: - for literal, value in zip(self.literals, self.values): - if are_literals_equal(literal, i): - return value.clone() - return UnknownType() - else: - return self.keys.clone() - - def update_key(self, literal_key, type): - self.literals.append(literal_key) - self.values.append(type) - - def load_attr(self, attr, tifa, callee=None, callee_position=None): - if attr == 'items': - def _items(tifa, function_type, callee, args, position): - if self.literals is None: - return ListType(TupleType([self.keys, self.values]), - empty=False) - else: - return ListType(TupleType([self.literals[0].type(), - self.values[0]]), - empty=False) - - return FunctionType(_items, 'items') - elif attr == 'keys': - def _keys(tifa, function_type, callee, args, position): - if self.literals is None: - return ListType(self.keys, empty=False) - else: - return ListType(self.literals[0].type(), empty=False) - - return FunctionType(_keys, 'keys') - elif attr == 'values': - def _values(tifa, function_type, callee, args, position): - if self.literals is None: - return ListType(self.values, empty=False) - else: - return ListType(self.values[0], empty=False) - - return FunctionType(_values, 'values') - return Type.load_attr(self, attr, tifa, callee, callee_position) - - -class ModuleType(Type): - singular_name = 'a module' - - def __init__(self, name="*UnknownModule", submodules=None, fields=None): - self.name = name - if submodules is None: - submodules = {} - self.submodules = submodules - if fields is None: - fields = {} - self.fields = fields - - -class SetType(ListType): - singular_name = 'a set' - - -class GeneratorType(ListType): - singular_name = 'a generator' - - -# Custom parking class in blockpy - -class TimeType(Type): - singular_name = 'a time of day' - - -class DayType(Type): - singular_name = 'a day of the week' - - -try: - from numbers import Number -except Exception: - Number = int - -TYPE_LOOKUPS = { - FunctionType: ('function', FunctionType, 'FunctionType'), - ClassType: ('class', ClassType, 'ClassType'), - InstanceType: ('instance', InstanceType, 'InstanceType'), - NumType: ('num', int, float, complex, NumType, Number, 'NumType'), - BoolType: ('bool', bool, BoolType, 'BoolType'), - NoneType: ('None', None, NoneType, 'NoneType'), - TupleType: ('tuple', tuple, TupleType, 'TupleType'), - ListType: ('list', list, ListType, 'ListType'), - StrType: ('str', str, StrType, 'StrType'), - FileType: ('file', FileType, 'FileType'), - DictType: ('dict', dict, DictType, 'DictType'), - SetType: ('set', set, SetType, 'SetType'), -} - - -def type_from_json(val): - if val['type'] == 'DictType': - values = [type_from_json(v) for v in val['values']] - empty = val.get('empty', None) - if 'literals' in val: - literals = [literal_from_json(l) for l in val['literals']] - return DictType(empty, literals=literals, values=values) - else: - keys = [type_from_json(k) for k in val['keys']] - return DictType(empty, keys=keys, values=values) - elif val['type'] == 'ListType': - return ListType(type_from_json(val.get('subtype', None)), - val.get('empty', None)) - elif val['type'] == 'StrType': - return StrType(val.get('empty', None)) - elif val['type'] == 'BoolType': - return BoolType() - elif val['type'] == 'NoneType': - return NoneType() - elif val['type'] == 'NumType': - return NumType() - elif val['type'] == 'ModuleType': - submodules = {name: type_from_json(m) - for name, m in val.get('submodules', {}).items()} - fields = {name: type_from_json(m) - for name, m in val.get('fields', {}).items()} - return ModuleType(name=val.get('name'), submodules=submodules, - fields=fields) - elif val['type'] == 'FunctionType': - returns = type_from_json(val.get('returns', {'type': 'NoneType'})) - return FunctionType(name=val.get('name'), returns=returns) - - -def type_to_literal(type): - if isinstance(type, NumType): - return LiteralNum(0) - elif isinstance(type, StrType): - return LiteralStr("") - else: - # TODO: Finish the mapping - return LiteralStr("") - - -TYPE_STRINGS = { - "str": StrType, "string": StrType, - "num": NumType, "number": NumType, "int": NumType, "integer": NumType, "float": NumType, - "complex": NumType, - "bool": BoolType, "boolean": BoolType, - "none": NoneType, - "dict": DictType, "dictionary": DictType, - "list": ListType, - "tuple": TupleType, - "set": SetType, - "file": FileType, - "func": FunctionType, "function": FunctionType, - "class": ClassType, -} - - -def get_tifa_type_from_str(value, self): - # if value in custom_types: - # return custom_types[value] - if value.lower() in TYPE_STRINGS: - return TYPE_STRINGS[value.lower()]() - else: - variable = self.find_variable_scope(value) - if variable.exists: - state = self.load_variable(value) - return state.type - # custom_types.add(value) - return UnknownType() - # TODO: handle custom types - - -def get_tifa_type(v, self): - if isinstance(v, ast.Str): - return get_tifa_type_from_str(v.s, self) - elif isinstance(v, ast.Name): - return get_tifa_type_from_str(v.id, self) - elif isinstance(v, ast.List): - elements = v.elts - if elements: - return ListType(subtype=get_tifa_type(elements[0], self)) - else: - return ListType(empty=True) - elif isinstance(v, ast.Dict): - if not v.keys: - return DictType(empty=True) - if all(isinstance(k, ast.Str) for k in v.keys): - return DictType(literals=[LiteralStr(s.s) for s in v.keys], - values=[get_tifa_type(vv, self) for vv in v.values]) - return DictType(keys=[get_tifa_type(k, self) for k in v.keys], - values=[get_tifa_type(vv, self) for vv in v.values]) - # TODO: Finish filling in static type system - else: - return UnknownType() diff --git a/src/lib/pedal/tifa/type_operations.py b/src/lib/pedal/tifa/type_operations.py deleted file mode 100644 index 5fef8fde5a..0000000000 --- a/src/lib/pedal/tifa/type_operations.py +++ /dev/null @@ -1,153 +0,0 @@ -import ast - -from pedal.tifa.type_definitions import (UnknownType, NumType, BoolType, - TupleType, ListType, StrType, - DictType, SetType, GeneratorType, - DayType, TimeType, FunctionType, TYPE_STRINGS) - - -def merge_types(left, right): - # TODO: Check that lists/sets have the same subtypes - if isinstance(left, (ListType, SetType, GeneratorType)): - if left.empty: - return right.subtype - else: - return left.subtype.clone() - elif isinstance(left, TupleType): - return left.subtypes + right.subtypes - - -def NumType_any(*x): - return NumType() - - -def StrType_any(*x): - return StrType() - - -def BoolType_any(*x): - return BoolType() - - -def keep_left(left, right): - return left - - -def keep_right(left, right): - return right - - -VALID_BINOP_TYPES = { - ast.Add: {NumType: {NumType: NumType_any}, - StrType: {StrType: StrType_any}, - ListType: {ListType: merge_types}, - TupleType: {TupleType: merge_types}}, - ast.Sub: {NumType: {NumType: NumType_any}, - SetType: {SetType: merge_types}}, - ast.Div: {NumType: {NumType: NumType_any}}, - ast.FloorDiv: {NumType: {NumType: NumType_any}}, - ast.Mult: {NumType: {NumType: NumType_any, - StrType: StrType_any, - ListType: keep_right, - TupleType: keep_right}, - StrType: {NumType: StrType_any}, - ListType: {NumType: keep_left}, - TupleType: {NumType: keep_left}}, - ast.Pow: {NumType: {NumType: NumType_any}}, - # TODO: Should we allow old-fashioned string interpolation? - # Currently, I vote no because it makes the code harder and is bad form. - ast.Mod: {NumType: {NumType: NumType_any}}, - ast.LShift: {NumType: {NumType: NumType_any}}, - ast.RShift: {NumType: {NumType: NumType_any}}, - ast.BitOr: {NumType: {NumType: NumType_any}, - BoolType: {NumType: NumType_any, - BoolType: BoolType_any}, - SetType: {SetType: merge_types}}, - ast.BitXor: {NumType: {NumType: NumType_any}, - BoolType: {NumType: NumType_any, - BoolType: BoolType_any}, - SetType: {SetType: merge_types}}, - ast.BitAnd: {NumType: {NumType: NumType_any}, - BoolType: {NumType: NumType_any, - BoolType: BoolType_any}, - SetType: {SetType: merge_types}} -} -VALID_UNARYOP_TYPES = { - ast.UAdd: {NumType: NumType}, - ast.USub: {NumType: NumType}, - ast.Invert: {NumType: NumType} -} - - -def are_types_equal(left, right, formal=False): - """ - Determine if two types are equal. - - This could be more Polymorphic - move the code for each type into - its respective class instead. - - Args: - formal (bool): Whether the left argument is formal, indicating that it can accept - type names. - """ - if left is None or right is None: - return False - elif isinstance(left, UnknownType) or isinstance(right, UnknownType): - return False - elif not isinstance(left, type(right)): - return False - elif isinstance(left, (GeneratorType, ListType)): - if left.empty or right.empty: - return True - else: - return are_types_equal(left.subtype, right.subtype) - elif isinstance(left, TupleType): - if left.empty or right.empty: - return True - elif len(left.subtypes) != len(right.subtypes): - return False - else: - for l, r in zip(left.subtypes, right.subtypes): - if not are_types_equal(l, r): - return False - return True - elif isinstance(left, DictType): - # print(left.empty, left.keys, left.literals, right) - if not left.keys and not left.literals: - return isinstance(right, DictType) - # print("L", [literal.value for literal in left.literals], [v.singular_name - # if not formal and not isinstance(v, FunctionType) - # else TYPE_STRINGS[v.name]().singular_name - # for v in left.values]) - # print("R", [literal.value for literal in right.literals], [v.singular_name for v in right.values]) - if left.empty or right.empty: - return True - elif left.literals is not None and right.literals is not None: - if len(left.literals) != len(right.literals): - return False - else: - for l, r in zip(left.literals, right.literals): - if not are_types_equal(l, r): - return False - for l, r in zip(left.values, right.values): - if formal: - if isinstance(l, FunctionType) and l.name in TYPE_STRINGS: - l = TYPE_STRINGS[l.name]() - if isinstance(r, FunctionType) and r.name in TYPE_STRINGS: - r = TYPE_STRINGS[r.name]() - if not are_types_equal(l, r): - return False - return True - elif left.literals is not None or right.literals is not None: - return False - else: - keys_equal = are_types_equal(left.keys, right.keys) - values_equal = are_types_equal(left.values, right.values) - return keys_equal and values_equal - else: - return True - - -ORDERABLE_TYPES = (NumType, BoolType, StrType, ListType, DayType, TimeType, - SetType, TupleType) -INDEXABLE_TYPES = (StrType, ListType, SetType, TupleType, DictType) diff --git a/src/lib/pedal/toolkit/__init__.py b/src/lib/pedal/toolkit/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/lib/pedal/toolkit/files.py b/src/lib/pedal/toolkit/files.py deleted file mode 100644 index e8edf6c73f..0000000000 --- a/src/lib/pedal/toolkit/files.py +++ /dev/null @@ -1,56 +0,0 @@ -from pedal.cait.cait_api import parse_program -from pedal.report.imperative import explain -from pedal.toolkit.utilities import ensure_literal - - -def files_not_handled_correctly(*filenames): - """ - Statically detect if files have been opened and closed correctly. - This is only useful in the case of very simplistic file handling. - """ - if filenames and isinstance(filenames[0], int): - num_filenames = filenames[0] - actual_filenames = False - else: - num_filenames = len(filenames) - actual_filenames = True - ast = parse_program() - calls = ast.find_all("Call") - called_open = [] - closed = [] - for a_call in calls: - if a_call.func.ast_name == 'Name': - if a_call.func.id == 'open': - if not a_call.args: - explain("You have called the open function " - "without any arguments. It needs a filename.") - return True - called_open.append(a_call) - elif a_call.func.id == 'close': - explain("You have attempted to call close as a " - "function, but it is actually a method of the " - "file object.", 'verifier') - return True - elif a_call.func.ast_name == 'Attribute': - if a_call.func.attr == 'open': - explain("You have attempted to call open as a " - "method, but it is actually a built-in function.") - return True - elif a_call.func.attr == 'close': - closed.append(a_call) - if len(called_open) < num_filenames: - explain("You have not opened all the files you were supposed to.") - return True - elif len(called_open) > num_filenames: - explain("You have opened more files than you were supposed to.") - return True - withs = ast.find_all("With") - if len(withs) + len(closed) < num_filenames: - explain("You have not closed all the files you were supposed to.") - return True - elif len(withs) + len(closed) > num_filenames: - explain("You have closed more files than you were supposed to.") - return True - if actual_filenames: - return ensure_literal(*filenames) - return False diff --git a/src/lib/pedal/toolkit/functions.py b/src/lib/pedal/toolkit/functions.py deleted file mode 100644 index 9dad896dfa..0000000000 --- a/src/lib/pedal/toolkit/functions.py +++ /dev/null @@ -1,401 +0,0 @@ -from pedal.cait.cait_api import parse_program -from pedal.report.imperative import gently, explain, gently_r, explain_r, MAIN_REPORT -from pedal.sandbox import compatibility -import ast - -from pedal.toolkit.signatures import type_check, parse_type, normalize_type, parse_type_value, test_type_equality - -DELTA = 0.001 - - -def all_documented(): - ast = parse_program() - defs = ast.find_all('FunctionDef') + ast.find_all("ClassDef") - for a_def in defs: - if a_def.name == "__init__": - continue - if (a_def.body and - (a_def.body[0].ast_name != "Expr" or - a_def.body[0].value.ast_name != "Str")): - if a_def.ast_name == 'FunctionDef': - gently("You have an undocumented function: " + a_def.name) - else: - gently("You have an undocumented class: " + a_def.name) - return False - return True - - -def get_arg_name(node): - name = node.id - if name is None: - return node.arg - else: - return name - - -def match_function(name, root=None): - if root is None: - ast = parse_program() - else: - ast = root - defs = ast.find_all('FunctionDef') - for a_def in defs: - if a_def._name == name: - return a_def - return None - - -def match_signature_muted(name, length, *parameters): - ast = parse_program() - defs = ast.find_all('FunctionDef') - for a_def in defs: - if a_def._name == name: - found_length = len(a_def.args.args) - if found_length != length: - return None - elif parameters: - for parameter, arg in zip(parameters, a_def.args.args): - arg_name = get_arg_name(arg) - if arg_name != parameter: - return None - else: - return a_def - else: - return a_def - return None - - -def find_def_by_name(name, root=None): - if root is None: - root = parse_program() - defs = root.find_all('FunctionDef') - for a_def in defs: - if a_def._name == name: - return a_def - return None - - -def match_parameters(name, *types, returns=None, root=None): - defn = find_def_by_name(name, root) - if defn: - for expected, actual in zip(types, defn.args.args): - if actual.annotation: - expected = parse_type_value(expected, True) - actual_type = parse_type(actual.annotation) - if not test_type_equality(expected, actual_type): - gently_r("Error in definition of function `{}` parameter `{}`. Expected `{}`, " - "instead found `{}`.".format(name, actual.arg, expected, actual_type), - "wrong_parameter_type") - return None - else: - if returns is not None: - if not isinstance(returns, str): - returns = returns.__name__ - if defn.returns: - actual_type = parse_type(defn.returns) - if not type_check(returns, actual_type): - gently_r("Error in definition of function `{}` return type. Expected `{}`, " - "instead found {}.".format(name, returns, actual_type), - "wrong_return_type") - return None - else: - gently_r("Error in definition of function `{}` return type. Expected `{}`, " - "but there was no return type specified.".format(name, returns), - "missing_return_type") - return None - return defn - - -def match_signature(name, length, *parameters): - ast = parse_program() - defs = ast.find_all('FunctionDef') - for a_def in defs: - if a_def._name == name: - found_length = len(a_def.args.args) - if found_length < length: - gently_r("The function named {} has fewer parameters ({}) " - "than expected ({}). ".format(name, found_length, length), "insuff_args") - elif found_length > length: - gently_r("The function named {} has more parameters ({}) " - "than expected ({}). ".format(name, found_length, length), "excess_args") - elif parameters: - for parameter, arg in zip(parameters, a_def.args.args): - arg_name = get_arg_name(arg) - if arg_name != parameter: - gently_r("Error in definition of {}. Expected a parameter named {}, " - "instead found {}.".format(name, parameter, arg_name), "name_missing") - return None - else: - return a_def - else: - return a_def - else: - gently_r("No function named {name} was found.".format(name=name), - "missing_func_{name}".format(name=name)) - return None - - -TEST_TABLE_HEADER = "" -TEST_TABLE_OUTPUT = TEST_TABLE_HEADER + ( - "" -) -TEST_TABLE_UNITS = TEST_TABLE_HEADER + ( - "" -) -GREEN_CHECK = "" -RED_X = "" - - -def output_test(name, *tests): - student = compatibility.get_student_data() - if name in student.data: - the_function = student.data[name] - if callable(the_function): - result = TEST_TABLE_OUTPUT - success = True - success_count = 0 - for test in tests: - inp = test[:-1] - inputs = ', '.join(["{}".format(repr(i)) for i in inp]) - out = test[-1] - tip = "" - if isinstance(out, tuple): - tip = out[1] - out = out[0] - message = "" + ("" * 2) - test_out = compatibility.capture_output(the_function, *inp) - if isinstance(out, str): - if len(test_out) < 1: - message = message.format(inputs, repr(out), "No output", tip) - message = "" + RED_X + message + "" - if tip: - message += "" - success = False - elif len(test_out) > 1: - message = message.format(inputs, "\n".join(out), "Too many outputs", tip) - message = "" + RED_X + message + "" - if tip: - message += "" - success = False - elif out not in test_out: - message = message.format(inputs, "\n".join(out), "\n".join(test_out), tip) - message = "" + RED_X + message + "" - if tip: - message += "" - success = False - else: - message = message.format(inputs, "\n".join(out), "\n".join(test_out), tip) - message = "" + GREEN_CHECK + message + "" - success_count += 1 - elif out != test_out: - if len(test_out) < 1: - message = message.format(inputs, "\n".join(out), "No output", tip) - else: - message = message.format(inputs, "\n".join(out), "\n".join(test_out), tip) - message = "" + RED_X + message + "" - if tip: - message += "" - success = False - else: - message = message.format(inputs, "\n".join(out), "\n".join(test_out), tip) - message = "" + GREEN_CHECK + message + "" - success_count += 1 - result += message - if success: - return the_function - else: - result = ("I ran your function {} on some new arguments, and it gave the wrong output " - "{}/{} times.".format(name, len(tests) - success_count, len(tests)) + result) - gently_r(result + "
    ArgumentsExpectedActual
    ArgumentsReturnedExpected
    {}
    {}
    " + tip + "
    " + tip + "
    " + tip + "
    " + tip + "
    ", "wrong_output") - return None - else: - gently_r("You defined {}, but did not define it as a function.".format(name), "not_func_def") - return None - else: - gently_r("The function {} was not defined.".format(name), "no_func_def") - return None - - -def unit_test(name, *tests): - """ - Show a table - :param name: - :param tests: - :return: - """ - student = compatibility.get_student_data() - if name in student.data: - the_function = student.data[name] - if callable(the_function): - result = TEST_TABLE_UNITS - success = True - success_count = 0 - for test in tests: - inp = test[:-1] - inputs = ', '.join(["{}".format(repr(i)) for i in inp]) - out = test[-1] - tip = "" - if isinstance(out, tuple): - tip = out[1] - out = out[0] - message = ("{}" * 3) - ran = True - try: - test_out = the_function(*inp) - except Exception as e: - message = message.format(inputs, str(e), repr(out)) - message = "" + RED_X + message + "" - success = False - ran = False - if not ran: - result += message - continue - message = message.format(inputs, repr(test_out), repr(out)) - if (isinstance(out, float) and - isinstance(test_out, (float, int)) and - abs(out - test_out) < DELTA): - message = "" + GREEN_CHECK + message + "" - success_count += 1 - elif out != test_out: - # gently(message) - message = "" + RED_X + message + "" - if tip: - message += "" + tip + "" - success = False - else: - message = "" + GREEN_CHECK + message + "" - success_count += 1 - result += message - if success: - return the_function - else: - result = "I ran your function {} on some new arguments, " \ - "and it failed {}/{} tests.".format(name, len(tests) - success_count, len(tests)) + result - gently_r(result + "", "tests_failed") - return None - else: - gently("You defined {}, but did not define it as a function.".format(name)) - return None - else: - gently("The function {} was not defined.".format(name)) - return None - - -class _LineVisitor(ast.NodeVisitor): - """ - NodeVisitor subclass that visits every statement of a program and tracks - their line numbers in a list. - - Attributes: - lines (list[int]): The list of lines that were visited. - """ - - def __init__(self): - self.lines = [] - - def _track_lines(self, node): - self.lines.append(node.lineno) - self.generic_visit(node) - - visit_FunctionDef = _track_lines - visit_AsyncFunctionDef = _track_lines - visit_ClassDef = _track_lines - visit_Return = _track_lines - visit_Delete = _track_lines - visit_Assign = _track_lines - visit_AugAssign = _track_lines - visit_AnnAssign = _track_lines - visit_For = _track_lines - visit_AsyncFor = _track_lines - visit_While = _track_lines - visit_If = _track_lines - visit_With = _track_lines - visit_AsyncWith = _track_lines - visit_Raise = _track_lines - visit_Try = _track_lines - visit_Assert = _track_lines - visit_Import = _track_lines - visit_ImportFrom = _track_lines - visit_Global = _track_lines - visit_Nonlocal = _track_lines - visit_Expr = _track_lines - visit_Pass = _track_lines - visit_Continue = _track_lines - visit_Break = _track_lines - - -def check_coverage(report=None): - """ - Checks that all the statements in the program have been executed. - This function only works when a tracer_style has been set in the sandbox, - or you are using an environment that automatically traces calls (e.g., - BlockPy). - - TODO: Make compatible with tracer_style='coverage' - - Args: - report (Report): The Report to draw source code from; if not given, - defaults to MAIN_REPORT. - Returns: - bool or set[int]: If the source file was not parsed, None is returned. - If there were fewer lines traced in execution than are found in - the AST, then the set of unexecuted lines are returned. Otherwise, - False is returned. - """ - if report is None: - report = MAIN_REPORT - if not report['source']['success']: - return None, 0 - lines_executed = set(compatibility.trace_lines()) - if -1 in lines_executed: - lines_executed.remove(-1) - student_ast = report['source']['ast'] - visitor = _LineVisitor() - visitor.visit(student_ast) - lines_in_code = set(visitor.lines) - if lines_executed < lines_in_code: - return lines_in_code - lines_executed, len(lines_executed) / len(lines_in_code) - else: - return False, 1 - - -def ensure_coverage(percentage=.5, destructive=False, report=None): - """ - Note that this avoids destroying the current sandbox instance stored on the - report, if there is one present. - - Args: - destructive (bool): Whether or not to remove the sandbox. - """ - if report is None: - report = MAIN_REPORT - student_code = report['source']['code'] - unexecuted_lines, percent_covered = check_coverage(report) - if unexecuted_lines: - if percent_covered <= percentage: - gently("Your code coverage is not adequate. You must cover at least half your code to receive feedback.") - return False - return True - - -def ensure_cisc108_tests(test_count, report=None): - student = compatibility.get_student_data() - if 'assert_equal' not in student.data: - gently("You have not imported assert_equal from the cisc108 module.") - return False - assert_equal = student.data['assert_equal'] - if not hasattr(assert_equal, 'student_tests'): - gently("The assert_equal function has been modified. Do not let it be overwritten!", - label="Assertion Function Corrupted") - return False - student_tests = assert_equal.student_tests - if student_tests.tests == 0: - gently("You are not unit testing the result.", label="No Student Unit Tests") - return False - elif student_tests.tests < test_count: - gently("You have not written enough unit tests.", label="Not Enough Student Unit Tests") - return False - elif student_tests.failures > 0: - gently("Your unit tests are not passing.", label="Student Unit Tests Failing") - return False - return True diff --git a/src/lib/pedal/toolkit/imports.py b/src/lib/pedal/toolkit/imports.py deleted file mode 100644 index a5e352ea97..0000000000 --- a/src/lib/pedal/toolkit/imports.py +++ /dev/null @@ -1,25 +0,0 @@ -from pedal.cait.cait_api import parse_program -from pedal.report.imperative import explain - - -def ensure_imports(*modules): - ast = parse_program() - for module in modules: - imports = ast.find_all("Import") - import_froms = ast.find_all("ImportFrom") - if not imports and not import_froms: - explain("You need to import the {} module.".format(module)) - return True - success = False - if imports: - if any(alias._name == module - for i in imports - for alias in i.names): - success = True - if import_froms: - if any(i.module == module for i in import_froms): - success = True - if not success: - explain("You need to import the {} module.".format(module)) - return True - return False diff --git a/src/lib/pedal/toolkit/plotting.py b/src/lib/pedal/toolkit/plotting.py deleted file mode 100644 index a7b4b726db..0000000000 --- a/src/lib/pedal/toolkit/plotting.py +++ /dev/null @@ -1,184 +0,0 @@ -from pedal.toolkit.utilities import function_is_called -from pedal.cait.cait_api import parse_program, def_use_error -from pedal.report.imperative import gently, explain_r, gently_r -from pedal.sandbox import compatibility - -PLOT_LABEL = {'plot': 'line plot', - 'hist': 'histogram', - 'scatter': 'scatter plot'} - - -def prevent_incorrect_plt(): - ast = parse_program() - plts = [n for n in ast.find_all("Name") if n.id == 'plt'] - if plts and def_use_error(plts[0]): - # TODO: I converted this to the explain_r function, but I wasn't sure about the priority thing ~Luke Gusukuma - # explain("You have imported the matplotlib.pyplot module, " - # "but you did not rename it to plt using " - # "import matplotlib.pyplot as plt.

    (plt_rename_err)

    ", 'verifier') - explain_r("You have imported the matplotlib.pyplot module, " - "but you did not rename it to plt using " - "import matplotlib.pyplot as plt.", - "plt_rename_err", - priority='verifier') - return True - matplotlib_names = ['plot', 'hist', 'scatter', - 'title', 'xlabel', 'ylabel', 'show'] - for name in matplotlib_names: - for n in ast.find_all("Name"): - if n.id == name: - if def_use_error(n): - # explain(("You have attempted to use the MatPlotLib " - # "function named {0}. However, you " - # "imported MatPlotLib in a way that does not " - # "allow you to use the function directly. I " - # "recommend you use plt.{0} instead, " - # "after you use import matplotlib.pyplot as " - # "plt.

    (plt_wrong_import)

    ").format(name), 'verifier') - explain_r(("You have attempted to use the MatPlotLib " - "function named {0}. However, you " - "imported MatPlotLib in a way that does not " - "allow you to use the function directly. I " - "recommend you use plt.{0} instead, " - "after you use import matplotlib.pyplot as " - "plt.").format(name), - "plt_wrong_import", - priority='verifier') - return True - return False - - -def ensure_correct_plot(function_name): - for a_plot, label in PLOT_LABEL.items(): - if function_name == a_plot: - if not function_is_called(function_name): - gently_r("You are not calling the {func_name} function.".format(func_name=function_name), - "no_{func_name}_call".format(func_name=function_name)) - return True - elif function_is_called(a_plot): - gently_r("You have called the {} function, which makes a {}.".format(a_plot, label), - "wrong_plt") - return True - return False - - -def ensure_show(): - if not function_is_called("show"): - gently_r("You have not called show function, which " - "actually creates the graph.", "no_show") - return True - return False - - -def compare_data(plt_type, correct, given): - """ - Determines whether the given data matches any of the data found in the - correct data. This handles plots of different types: if a histogram - was plotted with the expected data for a line plot, it will return True. - - Args: - plt_type (str): The expected type of this plot - correct (List of Int or List of List of Int): The expected data. - given (Dict): The actual plotted data and information - Returns: - bool: Whether the correct data was found in the given plot. - """ - # Infer arguments - if plt_type == 'hist': - correct_xs = None - correct_ys = correct - elif not correct: - correct_xs = [] - correct_ys = [] - elif isinstance(correct[0], (tuple, list)): - # We were given a list of lists of ints - correct_xs, correct_ys = correct - else: - # Assume it is a singular list - correct_xs = list(range(len(correct))) - correct_ys = correct - - if given['type'] == 'hist': - return correct_ys == given['values'] - elif plt_type == 'hist': - return correct_ys == given['y'] - else: - return correct_xs == given['x'] and correct_ys == given['y'] - - -GRAPH_TYPES = {'line': 'line plot', - 'hist': 'histogram', - 'scatter': 'scatter plot'} - - -def check_for_plot(plt_type, data): - """ - Returns any errors found for this plot type and data. - In other words, if it returns False, the plot was found correctly. - """ - if plt_type == 'plot': - plt_type = 'line' - type_found = False - data_found = False - for graph in compatibility.get_plots(): - for a_plot in graph['data']: - data_found_here = compare_data(plt_type, data, a_plot) - if a_plot['type'] == plt_type and data_found_here: - return False - if a_plot['type'] == plt_type: - type_found = True - if data_found_here: - data_found = True - plt_type = GRAPH_TYPES.get(plt_type, plt_type) - if type_found and data_found: - return ("You have created a {}, but it does not have the right data. That data appears to have been plotted " - "in another graph.

    (other_plt)

    ".format(plt_type)) - elif type_found: - return ("You have created a {}, but it does not have the right data." - "

    (wrong_plt_data)

    ".format(plt_type)) - elif data_found: - return ("You have plotted the right data, but you appear to have not plotted it as a {}." - "

    (wrong_plt_type)

    ".format(plt_type)) - else: - return ("You have not created a {} with the proper data." - "

    (no_plt)

    ".format(plt_type)) - - -def check_for_plot_r(plt_type, data): - """ - Returns any errors found for this plot type and data. - In other words, if it returns False, the plot was found correctly. - """ - if plt_type == 'plot': - plt_type = 'line' - type_found = False - data_found = False - for graph in compatibility.get_plots(): - for a_plot in graph['data']: - data_found_here = compare_data(plt_type, data, a_plot) - if a_plot['type'] == plt_type and data_found_here: - return False - if a_plot['type'] == plt_type: - type_found = True - if data_found_here: - data_found = True - plt_type = GRAPH_TYPES.get(plt_type, plt_type) - if type_found and data_found: - return {"message": "You have created a {}, but it does not have the right data. " - "That data appears to have been plotted in another graph.".format(plt_type), - "code": "other_plt", - "label": "Plotting Another Graph"} - elif type_found: - return {"message": "You have created a {}, but it does not have the right data.".format(plt_type), - "code": "wrong_plt_data", - "label": "Plot Data Incorrect"} - elif data_found: - return {"message": "You have plotted the right data, but you appear to have not plotted it as a {}.".format( - plt_type), - "code": "wrong_plt_type", - "label": "Wrong Plot Type" - } - else: - return {"message": "You have not created a {} with the proper data.".format(plt_type), - "code": "no_plt", - "label": "Missing Plot"} diff --git a/src/lib/pedal/toolkit/printing.py b/src/lib/pedal/toolkit/printing.py deleted file mode 100644 index a0191fc2dc..0000000000 --- a/src/lib/pedal/toolkit/printing.py +++ /dev/null @@ -1,22 +0,0 @@ -from pedal.report.imperative import gently_r -from pedal.toolkit.utilities import find_function_calls, is_top_level - - -def ensure_prints(count): - prints = find_function_calls('print') - if not prints: - gently_r("You are not using the print function!", "no_print", label="Missing Print") - return False - elif len(prints) > count: - gently_r("You are printing too many times!", "multiple_print", label="Too Many Prints") - return False - elif len(prints) < count: - gently_r("You are not printing enough things!", "too_few_print", label="Too Few Prints") - return False - else: - for a_print in prints: - if not is_top_level(a_print): - gently_r("You have a print function that is not at the top level. That is incorrect for this problem!", - "not_top_level_print", label="Non-Top Level Print") - return False - return prints diff --git a/src/lib/pedal/toolkit/records.py b/src/lib/pedal/toolkit/records.py deleted file mode 100644 index de58f567f1..0000000000 --- a/src/lib/pedal/toolkit/records.py +++ /dev/null @@ -1,32 +0,0 @@ -from pedal.report.imperative import gently, explain, gently_r, explain_r, MAIN_REPORT -from pedal.sandbox import compatibility - - -def check_record_instance(record_instance, record_type, instance_identifier, type_identifier): - if not isinstance(record_instance, dict): - explain("{} was not a {} because it is not a dictionary.".format(instance_identifier, type_identifier)) - return False - for expected_key, expected_value_type in record_type.items(): - if expected_key not in record_instance: - explain("{} was supposed to have the key `{}`, but it did not.".format(instance_identifier, expected_key)) - return False - actual_value = record_instance[expected_key] - # Handle nested record types - if isinstance(expected_value_type, list): - if not isinstance(actual_value, list): - explain("{} was not a {} because its key `{}` did not have a list.".format( - instance_identifier, type_identifier, expected_key - )) - return False - elif actual_value: - actual_value = actual_value[0] - expected_value_type = expected_value_type[0] - if not isinstance(actual_value, expected_value_type): - explain("{} was not a {} because its key `{}` did not have a `{}` value".format( - instance_identifier, type_identifier, expected_key, expected_value_type.__name__ - )) - return False - if len(record_type) != len(record_instance): - explain("{} had extra keys that it should not have.".format(instance_identifier)) - return False - return True diff --git a/src/lib/pedal/toolkit/signatures.py b/src/lib/pedal/toolkit/signatures.py deleted file mode 100644 index 94c0975dd9..0000000000 --- a/src/lib/pedal/toolkit/signatures.py +++ /dev/null @@ -1,426 +0,0 @@ -import ast -import re - -from pedal.cait.cait_api import parse_program -from pedal.cait.cait_node import CaitNode -from pedal.report.imperative import gently, explain - -""" -Verify indentation - -Format: - - -Any number of text. One final newline separates the next section. - -If line is "Args:" or "Returns:" - Next line will be a "param (type): Description" or "type: Description" - If the next line is indented more than current level, then it is part of the previous part's description. - Otherwise, new entry - -"Note:" - Any level of indentation indicates -""" - -PRIMITIVES = { - 'text': ['text'], - 'str': ['string', 'str', 'unicode'], - 'bytes': ['bytes'], - 'io': ['io'], - 'file': ['file'], - 'num': ['number', 'num', 'numeric'], - 'int': ['int', 'integer'], - 'float': ['float', 'floating'], - 'bool': ['bool', 'boolean'], - 'none': ['none'], - 'any': ['any'] -} -NORMALIZE_PRIMITIVES = {synonym: formal - for formal, synonyms in PRIMITIVES.items() - for synonym in synonyms} -CONTAINERS = { - 'list': (1, ['list']), - 'set': (1, ['set']), - 'optional': (1, ['optional', 'maybe']), - 'dict': (2, ['dict', 'dictionary']), - 'callable': (2, ['callable', 'function', 'func']), - 'union': ('*', ['union', 'itemization']), - 'tuple': ('*', ['tuple', 'pair']), -} -NORMALIZE_CONTAINERS = {synonym: formal - for formal, (length, synonyms) in CONTAINERS.items() - for synonym in synonyms} - -INHERITANCE = { - 'int': 'num', - 'float': 'num', - 'bool': 'num', - 'str': 'text', - 'bytes': 'text', - 'list': 'iterable', - 'tuple': 'iterable', - 'set': 'iterable', - 'dict': 'iterable', - 'file': 'iterable', - 'text': 'iterable' -} - -SPECIAL_PARAMETERS = ["_returns", "yields", "prints", "_raises", - "_report", "_root"] - -''' -Type validation: - Caps does not matter - Primitives: - Containers - Unions - X or Y - X, Y, or Z - X, Y, Z - Function - (X -> Y) - - list[int, str, or bool], dict[int: str], or bool or int -''' - - -def parse_type_slice(slice): - if slice.ast_name == "Index": - return parse_type(slice.value) - elif slice.ast_name == "Slice": - return "{}:{}".format(parse_type(slice.lower), parse_type(slice.upper)) - elif slice.ast_name == "ExtSlice": - return ", ".join(parse_type_slice(s) for s in slice.dims) - - -def parse_type(node): - if node == None: - return "Any" - if node.ast_name == "Str": - try: - return parse_type(ast.parse(node.s).body[0].value) - except: - return node.s - elif node.ast_name == "Name": - return node.id - elif node.ast_name == "NameConstant": - return node.value - elif node.ast_name == "List": - return "[{}]".format(", ".join([parse_type(n) for n in node.elts])) - elif node.ast_name == "Dict": - return "{" + (", ".join(["{}: {}".format(parse_type(k), parse_type(v)) - for k, v in zip(node.keys, node.values)])) + "}" - elif node.ast_name == "Subscript": - return parse_type(node.value) + "[{}]".format(parse_type_slice(node.slice)) - elif node.ast_name == "BoolOp": - if node.op.ast_name == "Or": - return " or ".join(parse_type(v) for v in node.values) - return "?" - - -def parse_type_value(value, parse_strings=False): - if isinstance(value, str): - if parse_strings: - return parse_type(CaitNode(ast.parse(value).body[0].value)) - else: - return repr(value) - elif value in (int, str, bool, float, list, dict, object): - return value.__name__ - elif value is None: - return "None" - elif isinstance(value, list): - if value == []: - return "[]" - else: - return "[{}]".format(parse_type_value(value[0])) - elif isinstance(value, tuple): - if value == (): - return "()" - else: - return "({})".format("".join(["{}, ".format(parse_type_value(v)) - for v in value])) - elif isinstance(value, dict): - if value == {}: - return "{}" - else: - return "{" + (", ".join(["{}: {}".format(parse_type_value(k), parse_type_value(v)) - for k, v in value.items()])) + "}" - - -def test_type_equality(left, right): - return left == right - - -class SignatureException(Exception): - pass - - -class Stack: - def __init__(self, identifier="union"): - self.body = [] - self.identifier = identifier - - def append(self, value): - self.body.append(value) - - def __repr__(self): - return "{}[{}]".format(self.identifier, ", ".join(map(repr, self.body))) - - def __hash__(self): - return hash(tuple(self.identifier, self.body)) - - def __lt__(self, other): - if isinstance(other, Stack): - return self.identifier < other.identifier and self.body < other.body - return self.identifier < other - - def __gt__(self, other): - if isinstance(other, Stack): - return self.identifier > other.identifier and self.body > other.body - return self.identifier > other - - def __eq__(self, other): - if isinstance(other, Stack): - return self.identifier == other.identifier and self.body == other.body - return False - - -def _normalize_identifier(identifier): - if identifier in NORMALIZE_PRIMITIVES: - return NORMALIZE_PRIMITIVES[identifier] - elif identifier in NORMALIZE_CONTAINERS: - return NORMALIZE_CONTAINERS[identifier] - else: - return identifier - - -SPECIAL_SYMBOLS = r"\s*(->|\s*[\[\],\(\)\:\{\}]|or)\s*" - - -def _parse_tokens(tokens): - result_stack = [Stack()] - tokens = list(reversed(list(tokens))) - while tokens: - current = tokens.pop() - # Ending a parenthetical, better stop here. - if current == ")": - subexpression = result_stack.pop() - result_stack[-1].append(subexpression) - # Ending a square bracket, better stop here. - elif current == "]": - subexpression = result_stack.pop() - result_stack[-1].append(subexpression) - # Ending a curly bracket, better stop here. - elif current == "}": - subexpression = result_stack.pop() - result_stack[-1].append(subexpression) - # We've reached the last token! - elif not tokens: - # And had no tokens before this one - # Return the set of tokens - result_stack[-1].append(_normalize_identifier(current)) - # Starting a parentheized expression - elif current == "(": - result_stack.append(Stack()) - elif current == "[": - result_stack.append(Stack("list")) - elif current == "{": - result_stack.append(Stack("dict")) - # Nullary function - elif current == "->": - result_stack[-1].append(Stack("callable")) - elif current in ("or", ",", ":"): - pass - else: - next = tokens.pop() - # X or ... - if current == "," and next == "or": - tokens.append(next) - if next in ("or", ",", "->", ":"): - result_stack[-1].append(_normalize_identifier(current)) - # X [ ... - elif next == "[": - result_stack.append(Stack(_normalize_identifier(current))) - else: - tokens.append(next) - result_stack[-1].append(_normalize_identifier(current)) - return result_stack.pop() - - -def sort_stacks(s): - if isinstance(s, Stack): - return (True, (s.identifier, s.body)) - return (False, s) - - -def normalize_type(t): - t = t.strip() - tokens = re.split(SPECIAL_SYMBOLS, t) - tokens = [token for token in tokens if token] - parsed = _parse_tokens(tokens) - return parsed - - -def check_piece(left, right, indent=1): - if type(left) != type(right): - return False - elif isinstance(left, Stack): - if left.identifier != right.identifier: - return False - elif len(left.body) != len(right.body): - return False - elif left.identifier == "union": - # Handle them in any order - left.body.sort(key=sort_stacks) - right.body.sort(key=sort_stacks) - # Match them in exact order - for l, r in zip(left.body, right.body): - if not check_piece(l, r, indent=indent + 1): - return False - return True - else: - return left == right - - -def type_check(left, right): - left = normalize_type(left) - right = normalize_type(right) - return check_piece(left, right) - - -def find_colon(str): - parens_stack = [] - for i, character in enumerate(str): - if character in '[(': - parens_stack.append(character) - elif character in '])': - parens_stack.pop() - elif character == ':' and not parens_stack: - return i - return 0 - - -ARGS = ('args:', 'arg:', 'argument:', 'arguments:', - 'parameters:', 'params:', 'parameter:', 'param:') -ARG_PATTERN = r'(.+)\s*\((.+)\)\s*:(.+)' -RETURNS = ('returns:', 'return:') - - -def parse_docstring(doc): - # First line's indentation may be different from rest - trust first - # non empty line after the first one. - # Remove taht number of spaces from subsequent lines - # If Line is "Args:" or other special... - # - lines = doc.split("\n") - body = [lines[0]] - args = {} - current_arg = None - returns = [] - current_component = 'body' - indentation = None - inner_indentation = None - for line in lines[1:]: - # Blank line, not interesting! - if not line.strip(): - continue - # Get the actual text - if indentation is None: - indentation = len(line) - len(line.lstrip()) - line = line[indentation:] - potential_command = line.lower().strip() - # New command region? - if potential_command in ARGS: - current_component = 'args' - inner_indentation = None - continue - elif potential_command in RETURNS: - current_component = 'returns' - inner_indentation = None - continue - # Okay, it's content - let's process it - if current_component == 'body': - body.append(line) - else: - if inner_indentation is None: - inner_indentation = len(line) - len(line.lstrip()) - line = line[inner_indentation:] - # Skip indented lines - if not re.match(r'\s', line): - if current_component == 'args': - match = re.search(ARG_PATTERN, line) - current_arg = match.group(1) - type_str = match.group(2) - args[current_arg.strip()] = type_str.strip() - elif current_component == 'returns': - position = find_colon(line) - return_type, comment = line[:position], line[position:] - returns.append(return_type.strip()) - return body, args, ' or '.join(returns) - - -def function_signature(function_name, returns=None, yields=None, - prints=None, raises=None, report=None, root=None, - **kwargs): - """ - Determines whether the function with this signature is in the AST. - - TODO: Implement raises, prints, yields - """ - if root is None: - root = parse_program() - # If you encounter any special parameters with a "_", then fix their - # name. This allows for students to have parameters with the given name. - for special_parameter in SPECIAL_PARAMETERS: - if special_parameter in kwargs: - kwargs[special_parameter[1:]] = kwargs.pop(special_parameter) - # Go get the actual docstring, parse it - docstring = None - for function_def in root.find_all("FunctionDef"): - if function_def._name == function_name: - if function_def.body: - if (function_def.body[0].ast_name == "Expr" and - function_def.body[0].value.ast_name == "Str"): - docstring = function_def.body[0].value.s - # Try to match each element in turn. - if docstring is None: - return False - - try: - body, args, parsed_returns = parse_docstring(docstring) - except Exception as e: - return [e], False - failing_parameters = [] - for name, type in kwargs.items(): - if name in args: - if not type_check(type, args[name]): - failing_parameters.append(name) - else: - failing_parameters.append(name) - if returns is None and not returns: - return failing_parameters, True - elif returns is not None and returns: - return failing_parameters, type_check(parsed_returns, returns) - else: - return failing_parameters, False - - -def class_signature(class_name, report=None, root=None, **attributes): - """ - - Args: - class_name: - **attributes: - report: - root: - - Returns: - - """ - if root is None: - root = parse_program() - - -""" - -""" diff --git a/src/lib/pedal/toolkit/upload.py b/src/lib/pedal/toolkit/upload.py deleted file mode 100644 index 54993edd59..0000000000 --- a/src/lib/pedal/toolkit/upload.py +++ /dev/null @@ -1,54 +0,0 @@ -import re -from pedal.source import get_program -from pedal.sandbox.compatibility import get_output -from pedal.report.imperative import gently_r, explain_r - - -# Feedback for author's name -def check_author_name_on_header(): - code = get_program() - m_author = re.search('Author: \\w+', code) - if not m_author: - gently_r("You need to add your name to the author field at the top of the file.", "name_missing", - label="Missing Name") - - -def get_plots(output): - # The p[0] is the first plot in a graph/show - return [p[0] for p in output if isinstance(p[0], dict)] - - -def find_plot_of_type(plot_list, plot_type): - return [p['data'] for p in plot_list if p['type'] == plot_type] - - -# Feedback for copying output of the program in the documentation -def check_output_on_header(expected_output): - code = get_program() - expected_output = str(expected_output) - between_stars = code.split("*****")[2].strip() - between_stars = "\\n".join([x.strip() for x in between_stars.split("\\n")]) - if 'REPLACE THIS TEXT WITH THE OUTPUT OF THIS PROGRAM' in between_stars: - gently_r("In your code, you need to 'REPLACE THIS TEXT WITH THE OUTPUT OF THIS PROGRAM'", "wrong_output_blank", - label="Blank Output") - elif expected_output not in between_stars: - gently_r("The output you copied between the *****, seems to be incorrect. " - "You may have copied it into the wrong location, or it is incomplete.", "wrong_output_fill", label="") - - -def check_problem_submission(prob_id): - if prob_id not in get_program(): - explain_r("Make sure that you are turning in {}
    ".format(prob_id), "wrong_problem", label="Wrong Problem") - return True - - -def check_print_output(multiple_lines): - for line in multiple_lines: - if line not in get_output(): - gently_r("You are not doing the correct calculation
    ", "catch_all", label="Wrong Output") - return True - - -def find_in_code(regex): - code = get_program() - return re.search(regex, code) diff --git a/src/lib/pedal/toolkit/utilities.py b/src/lib/pedal/toolkit/utilities.py deleted file mode 100644 index 7e5b4fe23a..0000000000 --- a/src/lib/pedal/toolkit/utilities.py +++ /dev/null @@ -1,361 +0,0 @@ -from pedal.cait.cait_api import parse_program -from pedal.report.imperative import gently, explain -from pedal.report.imperative import gently_r, explain_r - - -def is_top_level(ast_node): - ast = parse_program() - for element in ast.body: - if element.ast_name == 'Expr': - if element.value == ast_node: - return True - elif element == ast_node: - return True - return False - - -def no_nested_function_definitions(): - ast = parse_program() - defs = ast.find_all('FunctionDef') - for a_def in defs: - if not is_top_level(a_def): - gently("You have defined a function inside of another block. For instance, you may have placed it inside " - "another function definition, or inside of a loop. Do not nest your function definition!" - "

    (nest_func)

    ") - return False - return True - - -def function_prints(): - ast = parse_program() - defs = ast.find_all('FunctionDef') - for a_def in defs: - all_calls = a_def.find_all('Call') - for a_call in all_calls: - if a_call.func.ast_name == 'Name': - if a_call.func.id == 'print': - return True - return False - - -def find_function_calls(name, root=None): - if root is None: - root = parse_program() - all_calls = root.find_all('Call') - calls = [] - for a_call in all_calls: - if a_call.func.ast_name == 'Attribute': - if a_call.func.attr == name: - calls.append(a_call) - elif a_call.func.ast_name == 'Name': - if a_call.func.id == name: - calls.append(a_call) - return calls - - -def function_is_called(name): - return len(find_function_calls(name)) - - -def no_nonlist_nums(): - pass - - -def only_printing_variables(): - ast = parse_program() - all_calls = ast.find_all('Call') - for a_call in all_calls: - if a_call.func.ast_name == 'Name' and a_call.func.id == "print": - for arg in a_call.args: - if arg.ast_name != "Name": - return False - elif arg.id in ('True', 'False', 'None'): - return False - return True - - -def find_prior_initializations(node): - if node.ast_name != "Name": - return None - ast = parse_program() - assignments = ast.find_all("Assign") - cur_line_no = node.lineno - all_assignments = [] - for assignment in assignments: - if assignment.has(node): - if assignment.lineno < cur_line_no: - all_assignments.append(assignment) - return all_assignments - - -def prevent_unused_result(): - ast = parse_program() - exprs = ast.find_all('Expr') - for expr in exprs: - if expr.value.ast_name == "Call": - a_call = expr.value - if a_call.func.ast_name == 'Attribute': - if a_call.func.attr == 'append': - pass - elif a_call.func.attr in ('replace', 'strip', 'lstrip', 'rstrip'): - gently("Remember! You cannot modify a string directly. Instead, you should assign the result back " - "to the string variable.

    (str_mutate)

    ") - - -def prevent_builtin_usage(function_names): - message = "You cannot use the builtin function {}." - code = "builtin_use" - label = "Builtin Usage" - # Prevent direction calls - ast = parse_program() - all_calls = ast.find_all('Call') - for a_call in all_calls: - if a_call.func.ast_name == 'Name': - if a_call.func.id in function_names: - explain_r(message.format(a_call.func.id), code, label=label) - return a_call.func.id - return None - - -def find_negatives(root=None): - if root is None: - root = parse_program() - return [-op.operand.n for op in root.find_all("UnaryOp") - if op.op.ast_name == "USub" and op.operand.ast_name == "Num"] - - -# TODO: UGLY HACK. This is to avoid muted=False kwargs in the following -# functions. Apparently skulpt doesn't support this syntax. -muted = False - - -def prevent_literal(*literals): - """ - Confirms that the literal is not in the code, returning False if it is not. - - Args: - *literals (Any...): A series of literal values to look for. - Returns: - AstNode or False: If the literal is found in the code, then it is returned. - """ - message = "Do not use the literal value {} in your code." - code = "hard_code" - label = "Hard Coding" - ast = parse_program() - str_values = [s.s for s in ast.find_all("Str")] - num_values = [n.n for n in ast.find_all("Num")] - negative_values = find_negatives(ast) - name_values = ([name.id for name in ast.find_all("Name")] + - [name.value for name in ast.find_all("NameConstant")]) - for literal in literals: - if isinstance(literal, (int, float)): - if literal in num_values or literal in negative_values: - if not muted: - explain_r(message.format(repr(literal)), code, label=label) - return literal - elif isinstance(literal, str): - if literal in str_values: - if not muted: - explain_r(message.format(repr(literal)), code, label=label) - return literal - elif literal in (True, False, None): - if str(literal) in name_values: - if not muted: - explain_r(message.format(repr(literal)), code, label=label) - return literal - return False - - -def ensure_literal(*literals): - """ - Confirms that the literal IS in the code, returning False if it is not. - - Args: - *literals (Any...): A series of literal values to look for. - Returns: - AstNode or False: If the literal is found in the code, then it is returned. - """ - message = "You need the literal value {} in your code." - code = "missing_literal" - label = "Missing Literal" - ast = parse_program() - str_values = [s.s for s in ast.find_all("Str")] - num_values = [n.n for n in ast.find_all("Num")] - negative_values = find_negatives(ast) - name_values = ([str(name.id) for name in ast.find_all("Name")] + - [str(name.value) for name in ast.find_all("NameConstant")]) - for literal in literals: - if literal in (True, False, None): - if str(literal) not in name_values: - if not muted: - explain_r(message.format(repr(literal)), code, label=label) - return True - elif isinstance(literal, (int, float)): - if literal not in num_values and literal not in negative_values: - if not muted: - explain_r(message.format(repr(literal)), code, label=label) - return literal - elif isinstance(literal, str): - if literal not in str_values: - if not muted: - explain_r(message.format(repr(literal)), code, label=label) - return literal - return False - - -def prevent_advanced_iteration(): - message = "You should not use a while loop to solve this problem." - code = "while_usage" - label = "Usage of while" - ast = parse_program() - if ast.find_all('While'): - explain_r(message, code, label=label) - prevent_builtin_usage(['sum', 'map', 'filter', 'reduce', 'len', 'max', 'min', - 'max', 'sorted', 'all', 'any', 'getattr', 'setattr', - 'eval', 'exec', 'iter']) - - -COMPARE_OP_NAMES = { - "==": "Eq", - "<": "Lt", - "<=": "Lte", - ">=": "Gte", - ">": "Gt", - "!=": "NotEq", - "is": "Is", - "is not": "IsNot", - "in": "In", - "not in": "NotIn"} -BOOL_OP_NAMES = { - "and": "And", - "or": "Or"} -BIN_OP_NAMES = { - "+": "Add", - "-": "Sub", - "*": "Mult", - "/": "Div", - "//": "FloorDiv", - "%": "Mod", - "**": "Pow", - ">>": "LShift", - "<<": "RShift", - "|": "BitOr", - "^": "BitXor", - "&": "BitAnd", - "@": "MatMult"} -UNARY_OP_NAMES = { - # "+": "UAdd", - # "-": "USub", - "not": "Not", - "~": "Invert" -} - - -def ensure_operation(op_name, root=None): - message = "You are not using the {} operator.".format(op_name) - code = "missing_op" - label = "Missing {} Operator".format(op_name) - if root is None: - root = parse_program() - result = find_operation(op_name, root) - if not result: - gently_r(message, code, label) - return result - - -def prevent_operation(op_name, root=None): - message = "You may not use the {} operator.".format(op_name) - code = "bad_op" - label = "Bad Operator".format(op_name) - if root is None: - root = parse_program() - result = find_operation(op_name, root) - if result: - gently_r(message, code, label=label) - return result - - -def find_operation(op_name, root): - if op_name in COMPARE_OP_NAMES: - compares = root.find_all("Compare") - for compare in compares: - for op in compare.ops: - if op.ast_name == COMPARE_OP_NAMES[op_name]: - return compare - elif op_name in BOOL_OP_NAMES: - boolops = root.find_all("BoolOp") - for boolop in boolops: - if boolop.op_name == BOOL_OP_NAMES[op_name]: - return boolop - elif op_name in BIN_OP_NAMES: - binops = root.find_all("BinOp") - for binop in binops: - if binop.op_name == BIN_OP_NAMES[op_name]: - return binop - elif op_name in UNARY_OP_NAMES: - unaryops = root.find_all("UnaryOp") - for unaryop in unaryops: - if unaryop.op_name == UNARY_OP_NAMES[op_name]: - return unaryop - return False - - -def ensure_recursion(function_name, root=None): - if root is None: - root = parse_program() - all_calls = root.find_all('Call') - calls = [] - for a_call in all_calls: - if a_call.func.ast_name == 'Attribute': - if a_call.func.attr == function_name: - calls.append(a_call) - elif a_call.func.ast_name == 'Name': - if a_call.func.id == function_name: - calls.append(a_call) - return calls - - -def ensure_assignment(variable_name, type=None, value=None, root=None): - """ - Consumes a variable name - TODO: Implement the value parameter - - :param variable_name: The variable name the student is expected to define. - :type variable_name: str - :param type: The string type of the node on the right side of the - assignment. Check GreenTreeSnakes (e.g., "Num", or "Str"). - :type type: str - :return: False or str - """ - if root is None: - root = parse_program() - assignments = root.find_all("Assign") - potentials = [] - for assign in assignments: - if assign.targets[0].ast_name != "Name": - continue - if assign.targets[0].id == variable_name: - potentials.append(assign) - if type is None: - return assign - elif (type == 'Bool' and - assign.value.ast_name == 'Name' and - assign.value.id in ('True', 'False')): - return assign - elif (type == 'Bool' and - assign.value.ast_name == 'NameConstant' and - assign.value.value in (True, False)): - return assign - elif assign.value.ast_name == type: - return assign - if potentials and potentials[0].value.ast_name not in ("Str", "Bool", "Num", "List", "Tuple"): - explain_r(("You needed to assign a literal value to {variable}, but you " - "created an expression instead.").format(variable=variable_name), "exp_vs_lit", - label="Expression Instead of Literal") - elif type is None: - explain_r(("You have not properly assigned anything to the variable " - "{variable}.").format(variable=variable_name), "no_assign", label="No Proper Assignment") - else: - explain_r(("You have not assigned a {type} to the variable {variable}." - "").format(type=type, variable=variable_name), "type_assign", label="Unexpected Variable Type") - return False From 1a2477739a1f554ae472e9dbe16e09afdf94f903 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 6 Aug 2020 09:48:19 -0400 Subject: [PATCH 40/68] Fix precompile (possibly), start working on __class_getitem__ --- src/abstract.js | 8 ++++++++ support/precompile/precompile.js | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/abstract.js b/src/abstract.js index c74982b6ec..2917cdbee6 100644 --- a/src/abstract.js +++ b/src/abstract.js @@ -639,6 +639,14 @@ Sk.exportSymbol("Sk.abstr.objectDelItem", Sk.abstr.objectDelItem); Sk.abstr.objectGetItem = function (o, key, canSuspend) { if (o.mp$subscript) { return o.mp$subscript(key, canSuspend); + } else if (Sk.builtin.checkClass(o)) { + if (o === Sk.builtin.type) { + return o; + } + let f = Sk.abstr.lookupSpecial(o, Sk.builtin.str.$class_getitem); + if (f) { + return Sk.misceval.callsimOrSuspendArray(f, [o, key]); + } } throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(o) + "' does not support indexing"); }; diff --git a/support/precompile/precompile.js b/support/precompile/precompile.js index de00658f44..7fa8b308f7 100644 --- a/support/precompile/precompile.js +++ b/support/precompile/precompile.js @@ -81,6 +81,6 @@ for (let filename in result) { let contents = result[filename]; output.push("Sk.builtinFiles.files['"+filename+"'] = "+JSON.stringify(contents)); } -fs.writeFileSync(OUTPUT_FILE, output.join("\n"), "utf8"); +fs.writeFileSync(OUTPUT_FILE, output.join("\n"), "utf8", {flag: "w"}); console.log(Object.keys(result)); From 17eba5c2ebdee7e3af430eca9dd6da35cd6fad8d Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 10 Aug 2020 10:12:55 -0400 Subject: [PATCH 41/68] F-string syntaxerror seemed to be missing a filename. --- src/ast.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast.js b/src/ast.js index b0e79270a5..b6518bfd73 100644 --- a/src/ast.js +++ b/src/ast.js @@ -2529,7 +2529,7 @@ function fstring_parse(str, start, end, raw, recurse_lvl, c, n) { // We need to error out on any lone }s, and // replace doubles with singles. if (/(^|[^}])}(}})*($|[^}])/.test(literal)) { - throw new SyntaxError("f-string: single '}' is not allowed", LINENO(n), n.col_offset); + throw new SyntaxError("f-string: single '}' is not allowed", c.c_filename, LINENO(n), n.col_offset); } literal = literal.replace(/}}/g, "}"); } From 87bf6e304da50615f7a0704847ee7d7fd789127b Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 10 Aug 2020 10:13:20 -0400 Subject: [PATCH 42/68] Comment out some unnecessary debug printing. --- src/builtin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/builtin.js b/src/builtin.js index c608fc3746..ee222e49e2 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -1117,7 +1117,7 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { new_globals_copy.__package__ = Sk.builtin.none.none$; } var backupGlobals = Sk.globals; - console.log(Sk.globals); + //console.log(Sk.globals); Sk.globals = new_globals_copy; // Possibly copy over some "default" ones? var name = filename.endsWith(".py") ? filename.slice(0, -3) : filename; var pyName = new Sk.builtin.str(name); @@ -1127,7 +1127,7 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { try { Sk.importModuleInternal_(name, false, modname, pythonCode, undefined, false, true); } catch (e) { - console.log("SYSTEMATIC ERROR"); + //console.log("SYSTEMATIC ERROR"); caughtError = e; } Sk.globals = backupGlobals; From 7b502c552170bb726c0038dd38b845a7d726bfa4 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 10 Aug 2020 10:13:46 -0400 Subject: [PATCH 43/68] Fix syntax errors and tracebacks for new style exceptions. --- src/errors.js | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/src/errors.js b/src/errors.js index 38bcaa5eab..0df921e4ee 100644 --- a/src/errors.js +++ b/src/errors.js @@ -239,13 +239,42 @@ Sk.abstr.setUpInheritance("OverflowError", Sk.builtin.OverflowError, Sk.builtin. */ Sk.builtin.SyntaxError = function (...args) { Sk.builtin.Exception.apply(this, args); - this.text = arguments.length >= 1 ? Sk.ffi.remapToPy(arguments[0]) : Sk.builtin.none.none$; - this.msg = this.text; + this.msg = arguments.length >= 1 ? Sk.ffi.remapToPy(arguments[0]) : Sk.builtin.none.none$; this.filename = arguments.length >= 2 ? Sk.ffi.remapToPy(arguments[1]) : Sk.builtin.none.none$; this.lineno = arguments.length >= 3 ? Sk.ffi.remapToPy(arguments[2]) : Sk.builtin.none.none$; this.offset = arguments.length >= 4 ? Sk.ffi.remapToPy(arguments[3]) : Sk.builtin.none.none$; + try { + this.text = Sk.parse.linecache[arguments[1]][arguments[2]-1]; + } catch (e) { + this.text = ""; + } + /*this.tp$setattr(new Sk.builtin.str("filename"), this.filename); + this.tp$setattr(new Sk.builtin.str("lineno"), this.lineno); + this.tp$setattr(new Sk.builtin.str("offset"), this.offset);*/ }; Sk.abstr.setUpInheritance("SyntaxError", Sk.builtin.SyntaxError, Sk.builtin.Exception); +Sk.abstr.setUpGetSets(Sk.builtin.SyntaxError, { + filename: { + $get: function () { return this.filename; }, + $set: function(v) { this.filename = v; } + }, + lineno: { + $get: function () { return this.lineno; }, + $set: function(v) { this.lineno = v; } + }, + offset: { + $get: function () { return this.offset; }, + $set: function(v) { this.offset = v; } + }, + text: { + $get: function () { return this.text; }, + $set: function(v) { this.text = v; } + }, + msg: { + $get: function () { return this.msg; }, + $set: function(v) { this.msg = v; } + }, +}); /** * @constructor @@ -475,6 +504,15 @@ Sk.builtin.frame.prototype.tp$getattr = function (name) { _name = Sk.ffi.remapToJs(name); } + let line = this.trace.source; + if (line == null) { + if (this.trace.filename != null && this.trace.lineno != null) { + if (Sk.parse.linecache[this.trace.filename]) { + line = Sk.parse.linecache[this.trace.filename][this.trace.lineno-1]; + } + } + } + switch (_name) { case "f_back": return Sk.builtin.none.none$; @@ -489,7 +527,7 @@ Sk.builtin.frame.prototype.tp$getattr = function (name) { case "f_lineno": return Sk.ffi.remapToPy(this.trace.lineno); case "f_line": - return Sk.ffi.remapToPy(this.trace.source); + return Sk.ffi.remapToPy(line); case "f_locals": return Sk.builtin.none.none$; case "f_trace": From 86f57bf7be20b4914e22a5f9e7968f4eca37fe6e Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 10 Aug 2020 10:14:15 -0400 Subject: [PATCH 44/68] Unnecessary edits to commented out code. This module needs to be updated anyway. --- src/lib/PIL/__init__.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/PIL/__init__.js b/src/lib/PIL/__init__.js index b490e0f3d5..ad7b1a58ca 100644 --- a/src/lib/PIL/__init__.js +++ b/src/lib/PIL/__init__.js @@ -95,14 +95,14 @@ var $builtinmodule = function (name) { //throw new Sk.builtin.IOError(susp.data["error"].message); throw susp.data["error"]; } else { - console.log("RESUMED"); + //console.log("RESUMED"); return self.image; } }; susp.data = { type: "Sk.promise", promise: imagePromise.then(function (value) { - console.log("PROMISED"); + //console.log("PROMISED"); self.image = value; self.canvas = document.createElement("canvas"); self.canvas.width = self.image.width; From 8986e56b3e193ee4df0c6dabe22f585e43c5fb1b Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 10 Aug 2020 10:15:04 -0400 Subject: [PATCH 45/68] Set up Sk.parse.linecache so we can provide lines of code to interested parties (e.g., traceback). --- src/parser.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/parser.js b/src/parser.js index 86d2b9a682..e85e18bc99 100644 --- a/src/parser.js +++ b/src/parser.js @@ -295,7 +295,6 @@ function makeParser(filename, style) { return p; } - Sk.parse = function parse(filename, input) { var T_COMMENT = Sk.token.tokens.T_COMMENT; @@ -313,7 +312,9 @@ Sk.parse = function parse(filename, input) { * @returns {function(): string} */ function readline(input) { - var lines = input.split("\n").reverse();//.map(function (l) { return l + "\n"; }); + let lines = input.split("\n"); + Sk.parse.linecache[filename] = lines.slice(); + lines = lines.reverse(); return function () { if (lines.length === 0) { @@ -373,6 +374,8 @@ Sk.parse = function parse(filename, input) { return result; }; +Sk.parse.linecache = {}; + Sk.parseTreeDump = function parseTreeDump(n, indent) { //return JSON.stringify(n, null, 2); var i; @@ -395,3 +398,4 @@ Sk.parseTreeDump = function parseTreeDump(n, indent) { Sk.exportSymbol("Sk.Parser", Parser); Sk.exportSymbol("Sk.parse", Sk.parse); Sk.exportSymbol("Sk.parseTreeDump", Sk.parseTreeDump); +Sk.exportSymbol("Sk.parse.linecache", Sk.parse.linecache); From 8bd245721d9c79da9318e37d72658fe6de1cb2d7 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 10 Aug 2020 10:15:15 -0400 Subject: [PATCH 46/68] Test for mocking input. --- test/test_io.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/test/test_io.py b/test/test_io.py index f6a9fe829b..dac09de43d 100644 --- a/test/test_io.py +++ b/test/test_io.py @@ -1 +1,20 @@ -import io \ No newline at end of file +import io +from unittest.mock import patch + +def haha(*args, **kwargs): + print("TEST") + return 'haha' + +overridden_modules = { + '__builtins__': { + 'input': haha + } +} + +s = patch.dict('sys.modules', overridden_modules) +s.start() +d = {'input': haha} +c = compile('print(input())', "test.py", 'exec') +exec(c, d) +s.stop() +print(d) \ No newline at end of file From a56e4b1e36035040d46983915d52fe4d86fd2447 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 11 Aug 2020 12:43:19 -0400 Subject: [PATCH 47/68] Not initializing Sk.execPaused anywhere, it seems. --- src/env.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/env.js b/src/env.js index 763d32952b..9274ba689d 100644 --- a/src/env.js +++ b/src/env.js @@ -242,6 +242,13 @@ Sk.timeoutMsg = function () { }; Sk.exportSymbol("Sk.timeoutMsg", Sk.timeoutMsg); + +/** + * If the timer needs to be paused, store it here. + * @type {number} + */ +Sk.execPaused = 0; + /* * Hard execution timeout, throws an error. Set to null to disable */ From e0486e210636bf2182abecba08f05fcf507d9df8 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 11 Aug 2020 12:43:57 -0400 Subject: [PATCH 48/68] More complicated machinery for the sys.modules lookup; if it finds the sys module, then it tries to use that instead. --- src/import.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/import.js b/src/import.js index e54558331c..2440372d8b 100644 --- a/src/import.js +++ b/src/import.js @@ -167,12 +167,20 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela topLevelModuleToReturn = Sk.importModuleInternal_(parentModName, dumpJS, undefined, undefined, relativeToPackage, returnUndefinedOnTopLevelNotFound, canSuspend); } + let sysmodules = Sk.sysmodules; + try { + let sys = sysmodules.mp$subscript(new Sk.builtin.str("sys")); + if (sys != undefined) { + sysmodules = sys.tp$getattr(new Sk.builtin.str("modules")); + } + } catch (x) {} + ret = Sk.misceval.chain(topLevelModuleToReturn, function (topLevelModuleToReturn_) { topLevelModuleToReturn = topLevelModuleToReturn_; // if leaf is already in sys.modules, early out try { - prev = Sk.sysmodules.mp$subscript(new Sk.builtin.str(modname)); + prev = sysmodules.mp$subscript(new Sk.builtin.str(modname)); // if we're a dotted module, return the top level, otherwise ourselves return topLevelModuleToReturn || prev; } catch (x) { @@ -189,7 +197,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela if (!topLevelModuleToReturn) { return undefined; } - parentModule = Sk.sysmodules.mp$subscript(new Sk.builtin.str(absolutePackagePrefix + parentModName)); + parentModule = sysmodules.mp$subscript(new Sk.builtin.str(absolutePackagePrefix + parentModName)); searchFileName = modNameSplit[modNameSplit.length - 1]; searchPath = parentModule.tp$getattr(Sk.builtin.str.$path); } @@ -259,7 +267,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela } // Now we know this module exists, we can add it to the cache - Sk.sysmodules.mp$ass_subscript(new Sk.builtin.str(modname), module); + sysmodules.mp$ass_subscript(new Sk.builtin.str(modname), module); module.$js = co.code; // todo; only in DEBUG? finalcode = co.code; From df3481af95c38fdd09fb7e786e9f4a108c2f9f04 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 11 Aug 2020 12:44:21 -0400 Subject: [PATCH 49/68] Allow setattr to add a __getattr__ to new modules. --- src/module.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/module.js b/src/module.js index 7f54dee027..ff9096ee6a 100644 --- a/src/module.js +++ b/src/module.js @@ -15,12 +15,19 @@ Sk.builtin.module = Sk.abstr.buildNativeClass("module", { return Sk.builtin.none.none$; }, tp$getattr: function (pyName, canSuspend) { + let customGetAttr = this.$d["__getattr__"]; + if (customGetAttr) { + const ret = Sk.misceval.callsimArray(customGetAttr, [pyName]); + if (ret !== undefined) { + return ret; + } + } var jsMangled = pyName.$mangled; const ret = this.$d[jsMangled]; if (ret !== undefined) { return ret; } - // technically this is the wrong way round but its seems performance wise better + // technically this is the wrong way round but its seems performance wise better // to just return the module elements before checking for descriptors const descr = this.ob$type.$typeLookup(pyName); if (descr !== undefined) { From f4f6958bff9ff784f77759697f53c0e0ff8f020a Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 11 Aug 2020 12:44:43 -0400 Subject: [PATCH 50/68] Update Turtles to correctly return None at the end of constructors. --- src/lib/turtle.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/turtle.js b/src/lib/turtle.js index 3a8cb70c48..aeead59da8 100644 --- a/src/lib/turtle.js +++ b/src/lib/turtle.js @@ -2293,6 +2293,7 @@ var $builtinmodule = function (name) { $loc.__init__ = new Sk.builtin.func(function (self) { self.instance = new Turtle(); self.instance.skInstance = self; + return Sk.builtin.none.none$; }); for (var key in Turtle.prototype) { @@ -2305,6 +2306,7 @@ var $builtinmodule = function (name) { function ScreenWrapper($gbl, $loc) { $loc.__init__ = new Sk.builtin.func(function (self) { self.instance = getScreen(); + return Sk.builtin.none.none$; }); for (var key in Screen.prototype) { From 6bcd514b3b0f4c508db144302ffc05590bbc6f0d Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 11 Aug 2020 12:44:57 -0400 Subject: [PATCH 51/68] Some tests related to recent commits about infinite loops and mocking. --- test/test_custom_module.py | 24 ++++++++++++++++++++++++ test/test_infinite_loop.py | 7 +++++++ test/test_io.py | 20 ++++++++++++++++++-- 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 test/test_custom_module.py diff --git a/test/test_custom_module.py b/test/test_custom_module.py new file mode 100644 index 0000000000..cd9fc1e514 --- /dev/null +++ b/test/test_custom_module.py @@ -0,0 +1,24 @@ +from types import ModuleType + +turtle = ModuleType('turtle') + +class MockTurtle: + def __init__(self): + self.calls = [] + def getter(self, key, *args): + print(">>>", key, args) + def _fake_call(*args, **kwargs): + self.calls.append((key, args, kwargs)) + return self._fake_call(args, kwargs) + return _fake_call + def _fake_call(self, *args, **kwargs): + return 0 + #self.calls.append((args, kwargs)) + + +mt = MockTurtle() +setattr(turtle, '__getattr__', mt.getter) + +print(turtle.forward(100)) + +print(mt.calls) \ No newline at end of file diff --git a/test/test_infinite_loop.py b/test/test_infinite_loop.py index 0ba4f1f53d..82ed376a67 100644 --- a/test/test_infinite_loop.py +++ b/test/test_infinite_loop.py @@ -1,6 +1,13 @@ import sys sys.setExecutionLimit(1000) +print("Gonna infinite loop myself, brb.") +try: + while True: + pass +except TimeoutError: + print("Timed it out") + quit = False while not quit: quit = "quit" == input("Type 'quit' to quit.").lower() diff --git a/test/test_io.py b/test/test_io.py index dac09de43d..6f7bd9a1d2 100644 --- a/test/test_io.py +++ b/test/test_io.py @@ -5,16 +5,32 @@ def haha(*args, **kwargs): print("TEST") return 'haha' +class MockTurtle: + def forward(self, amount): + print("MOVING ONWARD:", amount) + overridden_modules = { '__builtins__': { 'input': haha - } + }, + 'turtle': MockTurtle() } s = patch.dict('sys.modules', overridden_modules) s.start() d = {'input': haha} c = compile('print(input())', "test.py", 'exec') +s.stop() +print(d) + +s.start() +exec(c, d) +c = compile('import turtle\nprint(turtle.forward(10))', "test2.py", 'exec') exec(c, d) s.stop() -print(d) \ No newline at end of file +print(d) + +try: + import turtle +except e: + print(e) \ No newline at end of file From 54a5019b46c9a740ffda7b13c2e0db57939d6c51 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 25 Aug 2020 16:30:28 -0400 Subject: [PATCH 52/68] Handle timeouts better, fix bizarre inheritance issue with super, and correctly handle built-in variable names being used in exec. --- src/builtin.js | 2 +- src/builtin/sys.js | 1 + src/compile.js | 7 ++++--- src/env.js | 1 + src/file.js | 2 +- src/import.js | 2 ++ src/lib/turtle.js | 2 ++ src/misceval.js | 1 + src/type.js | 4 ++++ support/precompile/precompile.js | 2 +- test/test_crazy_subclass.py | 20 ++++++++++++++++++++ test/test_exec.py | 14 ++++++++++++++ 12 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 test/test_crazy_subclass.py create mode 100644 test/test_exec.py diff --git a/src/builtin.js b/src/builtin.js index ee222e49e2..bff185b0ad 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -1088,7 +1088,7 @@ var extractDict = function (obj) { } kAsJs = Sk.ffi.remapToJs(k); // todo; assert that this is a reasonble lhs? - ret[kAsJs] = v; + ret[Sk.fixReserved(kAsJs)] = v; } return ret; }; diff --git a/src/builtin/sys.js b/src/builtin/sys.js index 52e29a6909..ae75a5d600 100644 --- a/src/builtin/sys.js +++ b/src/builtin/sys.js @@ -53,6 +53,7 @@ var $builtinmodule = function (name) { sys.resetTimeout = new Sk.builtin.func(function () { Sk.execStart = new Date(); Sk.execPaused = 0; + Sk.execPausedAmount = 0; }); sys.getYieldLimit = new Sk.builtin.func(function () { diff --git a/src/compile.js b/src/compile.js index 90785a6be1..42380630f7 100644 --- a/src/compile.js +++ b/src/compile.js @@ -232,8 +232,9 @@ Compiler.prototype.outputInterruptTest = function () { // Added by RNL var output = ""; if (Sk.execLimit !== null || Sk.yieldLimit !== null && this.u.canSuspend) { output += "var $dateNow = Date.now();"; + //output += "console.log($dateNow, Sk.execStart, Sk.execPaused, Sk.execPausedAmount, $dateNow-Sk.execStart-Sk.execPausedAmount, Sk.execLimit, );"; if (Sk.execLimit !== null) { - output += ("if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPaused > Sk.execLimit){" + + output += ("if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPausedAmount > Sk.execLimit){" + "throw new Sk.builtin.TimeoutError(Sk.timeoutMsg())}"); } if (Sk.yieldLimit !== null && this.u.canSuspend) { @@ -576,7 +577,7 @@ Compiler.prototype.ccall = function (e) { // so we should probably add self to the mangling // TODO: feel free to ignore the above out("if (typeof self === \"undefined\" || self.toString().indexOf(\"Window\") > 0) { throw new Sk.builtin.RuntimeError(\"super(): no arguments\") };"); - positionalArgs = "[$gbl.__class__,self]"; + positionalArgs = "[__class__,self]"; } out("$ret = (", func, ".tp$call)?", func, ".tp$call(", positionalArgs, ",", keywordArgs, ") : Sk.misceval.applyOrSuspend(", func, ",undefined,undefined,", keywordArgs, ",", positionalArgs, ");"); @@ -1969,7 +1970,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal // inject __class__ cell when running python3 if (Sk.__future__.python3 && class_for_super) { - this.u.varDeclsCode += "$gbl.__class__=$gbl." + class_for_super.v + ";"; + this.u.varDeclsCode += "let __class__=$gbl." + class_for_super.v + ";"; } // finally, set up the block switch that the jump code expects diff --git a/src/env.js b/src/env.js index 9274ba689d..5ddb51352e 100644 --- a/src/env.js +++ b/src/env.js @@ -248,6 +248,7 @@ Sk.exportSymbol("Sk.timeoutMsg", Sk.timeoutMsg); * @type {number} */ Sk.execPaused = 0; +Sk.execPausedAmount = 0; /* * Hard execution timeout, throws an error. Set to null to disable diff --git a/src/file.js b/src/file.js index c1665a07cc..601a693088 100644 --- a/src/file.js +++ b/src/file.js @@ -170,7 +170,7 @@ Sk.builtin.file.$readline = function (self, size, prompt) { return susp; } else { - Sk.execPaused = Date.now() - Sk.execPaused; + Sk.misceval.unpauseTimer(); return new Sk.builtin.str(x); } } else { diff --git a/src/import.js b/src/import.js index 2440372d8b..7e6f5ea79b 100644 --- a/src/import.js +++ b/src/import.js @@ -420,6 +420,8 @@ Sk.importMainWithBody = function (name, dumpJS, body, canSuspend, sysmodules) { Sk.sysmodules = sysmodules; } Sk.realsyspath = undefined; + Sk.execPausedAmount = 0; + Sk.execPaused = 0; Sk.resetCompiler(); diff --git a/src/lib/turtle.js b/src/lib/turtle.js index aeead59da8..4ec66b136f 100644 --- a/src/lib/turtle.js +++ b/src/lib/turtle.js @@ -297,8 +297,10 @@ var $builtinmodule = function (name) { this._frames = []; this._frameCount = 0; + Sk.misceval.pauseTimer(); return new Promise(function (resolve) { animationFrame(function paint() { + Sk.misceval.unpauseTimer(); for (i = 0; i < frames.length; i++) { if (frames[i]) { frames[i](); diff --git a/src/misceval.js b/src/misceval.js index ead9e01783..a4d4b1f80c 100644 --- a/src/misceval.js +++ b/src/misceval.js @@ -1326,6 +1326,7 @@ Sk.exportSymbol("Sk.misceval.pauseTimer", Sk.misceval.pauseTimer); Sk.misceval.unpauseTimer = function () { Sk.execPaused = Date.now() - Sk.execPaused; + Sk.execPausedAmount += Sk.execPaused; }; Sk.exportSymbol("Sk.misceval.unpauseTimer", Sk.misceval.unpauseTimer); diff --git a/src/type.js b/src/type.js index 2a22e75937..9b0d965c55 100644 --- a/src/type.js +++ b/src/type.js @@ -609,3 +609,7 @@ Sk.builtin.type.$best_base = function (bases) { } return base; }; + +Sk.builtin.type.prototype.__class_getitem__ = function(self, key) { + return self; +}; \ No newline at end of file diff --git a/support/precompile/precompile.js b/support/precompile/precompile.js index 7fa8b308f7..7b9f18d9f7 100644 --- a/support/precompile/precompile.js +++ b/support/precompile/precompile.js @@ -44,7 +44,7 @@ function buildPythonFile(ret, fullname, contents) { } internalName = internalName.replace(/\.py$/, ".js"); contents = co.code + "\nvar $builtinmodule = " + co.funcname + ";"; - //contents = minify(contents).code; + contents = minify(contents).code; ret[internalName] = contents; } diff --git a/test/test_crazy_subclass.py b/test/test_crazy_subclass.py new file mode 100644 index 0000000000..8cfd001d35 --- /dev/null +++ b/test/test_crazy_subclass.py @@ -0,0 +1,20 @@ +class A: + def __init__(self): + print("Never Reached") + +class B(A): + def __init__(self): + self.lower_method() + super().__init__() + + def lower_method(self): + print("I expect to be clobbered.") + +class C(B): + def __init__(self): + super().__init__() + + def lower_method(self): + print("I reset the scope, ha ha ha!") + +C() \ No newline at end of file diff --git a/test/test_exec.py b/test/test_exec.py new file mode 100644 index 0000000000..9c7ba76fc9 --- /dev/null +++ b/test/test_exec.py @@ -0,0 +1,14 @@ +import sys +data = {} +c = compile('banana=0 ; banana', "exec_test.py", 'exec') +exec(c, data) +print(data) +c = compile('x = banana', "exec_test.py", 'exec') +exec(c, data) +print(data) +c = compile('name=0 ; name', "exec_test.py", 'exec') +exec(c, data) +print(data) +c = compile('x = name', "exec_test.py", 'exec') +exec(c, data) +print(data) \ No newline at end of file From 5ede4fd8010e706bdee7218ddcfa24eb1b5335a6 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 1 Sep 2020 15:16:59 -0400 Subject: [PATCH 53/68] No longer need to mangle names in AST.js library. --- src/lib/ast.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/lib/ast.js b/src/lib/ast.js index 0246bca790..8af378d098 100644 --- a/src/lib/ast.js +++ b/src/lib/ast.js @@ -1,15 +1,6 @@ var $builtinmodule = function (name) { var mod = {__name__: new Sk.builtin.str("_ast")}; - function mangleAppropriately(name) { - switch (name) { - case "name": - return "name_$rn$"; - default: - return name; - } - } - /** * Consumes an AST Node (JS version). Return a list of tuples of * ``(fieldname, value)`` for each field in ``node._fields`` that is @@ -349,7 +340,7 @@ var $builtinmodule = function (name) { } else { value = convertValue(value); } - field = mangleAppropriately(field); + //field = mangleAppropriately(field); Sk.abstr.sattr(self, new Sk.builtin.str(field), value, true); // TODO: Figure out why name is getting manged, and make it stop! self._fields.push(new Sk.builtin.tuple([new Sk.builtin.str(field), value])); From e115e60927a802624fe67fb26b98ca475c4633d5 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 1 Sep 2020 15:17:19 -0400 Subject: [PATCH 54/68] Support optional --minify argument for precompile --- support/precompile/precompile.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/support/precompile/precompile.js b/support/precompile/precompile.js index 7b9f18d9f7..59a2313ef7 100644 --- a/support/precompile/precompile.js +++ b/support/precompile/precompile.js @@ -3,7 +3,9 @@ const program = require("commander"); -program.parse(process.argv); +program + .option("-m, --minify", "minify the resulting code") + .parse(process.argv); if (program.args.length !== 3) { console.log(chalk.red("error: must specify output directory, input directory, and module name")); @@ -26,7 +28,7 @@ const path = require("path"); const minify = require("babel-minify"); const beautify = require("js-beautify"); -function buildPythonFile(ret, fullname, contents) { +function buildPythonFile(ret, fullname, contents, shouldMinify) { var internalName = fullname; while (internalName.startsWith(SOURCE_DIR)) { internalName = internalName.slice(SOURCE_DIR.length); @@ -44,7 +46,9 @@ function buildPythonFile(ret, fullname, contents) { } internalName = internalName.replace(/\.py$/, ".js"); contents = co.code + "\nvar $builtinmodule = " + co.funcname + ";"; - contents = minify(contents).code; + if (shouldMinify) { + contents = minify(contents).code; + } ret[internalName] = contents; } @@ -65,7 +69,7 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { if (exts.includes(ext)) { let contents = fs.readFileSync(fullname, "utf8"); if (ext === ".py") { - buildPythonFile(ret, fullname, contents); + buildPythonFile(ret, fullname, contents, minifyjs); } } } @@ -75,7 +79,7 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { } var result = {}; -processDirectories([SOURCE_DIR+MODULE_NAME], true, ".py", result, true, []); +processDirectories([SOURCE_DIR+MODULE_NAME], true, ".py", result, program.minify, []); let output = []; for (let filename in result) { let contents = result[filename]; From 2845f6174f37de8feb32d303893871817d2cdf11 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Fri, 18 Sep 2020 14:56:30 -0400 Subject: [PATCH 55/68] Fix isinstance to actually check the __class__ attribute (necessary for SandboxResult) --- src/builtin.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/builtin.js b/src/builtin.js index bff185b0ad..7f7445aceb 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -528,7 +528,16 @@ Sk.builtin.isinstance = function isinstance(obj, type) { // Normal case if (!(type instanceof Sk.builtin.tuple)) { - return obj.ob$type.$isSubType(type) ? Sk.builtin.bool.true$ : Sk.builtin.bool.false$; + // Overridden __class__ + var objType; + var __class__ = obj.tp$getattr(Sk.builtin.str.$class); + if (__class__ !== undefined) { + objType = __class__; + } else { + objType = obj.ob$type; + } + + return objType.$isSubType(type) ? Sk.builtin.bool.true$ : Sk.builtin.bool.false$; } // Handle tuple type argument for (let i = 0; i < type.v.length; ++i) { From 04d3f4846db2e7c63ddd1914a86f8b27db3006e9 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 18 Oct 2020 12:45:04 -0400 Subject: [PATCH 56/68] Add sys constant to strings. --- src/constants.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/constants.js b/src/constants.js index cf14bfb36b..934fef2ed1 100644 --- a/src/constants.js +++ b/src/constants.js @@ -53,6 +53,7 @@ Sk.builtin.str.$setitem = new Sk.builtin.str("__setitem__"); Sk.builtin.str.$str = new Sk.builtin.str("__str__"); Sk.builtin.str.$trunc = new Sk.builtin.str("__trunc__"); Sk.builtin.str.$write = new Sk.builtin.str("write"); +Sk.builtin.str.$sys = new Sk.builtin.str("sys"); Sk.misceval.op2method_ = { "Eq": Sk.builtin.str.$eq, From efcc829bed09ab88d1856d0afa36a3d5e1559d7b Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 18 Oct 2020 12:45:54 -0400 Subject: [PATCH 57/68] Fix handling sys module mocking in all imported libraries; also trace docstrings separately. --- src/builtin.js | 59 +++++++++++++++++++++++++++++--------------------- src/compile.js | 27 ++++++++--------------- src/import.js | 27 ++++++++++++++++------- 3 files changed, 62 insertions(+), 51 deletions(-) diff --git a/src/builtin.js b/src/builtin.js index 7f7445aceb..a4f98ddb1f 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -1130,32 +1130,41 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { Sk.globals = new_globals_copy; // Possibly copy over some "default" ones? var name = filename.endsWith(".py") ? filename.slice(0, -3) : filename; var pyName = new Sk.builtin.str(name); - var sysModules = Sk.sysmodules.mp$subscript(new Sk.builtin.str("sys"));//Sk.getCurrentSysModules(); - var modname = name; - var caughtError = null; - try { - Sk.importModuleInternal_(name, false, modname, pythonCode, undefined, false, true); - } catch (e) { - //console.log("SYSTEMATIC ERROR"); - caughtError = e; - } - Sk.globals = backupGlobals; - // Only try to delete if we succeeded in creating it! - if (Sk.sysmodules.mp$lookup(pyName)) { - Sk.sysmodules.del$item(pyName); - //Sk.sysmodules.mp$del_subscript(pyName); - } - for (var key in new_globals_copy) { - if (new_globals_copy.hasOwnProperty(key)) { - var pykey = Sk.ffi.remapToPy(Sk.unfixReserved(key)); - Sk.builtin.dict.prototype.mp$ass_subscript.call(new_globals, pykey, new_globals_copy[key]); - } - } - Sk.retainGlobals = backupRG; - if (caughtError !== null) { - throw caughtError; + var loadModule = function() { + var sysModules = Sk.sysmodules.mp$subscript(Sk.builtin.str.$sys); + var modname = name; + var caughtError = null; + const res = Sk.misceval.tryCatch(() => { + Sk.importModuleInternal_(name, false, modname, pythonCode, undefined, false, true); + }, (e) => { + console.error("SYSTEMATIC ERROR", e); + caughtError = e; + }); + Sk.misceval.chain(res, function() { + Sk.globals = backupGlobals; + // Only try to delete if we succeeded in creating it! + if (Sk.sysmodules.mp$lookup(pyName)) { + Sk.sysmodules.del$item(pyName); + } + for (var key in new_globals_copy) { + if (new_globals_copy.hasOwnProperty(key)) { + var pykey = Sk.ffi.remapToPy(Sk.unfixReserved(key)); + Sk.builtin.dict.prototype.mp$ass_subscript.call(new_globals, pykey, new_globals_copy[key]); + } + } + Sk.retainGlobals = backupRG; + if (caughtError !== null) { + throw caughtError; + } + resolve(); + }); + }; + if (!Sk.sysmodules.sq$contains(Sk.builtin.str.$sys)) { + Sk.misceval.chain(Sk.importModule("sys", false, true), + loadModule); + } else { + loadModule(); } - resolve(); }); return Sk.misceval.promiseToSuspension(prom); diff --git a/src/compile.js b/src/compile.js index 42380630f7..08ba131ea5 100644 --- a/src/compile.js +++ b/src/compile.js @@ -110,19 +110,16 @@ Compiler.prototype.annotateSource = function (ast, shouldStep) { lineno = ast.lineno; col_offset = ast.col_offset; sourceLine = this.getSourceLine(lineno); - /*out("\n//\n// line ", lineno, ":\n// ", sourceLine, "\n// "); - for (i = 0; i < col_offset; ++i) { - out(" "); - } - out("^\n//\n");*/ - Sk.asserts.assert(ast.lineno !== undefined && ast.col_offset !== undefined); out("\n$currLineNo=Sk.currLineNo=", lineno, ";$currColNo=Sk.currColNo=", col_offset, ";"); - out("Sk.currFilename='", this.filename, "';$currSource=", JSON.stringify(sourceLine), ";"); + // TODO: Make filename a module-global, and update it via that quickly. + out("$currFilename=Sk.currFilename='", this.filename, "';$currSource=", JSON.stringify(sourceLine), ";"); + let isDocstring = !!(ast.constructor === Sk.astnodes.Expr && + ast.value.constructor === Sk.astnodes.Str); // Do not trace the standard library if (shouldStep && (!this.filename || !this.filename.startsWith("src/lib/"))) { - out("Sk.afterSingleExecution && Sk.afterSingleExecution($gbl,$loc," + lineno + "," + col_offset + "," + JSON.stringify(this.filename) + ");\n"); + out(`Sk.afterSingleExecution && Sk.afterSingleExecution($gbl,$loc,${lineno}, ${col_offset}, $currFilename, ${isDocstring});\n`); } } }; @@ -1054,7 +1051,7 @@ Compiler.prototype.outputLocals = function (unit) { for (i = 0; unit.argnames && i < unit.argnames.length; ++i) { have[unit.argnames[i]] = true; } - unit.localnames.sort(); + //unit.localnames.sort(); output = []; for (i = 0; i < unit.localnames.length; ++i) { name = unit.localnames[i]; @@ -1817,10 +1814,10 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal funcArgs.push("$kwa"); this.u.tempsToSave.push("$kwa"); } - for (i = 0; args && i < args.args.length; ++i) { + for (i = 0; args && i < args.args.length; i++) { funcArgs.push(this.nameop(args.args[i].arg, Sk.astnodes.Param)); } - for (i = 0; args && args.kwonlyargs && i < args.kwonlyargs.length; ++i) { + for (i = 0; args && args.kwonlyargs && i < args.kwonlyargs.length; i++) { funcArgs.push(this.nameop(args.kwonlyargs[i].arg, Sk.astnodes.Param)); } if (vararg) { @@ -1985,13 +1982,6 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal this.u.switchCode += "switch($blk){"; this.u.suffixCode = "}" + this.handleTraceback(true, coname.v); this.u.suffixCode += "});"; - /*this.u.suffixCode = ("} }catch(err){ "+ - "if (err instanceof Sk.builtin.TimeoutError) {"+ - "Sk.execStart = Date.now();Sk.execPaused=0"+ - "} if (!(err instanceof Sk.builtin.BaseException)) {"+ - "err = new Sk.builtin.ExternalError(err);" + - "} err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'});"+ - "if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }}");*/ // @@ -2539,6 +2529,7 @@ Compiler.prototype.nameop = function (name, ctx, dataToStore) { case Sk.astnodes.Load: case Sk.astnodes.Param: // Need to check that it is bound! + // out("Sk.misceval.checkUnbound("+mangled+", '"+mangled+"');"); out("if (", mangled, " === undefined) { throw new Sk.builtin.UnboundLocalError('local variable \\\'", mangled, "\\\' referenced before assignment'); }\n"); return mangled; case Sk.astnodes.Store: diff --git a/src/import.js b/src/import.js index 7e6f5ea79b..53c9c5a312 100644 --- a/src/import.js +++ b/src/import.js @@ -7,6 +7,21 @@ Sk.sysmodules = new Sk.builtin.dict([]); Sk.realsyspath = undefined; +/** + * Retrieves sysmodules, going through any artificial sys modules that we may have. + * @returns {Object} + */ +Sk.getSysModulesPolitely = function() { + let sysmodules = Sk.sysmodules; + try { + let sys = sysmodules.mp$subscript(new Sk.builtin.str("sys")); + if (sys != undefined) { + sysmodules = sys.tp$getattr(new Sk.builtin.str("modules")); + } + } catch (x) {} + return sysmodules; +} + /** * @param {string} name to look for * @param {string} ext extension to use (.py or .js) @@ -167,13 +182,7 @@ Sk.importModuleInternal_ = function (name, dumpJS, modname, suppliedPyBody, rela topLevelModuleToReturn = Sk.importModuleInternal_(parentModName, dumpJS, undefined, undefined, relativeToPackage, returnUndefinedOnTopLevelNotFound, canSuspend); } - let sysmodules = Sk.sysmodules; - try { - let sys = sysmodules.mp$subscript(new Sk.builtin.str("sys")); - if (sys != undefined) { - sysmodules = sys.tp$getattr(new Sk.builtin.str("modules")); - } - } catch (x) {} + let sysmodules = Sk.getSysModulesPolitely(); ret = Sk.misceval.chain(topLevelModuleToReturn, function (topLevelModuleToReturn_) { topLevelModuleToReturn = topLevelModuleToReturn_; @@ -532,7 +541,9 @@ Sk.builtin.__import__ = function (name, globals, locals, fromlist, level) { var leafModule; var importChain; - leafModule = Sk.sysmodules.mp$subscript( + let sysmodules = Sk.getSysModulesPolitely(); + + leafModule = sysmodules.mp$subscript( new Sk.builtin.str((relativeToPackageName || "") + ((relativeToPackageName && name) ? "." : "") + name)); From c13e1d9276dc5ec12f12e891d121b22408f739f7 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 24 Nov 2020 20:51:12 -0500 Subject: [PATCH 58/68] Optimizations for compilation, fixes for requests, and other work --- .gitignore | 1 + src/builtin.js | 50 +++++++++++++++++++++++++++--- src/compile.js | 14 ++++++--- src/errors.js | 2 +- src/lib/requests/__init__.js | 1 + src/misceval.js | 13 ++++++-- src/parser.js | 6 ++-- support/build/wrapmodules.js | 52 ++++++++++++++++++-------------- support/precompile/precompile.js | 2 +- test/test_exec.py | 27 ++++++++++++++++- 10 files changed, 130 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index 247318dab6..aa4feead9b 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ Pipfile package-lock.json *~ /deleteme.txt +/delete*.js \ No newline at end of file diff --git a/src/builtin.js b/src/builtin.js index a4f98ddb1f..91d0954936 100644 --- a/src/builtin.js +++ b/src/builtin.js @@ -409,11 +409,12 @@ Sk.builtin.chr = function chr(x) { } x = Sk.builtin.asnum$(x); - if (x < 0 || x > 255) { - throw new Sk.builtin.ValueError("chr() arg not in range(256)"); + // Should be sys.maxunicode + if (x < 0) { + throw new Sk.builtin.ValueError("chr() arg not in range(0x110000)"); } - return new Sk.builtin.str(String.fromCharCode(x)); + return new Sk.builtin.str(String.fromCodePoint(x)); }; Sk.builtin.unichr = function unichr(x) { @@ -1102,8 +1103,44 @@ var extractDict = function (obj) { return ret; }; -Sk.builtin.exec = function execf(pythonCode, new_globals) { +var mergeDict = function(obj1, obj2) { + var k, v, kAsJs, iter; + if (obj2 === undefined) { + return obj1; + } + for (iter = obj2.tp$iter(), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { + v = obj2.mp$subscript(k); + if (v === undefined) { + v = null; + } + kAsJs = Sk.ffi.remapToJs(k); + // todo; assert that this is a reasonble lhs? + obj1[Sk.fixReserved(kAsJs)] = v; + } + return obj1; +} + +Sk.builtin.exec = function execf(pythonCode, new_globals, newLocals) { Sk.builtin.pyCheckArgs("exec", arguments, 1, 2); + /* + var filename = ""; + if (pythonCode instanceof Sk.builtin.code) { + filename = pythonCode.filename; + pythonCode = pythonCode.source; + } else { + pythonCode = Sk.ffi.remapToJs(pythonCode); + } + let code = Sk.compile(pythonCode, filename, "exec", true, true); + const tmp = Sk.globals; + globals = new_globals || tmp; + return Sk.misceval.chain( + code, + (co) => eval(co.code)(globals), + (new_locals) => { + Sk.globals = tmp; + return new_locals; + } + );*/ var prom = new Promise(function (resolve, reject) { var backupRG = Sk.retainGlobals; @@ -1128,6 +1165,9 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { var backupGlobals = Sk.globals; //console.log(Sk.globals); Sk.globals = new_globals_copy; // Possibly copy over some "default" ones? + //console.log(Sk.globals, new_globals); + //mergeDict(Sk.globals, new_globals); + var name = filename.endsWith(".py") ? filename.slice(0, -3) : filename; var pyName = new Sk.builtin.str(name); var loadModule = function() { @@ -1136,6 +1176,7 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { var caughtError = null; const res = Sk.misceval.tryCatch(() => { Sk.importModuleInternal_(name, false, modname, pythonCode, undefined, false, true); + //console.log(Sk.sysmodules.mp$subscript(pyName).$js); }, (e) => { console.error("SYSTEMATIC ERROR", e); caughtError = e; @@ -1150,6 +1191,7 @@ Sk.builtin.exec = function execf(pythonCode, new_globals) { if (new_globals_copy.hasOwnProperty(key)) { var pykey = Sk.ffi.remapToPy(Sk.unfixReserved(key)); Sk.builtin.dict.prototype.mp$ass_subscript.call(new_globals, pykey, new_globals_copy[key]); + //Sk.builtin.dict.prototype.mp$ass_subscript.call(Sk.globals, pykey, Sk.globals[key]); } } Sk.retainGlobals = backupRG; diff --git a/src/compile.js b/src/compile.js index 08ba131ea5..d00b31e6d3 100644 --- a/src/compile.js +++ b/src/compile.js @@ -111,15 +111,18 @@ Compiler.prototype.annotateSource = function (ast, shouldStep) { col_offset = ast.col_offset; sourceLine = this.getSourceLine(lineno); Sk.asserts.assert(ast.lineno !== undefined && ast.col_offset !== undefined); - out("\n$currLineNo=Sk.currLineNo=", lineno, ";$currColNo=Sk.currColNo=", col_offset, ";"); + out("\n$currLineNo=", lineno, ";$currColNo=", col_offset, ";"); // TODO: Make filename a module-global, and update it via that quickly. - out("$currFilename=Sk.currFilename='", this.filename, "';$currSource=", JSON.stringify(sourceLine), ";"); + // JSON.stringify(sourceLine) + let chompedLine = sourceLine; + if (chompedLine.length > 24) {chompedLine = chompedLine.substr(0, 24)+"...";} + out("Sk.currFilename=$fname;$currSource=", JSON.stringify(chompedLine), ";"); let isDocstring = !!(ast.constructor === Sk.astnodes.Expr && ast.value.constructor === Sk.astnodes.Str); // Do not trace the standard library if (shouldStep && (!this.filename || !this.filename.startsWith("src/lib/"))) { - out(`Sk.afterSingleExecution && Sk.afterSingleExecution($gbl,$loc,${lineno}, ${col_offset}, $currFilename, ${isDocstring});\n`); + out(`Sk.afterSingleExecution && Sk.afterSingleExecution($gbl,$loc,${lineno}, ${col_offset}, $fname, ${isDocstring});\n`); } } }; @@ -2530,7 +2533,8 @@ Compiler.prototype.nameop = function (name, ctx, dataToStore) { case Sk.astnodes.Param: // Need to check that it is bound! // out("Sk.misceval.checkUnbound("+mangled+", '"+mangled+"');"); - out("if (", mangled, " === undefined) { throw new Sk.builtin.UnboundLocalError('local variable \\\'", mangled, "\\\' referenced before assignment'); }\n"); + //out("if (", mangled, " === undefined) { throw new Sk.builtin.UnboundLocalError('local variable \\\'", mangled, "\\\' referenced before assignment'); }\n"); + out("if (", mangled, " === undefined) { $ule('", mangled, "') }\n"); return mangled; case Sk.astnodes.Store: out(mangled, "=", dataToStore, ";"); @@ -2798,7 +2802,7 @@ Sk.compile = function (source, filename, mode, canSuspend, annotate) { // Restore the global __future__ flags Sk.__future__ = savedFlags; - var shortCutConstants = "const $fname='" + filename + "',$moduleConstants={};"; + var shortCutConstants = "const $fname='" + filename + "',$moduleConstants={},$ule=Sk.misceval.errorUL;"; var constantDefinitions = []; for (var constant in c.consts) { if (c.consts.hasOwnProperty(constant)) { diff --git a/src/errors.js b/src/errors.js index 0df921e4ee..0ab4c88bf3 100644 --- a/src/errors.js +++ b/src/errors.js @@ -244,7 +244,7 @@ Sk.builtin.SyntaxError = function (...args) { this.lineno = arguments.length >= 3 ? Sk.ffi.remapToPy(arguments[2]) : Sk.builtin.none.none$; this.offset = arguments.length >= 4 ? Sk.ffi.remapToPy(arguments[3]) : Sk.builtin.none.none$; try { - this.text = Sk.parse.linecache[arguments[1]][arguments[2]-1]; + this.text = Sk.parse.linecache[arguments[1]][arguments[2]-1] || ""; } catch (e) { this.text = ""; } diff --git a/src/lib/requests/__init__.js b/src/lib/requests/__init__.js index 02de4855b5..356df739d3 100644 --- a/src/lib/requests/__init__.js +++ b/src/lib/requests/__init__.js @@ -24,6 +24,7 @@ var $builtinmodule = function (name) { self.currentLine = 0; self.pos$ = 0; Sk.abstr.sattr(self, new Sk.builtin.str("text"), Sk.ffi.remapToPy(self.data$), true); + return Sk.builtin.none.none$; }); diff --git a/src/misceval.js b/src/misceval.js index a4d4b1f80c..12496cc296 100644 --- a/src/misceval.js +++ b/src/misceval.js @@ -628,7 +628,7 @@ Sk.exportSymbol("Sk.misceval.print_", Sk.misceval.print_); * Sk.misceval.loadname("foo", Sk.globals); */ Sk.misceval.loadname = function (name, other) { - var bi; + var builtinModuleVersion, bi; var v = other[name]; if (v !== undefined) { if (typeof v === "function" && v.sk$object === undefined) { @@ -637,6 +637,15 @@ Sk.misceval.loadname = function (name, other) { return v; } + // Check if we've overridden the builtin via the builtin's module + if (other["__builtins__"] !== undefined) { + builtinModuleVersion = other["__builtins__"].mp$lookup(new Sk.builtin.str(name)); + //console.log("Overrode __builtins__", other, name, builtinModuleVersion); + if (builtinModuleVersion !== undefined) { + return builtinModuleVersion; + } + } + bi = Sk.builtins[name]; if (bi !== undefined) { return bi; @@ -1331,6 +1340,6 @@ Sk.misceval.unpauseTimer = function () { Sk.exportSymbol("Sk.misceval.unpauseTimer", Sk.misceval.unpauseTimer); Sk.misceval.errorUL = function (mangled) { - return new Sk.builtin.UnboundLocalError("local variable \"" + mangled + "\" referenced before assignment"); + throw new Sk.builtin.UnboundLocalError("local variable '" + mangled + "' referenced before assignment"); }; Sk.exportSymbol("Sk.misceval.errorUL", Sk.misceval.errorUL); \ No newline at end of file diff --git a/src/parser.js b/src/parser.js index e85e18bc99..70ac2dbf79 100644 --- a/src/parser.js +++ b/src/parser.js @@ -154,7 +154,7 @@ Parser.prototype.addtoken = function (type, value, context) { //print("WAA"); this.pop(); if (this.stack.length === 0) { - throw new Sk.builtin.SyntaxError("too much input", this.filename); + throw new Sk.builtin.SyntaxError("too much input", this.filename, "", context); } } else { // no transition @@ -305,6 +305,7 @@ Sk.parse = function parse(filename, input) { var endmarker_seen = false; var parser = makeParser(filename); + var totalLines = 0; /** * takes a string splits it on '\n' and returns a function that returns @@ -315,6 +316,7 @@ Sk.parse = function parse(filename, input) { let lines = input.split("\n"); Sk.parse.linecache[filename] = lines.slice(); lines = lines.reverse(); + totalLines = lines.length; return function () { if (lines.length === 0) { @@ -364,7 +366,7 @@ Sk.parse = function parse(filename, input) { }, filename); if (!endmarker_seen) { - throw new Sk.builtin.SyntaxError("incomplete input", this.filename); + throw new Sk.builtin.SyntaxError("incomplete input", this.filename, "", [0, 0, totalLines]); } /** diff --git a/support/build/wrapmodules.js b/support/build/wrapmodules.js index e5b6b4de4a..f0dced6092 100644 --- a/support/build/wrapmodules.js +++ b/support/build/wrapmodules.js @@ -1,19 +1,31 @@ -const fs = require('fs'); -const path = require('path'); -const minify = require('babel-minify'); -const beautify = require('js-beautify'); +const fs = require("fs"); +const path = require("path"); +const minify = require("babel-minify"); +const beautify = require("js-beautify"); -const reqskulpt = require('../run/require-skulpt').requireSkulpt; +const reqskulpt = require("../run/require-skulpt").requireSkulpt; var skulpt = reqskulpt(); -//Sk.configure({__future__: Sk.python3}); +if (skulpt === null) { + process.exit(1); +} +Sk.configure({__future__: Sk.python3}); -var WHITE_LIST = ['tifa.py', 'sandbox.py', 'stretchy_tree_matching.py']; +var ALLOW_LIST = ["src/lib/posixpath.py", "src/lib/traceback.py", "src/lib/io.py", "cisc108/", "unittest/"]; function endsWithAny(string, suffixes) { return suffixes.some(function (suffix) { return string.endsWith(suffix); }); } +function inAllowList(filename, extension) { + return ALLOW_LIST.some(function (entry) { + if (entry.endsWith('/')) { + return filename.startsWith('src/lib/'+entry); + } else { + return filename.endsWith(entry); + } + }); +} /** * If this optional file exists in the top level directory, it will be @@ -40,30 +52,26 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { let files = fs.readdirSync(dir); files.forEach((file) => { - let fullname = dir + '/' + file; + let fullname = dir + "/" + file; if (!excludes.includes(fullname)) { - let stat = fs.statSync(fullname) + let stat = fs.statSync(fullname); if (recursive && stat.isDirectory()) { processDirectories([fullname], recursive, exts, ret, minifyjs, excludes); } else if (stat.isFile()) { let ext = path.extname(file); if (exts.includes(ext)) { - let contents = fs.readFileSync(fullname, 'utf8'); + let contents = fs.readFileSync(fullname, "utf8"); if (minifyjs && (ext === ".js")) { let result = minify(contents); contents = result.code; - } // AOT compilation - - else if (ext === ".py" && - //endsWithAny(fullname, WHITE_LIST) && - fullname.startsWith("src/lib/pedal/")) { - return; + } else if (ext === ".py" && //endsWithAny(fullname, ALLOW_LIST) + inAllowList(fullname, ext)) { var co; try { - co = Sk.compile(contents, fullname, "exec", true, false); + co = Sk.compile(contents, fullname, "exec", true, true); console.log("Compiled: "+fullname); } catch (e) { console.log("Failed to compile: "+fullname); @@ -74,7 +82,7 @@ function processDirectories(dirs, recursive, exts, ret, minifyjs, excludes) { fullname = fullname.replace(/\.py$/, ".js"); contents = co.code + "\nvar $builtinmodule = " + co.funcname + ";"; //fs.writeFileSync("dist/parts/"+file+".js", beautify(contents), 'utf8'); - //contents = minify(contents).code; + contents = minify(contents).code; //fs.writeFileSync("dist/parts/"+file+".minified.js", contents, 'utf8'); //fs.writeFileSync("dist/parts/"+file+".minified.beautified.js", beautify(contents), 'utf8'); } @@ -100,7 +108,7 @@ function buildJsonFile(name, dirs, exts, outfile, options) { processDirectories(dirs, recursive, exts, ret, minifyjs, excludes); let contents = "Sk." + name + "=" + JSON.stringify(ret); - fs.writeFileSync(outfile, contents, 'utf8'); + fs.writeFileSync(outfile, contents, "utf8"); console.log("Updated " + outfile + "."); } @@ -121,15 +129,15 @@ if (process.argv.includes("internal")) { fs.mkdirSync("dist/parts/"); } - buildJsonFile("builtinFiles", ["src/builtin", "src/lib"], [".js", ".py"], "dist/skulpt-stdlib.js", opts) + buildJsonFile("builtinFiles", ["src/builtin", "src/lib"], [".js", ".py"], "dist/skulpt-stdlib.js", opts); } else if (process.argv.includes("unit2")) { if (!fs.existsSync("support/tmp")) { - fs.mkdirSync("support/tmp"); + fs.mkdirSync("support/tmp"); } buildJsonFile("unit2", ["test/unit"], [".py"], "support/tmp/unit2.js", { recursive: true }); } else if (process.argv.includes("unit3")) { if (!fs.existsSync("support/tmp")) { - fs.mkdirSync("support/tmp"); + fs.mkdirSync("support/tmp"); } buildJsonFile("unit3", ["test/unit3"], [".py"], "support/tmp/unit3.js"); } diff --git a/support/precompile/precompile.js b/support/precompile/precompile.js index 59a2313ef7..bd63341708 100644 --- a/support/precompile/precompile.js +++ b/support/precompile/precompile.js @@ -36,7 +36,7 @@ function buildPythonFile(ret, fullname, contents, shouldMinify) { internalName = "src/lib/"+internalName; try { // TODO: Support compile mode where we remove type annotations and docstrings - co = Sk.compile(contents, internalName, "exec", true, false); + co = Sk.compile(contents, internalName, "exec", true, true); console.log("Compiled: "+internalName); } catch (e) { console.log("Failed to compile: "+internalName); diff --git a/test/test_exec.py b/test/test_exec.py index 9c7ba76fc9..e683f1bdff 100644 --- a/test/test_exec.py +++ b/test/test_exec.py @@ -11,4 +11,29 @@ print(data) c = compile('x = name', "exec_test.py", 'exec') exec(c, data) -print(data) \ No newline at end of file +print(data) + +############## +# Test changing input +d1 = [] +d2 = [] +def mock_input1(*prompt): + d1.append("1") + return 1 +def mock_input2(*prompt): + d2.append("2") + return 2 +first = """ +def x(): + return input() +""" +second = "y = x()" +d = {'input': mock_input1} +# First exec, define x +exec(first, d) +exec(second, d) +d['input'] = mock_input2 +exec(second, d) +print(d1) +print(d2) + From 0a351000a90b3364eb10b81f0f28101889e6a69c Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 12 Jan 2021 09:55:46 -0500 Subject: [PATCH 59/68] Optimizations, add barchart to matplotlib --- example/webglq2/q2bspload.py | 428 +++++++++++++------------- src/compile.js | 64 ++-- src/lib/matplotlib/pyplot/__init__.js | 109 ++++++- src/lib/turtle.js | 1 + src/misceval.js | 59 +++- 5 files changed, 406 insertions(+), 255 deletions(-) diff --git a/example/webglq2/q2bspload.py b/example/webglq2/q2bspload.py index 9db1150278..36b8d32332 100644 --- a/example/webglq2/q2bspload.py +++ b/example/webglq2/q2bspload.py @@ -1,214 +1,214 @@ -""" - -quick hack script to convert from quake2 .bsp to simple format to draw. drops -most information. keeps only raw polys and lightmaps. - -makes .blv and .llv file (big and little endian level) - -file format is - 'BLV1' or 'LLV1' - int texwidth - int texheight - int numtris - float startx, starty, startz, startyaw (rads) - uint32 texdata[texwidth*texheight] - float texcoords[numtris*6] - float verts[numtris*9] - -this makes for trivial drawing, but of course it's very inefficient. - -this is very different from the original format which has a bunch of -optimizations of pvs, culling, uses tris/quads/polys, level-specific data, -textures, etc. there's also lots of trickery to save space in packing, -indirection, etc. but we just flatten all that out to triangles. it's probably -faster these days for such small amounts of geometry anyway. - -http://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml - -scott.q2convert@h4ck3r.net - -""" - -import os -import sys -import struct - -def strunpack(stream, format): - return struct.unpack(format, stream.read(struct.calcsize(format))) - -def die(msg): - print msg - raise SystemExit(1) - -def convCoord(v): - """swizzle, the quake order is wacky. also scale by an arbitrary amount.""" - scale = 1.0/15.0 - return (v[1]*scale, v[2]*scale, v[0]*scale) - -def loadRawData(raw): - # header - headerstr = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII" - data = (magic, version, - off_Entities, len_Entities, - off_Planes, len_Planes, - off_Vertices, len_Vertices, - off_Visibility, len_Visibility, - off_Nodes, len_Nodes, - off_Texture_Information, len_Texture_Information, - off_Faces, len_Faces, - off_Lightmaps, len_Lightmaps, - off_Leaves, len_Leaves, - off_Leaf_Face_Table, len_Leaf_Face_Table, - off_Leaf_Brush_Table, len_Leaf_Brush_Table, - off_Edges, len_Edges, - off_Face_Edge_Table, len_Face_Edge_Table, - off_Models, len_Models, - off_Brushes, len_Brushes, - off_Brush_Sides, len_Brush_Sides, - off_Pop, len_Pop, - off_Areas, len_Areas, - off_Area_Portals, len_Area_Portals) = struct.unpack_from(headerstr, raw) - - - if struct.pack("BBBB", magic>>24, (magic>>16)&0xff, (magic>>8)&0xff, magic&0xff) != "PSBI": die("Bad header") - if version != 38: die("Bad version") - - Leaves = [] - leaf_Size = 28 - for i in range(len_Leaves / leaf_Size): - (brush_or, - cluster, - area, - bbox_minX, bbox_minY, bbox_minZ, - bbox_maxX, bbox_maxY, bbox_maxZ, - first_leaf_face, num_leaf_faces, - first_leaf_brush, num_leaf_brushes) = struct.unpack_from("IHHhhhhhhHHHH", raw, off_Leaves + i*leaf_Size) - Leaves.append((first_leaf_face, num_leaf_faces)) - print "Leaves: %d" % len(Leaves) - - Leaf_Face_Table = [] - leafface_Size = 2 - for i in range(len_Leaf_Face_Table / leafface_Size): - Leaf_Face_Table.append(struct.unpack_from("H", raw, off_Leaf_Face_Table + i*leafface_Size)[0]) - - Faces = [] - face_Size = 20 - for i in range(len_Faces / face_Size): - (plane, plane_Size, - first_edge, num_edges, - texture_info, - style0, style1, style2, style3, - lightmap_offset) = struct.unpack_from("HHIHHBBBBI", raw, off_Faces + i*face_Size) - Faces.append((first_edge, num_edges, lightmap_offset)) - print "Faces: %d" % len(Faces) - - Face_Edge_Table = [] - faceedge_Size = 4 - for i in range(len_Face_Edge_Table / faceedge_Size): - Face_Edge_Table.append(struct.unpack_from("i", raw, off_Face_Edge_Table + i*faceedge_Size)[0]) - - Edges = [] - edge_Size = 4 - for i in range(len_Edges / edge_Size): - (v0, v1) = struct.unpack_from("HH", raw, off_Edges + i*edge_Size) - Edges.append((v0, v1)) - print "Edges: %d" % len(Edges) - - Vertices = [] - vert_Size = 12 - for i in range(len_Vertices / vert_Size): - v = struct.unpack_from("fff", raw, off_Vertices + i*vert_Size) - Vertices.append(convCoord(v)) - print "Vertices: %d" % len(Vertices) - - ents = struct.unpack_from("%ds" % len_Entities, raw, off_Entities)[0][1:-3] # opening { and final }+nul - Entities = [] - for ent in ents.split("}\n{"): - entdata = {} - for line in ent.lstrip("\n").rstrip("\n").split("\n"): - k,v = line.lstrip('"').rstrip('"').split('" "') - entdata[k] = v - Entities.append(entdata) - - return Leaves, Leaf_Face_Table, Faces, Face_Edge_Table, Edges, Vertices, Entities - -def writeBinary(triverts, Entities, packprefix, fileext): - - playerStart = findEntity(Entities, "info_player_start") - sx, sy, sz = playerStart['origin'].split(' ') - - numtris = len(triverts) / 9 - texwidth = 0 - texheight = 0 - startx, starty, startz = convCoord((float(sx), float(sy), float(sz))) - startyaw = float(playerStart['angle']) - texcoords = (0.0, 0.0) * numtris * 3 - - outname = os.path.splitext(sys.argv[1])[0] + fileext - print "writing", outname + "...", - out = open(outname, "wb") - out.write(struct.pack(packprefix + "4sIIIffff", 'LLV1', texwidth, texheight, numtris, startx, starty, startz, startyaw)) - for i in range(numtris*3): - out.write(struct.pack(packprefix + "ff", 0.0, 0.0)) - for tv in triverts: - out.write(struct.pack(packprefix + "f", tv)) - out.close() - print "done" - -def findEntity(entities, class_name): - for item in entities: - if item['classname'] == class_name: - return item - -def main(): - if len(sys.argv) != 2: die("usage: convert ") - - raw = open(sys.argv[1], "rb").read() - - # Leaves are first+len into Leaf_Face_Table - # Leaf_Face_Table is indices into Faces - # Faces is first+len into Face_Edge_Table - # Face_Edge_Table is indices of Edges. if index is positive then (v0,v1) for the edge else (v1,v0) - # Edges is pairs of indices into Vertices - # Vertices are list of 3 floats - # - # Seems crazily complicated these days (thankfully :) - - Leaves, Leaf_Face_Table, Faces, Face_Edge_Table, Edges, Vertices, Entities = loadRawData(raw) - - triverts = [] - - # get all polys - for first_leaf_face, num_leaf_faces in Leaves: - for leaf_face_i in range(first_leaf_face, first_leaf_face + num_leaf_faces): - face_index = Leaf_Face_Table[leaf_face_i] - first_face_edge, num_face_edges, lightmapoffset = Faces[face_index] - polyedges = [] - for face_edge_i in range(first_face_edge, first_face_edge + num_face_edges): - edgei_signed = Face_Edge_Table[face_edge_i] - #print "edgei:", edgei_signed - if edgei_signed >= 0: - e0, e1 = Edges[edgei_signed] - else: - e1, e0 = Edges[-edgei_signed] - v0 = Vertices[e0] - v1 = Vertices[e1] - polyedges.append((v0, v1)) - #print "(%f,%f,%f) -> (%f,%f,%f)" % (v0[0], v0[1], v0[2], v1[0], v1[1], v1[2]) - polyverts = [] - for pei in range(len(polyedges)): - if polyedges[pei][1] != polyedges[(pei+1) % len(polyedges)][0]: - die("poly isn't normal closed style") - polyverts.append(polyedges[pei][0]) - for i in range(1, len(polyverts) - 1): - triverts.extend(polyverts[0]) - triverts.extend(polyverts[i]) - triverts.extend(polyverts[i+1]) - texid.append(lightmapoffset) - - - - writeBinary(triverts, Entities, "<", ".llv") - writeBinary(triverts, Entities, ">", ".blv") - -if __name__ == "__main__": main() +""" + +quick hack script to convert from quake2 .bsp to simple format to draw. drops +most information. keeps only raw polys and lightmaps. + +makes .blv and .llv file (big and little endian level) + +file format is + 'BLV1' or 'LLV1' + int texwidth + int texheight + int numtris + float startx, starty, startz, startyaw (rads) + uint32 texdata[texwidth*texheight] + float texcoords[numtris*6] + float verts[numtris*9] + +this makes for trivial drawing, but of course it's very inefficient. + +this is very different from the original format which has a bunch of +optimizations of pvs, culling, uses tris/quads/polys, level-specific data, +textures, etc. there's also lots of trickery to save space in packing, +indirection, etc. but we just flatten all that out to triangles. it's probably +faster these days for such small amounts of geometry anyway. + +http://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml + +scott.q2convert@h4ck3r.net + +""" + +import os +import sys +import struct + +def strunpack(stream, format): + return struct.unpack(format, stream.read(struct.calcsize(format))) + +def die(msg): + print msg + raise SystemExit(1) + +def convCoord(v): + """swizzle, the quake order is wacky. also scale by an arbitrary amount.""" + scale = 1.0/15.0 + return (v[1]*scale, v[2]*scale, v[0]*scale) + +def loadRawData(raw): + # header + headerstr = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII" + data = (magic, version, + off_Entities, len_Entities, + off_Planes, len_Planes, + off_Vertices, len_Vertices, + off_Visibility, len_Visibility, + off_Nodes, len_Nodes, + off_Texture_Information, len_Texture_Information, + off_Faces, len_Faces, + off_Lightmaps, len_Lightmaps, + off_Leaves, len_Leaves, + off_Leaf_Face_Table, len_Leaf_Face_Table, + off_Leaf_Brush_Table, len_Leaf_Brush_Table, + off_Edges, len_Edges, + off_Face_Edge_Table, len_Face_Edge_Table, + off_Models, len_Models, + off_Brushes, len_Brushes, + off_Brush_Sides, len_Brush_Sides, + off_Pop, len_Pop, + off_Areas, len_Areas, + off_Area_Portals, len_Area_Portals) = struct.unpack_from(headerstr, raw) + + + if struct.pack("BBBB", magic>>24, (magic>>16)&0xff, (magic>>8)&0xff, magic&0xff) != "PSBI": die("Bad header") + if version != 38: die("Bad version") + + Leaves = [] + leaf_Size = 28 + for i in range(len_Leaves / leaf_Size): + (brush_or, + cluster, + area, + bbox_minX, bbox_minY, bbox_minZ, + bbox_maxX, bbox_maxY, bbox_maxZ, + first_leaf_face, num_leaf_faces, + first_leaf_brush, num_leaf_brushes) = struct.unpack_from("IHHhhhhhhHHHH", raw, off_Leaves + i*leaf_Size) + Leaves.append((first_leaf_face, num_leaf_faces)) + print "Leaves: %d" % len(Leaves) + + Leaf_Face_Table = [] + leafface_Size = 2 + for i in range(len_Leaf_Face_Table / leafface_Size): + Leaf_Face_Table.append(struct.unpack_from("H", raw, off_Leaf_Face_Table + i*leafface_Size)[0]) + + Faces = [] + face_Size = 20 + for i in range(len_Faces / face_Size): + (plane, plane_Size, + first_edge, num_edges, + texture_info, + style0, style1, style2, style3, + lightmap_offset) = struct.unpack_from("HHIHHBBBBI", raw, off_Faces + i*face_Size) + Faces.append((first_edge, num_edges, lightmap_offset)) + print "Faces: %d" % len(Faces) + + Face_Edge_Table = [] + faceedge_Size = 4 + for i in range(len_Face_Edge_Table / faceedge_Size): + Face_Edge_Table.append(struct.unpack_from("i", raw, off_Face_Edge_Table + i*faceedge_Size)[0]) + + Edges = [] + edge_Size = 4 + for i in range(len_Edges / edge_Size): + (v0, v1) = struct.unpack_from("HH", raw, off_Edges + i*edge_Size) + Edges.append((v0, v1)) + print "Edges: %d" % len(Edges) + + Vertices = [] + vert_Size = 12 + for i in range(len_Vertices / vert_Size): + v = struct.unpack_from("fff", raw, off_Vertices + i*vert_Size) + Vertices.append(convCoord(v)) + print "Vertices: %d" % len(Vertices) + + ents = struct.unpack_from("%ds" % len_Entities, raw, off_Entities)[0][1:-3] # opening { and final }+nul + Entities = [] + for ent in ents.split("}\n{"): + entdata = {} + for line in ent.lstrip("\n").rstrip("\n").split("\n"): + k,v = line.lstrip('"').rstrip('"').split('" "') + entdata[k] = v + Entities.append(entdata) + + return Leaves, Leaf_Face_Table, Faces, Face_Edge_Table, Edges, Vertices, Entities + +def writeBinary(triverts, Entities, packprefix, fileext): + + playerStart = findEntity(Entities, "info_player_start") + sx, sy, sz = playerStart['origin'].split(' ') + + numtris = len(triverts) / 9 + texwidth = 0 + texheight = 0 + startx, starty, startz = convCoord((float(sx), float(sy), float(sz))) + startyaw = float(playerStart['angle']) + texcoords = (0.0, 0.0) * numtris * 3 + + outname = os.path.splitext(sys.argv[1])[0] + fileext + print "writing", outname + "...", + out = open(outname, "wb") + out.write(struct.pack(packprefix + "4sIIIffff", 'LLV1', texwidth, texheight, numtris, startx, starty, startz, startyaw)) + for i in range(numtris*3): + out.write(struct.pack(packprefix + "ff", 0.0, 0.0)) + for tv in triverts: + out.write(struct.pack(packprefix + "f", tv)) + out.close() + print "done" + +def findEntity(entities, class_name): + for item in entities: + if item['classname'] == class_name: + return item + +def main(): + if len(sys.argv) != 2: die("usage: convert ") + + raw = open(sys.argv[1], "rb").read() + + # Leaves are first+len into Leaf_Face_Table + # Leaf_Face_Table is indices into Faces + # Faces is first+len into Face_Edge_Table + # Face_Edge_Table is indices of Edges. if index is positive then (v0,v1) for the edge else (v1,v0) + # Edges is pairs of indices into Vertices + # Vertices are list of 3 floats + # + # Seems crazily complicated these days (thankfully :) + + Leaves, Leaf_Face_Table, Faces, Face_Edge_Table, Edges, Vertices, Entities = loadRawData(raw) + + triverts = [] + + # get all polys + for first_leaf_face, num_leaf_faces in Leaves: + for leaf_face_i in range(first_leaf_face, first_leaf_face + num_leaf_faces): + face_index = Leaf_Face_Table[leaf_face_i] + first_face_edge, num_face_edges, lightmapoffset = Faces[face_index] + polyedges = [] + for face_edge_i in range(first_face_edge, first_face_edge + num_face_edges): + edgei_signed = Face_Edge_Table[face_edge_i] + #print "edgei:", edgei_signed + if edgei_signed >= 0: + e0, e1 = Edges[edgei_signed] + else: + e1, e0 = Edges[-edgei_signed] + v0 = Vertices[e0] + v1 = Vertices[e1] + polyedges.append((v0, v1)) + #print "(%f,%f,%f) -> (%f,%f,%f)" % (v0[0], v0[1], v0[2], v1[0], v1[1], v1[2]) + polyverts = [] + for pei in range(len(polyedges)): + if polyedges[pei][1] != polyedges[(pei+1) % len(polyedges)][0]: + die("poly isn't normal closed style") + polyverts.append(polyedges[pei][0]) + for i in range(1, len(polyverts) - 1): + triverts.extend(polyverts[0]) + triverts.extend(polyverts[i]) + triverts.extend(polyverts[i+1]) + texid.append(lightmapoffset) + + + + writeBinary(triverts, Entities, "<", ".llv") + writeBinary(triverts, Entities, ">", ".blv") + +if __name__ == "__main__": main() diff --git a/src/compile.js b/src/compile.js index d00b31e6d3..e7e29237aa 100644 --- a/src/compile.js +++ b/src/compile.js @@ -111,17 +111,17 @@ Compiler.prototype.annotateSource = function (ast, shouldStep) { col_offset = ast.col_offset; sourceLine = this.getSourceLine(lineno); Sk.asserts.assert(ast.lineno !== undefined && ast.col_offset !== undefined); - out("\n$currLineNo=", lineno, ";$currColNo=", col_offset, ";"); - // TODO: Make filename a module-global, and update it via that quickly. - // JSON.stringify(sourceLine) - let chompedLine = sourceLine; - if (chompedLine.length > 24) {chompedLine = chompedLine.substr(0, 24)+"...";} - out("Sk.currFilename=$fname;$currSource=", JSON.stringify(chompedLine), ";"); let isDocstring = !!(ast.constructor === Sk.astnodes.Expr && ast.value.constructor === Sk.astnodes.Str); // Do not trace the standard library if (shouldStep && (!this.filename || !this.filename.startsWith("src/lib/"))) { + out("\n$currLineNo=", lineno, ";$currColNo=", col_offset, ";"); + // TODO: Make filename a module-global, and update it via that quickly. + // JSON.stringify(sourceLine) + let chompedLine = sourceLine; + if (chompedLine.length > 24) {chompedLine = chompedLine.substr(0, 24)+"...";} + out("Sk.currFilename=$fname;$currSource=", JSON.stringify(chompedLine), ";"); out(`Sk.afterSingleExecution && Sk.afterSingleExecution($gbl,$loc,${lineno}, ${col_offset}, $fname, ${isDocstring});\n`); } } @@ -234,12 +234,13 @@ Compiler.prototype.outputInterruptTest = function () { // Added by RNL output += "var $dateNow = Date.now();"; //output += "console.log($dateNow, Sk.execStart, Sk.execPaused, Sk.execPausedAmount, $dateNow-Sk.execStart-Sk.execPausedAmount, Sk.execLimit, );"; if (Sk.execLimit !== null) { - output += ("if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPausedAmount > Sk.execLimit){" + - "throw new Sk.builtin.TimeoutError(Sk.timeoutMsg())}"); + //output += ("if (Sk.execLimit !== null && $dateNow - Sk.execStart - Sk.execPausedAmount > Sk.execLimit){" + + // "throw new Sk.builtin.TimeoutError(Sk.timeoutMsg())}"); + output += "Sk.misceval.timeoutCheck($dateNow);"; } if (Sk.yieldLimit !== null && this.u.canSuspend) { output += "if ($dateNow - Sk.lastYield > Sk.yieldLimit) {"; - output += "var $susp = $saveSuspension({data: {type: 'Sk.yield'}, resume: function() {}}, '" + this.filename + "',$currLineNo,$currColNo, $currSource);"; + output += "var $susp = $saveSuspension($mys(), $fname,$currLineNo,$currColNo, $currSource);"; output += "$susp.$blk = $blk;"; output += "$susp.optional = true;"; output += "return $susp;"; @@ -823,11 +824,12 @@ Compiler.prototype.vexpr = function (e, data, augvar, augsubs) { this._checkSuspension(e); return this._gr("lattr", "$ret"); case Sk.astnodes.Load: - out("$ret = ", val, ".tp$getattr(", mname, ", true);"); + /*out("$ret = ", val, ".tp$getattr(", mname, ", true);"); out("\nif ($ret === undefined) {"); out("\nconst error_name =", val, ".sk$type ? \"type object '\" +", val, ".prototype.tp$name + \"'\" : \"'\" + Sk.abstr.typeName(", val, ") + \"' object\";"); out("\nthrow new Sk.builtin.AttributeError(error_name + \" has no attribute '\" + ", mname, ".$jsstr() + \"'\");"); - out("\n};"); + out("\n};");*/ + out("$ret=Sk.misceval.loadattr(", val, ", ", mname, ");"); this._checkSuspension(e); return this._gr("lattr", "$ret"); case Sk.astnodes.AugStore: @@ -1104,19 +1106,21 @@ Compiler.prototype.outputSuspensionHelpers = function (unit) { // Close out function ";"); - output += "var $saveSuspension = function($child, $filename, $lineno, $colno, $source) {" + - "var susp = new Sk.misceval.Suspension(); susp.child=$child;" + - "susp.resume=function(){" + unit.scopename + ".$wakingSuspension=susp; return " + unit.scopename + "(" + (unit.ste.generator ? "$gen" : "") + "); };" + - "susp.data=susp.child.data;susp.$blk=$blk;susp.$loc=$loc;susp.$gbl=$gbl;susp.$exc=$exc;susp.$err=$err;susp.$postfinally=$postfinally;" + - "susp.$filename=$filename;susp.$lineno=$lineno;susp.$colno=$colno;susp.source=$source;" + - "susp.optional=susp.child.optional;" + - (hasCell ? "susp.$cell=$cell;" : ""); - for (i = 0; i < localsToSave.length; i++) { t = localsToSave[i]; localSaveCode.push("\"" + t + "\":" + t); } - output += "susp.$tmps={" + localSaveCode.join(",") + "};" + + + output += "var $mys = function(){return {data: {type: 'Sk.yield'}, resume: function(){} } };"; + output += "var $saveSuspension = function($child, $filename, $lineno, $colno, $source) {" + + //"var susp = new Sk.misceval.Suspension(); susp.child=$child;" + + "var susp = Sk.misceval.injectSusp($child,$blk,$loc,$gbl,$exc,$err,$postfinally,$filename,$lineno,$colno,$source,{" + localSaveCode.join(",") + "});"+ + "susp.resume=function(){" + unit.scopename + ".$wakingSuspension=susp; return " + unit.scopename + "(" + (unit.ste.generator ? "$gen" : "") + "); };" + + /*"susp.data=susp.child.data;susp.$blk=$blk;susp.$loc=$loc;susp.$gbl=$gbl;susp.$exc=$exc;susp.$err=$err;susp.$postfinally=$postfinally;" + + "susp.$filename=$filename;susp.$lineno=$lineno;susp.$colno=$colno;susp.source=$source;" + + "susp.optional=susp.child.optional;" +*/ + (hasCell ? "susp.$cell=$cell;" : "") + + //"susp.$tmps={" + localSaveCode.join(",") + "};" + "return susp;" + "};"; @@ -1162,7 +1166,8 @@ Compiler.prototype.outputAllUnits = function () { break; } } else { - ret += "throw new Sk.builtin.SystemError('internal error: unterminated block');"; + // Shouldn't really be possible to hit this + //ret += "throw new Sk.builtin.SystemError('internal error: unterminated block');"; break; } } @@ -1885,7 +1890,7 @@ Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, cal this.u.varDeclsCode += "Sk.misceval.startTimer();"; } if (Sk.yieldLimit !== null && this.u.canSuspend) { - this.u.varDeclsCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}"; + this.u.varDeclsCode += "Sk.misceval.resetYield();"; } // @@ -2253,7 +2258,7 @@ Compiler.prototype.cclass = function (s) { this.u.switchCode += "Sk.misceval.startTimer();"; } if (Sk.yieldLimit !== null && this.u.canSuspend) { - this.u.switchCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}"; + this.u.switchCode += "Sk.misceval.resetYield();"; } this.u.switchCode += "while(true){try{"; @@ -2646,10 +2651,11 @@ Compiler.prototype.exitScope = function () { mangled = prev.name["$r"]().v; mangled = mangled.substring(1, mangled.length - 1); // mangled = fixReserved(mangled); - out(prev.scopename, ".co_name=new Sk.builtins['str']('", mangled, "');"); + let mname = this.makeConstant("new Sk.builtin.str('" + mangled + "')"); + out(prev.scopename, ".co_name="+mname+";"); if (this.stack.length && this.u.ste.blockType == "class") { const classname = this.u.name.v; - out(prev.scopename, ".co_qualname=new Sk.builtins['str']('" + classname + "." + mangled + "');"); + out(prev.scopename, ".co_qualname=new Sk.builtins.str('" + classname + "." + mangled + "');"); } } for (var constant in prev.consts) { @@ -2703,21 +2709,21 @@ Compiler.prototype.cmod = function (mod) { this.u.varDeclsCode = "var $gbl = $forcegbl || {}, $blk=" + entryBlock + ",$exc=[],$loc=$gbl,$cell={},$err=undefined;" + - "$loc.__file__=new Sk.builtins.str('" + this.filename + - "');var $ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;$currSource=undefined;"; + "$loc.__file__=new Sk.builtins.str($fname);" + + "var $ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;$currSource=undefined;"; if (Sk.execLimit !== null) { this.u.varDeclsCode += "Sk.misceval.startTimer();"; } if (Sk.yieldLimit !== null && this.u.canSuspend) { - this.u.varDeclsCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}"; + this.u.varDeclsCode += "Sk.misceval.resetYield();"; } this.u.varDeclsCode += "if (" + modf + ".$wakingSuspension!==undefined) { $wakeFromSuspension(); }" + "if (Sk.retainGlobals) {" + //" if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; }" + - " if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; $loc.__file__=new Sk.builtins.str('" + this.filename + "');}" + + " if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; $loc.__file__=new Sk.builtins.str($fname);}" + " else { Sk.globals = $gbl; }" + "} else { Sk.globals = $gbl; }"; diff --git a/src/lib/matplotlib/pyplot/__init__.js b/src/lib/matplotlib/pyplot/__init__.js index b98bf436f4..07d53dbbe4 100644 --- a/src/lib/matplotlib/pyplot/__init__.js +++ b/src/lib/matplotlib/pyplot/__init__.js @@ -222,27 +222,34 @@ var $builtinmodule = function (name) { } else if (chart.type === "bar") { yAxisBuffer = 5 * Math.max(extents["yMin"].toLocaleString().length, extents["yMax"].toLocaleString().length); - chart.xScale = d3.scale.ordinal() - .domain([extents["xMin"], extents["xMax"]]) - .rangeBands([0, chart.width - yAxisBuffer]); + chart.xScale = d3.scale.linear() + .domain([extents["xMin"], 1+extents["xMax"]]) + .range([0, chart.width - yAxisBuffer]); + //.rangeBands([0, chart.width - yAxisBuffer]); + var bins = plots[0]["bins"]; chart.xAxis = d3.svg.axis() .scale(chart.xScale) - .tickFormat(function (d) { + /*.tickFormat(function (d) { return d.index; - }) + })*/ .orient("bottom"); } // Fix y-axis - if (chart.type !== "hist") { - chart.yScale = d3.scale.linear() - .domain([extents["yMin"], extents["yMax"]]) - .range([chart.height, 0]); - } else { + if (chart.type === "hist") { chart.yScale = d3.scale.linear() .domain([0, d3.max(histMapper, function (d) { return d.y; })]) .range([chart.height, 0]); + } else if (chart.type === "bar") { + chart.yScale = d3.scale.linear() + .domain([Math.min(0, extents["yMin"]), + Math.max(0, extents["yMax"])]) + .range([chart.height, 0]); + } else { + chart.yScale = d3.scale.linear() + .domain([extents["yMin"], extents["yMax"]]) + .range([chart.height, 0]); } chart.yAxis = d3.svg.axis() .scale(chart.yScale) @@ -366,6 +373,25 @@ var $builtinmodule = function (name) { .attr("height", function (d) { return chart.height - chart.yScale(d.y); }); + } else if (plot["type"] === "bar") { + chart.canvas.selectAll(".bar") + .data(plot["data"]) + .enter().append("rect") + .attr("class", "bar") + .style("fill", plot["style"]["color"]) + .style("stroke", "black") + .attr("x", function (d) { + return chart.xScale(d.x); + }) + .attr("width", function (d) { + return chart.xScale(d.x+1) - chart.xScale(d.x); + }) + .attr("y", function (d) { + return chart.yScale(d.y); + }) + .attr("height", function (d) { + return chart.height - chart.yScale(d.y); + }); } } @@ -451,7 +477,7 @@ var $builtinmodule = function (name) { }; mod.clf = new Sk.builtin.func(clf_f); - UNSUPPORTED = ["semilogx", "semilogy", "specgram", "stackplot", "stem", "step", "streamplot", "tricontour", "tricontourf", "tripcolor", "triplot", "vlines", "xcorr", "barbs", "cla", "grid", "table", "text", "annotate", "ticklabel_format", "locator_params", "tick_params", "margins", "autoscale", "autumn", "cool", "copper", "flag", "gray", "hot", "hsv", "jet", "pink", "prism", "spring", "summer", "winter", "spectral", "hlines", "loglog", "magnitude_spectrum", "pcolor", "pcolormesh", "phase_spectrum", "pie", "plot_date", "psd", "quiver", "quiverkey", "findobj", "switch_backend", "isinteractive", "ioff", "ion", "pause", "rc", "rc_context", "rcdefaults", "gci", "sci", "xkcd", "figure", "gcf", "get_fignums", "get_figlabels", "get_current_fig_manager", "connect", "disconnect", "close", "savefig", "ginput", "waitforbuttonpress", "figtext", "suptitle", "figimage", "figlegend", "hold", "ishold", "over", "delaxes", "sca", "gca", "subplot", "subplots", "subplot2grid", "twinx", "twiny", "subplots_adjust", "subplot_tool", "tight_layout", "box", "xlim", "ylim", "xscale", "yscale", "xticks", "yticks", "minorticks_on", "minorticks_off", "rgrids", "thetagrids", "plotting", "get_plot_commands", "colors", "colormaps", "_setup_pyplot_info_docstrings", "colorbar", "clim", "set_cmap", "imread", "imsave", "matshow", "polar", "plotfile", "_autogen_docstring", "acorr", "arrow", "axhline", "axhspan", "axvline", "axvspan", "bar", "barh", "broken_barh", "boxplot", "cohere", "clabel", "contour", "contourf", "csd", "errorbar", "eventplot", "fill", "fill_between", "fill_betweenx", "hexbin", "hist2d", "axis"]; + UNSUPPORTED = ["semilogx", "semilogy", "specgram", "stackplot", "stem", "step", "streamplot", "tricontour", "tricontourf", "tripcolor", "triplot", "vlines", "xcorr", "barbs", "cla", "grid", "table", "text", "annotate", "ticklabel_format", "locator_params", "tick_params", "margins", "autoscale", "autumn", "cool", "copper", "flag", "gray", "hot", "hsv", "jet", "pink", "prism", "spring", "summer", "winter", "spectral", "hlines", "loglog", "magnitude_spectrum", "pcolor", "pcolormesh", "phase_spectrum", "pie", "plot_date", "psd", "quiver", "quiverkey", "findobj", "switch_backend", "isinteractive", "ioff", "ion", "pause", "rc", "rc_context", "rcdefaults", "gci", "sci", "xkcd", "figure", "gcf", "get_fignums", "get_figlabels", "get_current_fig_manager", "connect", "disconnect", "close", "savefig", "ginput", "waitforbuttonpress", "figtext", "suptitle", "figimage", "figlegend", "hold", "ishold", "over", "delaxes", "sca", "gca", "subplot", "subplots", "subplot2grid", "twinx", "twiny", "subplots_adjust", "subplot_tool", "tight_layout", "box", "xlim", "ylim", "xscale", "yscale", "xticks", "yticks", "minorticks_on", "minorticks_off", "rgrids", "thetagrids", "plotting", "get_plot_commands", "colors", "colormaps", "_setup_pyplot_info_docstrings", "colorbar", "clim", "set_cmap", "imread", "imsave", "matshow", "polar", "plotfile", "_autogen_docstring", "acorr", "arrow", "axhline", "axhspan", "axvline", "axvspan",, "broken_barh", "boxplot", "cohere", "clabel", "contour", "contourf", "csd", "errorbar", "eventplot", "fill", "fill_between", "fill_betweenx", "hexbin", "hist2d", "axis"]; for (var i = 0; i < UNSUPPORTED.length; i += 1) { mod[UNSUPPORTED[i]] = new Sk.builtin.func(function () { throw new Sk.builtin.NotImplementedError(UNSUPPORTED[i] + " is not yet implemented"); @@ -599,6 +625,67 @@ var $builtinmodule = function (name) { scatter_f.co_kwargs = true; mod.scatter = new Sk.builtin.func(scatter_f); + var bar_f = function (kwa) { + // Parse arguments + Sk.builtin.pyCheckArgs("bar", arguments, 1, Infinity, true, false); + args = Array.prototype.slice.call(arguments, 1); + kwargs = new Sk.builtins.dict(kwa); // is pretty useless for handling kwargs + kwargs = Sk.ffi.remapToJs(kwargs); // create a proper dict + + // Keep a backup of the arguments for checker + mod.values.push(args); + + // Parse different argument combinations + var xdata = null; + var ydata = null; + var stylestring = null; + if (args.length === 2) { + // xdata + xdata = Sk.ffi.remapToJs(args[0]); + ydata = Sk.ffi.remapToJs(args[1]); + } else if (args.length === 3) { + // xdata, style + xdata = Sk.ffi.remapToJs(args[0]); + ydata = Sk.ffi.remapToJs(args[1]); + stylestring = Sk.ffi.remapToJs(args[2]); + } + + // Zip up the data + var actualData = d3.zip(xdata, ydata).map(function (e) { + return {"x": e[0], "y": e[1]}; + }); + + // empty canvas from previous plots + createChart("bar"); + + // Parse formatting, also keep ColorCycler updated + var cycle = jsplotlib.rc["axes.color_cycle"]; + var linestyle = " ", marker = "o", + color = cycle[colorCycle % cycle.length]; + if (stylestring !== null) { + var ftm_tuple = jsplotlib._process_plot_format(stylestring); + linestyle = ftm_tuple.linestyle; + marker = jsplotlib.parse_marker(ftm_tuple.marker); + color = ftm_tuple.color; + } else { + colorCycle += 1; + } + // Save + plots.push({ + "data": actualData, + "bins": xdata.length, + "type": "bar", + "style": { + "linestyle": linestyle, + "marker": marker, + "color": jsplotlib.color_to_hex(color) + } + }); + updateMinMax("x", xdata); + updateMinMax("y", ydata); + }; + bar_f.co_kwargs = true; + mod.bar = new Sk.builtin.func(bar_f); return mod; }; diff --git a/src/lib/turtle.js b/src/lib/turtle.js index 4ec66b136f..1792df0435 100644 --- a/src/lib/turtle.js +++ b/src/lib/turtle.js @@ -635,6 +635,7 @@ var $builtinmodule = function (name) { return [this.$xcor(), this.$ycor()]; }; proto.$position.returnType = function (value) { + // TODO: Should actually be a custom Vec2D return new Sk.builtin.tuple([ Sk.builtin.float_(value[0]), Sk.builtin.float_(value[1]) diff --git a/src/misceval.js b/src/misceval.js index 12496cc296..b8af51693b 100644 --- a/src/misceval.js +++ b/src/misceval.js @@ -1328,6 +1328,13 @@ Sk.misceval.startTimer = function () { }; Sk.exportSymbol("Sk.misceval.startTimer", Sk.misceval.startTimer); +Sk.misceval.resetYield = function() { + if (typeof Sk.lastYield === "undefined") { + Sk.lastYield = Date.now(); + } +}; +Sk.exportSymbol("Sk.misceval.resetYield", Sk.misceval.resetYield); + Sk.misceval.pauseTimer = function () { Sk.execPaused = Date.now(); }; @@ -1339,7 +1346,57 @@ Sk.misceval.unpauseTimer = function () { }; Sk.exportSymbol("Sk.misceval.unpauseTimer", Sk.misceval.unpauseTimer); +Sk.misceval.timeoutCheck = function(d) { + if (Sk.execLimit !== null && d - Sk.execStart - Sk.execPausedAmount > Sk.execLimit) { + let shouldContinue = null; + if (Sk.timeoutHandler) { + Sk.misceval.pauseTimer(); + shouldContinue = Sk.timeoutHandler(d - Sk.execStart - Sk.execPausedAmount, Sk.execLimit); + Sk.misceval.unpauseTimer(); + } + if (!shouldContinue) { + throw new Sk.builtin.TimeoutError(Sk.timeoutMsg()); + } + } +}; +Sk.exportSymbol("Sk.misceval.timeoutCheck", Sk.misceval.timeoutCheck); + +Sk.misceval.injectSusp = function($child,$blk,$loc,$gbl,$exc,$err,$postfinally,$filename,$lineno,$colno,$source,$tmps) { + var susp = new Sk.misceval.Suspension(); + susp.child=$child; + susp.data=susp.child.data; + susp.$blk=$blk; + susp.$loc=$loc; + susp.$gbl=$gbl; + susp.$exc=$exc; + susp.$err=$err; + susp.$postfinally=$postfinally; + susp.$filename=$filename; + susp.$lineno=$lineno; + susp.$colno=$colno; + susp.source=$source; + susp.optional=susp.child.optional; + susp.$tmps=$tmps; + return susp; +}; +Sk.exportSymbol("Sk.misceval.injectSusp", Sk.misceval.injectSusp); + Sk.misceval.errorUL = function (mangled) { throw new Sk.builtin.UnboundLocalError("local variable '" + mangled + "' referenced before assignment"); }; -Sk.exportSymbol("Sk.misceval.errorUL", Sk.misceval.errorUL); \ No newline at end of file +Sk.exportSymbol("Sk.misceval.errorUL", Sk.misceval.errorUL); + +Sk.misceval.loadattr = function(val, mname) { + $ret = val.tp$getattr(mname, true); + if ($ret === undefined) { + const error_name = val.sk$type ? "type object '"+val.prototype.tp$name+"\'" : "'"+ Sk.abstr.typeName(val) +"' object"; + throw new Sk.builtin.AttributeError(error_name+" has no attribute '"+mname.$jsstr()+"'"); + } + return $ret; +/*out("$ret = ", val, ".tp$getattr(", mname, ", true);"); + out("\nif ($ret === undefined) {"); + out("\nconst error_name =", val, ".sk$type ? \"type object '\" +", val, ".prototype.tp$name + \"'\" : \"'\" + Sk.abstr.typeName(", val, ") + \"' object\";"); + out("\nthrow new Sk.builtin.AttributeError(error_name + \" has no attribute '\" + ", mname, ".$jsstr() + \"'\");"); + out("\n};");*/ +}; +Sk.exportSymbol("Sk.misceval.loadattr", Sk.misceval.loadattr); From ba749a37500a301da3d9785f07b501b219d25261 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 26 Jan 2021 23:26:50 -0500 Subject: [PATCH 60/68] Massive rewrite of the MatPlotLib module! Support for a bunch of new line plots, and a better rendering engine. --- src/lib/matplotlib/pyplot/__init__.js | 1327 +++++++++++++++---------- 1 file changed, 776 insertions(+), 551 deletions(-) diff --git a/src/lib/matplotlib/pyplot/__init__.js b/src/lib/matplotlib/pyplot/__init__.js index 07d53dbbe4..00d706581b 100644 --- a/src/lib/matplotlib/pyplot/__init__.js +++ b/src/lib/matplotlib/pyplot/__init__.js @@ -1,331 +1,524 @@ -var jsplotlib = {}; +Sk.jsplotlib = Sk.jsplotlib || {}; // Skulpt translation -var $builtinmodule = function (name) { - var mod = {__name__: "matplotlib.pyplot"}; +let $builtinmodule = function (name) { + let mod = {__name__: "matplotlib.pyplot"}; + + const STRING_COLOR = new Sk.builtin.str("color"); + const STRING_COLORS = new Sk.builtin.str("colors"); + const STRING_DATA = new Sk.builtin.str("data"); + const STRING_LABEL = new Sk.builtin.str("label"); + const STRING_ALPHA = new Sk.builtin.str("alpha"); + const STRING_DASH_CAPSTYLE = new Sk.builtin.str("dash_capstyle"); + const STRING_DASH_JOINSTYLE = new Sk.builtin.str("dash_joinstyle"); + const STRING_FILLSTYLE = new Sk.builtin.str("fillstyle"); + const STRING_LINEWIDTH = new Sk.builtin.str("linewidth"); + const STRING_MARKER = new Sk.builtin.str("marker"); + const STRING_LINESTYLE = new Sk.builtin.str("linestyle"); + const STRING_LINESTYLES = new Sk.builtin.str("linestyles"); + const STRING_BINS = new Sk.builtin.str("bins"); + const STRING_ALIGN = new Sk.builtin.str("align"); + const STRING_DOT_LIMIT = new Sk.builtin.str("dot_limit"); + const STRING_S = new Sk.builtin.str("s"); + const STRING_C = new Sk.builtin.str("c"); + const STRING_LINEWIDTHS = new Sk.builtin.str("linewidths"); + const STRING_EDGECOLORS = new Sk.builtin.str("edgecolors"); + const STRING_WIDTH = new Sk.builtin.str("width"); + const STRING_TICK_LABEL = new Sk.builtin.str("tick_label"); + const STRING_ROTATION = new Sk.builtin.str("rotation"); + + const DEFAULT_PLOT_PADDING = .5; + + + if (Sk.console === undefined) { + throw new Sk.builtin.NameError("Can not resolve drawing area. Sk.console is undefined!"); + } + function getConsole() { + return Sk.console; + } // Unique ID generator for charts - var chartCounter = 0; - - var chart; // The aggregate object to hold multiple plots - var labels; // Title, X-axis title, Y-axis title - var plots; // All the plots to end up drawing - var extents; // The highest and lowest values across each axis - var colorCycle; - - function resetChart() { - chart = null; - colorCycle = 0; - labels = { - "title": "", - "x-axis": "", - "y-axis": "" - }; - plots = []; - extents = { - "xMin": null, - "yMin": null, - "xMax": null, - "yMax": null + let chartCounter = 0; + + function makePlot(type, data, style, label) { + return { + type: type, + data: data, + style: style, + label: label }; } - resetChart(); - - // Keep track of any plotted values for later checks - mod.values = []; + function makeChart() { + let margin = {top: 20, right: 30, bottom: 50, left: 40}; + let chartIdNumber = chartCounter++; + return { + plots: [], + labels: { + title: "", + "x-axis": "", + "y-axis": "" + }, + extents: { + xMin: null, + xMax: null, + yMin: null, + yMax: null + }, + ticks: { + x: {}, + y: {}, + xRotate: "horizontal", + yRotate: "horizontal", + xEstimate: new Set(), + yEstimate: new Set() + }, + margin: margin, + width: getConsole().getWidth() - margin.left - margin.right, + height: getConsole().getHeight() - margin.top - margin.bottom, + idNumber: chartIdNumber, + id: "chart" + chartIdNumber, + colorCycle: 0 + }; + } - // Creates the aggregate chart object that will hold 1 or more plots - var createChart = function (type) { - if (Sk.console === undefined) { - throw new Sk.builtin.NameError( - "Can not resolve drawing area. Sk.console is undefined!"); + function updateExtent(chart, attr, array, padding) { + if (padding === undefined) { + padding = 0; + } + if (chart.extents[attr + "Min"] === null) { + chart.extents[attr + "Min"] = d3.min(array)-padding; + } else { + chart.extents[attr + "Min"] = Math.min(d3.min(array)-padding, chart.extents[attr + "Min"]); + } + if (chart.extents[attr + "Max"] === null) { + chart.extents[attr + "Max"] = d3.max(array)+padding; + } else { + chart.extents[attr + "Max"] = Math.max(d3.max(array)+padding, chart.extents[attr + "Max"]); } + } + let chart = null; + function getChart() { if (!chart) { - // Create a new chart - chartCounter += 1; - chart = {}; + chart = makeChart(); + } + return chart; + } - chart.margin = {"top": 20, "right": 30, "bottom": 50, "left": 40}; - chart.width = Sk.console.getWidth() - chart.margin.left - chart.margin.right; - chart.height = Sk.console.getHeight() - chart.margin.top - chart.margin.bottom; - chart.id = "chart" + chartCounter; - chart.type = type; + function resetChart() { + chart = makeChart(); + } - return chart; + function parseFormat(styleString, chart, kwargs, defaultLinestyle, defaultMarker) { + // TODO: Handle ls, lw, and other abbreviated keyword parameters + let format = { + alpha: getKeywordParameter(kwargs, STRING_ALPHA, null), + dash_capstyle: getKeywordParameter(kwargs, STRING_DASH_CAPSTYLE, "butt"), + dash_joinstyle: getKeywordParameter(kwargs, STRING_DASH_JOINSTYLE, "miter"), + fillstyle: getKeywordParameter(kwargs, STRING_FILLSTYLE, "full"), + linewidth: getKeywordParameter(kwargs, STRING_LINEWIDTH, 1), + }; + if (styleString) { + let ftmTuple = Sk.jsplotlib._process_plot_format(styleString); + format["linestyle"] = getKeywordParameter(kwargs, STRING_LINESTYLE, ftmTuple.linestyle); + format["marker"] = getKeywordParameter(kwargs, STRING_MARKER, Sk.jsplotlib.parse_marker(ftmTuple.marker)); + format["color"] = getKeywordParameter(kwargs, STRING_COLOR, Sk.jsplotlib.color_to_hex(ftmTuple.color)); + } else { + let cycle = Sk.jsplotlib.rc["axes.color_cycle"]; + format["linestyle"] = getKeywordParameter(kwargs, STRING_LINESTYLE, defaultLinestyle); + format["marker"] = getKeywordParameter(kwargs, STRING_MARKER, defaultMarker); + format["color"] = getKeywordParameter(kwargs, STRING_COLOR, Sk.jsplotlib.color_to_hex(cycle[chart.colorCycle % cycle.length])); + chart.colorCycle += 1; } - }; + return format; + } - function updateMinMax(attr, array) { - if (extents[attr + "Min"] === null) { - extents[attr + "Min"] = d3.min(array); + function getIndices(values) { + return values.map((value, index) => index); + } + + function chompPlotArgs(args, data) { + if (data !== null) { + if (args.length >= 2) { + throw new Sk.builtin.ValueError("Must provide at least 2 arguments when plotting with 'data' keyword"); + } + let xAttr = args[0]; + let yAttr = args[1]; + let xs = [], ys = []; + const values = Sk.misceval.arrayFromIterable(data); + for (let i = 0; i < values.length; i++) { + if (values[i].sq$contains(xAttr)) { + xs.push(values[i].mp$subscript(xAttr)); + ys.push(values[i].mp$subscript(yAttr)); + } else { + throw new Sk.builtin.ValueError(`Item at index ${i} is missing expected attribute.`); + } + } + return [xs, ys, args[2]]; } else { - extents[attr + "Min"] = Math.min(d3.min(array), extents[attr + "Min"]); + // x, y, style + if (args.length >= 3) { + if (Sk.builtin.checkString(args[2])) { + return [[args[0], args[1], args[2]].map(Sk.ffi.remapToJs)].concat(chompPlotArgs(args.slice(3), data)); + } + } + if (args.length >= 2) { + let ys = Sk.ffi.remapToJs(args[0]); + if (Sk.builtin.checkString(args[1])) { + // y, style + let xs = getIndices(ys); + let style = Sk.ffi.remapToJs(args[1]); + return [[xs, ys, style]].concat(chompPlotArgs(args.slice(2), data)); + } else { + // x, y + let xs = ys; + ys = Sk.ffi.remapToJs(args[1]); + return [[xs, ys, ""]].concat(chompPlotArgs(args.slice(2), data)); + } + } + if (args.length >= 1) { + // y + let ys = Sk.ffi.remapToJs(args[0]); + let xs = getIndices(ys); + return [[xs, ys, ""]].concat(chompPlotArgs(args.slice(1), data)); + } + return []; } - if (extents[attr + "Max"] === null) { - extents[attr + "Max"] = d3.max(array); + } + + function getKeywordParameter(kwargs, key, otherwise) { + if (kwargs.sq$contains(key)) { + return Sk.ffi.remapToJs(kwargs.mp$subscript(key)); } else { - extents[attr + "Max"] = Math.max(d3.max(array), extents[attr + "Max"]); + return otherwise; } } - function getRandomSubarray(arr, size) { - var shuffled = arr.slice(0), i = arr.length, temp, index; - while (i--) { - index = Math.floor((i + 1) * Math.random()); - temp = shuffled[index]; - shuffled[index] = shuffled[i]; - shuffled[i] = temp; - } - return shuffled.slice(0, size); + function updateTickEstimates(chart, xValues, yValues) { + xValues.forEach(value => chart.ticks.xEstimate.add(value)); + yValues.forEach(value => chart.ticks.yEstimate.add(value)); } // Main plotting function - var plot_f = function (kwa) { + let plot_f = function (kwa) { // Parse arguments - Sk.builtin.pyCheckArgs("plotk", arguments, 1, Infinity, true, false); - args = Array.prototype.slice.call(arguments, 1); - kwargs = new Sk.builtins.dict(kwa); // is pretty useless for handling kwargs - kwargs = Sk.ffi.remapToJs(kwargs); // create a proper dict - - // Keep a backup of the arguments for checker - mod.values.push(args); - - // Parse different argument combinations - var xdata = null; - var ydata = null; - var stylestring = null; - if (args.length === 1) { - // ydata - ydata = Sk.ffi.remapToJs(args[0]); - } else if (args.length === 2) { - if (Sk.builtin.checkString(args[1])) { - // ydata, style - ydata = Sk.ffi.remapToJs(args[0]); - stylestring = Sk.ffi.remapToJs(args[1]); - } else { - // xdata, ydata - xdata = Sk.ffi.remapToJs(args[0]); - ydata = Sk.ffi.remapToJs(args[1]); + Sk.builtin.pyCheckArgs("plot", arguments, 1, Infinity, true, false); + let args = Array.prototype.slice.call(arguments, 1); + let kwargs = new Sk.builtins.dict(kwa); // Get argument as object + let data = getKeywordParameter(kwargs, STRING_DATA, null); + let label = getKeywordParameter(kwargs, STRING_LABEL, null); + let chart = getChart(); + let plotData = chompPlotArgs(args, data); + for (let i=0; i {return {x: value[0], y: value[1]};}); + let style = parseFormat(plotDatum[2], chart, kwargs, "-", ""); + let plot = makePlot("line", zippedData, style, label); + chart.plots.push(plot); + updateExtent(chart, "x", plotDatum[0], DEFAULT_PLOT_PADDING); + updateExtent(chart, "y", plotDatum[1], DEFAULT_PLOT_PADDING); + updateTickEstimates(chart, plotDatum[0], plotDatum[1]); + } + }; + plot_f.co_kwargs = true; + mod.plot = new Sk.builtin.func(plot_f); + + + let hist_f = function (kwa) { + // Parse arguments + Sk.builtin.pyCheckArgs("hist", arguments, 1, Infinity, true, false); + let args = Array.prototype.slice.call(arguments, 1); + let kwargs = new Sk.builtins.dict(kwa); // Get argument as object + + // Parse different args combinations + let plotData = Sk.ffi.remapToJs(args[0]); + if (plotData.length > 0) { + if (!Array.isArray(plotData[0])) { + plotData = [plotData]; + } + } + + let label = getKeywordParameter(kwargs, STRING_LABEL, null); + let chart = getChart(); + for (let i=0; i Math.max(Math.min(value, max), min)); + estimatedBins = plot.bins; } - } else if (args.length === 3) { - // xdata, ydata, style - xdata = Sk.ffi.remapToJs(args[0]); - ydata = Sk.ffi.remapToJs(args[1]); - stylestring = Sk.ffi.remapToJs(args[2]); + chart.plots.push(plot); + updateExtent(chart, "x", plotDatum, DEFAULT_PLOT_PADDING); + updateTickEstimates(chart, estimatedBins, d3.range(plotDatum.length)); } - if (xdata === null) { - xdata = []; - for (var i = 0; i < ydata.length; i++) { - xdata.push(i); + // Ensure that the axis line is always the middle! + updateExtent(chart, "y", [0]); + }; + hist_f.co_kwargs = true; + mod.hist = new Sk.builtin.func(hist_f); + + let scatter_f = function (kwa) { + // Parse arguments + Sk.builtin.pyCheckArgs("scatter", arguments, 1, Infinity, true, false); + let args = Array.prototype.slice.call(arguments, 1); + let kwargs = new Sk.builtins.dict(kwa); // Get argument as object + + let data = getKeywordParameter(kwargs, STRING_DATA, null); + let label = getKeywordParameter(kwargs, STRING_LABEL, null); + + let plotData = chompPlotArgs(args, data); + + // Special dot_limit parameter to prevent crashing from too many dots in browser + let dotLimit = getKeywordParameter(kwargs, STRING_DOT_LIMIT, 256); + if (plotData[0] && plotData[0].length > dotLimit) { + let xSampled = [], ySampled = []; + let LENGTH = plotData[0].length, RATE = LENGTH / dotLimit; + for (let i = 0; i < dotLimit; i += 1) { + let index = Math.floor((i + Math.random()) * RATE); + xSampled.push(plotData[0][index]); + ySampled.push(plotData[1][index]); } + plotData[0] = xSampled; + plotData[1] = ySampled; } - // empty canvas from previous plots - createChart("line"); - // Zip up the data - var actualData = d3.zip(xdata, ydata).map(function (e) { - return {"x": e[0], "y": e[1]}; - }); - // Parse formatting, also keep ColorCycler updated - var cycle = jsplotlib.rc["axes.color_cycle"]; - var linestyle = "-", marker = "", - color = cycle[colorCycle % cycle.length]; - if (stylestring !== null) { - var ftm_tuple = jsplotlib._process_plot_format(stylestring); - linestyle = ftm_tuple.linestyle; - marker = jsplotlib.parse_marker(ftm_tuple.marker); - color = ftm_tuple.color; - } else { - colorCycle += 1; + let chart = getChart(); + for (let i=0; i {return {x: value[0], y: value[1]};}); + let plot = makePlot("scatter", zippedData, style, label); + plot.sizes = getKeywordParameter(kwargs, STRING_S, null); + plot.colors = getKeywordParameter(kwargs, STRING_C, null); + plot.linewidths = getKeywordParameter(kwargs, STRING_LINEWIDTHS, 1.5); + plot.edgecolors = getKeywordParameter(kwargs, STRING_EDGECOLORS, "face"); + chart.plots.push(plot); + updateExtent(chart, "x", plotDatum[0], DEFAULT_PLOT_PADDING); + updateExtent(chart, "y", plotDatum[1], DEFAULT_PLOT_PADDING); + updateTickEstimates(chart, plotDatum[0], plotDatum[1]); } - // Save - plots.push({ - "data": actualData, - "type": "line", - "style": { - "linestyle": linestyle, - "marker": marker, - "color": jsplotlib.color_to_hex(color) - } - }); - // Update min/max - updateMinMax("x", xdata); - updateMinMax("y", ydata); + }; + scatter_f.co_kwargs = true; + mod.scatter = new Sk.builtin.func(scatter_f); - /*if (Sk.console.isMuted()) { - return; - }*/ + let bar_f = function (kwa) { + // Parse arguments + Sk.builtin.pyCheckArgs("bar", arguments, 1, Infinity, true, false); + let args = Array.prototype.slice.call(arguments, 1); + let kwargs = new Sk.builtins.dict(kwa); // Get argument as object + + let data = getKeywordParameter(kwargs, STRING_DATA, null); + let label = getKeywordParameter(kwargs, STRING_LABEL, null); + let tickLabels = getKeywordParameter(kwargs, STRING_TICK_LABEL, []); + + let plotData = chompPlotArgs(args, data); + + let chart = getChart(); + for (let i=0; i {return {x: value[0], y: value[1]};}); + let style = parseFormat(null, chart, kwargs, "", ""); + let plot = makePlot("bar", zippedData, style, label); + plot.width = getKeywordParameter(kwargs, STRING_WIDTH, 0.8); + plot.align = getKeywordParameter(kwargs, STRING_ALIGN, "center"); + for (let x=0; x a.length)) || 1; + args = args.map(a => Array.isArray(a) ? a : Array(length).fill(a)); + return d3.zip(args[0], args[1], args[2]); + } - if (!chart) { - createChart("line"); - } - // TODO: Currently, cannot overlay histograms on other charts - if (chart.type === "hist" && plots.length < 1) { - resetChart(); - return; - } - if (plots.length === 0) { - return; - } - if (extents["xMin"] === undefined || extents["yMin"] === undefined) { - return; + let hlines_f = function (kwa) { + // Parse arguments + Sk.builtin.pyCheckArgs("hlines", arguments, 1, Infinity, true, false); + let args = Array.prototype.slice.call(arguments, 1); + let kwargs = new Sk.builtins.dict(kwa); // Get argument as object + + let data = getKeywordParameter(kwargs, STRING_DATA, null); + let label = getKeywordParameter(kwargs, STRING_LABEL, null); + let colors = getKeywordParameter(kwargs, STRING_COLORS, null); + let linestyles = getKeywordParameter(kwargs, STRING_LINESTYLES, null); + + let plotData = getLinesData(args); + + let chart = getChart(); + let style = parseFormat(null, chart, kwargs, "", ""); + for (let i=0; i