Skip to content

all: fix iterable object, implement filter, map, oct and optimise hex #222

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions py/arithmetic.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,24 +147,6 @@ func MakeFloat(a Object) (Object, error) {
return nil, ExceptionNewf(TypeError, "unsupported operand type(s) for float: '%s'", a.Type().Name)
}

// Iter the python Object returning an Object
//
// Will raise TypeError if Iter can't be run on this object
func Iter(a Object) (Object, error) {

if A, ok := a.(I__iter__); ok {
res, err := A.M__iter__()
if err != nil {
return nil, err
}
if res != NotImplemented {
return res, nil
}
}

return nil, ExceptionNewf(TypeError, "unsupported operand type(s) for iter: '%s'", a.Type().Name)
}

// Add two python objects together returning an Object
//
// Will raise TypeError if can't be add can't be run on these objects
Expand Down
8 changes: 4 additions & 4 deletions py/dict.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func init() {
return nil, err
}
sMap := self.(StringDict)
o := make([]Object, 0, len(sMap))
o := make(Tuple, 0, len(sMap))
for k, v := range sMap {
o = append(o, Tuple{String(k), v})
}
Expand All @@ -49,7 +49,7 @@ func init() {
return nil, err
}
sMap := self.(StringDict)
o := make([]Object, 0, len(sMap))
o := make(Tuple, 0, len(sMap))
for k := range sMap {
o = append(o, String(k))
}
Expand All @@ -62,7 +62,7 @@ func init() {
return nil, err
}
sMap := self.(StringDict)
o := make([]Object, 0, len(sMap))
o := make(Tuple, 0, len(sMap))
for _, v := range sMap {
o = append(o, v)
}
Expand Down Expand Up @@ -204,7 +204,7 @@ func (a StringDict) M__repr__() (Object, error) {

// Returns a list of keys from the dict
func (d StringDict) M__iter__() (Object, error) {
o := make([]Object, 0, len(d))
o := make(Tuple, 0, len(d))
for k := range d {
o = append(o, String(k))
}
Expand Down
2 changes: 2 additions & 0 deletions py/exception.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ func ExceptionGivenMatches(err, exc Object) bool {
func IsException(exception *Type, r interface{}) bool {
var t *Type
switch ex := r.(type) {
case ExceptionInfo:
t = ex.Type
case *Exception:
t = ex.Type()
case *Type:
Expand Down
72 changes: 72 additions & 0 deletions py/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2023 The go-python Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package py

// A python Filter object
type Filter struct {
it Object
fun Object
}

var FilterType = NewTypeX("filter", `filter(function or None, iterable) --> filter object

Return an iterator yielding those items of iterable for which function(item)
is true. If function is None, return the items that are true.`,
FilterTypeNew, nil)

// Type of this object
func (f *Filter) Type() *Type {
return FilterType
}

// FilterTypeNew
func FilterTypeNew(metatype *Type, args Tuple, kwargs StringDict) (res Object, err error) {
var fun, seq Object
var it Object
err = UnpackTuple(args, kwargs, "filter", 2, 2, &fun, &seq)
if err != nil {
return nil, err
}
it, err = Iter(seq)
if err != nil {
return nil, err
}
return &Filter{it: it, fun: fun}, nil
}

func (f *Filter) M__iter__() (Object, error) {
return f, nil
}

func (f *Filter) M__next__() (Object, error) {
var ok bool
for {
item, err := Next(f.it)
if err != nil {
return nil, err
}
// if (lz->func == Py_None || lz->func == (PyObject *)&PyBool_Type)
if _, _ok := f.fun.(Bool); _ok || f.fun == None {
ok, err = ObjectIsTrue(item)
} else {
var good Object
good, err = Call(f.fun, Tuple{item}, nil)
if err != nil {
return nil, err
}
ok, err = ObjectIsTrue(good)
}
if ok {
return item, nil
}
if err != nil {
return nil, err
}
}
}

// Check interface is satisfied
var _ I__iter__ = (*Filter)(nil)
var _ I__next__ = (*Filter)(nil)
1 change: 0 additions & 1 deletion py/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ var data = Data{
{Name: "complex", Title: "MakeComplex", Operator: "complex", Unary: true, Conversion: "Complex"},
{Name: "int", Title: "MakeInt", Operator: "int", Unary: true, Conversion: "Int"},
{Name: "float", Title: "MakeFloat", Operator: "float", Unary: true, Conversion: "Float"},
{Name: "iter", Title: "Iter", Operator: "iter", Unary: true},
},
BinaryOps: Ops{
{Name: "add", Title: "Add", Operator: "+", Binary: true},
Expand Down
17 changes: 17 additions & 0 deletions py/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,3 +430,20 @@ func ReprAsString(self Object) (string, error) {
}
return string(str), nil
}

// Returns an iterator object
//
// Call __Iter__ Returns an iterator object
//
// If object is sequence object, create an iterator
func Iter(self Object) (res Object, err error) {
if I, ok := self.(I__iter__); ok {
return I.M__iter__()
} else if res, ok, err = TypeCall0(self, "__iter__"); ok {
return res, err
}
if ObjectIsSequence(self) {
return NewIterator(self), nil
}
return nil, ExceptionNewf(TypeError, "'%s' object is not iterable", self.Type().Name)
}
36 changes: 26 additions & 10 deletions py/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ package py

// A python Iterator object
type Iterator struct {
Pos int
Objs []Object
Pos int
Seq Object
}

var IteratorType = NewType("iterator", "iterator type")
Expand All @@ -20,10 +20,10 @@ func (o *Iterator) Type() *Type {
}

// Define a new iterator
func NewIterator(Objs []Object) *Iterator {
func NewIterator(Seq Object) *Iterator {
m := &Iterator{
Pos: 0,
Objs: Objs,
Pos: 0,
Seq: Seq,
}
return m
}
Expand All @@ -33,13 +33,29 @@ func (it *Iterator) M__iter__() (Object, error) {
}

// Get next one from the iteration
func (it *Iterator) M__next__() (Object, error) {
if it.Pos >= len(it.Objs) {
return nil, StopIteration
func (it *Iterator) M__next__() (res Object, err error) {
if tuple, ok := it.Seq.(Tuple); ok {
if it.Pos >= len(tuple) {
return nil, StopIteration
}
res = tuple[it.Pos]
it.Pos++
return res, nil
}
index := Int(it.Pos)
if I, ok := it.Seq.(I__getitem__); ok {
res, err = I.M__getitem__(index)
} else if res, ok, err = TypeCall1(it.Seq, "__getitem__", index); !ok {
return nil, ExceptionNewf(TypeError, "'%s' object is not iterable", it.Type().Name)
}
if err != nil {
if IsException(IndexError, err) {
return nil, StopIteration
}
return nil, err
}
r := it.Objs[it.Pos]
it.Pos++
return r, nil
return res, nil
}

// Check interface is satisfied
Expand Down
8 changes: 6 additions & 2 deletions py/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func (l *List) M__bool__() (Object, error) {
}

func (l *List) M__iter__() (Object, error) {
return NewIterator(l.Items), nil
return NewIterator(Tuple(l.Items)), nil
}

func (l *List) M__getitem__(key Object) (Object, error) {
Expand Down Expand Up @@ -496,7 +496,11 @@ func SortInPlace(l *List, kwargs StringDict, funcName string) error {
reverse = False
}
// FIXME: requires the same bool-check like CPython (or better "|$Op" that doesn't panic on nil).
s := ptrSortable{&sortable{l, keyFunc, ObjectIsTrue(reverse), nil}}
ok, err := ObjectIsTrue(reverse)
if err != nil {
return err
}
s := ptrSortable{&sortable{l, keyFunc, ok, nil}}
sort.Stable(s)
return s.s.firstErr
}
60 changes: 60 additions & 0 deletions py/map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2023 The go-python Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package py

// A python Map object
type Map struct {
iters Tuple
fun Object
}

var MapType = NewTypeX("filter", `map(func, *iterables) --> map object

Make an iterator that computes the function using arguments from
each of the iterables. Stops when the shortest iterable is exhausted.`,
MapTypeNew, nil)

// Type of this object
func (m *Map) Type() *Type {
return FilterType
}

// MapType
func MapTypeNew(metatype *Type, args Tuple, kwargs StringDict) (res Object, err error) {
numargs := len(args)
if numargs < 2 {
return nil, ExceptionNewf(TypeError, "map() must have at least two arguments.")
}
iters := make(Tuple, numargs-1)
for i := 1; i < numargs; i++ {
iters[i-1], err = Iter(args[i])
if err != nil {
return nil, err
}
}
return &Map{iters: iters, fun: args[0]}, nil
}

func (m *Map) M__iter__() (Object, error) {
return m, nil
}

func (m *Map) M__next__() (Object, error) {
numargs := len(m.iters)
argtuple := make(Tuple, numargs)

for i := 0; i < numargs; i++ {
val, err := Next(m.iters[i])
if err != nil {
return nil, err
}
argtuple[i] = val
}
return Call(m.fun, argtuple, nil)
}

// Check interface is satisfied
var _ I__iter__ = (*Map)(nil)
var _ I__next__ = (*Map)(nil)
53 changes: 41 additions & 12 deletions py/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,53 @@ func ObjectRepr(o Object) Object {
}

// Return whether the object is True or not
func ObjectIsTrue(o Object) bool {
if o == True {
return true
func ObjectIsTrue(o Object) (cmp bool, err error) {
switch o {
case True:
return true, nil
case False:
return false, nil
case None:
return false, nil
}
if o == False {
return false

var res Object
switch t := o.(type) {
case I__bool__:
res, err = t.M__bool__()
case I__len__:
res, err = t.M__len__()
case *Type:
var ok bool
if res, ok, err = TypeCall0(o, "__bool__"); ok {
break
}
if res, ok, err = TypeCall0(o, "__len__"); ok {
break
}
_ = ok // pass static-check
}
if err != nil {
return false, err
}

if o == None {
return false
switch t := res.(type) {
case Bool:
return t == True, nil
case Int:
return t > 0, nil
}
return true, nil
}

if I, ok := o.(I__bool__); ok {
cmp, err := I.M__bool__()
if err == nil && cmp == True {
// Return whether the object is a sequence
func ObjectIsSequence(o Object) bool {
switch t := o.(type) {
case I__getitem__:
return true
case *Type:
if t.GetAttrOrNil("__getitem__") != nil {
return true
} else if err == nil && cmp == False {
return false
}
}
return false
Expand Down
Loading