Skip to content

Commit

Permalink
Fixed problems with iterators and attribute lookup.
Browse files Browse the repository at this point in the history
  • Loading branch information
freakboy3742 committed Feb 17, 2017
1 parent 0f4c70e commit 73b8646
Show file tree
Hide file tree
Showing 46 changed files with 459 additions and 271 deletions.
13 changes: 5 additions & 8 deletions batavia/builtins/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,19 @@ function all(args, kwargs) {
throw new exceptions.TypeError.$pyclass('all() takes exactly one argument (' + args.length + ' given)');
}

if (!args[0].__iter__) {
throw new exceptions.TypeError.$pyclass("'" + type_name(args[0]) + "' object is not iterable");
}

var iterobj = args[0].__iter__();
try {
var iterobj = callables.call_method(args[0], "__iter__", []);

while (true) {
var next = iterobj.__next__();
var bool_next = next.__bool__();
var next = callables.call_method(iterobj, "__next__", []);
var bool_next = callables.call_method(next, "__bool__", []);
if (!bool_next) {
return false;
}
}
} catch (err) {
if (!(err instanceof exceptions.StopIteration.$pyclass)) {
throw err;
throw new exceptions.TypeError.$pyclass("'" + type_name(args[0]) + "' object is not iterable");
}
}

Expand Down
6 changes: 3 additions & 3 deletions batavia/builtins/any.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ function any(args, kwargs) {
throw new exceptions.TypeError.$pyclass("'" + type_name(args[0]) + "' object is not iterable");
}

