1
0
mirror of https://github.com/exaloop/codon.git synced 2025-06-03 15:03:52 +08:00
codon/stdlib/internal/builtin.codon
A. R. Shajii 5613c1a84b
v0.16 (#335)
* Add Python extension lowering pass

* Add DocstringAttribute

* Add extension module codegen

* Handle different argument counts efficiently

* Add warnings to extension lowering

* Fix module name

* Fix extension codegen

* Fix argument check

* Auto-convert Codon exceptions to Python exceptions

* Fix #183

* Fix #162; Fix #135

* Fix #155

* Fix CPython interface in codegen

* Fix #191

* Fix #187

* Fix #189

* Generate object file in pyext mode

* Convert Codon exceptions to Python exceptions

* Fix vtable init; Fix failing tests on Linux

* Fix #190

* Fix #156

* Fix union routing

* Remove need for import python

* Automatic @export and wrapping for toplevel functions

* Reorganize API

* Add Python extension IR structs

* Add special calls for no-suspend yield-expr

* Add special calls for no-suspend yield-expr

* pyextension.h support [wip]

* pyextension.h support [wip]

* pyextension.h support

* pyextension.h support for toplevel functions

* clang-format

* Add PyFunction::nargs field

* Update pyextension codegen (WIP)

* SUpport nargs

* Add support for @pycapture

* PyType codegen (WIP)

* Py method codegen (WIP)

* Add type ptr hook

* Add getset codegen

* Add type alloc function

* Add type pointer hook codegen

* Re-organize codegen

* Add member codegen

* Update module init codegen

* Update module init codegen

* Add support for typePtrHook and new to/from_py hooks

* Fix extension codegen

* Fix init codegen

* Fix init codegen; add "tp_new" slot

* Fix type hook

* Add extra flags

* Specialized wrappers (PyType specs)

* Add static Python link option

* Fix C imports

* Add guards

* Remove unused field

* Python mode only when pyExt set

* Update python module

* Fix assert

* Update codegen/passes

* Fix tuple parsing in index expression

* Fix empty tuple unification

* Do not Cythonize underscore fns

* clang-format

* Fix switch

* Add Py support for cmp/setitem

* Add Py support for cmp/setitem

* Add type is support

* GetSet support

* clang-format

* GetSet support (fixes)

* Avoid useless vtable alloc

* Add iter support

* Fix size_t capture bug

* clang-format

* Fix POD type unification with tuples

* Add __try_from_py__ API

* Fix annotation

* Add static reflection methods (setattr; internal.static.*); refactor PyExt to python.codon; handle errors and kwargs in PyExt

* Python compat fixes

* Update Python object conversions

* Fix PyErrors

* clang-format; add copyright

* Add PyFunction::keywords field

* Fix JIT MRO handling; Refactor out Jupyter support

* Refactor out Jupyter support

* Add support for custom linking args (link=[]) to TOML plugins

* Fix tests

* Use g++ instead of gcc

* Fix Jupyter CMAKE

* Fix Jupyter CMAKE

* Add _PyArg_Parser definition

* Add complex64 type

* Add extra complex64 tests

* Fix Python calls; add staticenumerate

* Fix call

* Fix calls

* Update pyext wrappers

* Fix staticenumerate; Support static calls in tuple()

* Fix pyext routing

* Add add/mul for tuples

* clang-format

* Fix pyext codegen

* Fix wrap_multiple

* Add seq_alloc_atomic_uncollectable

* Fix default generics issue

* Add binary/ternary ops

* Fix missing generic issue

* Fix number slots

* Update pow

* Remove unnecessary pyobj

* Fix allocation

* Refactor errors

* Add test extension

* Fix formatting

* clang-format

* Fix getitem/setitem/delitem in pyext

* Fix pyext iterators

* Add builtin pow() (fix #294)

* Fix #244

* Fix #231

* Fix #229

* Fix #205

* Update docs

* Fix error message

* Add pyext tests

* Add pyext support for @property

* Add pyext support for toplevel fns and @tuple classes

* More pyext tests

* More pyext tests

* Fix file error checking

* More pyext tests

* Update pyext tests

* Update docs

* Add pyext test to CI

* Add pyext support for @tuple.__new__

* Add pyext support for @tuple.__new__

* Fix hetero-tuple issue with fn_overloads

* More pyext tests

* Bump versions

* Fix del magic in pyext

* Fix init magic for tuples in pyext

* Have test-pypi only run on develop branch

* Make exception type indices unnamed-addr

* Fix #316; Fix #317 (slash issue)

* Use uncollectible-alloc for vtable

* Fix #249

* Add pyext docs

* Fix #249; Fix clashing vtables; Fix super() and class_copy

* Add content-atomic type property instruction

* __contents_atomic__ support

* Update internal functions

* Use PIC when generating Python extension

* Cleanup

* Add Dockerfile & fix -fPIC

* Cleanup

* Fix setup.py

* Fix pyext fn iteration

* Fix CI

* clang-format

* Update long conversions in Py bridge

* Support wide-int to str conversions

* Fix test

* Add pow for arbitrary-width ints

* Fix Linux backtraces

* Cleanup

* Add more tests

* Fix docs; Remove tuple.__add__ for scalars

* Update docs

---------

Co-authored-by: Ibrahim Numanagić <ibrahimpasa@gmail.com>
2023-04-12 18:13:54 -04:00

416 lines
9.5 KiB
Python

# Copyright (C) 2022-2023 Exaloop Inc. <https://exaloop.io>
class object:
def __init__(self):
pass
def __repr__(self) -> str:
return f"<{self.__class__.__name__} object at {self.__raw__()}>"
def id(x) -> int:
if isinstance(x, ByRef):
return int(x.__raw__())
else:
return 0
_stdout = _C.seq_stdout()
def print(*args, sep: str = " ", end: str = "\n", file=_stdout, flush: bool = False):
"""
Print args to the text stream file.
"""
fp = cobj()
if isinstance(file, cobj):
fp = file
else:
fp = file.fp
i = 0
for a in args:
if i and sep:
_C.seq_print_full(sep, fp)
_C.seq_print_full(str(a), fp)
i += 1
_C.seq_print_full(end, fp)
if flush:
_C.fflush(fp)
@extend
class __internal__:
def prn(*args):
print(*args)
def min(*args):
if staticlen(args) == 0:
raise ValueError("empty sequence")
elif staticlen(args) == 1 and hasattr(args[0], "__iter__"):
x = args[0].__iter__()
if not x.done():
s = x.next()
while not x.done():
i = x.next()
if i < s:
s = i
x.destroy()
return s
else:
x.destroy()
raise ValueError("empty sequence")
elif staticlen(args) == 2:
a, b = args
return a if a <= b else b
else:
m = args[0]
for i in args:
if i < m:
m = i
return m
def max(*args):
if staticlen(args) == 0:
raise ValueError("empty sequence")
elif staticlen(args) == 1 and hasattr(args[0], "__iter__"):
x = args[0].__iter__()
if not x.done():
s = x.next()
while not x.done():
i = x.next()
if i > s:
s = i
x.destroy()
return s
else:
x.destroy()
raise ValueError("empty sequence")
elif staticlen(args) == 2:
a, b = args
return a if a >= b else b
else:
m = args[0]
for i in args:
if i > m:
m = i
return m
def len(x) -> int:
"""
Return the length of x
"""
return x.__len__()
def iter(x):
"""
Return an iterator for the given object
"""
return x.__iter__()
def abs(x):
"""
Return the absolute value of x
"""
return x.__abs__()
def hash(x) -> int:
"""
Returns hashed value only for immutable objects
"""
return x.__hash__()
def ord(s: str) -> int:
"""
Return an integer representing the Unicode code point of s
"""
if len(s) != 1:
raise TypeError(
f"ord() expected a character, but string of length {len(s)} found"
)
return int(s.ptr[0])
def divmod(a, b):
if hasattr(a, "__divmod__"):
return a.__divmod__(b)
else:
return (a // b, a % b)
def chr(i: int) -> str:
"""
Return a string representing a character whose Unicode
code point is an integer
"""
p = cobj(1)
p[0] = byte(i)
return str(p, 1)
def next(g: Generator[T], default: Optional[T] = None, T: type) -> T:
"""
Return the next item from g
"""
if g.done():
if default is not None:
return default.__val__()
else:
raise StopIteration()
return g.next()
def any(x: Generator[T], T: type) -> bool:
"""
Returns True if any item in x is true,
False otherwise
"""
for a in x:
if a:
return True
return False
def all(x: Generator[T], T: type) -> bool:
"""
Returns True when all elements in x are true,
False otherwise
"""
for a in x:
if not a:
return False
return True
def zip(*args):
"""
Returns a zip object, which is an iterator of tuples
that aggregates elements based on the iterables passed
"""
if staticlen(args) == 0:
yield from List[int]()
else:
iters = tuple(iter(i) for i in args)
done = False
while not done:
for i in iters:
if i.done():
done = True
if not done:
yield tuple(i.next() for i in iters)
for i in iters:
i.destroy()
def filter(f: Callable[[T], bool], x: Generator[T], T: type) -> Generator[T]:
"""
Returns all a from the iterable x that are filtered by f
"""
for a in x:
if f(a):
yield a
def map(f, *args):
"""
Applies a function on all a in x and returns map object
"""
if staticlen(args) == 0:
compile_error("map() expects at least one iterator")
elif staticlen(args) == 1:
for a in args[0]:
yield f(a)
else:
for a in zip(*args):
yield f(*a)
def enumerate(x, start: int = 0):
"""
Creates a tuple containing a count (from start which defaults
to 0) and the values obtained from iterating over x
"""
i = start
for a in x:
yield (i, a)
i += 1
def staticenumerate(tup):
i = -1
return tuple(((i := i + 1), t) for t in tup)
i
def echo(x):
"""
Print and return argument
"""
print x
return x
def reversed(x):
"""
Return an iterator that accesses x in the reverse order
"""
if hasattr(x, "__reversed__"):
return x.__reversed__()
else:
i = x.__len__() - 1
while i >= 0:
yield x[i]
i -= 1
def round(x, n=0):
"""
Return the x rounded off to the given
n digits after the decimal point.
"""
nx = float.__pow__(10.0, n)
return float.__round__(x * nx) / nx
def _sum_start(x, start):
if isinstance(x.__iter__(), Generator[float]) and isinstance(start, int):
return float(start)
else:
return start
def sum(x, start=0):
"""
Return the sum of the items added together from x
"""
s = _sum_start(x, start)
for a in x:
# don't use += to avoid calling iadd
if isinstance(a, bool):
s = s + (1 if a else 0)
else:
s = s + a
return s
def repr(x):
"""Return the string representation of x"""
return x.__repr__()
def _int_format(a: int, base: int, prefix: str = ""):
assert base == 2 or base == 8 or base == 10 or base == 16
chars = "0123456789abcdef-"
b = a
digits = 0
while b != 0:
digits += 1
b //= base
sz = digits + (1 if a <= 0 else 0) + len(prefix)
p = Ptr[byte](sz)
q = p
if a < 0:
q[0] = chars[-1].ptr[0]
q += 1
if prefix:
str.memcpy(q, prefix.ptr, len(prefix))
q += len(prefix)
if digits != 0:
b = a
q += digits - 1
i = 1
while b != 0:
i += 1
q[0] = chars.ptr[abs(b % base)]
q += -1
b //= base
else:
q[0] = chars.ptr[0]
return str(p, sz)
def bin(n):
return _int_format(n.__index__(), 2, "0b")
def oct(n):
return _int_format(n.__index__(), 8, "0o")
def hex(n):
return _int_format(n.__index__(), 16, "0x")
def pow(base: float, exp: float):
return base ** exp
@overload
def pow(base: int, exp: int, mod: Optional[int] = None):
if exp < 0:
raise ValueError("pow() negative int exponent not supported")
if mod is not None:
if mod == 0:
raise ValueError("pow() 3rd argument cannot be 0")
base %= mod
result = 1
while exp > 0:
if exp & 1:
x = result * base
result = x % mod if mod is not None else x
y = base * base
base = y % mod if mod is not None else y
exp >>= 1
return result % mod if mod is not None else result
@extend
class int:
def _from_str(s: str, base: int):
from internal.gc import alloc_atomic, free
if base < 0 or base > 36 or base == 1:
raise ValueError("int() base must be >= 2 and <= 36, or 0")
s0 = s
s = s.strip()
buf = __array__[byte](32)
n = len(s)
need_dyn_alloc = n >= len(buf)
p = alloc_atomic(n + 1) if need_dyn_alloc else buf.ptr
str.memcpy(p, s.ptr, n)
p[n] = byte(0)
end = cobj()
result = _C.strtoll(p, __ptr__(end), i32(base))
if need_dyn_alloc:
free(p)
if n == 0 or end != p + n:
raise ValueError(
f"invalid literal for int() with base {base}: {s0.__repr__()}"
)
return result
@extend
class float:
def _from_str(s: str) -> float:
s0 = s
s = s.strip()
buf = __array__[byte](32)
n = len(s)
need_dyn_alloc = n >= len(buf)
p = alloc_atomic(n + 1) if need_dyn_alloc else buf.ptr
str.memcpy(p, s.ptr, n)
p[n] = byte(0)
end = cobj()
result = _C.strtod(p, __ptr__(end))
if need_dyn_alloc:
free(p)
if n == 0 or end != p + n:
raise ValueError(f"could not convert string to float: {s0.__repr__()}")
return result
def _jit_display(x, s: Static[str], bundle: Set[str] = Set[str]()):
if isinstance(x, None):
return
if hasattr(x, "_repr_mimebundle_") and s == "jupyter":
d = x._repr_mimebundle_(bundle)
# TODO: pick appropriate mime
mime = next(d.keys()) # just pick first
print(f"\x00\x00__codon/mime__\x00{mime}\x00{d[mime]}", end='')
elif hasattr(x, "__repr__"):
print(x.__repr__(), end='')
elif hasattr(x, "__str__"):
print(x.__str__(), end='')