From 919fd5d83ee54b5ebf62c8fb7a103eaa99b830d2 Mon Sep 17 00:00:00 2001 From: "John J. Camilleri" Date: Mon, 13 Sep 2021 14:38:05 +0200 Subject: [PATCH] Make Expr_str work for large (size > 1) and negative integers --- src/runtime/python/marshaller.c | 64 ++++++++++++++++++++++++-------- src/runtime/python/test_suite.py | 16 +++++--- 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src/runtime/python/marshaller.c b/src/runtime/python/marshaller.c index 79d7c2b1d..5be63221d 100644 --- a/src/runtime/python/marshaller.c +++ b/src/runtime/python/marshaller.c @@ -1,6 +1,9 @@ #define PY_SSIZE_T_CLEAN #include +#include +#include + #include #include "./compat.h" @@ -67,20 +70,23 @@ PgfExpr eimplarg(PgfUnmarshaller *this, PgfExpr expr) PgfLiteral lint(PgfUnmarshaller *this, size_t size, uintmax_t *v) { intmax_t *v0 = (intmax_t *)v; + PyObject *i = PyLong_FromLong(*v0); + if (size == 0) { return (PgfLiteral) 0; - } else if (size > 1) { - // TODO: string concatenation works but probably not optimal - PyObject *sb = PyUnicode_FromFormat("%ld", *v0); - for (size_t n = 1; n < size; n++) { - uintmax_t *vn = v + n; - PyObject *t = PyUnicode_FromFormat("%lu", *vn); - sb = PyUnicode_Concat(sb, t); - } - PyObject *i = PyLong_FromUnicodeObject(sb, 10); - return (PgfLiteral) i; } else { - PyObject *i = PyLong_FromLong(*v0); + PyObject *intShifter = PyLong_FromUnsignedLong(pow(10, floor(log10(ULONG_MAX)))); + for (int n = 1; n < size; n++) { + i = PyNumber_Multiply(i, intShifter); + if (*v0 >= 0) { + i = PyNumber_Add(i, PyLong_FromUnsignedLong(v[n])); + } else { + i = PyNumber_Subtract(i, PyLong_FromUnsignedLong(v[n])); + } + } + if (PyErr_Occurred()) { + return 0; + } return (PgfLiteral) i; } } @@ -123,7 +129,7 @@ PgfType dtyp(PgfUnmarshaller *this, int n_hypos, PgfTypeHypo *hypos, PgfText *ca void free_ref(PgfUnmarshaller *this, object x) { - Py_XDECREF(x); + // Py_XDECREF(x); } static PgfUnmarshallerVtbl unmarshallerVtbl = @@ -174,10 +180,36 @@ object match_lit(PgfMarshaller *this, PgfUnmarshaller *u, PgfLiteral lit) PyObject *pyobj = (PyObject *)lit; if (PyLong_Check(pyobj)) { - // TODO - uintmax_t i = PyLong_AsLong(pyobj); + PyObject *intShifter = PyLong_FromUnsignedLong(pow(10, floor(log10(ULONG_MAX)))); + + // determine size size_t size = 1; - return u->vtbl->lint(u, size, &i); + PyObject *x = PyNumber_Absolute(PyNumber_Long(pyobj)); // make a copy, ignore sign + while (PyObject_RichCompareBool(x, intShifter, Py_GE) == 1) { + size++; + x = PyNumber_FloorDivide(x, intShifter); + } + + // chop up into chunks, always positive + bool isPos = PyObject_RichCompareBool(pyobj, PyLong_FromLong(0), Py_GE) == 1; + x = PyNumber_Absolute(PyNumber_Long(pyobj)); // make a copy, ignore sign + uintmax_t *i = malloc(sizeof(uintmax_t)*size); + for (int n = size-1; n > 0; n--) { + PyObject *rem = PyNumber_Remainder(x, intShifter); + i[n] = PyLong_AsUnsignedLong(rem); + x = PyNumber_FloorDivide(x, intShifter); + } + + // first chunk, re-applying polarity + if (isPos) + i[0] = PyLong_AsLong(x); + else + i[0] = PyLong_AsLong(PyNumber_Negative(x)); + + if (PyErr_Occurred()) { + return 0; + } + return u->vtbl->lint(u, size, i); } else if (PyFloat_Check(pyobj)) { double d = PyFloat_AsDouble(pyobj); return u->vtbl->lflt(u, d); @@ -194,7 +226,7 @@ object match_expr(PgfMarshaller *this, PgfUnmarshaller *u, PgfExpr ex) { ExprObject *expr = (ExprObject *)ex; - if (expr->ob_base.ob_type == &pgf_ExprLitType) { + if (expr->ob_base.ob_type == &pgf_ExprLitType) { // use PyObject_IsInstance ? ExprLitObject *elit= (ExprLitObject *)expr; return this->vtbl->match_lit(this, u, (PgfLiteral) elit->value); } else { diff --git a/src/runtime/python/test_suite.py b/src/runtime/python/test_suite.py index 3b85ca8fe..37a53443e 100644 --- a/src/runtime/python/test_suite.py +++ b/src/runtime/python/test_suite.py @@ -165,11 +165,17 @@ def test_readExpr_str_int(): def test_readExpr_str_int_neg(): assert str(pgf.readExpr("-123")) == "-123" -# def test_readExpr_str_int_big(): -# assert str(pgf.readExpr("774763251095801167872")) == "774763251095801167872" -# -# def test_readExpr_str_int_big_neg(): -# assert str(pgf.readExpr("-774763251095801167872")) == "-774763251095801167872" +def test_readExpr_str_int_big_2(): + assert str(pgf.readExpr("774763251095801167872")) == "774763251095801167872" + +def test_readExpr_str_int_big_3(): + assert str(pgf.readExpr("7747632510958011678729003251095801167999")) == "7747632510958011678729003251095801167999" + +def test_readExpr_str_int_big_2_neg(): + assert str(pgf.readExpr("-774763251095801167872")) == "-774763251095801167872" + +def test_readExpr_str_int_big_3_neg(): + assert str(pgf.readExpr("-7747632510958011678729003251095801167999")) == "-7747632510958011678729003251095801167999" def test_readExpr_str_float(): assert str(pgf.readExpr("3.142")) == "3.142"