var iterobj = args[0].__iter__();
var iterobj = callables.call_method(args[0], "__iter__", []);
try {
while (true) {
var next = iterobj.__next__();
var bool_next = next.__bool__();
var next = callables.call_method(iterobj, "__next__", []);
var bool_next = callables.call_method(next, "__bool__", []);
if (bool_next) {
return true
}
Expand Down
2 changes: 1 addition & 1 deletion batavia/builtins/callable.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function callable(args, kwargs) {
if (!args || args.length != 1) {
throw new exceptions.TypeError.$pyclass('callable() expected exactly 1 argument (' + args.length + ' given)');
}
if (typeof(args[0]) === "function" || (args[0] && args[0].__call__)) {
if ((args[0] instanceof Function) || (args[0] instanceof types.Function)) {
return new types.Bool(true);
} else {
return new types.Bool(false);
Expand Down
14 changes: 4 additions & 10 deletions batavia/builtins/iter.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,11 @@ function iter(args, kwargs) {
if (args.length > 2) {
throw new exceptions.TypeError.$pyclass("iter() expected at most 2 arguments, got 3");
}
var iterobj = args[0];
if (iterobj !== None && typeof iterobj === 'object' && !iterobj.__class__) {
// this is a plain JS object, wrap it in a JSDict
iterobj = new types.JSDict(iterobj);
}

if (iterobj !== None && iterobj.__iter__) {
//needs to work for __iter__ in JS impl (e.g. Map/Filter) and python ones
return iterobj.__iter__();
} else {
throw new exceptions.TypeError.$pyclass("'" + type_name(iterobj) + "' object is not iterable");
try {
return callables.call_method(args[0], "__iter__", []);
} catch (e) {
throw new exceptions.TypeError.$pyclass("'" + type_name(args[0]) + "' object is not iterable");
}
}
iter.__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.';
Expand Down
34 changes: 11 additions & 23 deletions batavia/builtins/print.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,58 +5,46 @@ var types = require('../types');

function print(args, kwargs) {
var sys = require('../modules/sys');
var callables = require('../core/callables');
var sep = kwargs['sep'] || ' ';
var end = kwargs['end'] || '\n';
var file = kwargs['file'] || sys.stdout;
var elm;
var content;

if (args.length == 0) {
file.write([end]);
callables.call_method(file, "write", [end], null);
} else {
for (var i = 0; i < args.length; i++) {
var elm = args[i];
var content;
elm = args[i];

// output the content
if (elm === null || elm === undefined) {
content = "None";
} else if (elm.__getattr__) {
} else {
try {
var str_fn = elm.__getattr__('__str__');
// var str_method;
// if (str_fn instanceof types.Function) {
// str_method = new types.Method(elm, str_fn);
// } else {
// str_method = str_fn;
// }
if (str_fn.__call__) {
str_fn = str_fn.__call__;
}
content = str_fn([]);
content = callables.call_method(elm, "__str__", [], null);
} catch (e) {
if (e instanceof exceptions.AttributeError.$pyclass) {
content = elm.toString();
} else {
throw e;
}
}
// } else if (elm.__str__) {
// content = elm.__str__();
} else {
content = elm.toString();
}
file.write([content]);
callables.call_method(file, "write", [content], null);

// output the separator (or end marker if at the end of line)
if (i == args.length - 1) {
file.write([end]);
callables.call_method(file, "write", [end], null);
} else {
file.write([sep]);
callables.call_method(file, "write", [sep], null);
}
}
}

if (kwargs['flush']) {
file.flush();
callables.call_method(file, "flush", [], null);
}
}
print.__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.";
Expand Down
2 changes: 1 addition & 1 deletion batavia/builtins/range.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function range(args, kwargs){
throw new exceptions.TypeError.$pyclass('range() expected 1 arguments, got ' + args.length);
}
if (args.length > 3) {
throw new exceptions.TypeError.$pyclass('range() expected at most 3 arguments, got ' + args.length);
throw new exceptions.TypeError.$pyclass('range() expected at most 3 arguments, got ' + args.length);
}

return new types.Range(args[0], args[1], args[2]);
Expand Down
20 changes: 19 additions & 1 deletion batavia/builtins/type.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,25 @@ var type = function(args, kwargs) {
return args[0].__class__;
}
} else {
return new types.Type(args[0], args[1], args[2]);
return function (name, bases, dict) {
var new_type = new types.Type(args[0], args[1], args[2]);

function NewType() {
types.Object.call(this);
}

NewType.prototype = Object.create(types.Object.prototype);
NewType.prototype.__class__ = new_type
NewType.prototype.__class__.$pyclass = NewType;

for (var attr in dict) {
if (dict.hasOwnProperty(attr)) {
NewType.prototype[attr] = dict[attr];
}
}

return new_type;
}(args[0], args[1], args[2]);
}
}
type.__doc__ = "type(object_or_name, bases, dict)\ntype(object) -> the object's type\ntype(name, bases, dict) -> a new type";
Expand Down
32 changes: 31 additions & 1 deletion batavia/core/callables.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
var exceptions = require('./exceptions');
var native = require('./native');
var type_name = require('./types/Type').type_name;

var callables = {};

/********************
* Invoking functions
********************/

callables.call_function = function(func, args, kwargs) {
if (func.__call__) {
func = func.__call__;
}

var retval = func(args, kwargs);
return retval;
}

/******************
* Invoking methods
******************/

callables.call_method = function(obj, method_name, args, kwargs) {
var method;
if (obj.__getattr__) {
method = obj.__getattr__(method_name);
} else {
method = native.getattr(obj, method_name);
}

var retval = callables.call_function(method, args, kwargs);
return retval;
}

/************************
* Working with iterables
************************/
Expand All @@ -12,7 +42,7 @@ var callables = {};
callables.iter_for_each = function(iterobj, callback) {
try {
while (true) {
var next = iterobj.__next__([], null);
var next = callables.call_method(iterobj, "__next__", []);
callback(next);
}
} catch (err) {
Expand Down
29 changes: 20 additions & 9 deletions batavia/core/native.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ var exceptions = require('./exceptions');

var native = {};

native.getattr = function(obj, attr) {
native.getattr_raw = function(obj, attr, attributes_only) {
var type_name = require('../core').type_name;

var val = obj[attr];
if (val === undefined) {
throw new exceptions.AttributeError.$pyclass(
"'" + type_name(obj) + "' object has no attribute '" + attr + "'"
);
} else if (val instanceof Function) {
if (val instanceof Function) {
if (attributes_only) {
return undefined;
}
// If this is a native Javascript function, wrap the function
// so that the Python calling convention is used. If it's a
// class constructor, wrap it in a method that uses the Python
Expand Down Expand Up @@ -44,19 +43,31 @@ native.getattr = function(obj, attr) {
return val;
}

native.getattr = function(obj, attr) {
var type_name = require('../core').type_name;

var val = native.getattr_raw(obj, attr);
if (val === undefined) {
throw new exceptions.AttributeError.$pyclass(
"'" + type_name(obj) + "' object has no attribute '" + attr + "'"
);
}
return val;
}

native.setattr = function(obj, attr, value) {
obj[attr] = value;
}

native.delattr = function(obj, attr) {
var type_name = require('../core').type_name;

if (obj[name] === undefined) {
if (obj[attr] === undefined) {
throw new exceptions.AttributeError.$pyclass("'" + type_name(obj) +
"' object has no attribute '" + name + "'"
"' object has no attribute '" + attr + "'"
);
} else {
delete obj[name];
delete obj[attr];
}
}

Expand Down
11 changes: 11 additions & 0 deletions batavia/core/types/NoneType.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ NoneType.prototype.__str__ = function() {
var types = require('../../types');
return new types.Str("None");
}
/**************************************************
* Attribute manipulation
**************************************************/

NoneType.prototype.__setattr__ = function(attr, value) {
if (Object.getPrototypeOf(this)[attr] === undefined) {
throw new exceptions.AttributeError.$pyclass("'NoneType' object has no attribute '" + attr + "'");
} else {
throw new exceptions.AttributeError.$pyclass("'NoneType' object attribute '" + attr + "' is read-only");
}
}

/**************************************************
* Comparison operators
Expand Down
8 changes: 8 additions & 0 deletions batavia/core/types/Object.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ PyObject.prototype.toString = function() {
return '<' + this.__class__.__name__ + ' 0x...>';
}

PyObject.prototype.__repr__ = function() {
return '<' + this.__class__.__name__ + ' 0x...>';
}

PyObject.prototype.__str__ = function() {
return '<' + this.__class__.__name__ + ' 0x...>';
}

PyObject.prototype.__getattr__ = function(name) {
var native = require('../native');

Expand Down
26 changes: 19 additions & 7 deletions batavia/core/types/Type.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,15 @@ Type.prototype.__getattr__ = function(name) {
var exceptions = require('../exceptions');
var native = require('../native');

var attr = native.getattr(this, name);
var attr = native.getattr_raw(this, name);
if (attr === undefined) {
attr = native.getattr(this.$pyclass.prototype, name);
// if the Type doesn't have the attribute, look to the prototype
// of the instance. However, exclude functions; this step is
// only required for class attributes.
attr = native.getattr_raw(this.$pyclass.prototype, name, true);
if (attr === undefined) {
throw new exceptions.AttributeError.$pyclass(
"type object '" + type_name(this) + "' has no attribute '" + name + "'"
"type object '" + this.__name__ + "' has no attribute '" + name + "'"
);
}
}
Expand All @@ -93,21 +96,30 @@ Type.prototype.__getattr__ = function(name) {
Type.prototype.__setattr__ = function(name, value) {
var type_name = require('./Type').type_name;
var exceptions = require('../exceptions');
var native = require('../native');

if (Object.getPrototypeOf(this) === Type) {
throw new exceptions.TypeError.$pyclass(
"can't set attributes of built-in/extension type '" + this.__name__ + "'"
);
}

throw new exceptions.TypeError.$pyclass(
"can't set attributes of built-in/extension type '" + type_name(this) + "'"
);
native.setattr(this.$pyclass.prototype, name, value)
}

Type.prototype.__delattr__ = function(name) {
var type_name = require('./Type').type_name;
var exceptions = require('../exceptions');
var native = require('../native');

var attr = native.getattr_raw(this.$pyclass.prototype, name);
if (attr === undefined) {
throw new exceptions.AttributeError.$pyclass(
"type object '" + type_name(this) + "' has no attribute '" + name + "'"
"type object '" + this.__name__ + "' has no attribute '" + name + "'"
);
}

native.delattr(this.$pyclass.prototype, name);
}

Type.prototype.valueOf = function() {
Expand Down
1 change: 1 addition & 0 deletions batavia/types/Bool.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ var type_name = require('../core').type_name;
var Bool = Boolean;

Bool.prototype.__class__ = new Type('bool');
Bool.prototype.__class__.$pyclass = Bool;

/**************************************************
* Type conversions
Expand Down
Loading

0 comments on commit 73b8646

Please sign in to comment.