Commit 05f3b5b5 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Support two- and three-arg startswith/endswith

parent 0818c46d
...@@ -19,8 +19,6 @@ ...@@ -19,8 +19,6 @@
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include "llvm/Support/ErrorHandling.h" // For llvm_unreachable
#include "analysis/scoping_analysis.h" #include "analysis/scoping_analysis.h"
#include "core/ast.h" #include "core/ast.h"
#include "core/options.h" #include "core/options.h"
......
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include "Python.h" #include "Python.h"
#include "llvm/Support/ErrorHandling.h" // For llvm_unreachable
#include "capi/types.h" #include "capi/types.h"
#include "core/threading.h" #include "core/threading.h"
#include "core/types.h" #include "core/types.h"
...@@ -675,6 +677,11 @@ void setCAPIException(const ExcInfo& e) { ...@@ -675,6 +677,11 @@ void setCAPIException(const ExcInfo& e) {
cur_thread_state.curexc_traceback = e.traceback; cur_thread_state.curexc_traceback = e.traceback;
} }
void throwCAPIException() {
checkAndThrowCAPIException();
llvm_unreachable("No exception was thrown?");
}
void checkAndThrowCAPIException() { void checkAndThrowCAPIException() {
Box* _type = cur_thread_state.curexc_type; Box* _type = cur_thread_state.curexc_type;
if (!_type) if (!_type)
...@@ -1377,6 +1384,29 @@ extern "C" PyObject* PyCFunction_NewEx(PyMethodDef* ml, PyObject* self, PyObject ...@@ -1377,6 +1384,29 @@ extern "C" PyObject* PyCFunction_NewEx(PyMethodDef* ml, PyObject* self, PyObject
Py_FatalError("unimplemented"); Py_FatalError("unimplemented");
} }
extern "C" int _PyEval_SliceIndex(PyObject* v, Py_ssize_t* pi) noexcept {
if (v != NULL) {
Py_ssize_t x;
if (PyInt_Check(v)) {
/* XXX(nnorwitz): I think PyInt_AS_LONG is correct,
however, it looks like it should be AsSsize_t.
There should be a comment here explaining why.
*/
x = PyInt_AS_LONG(v);
} else if (PyIndex_Check(v)) {
x = PyNumber_AsSsize_t(v, NULL);
if (x == -1 && PyErr_Occurred())
return 0;
} else {
PyErr_SetString(PyExc_TypeError, "slice indices must be integers or "
"None or have an __index__ method");
return 0;
}
*pi = x;
}
return 1;
}
BoxedModule* importTestExtension(const std::string& name) { BoxedModule* importTestExtension(const std::string& name) {
std::string pathname_name = "test/test_extension/" + name + ".pyston.so"; std::string pathname_name = "test/test_extension/" + name + ".pyston.so";
const char* pathname = pathname_name.c_str(); const char* pathname = pathname_name.c_str();
......
...@@ -23,6 +23,7 @@ class BoxedModule; ...@@ -23,6 +23,7 @@ class BoxedModule;
BoxedModule* importTestExtension(const std::string&); BoxedModule* importTestExtension(const std::string&);
void checkAndThrowCAPIException(); void checkAndThrowCAPIException();
void throwCAPIException() __attribute__((noreturn));
struct ExcInfo; struct ExcInfo;
void setCAPIException(const ExcInfo& e); void setCAPIException(const ExcInfo& e);
} }
......
...@@ -1494,7 +1494,9 @@ Box* strContains(BoxedString* self, Box* elt) { ...@@ -1494,7 +1494,9 @@ Box* strContains(BoxedString* self, Box* elt) {
return True; return True;
} }
Box* strStartswith(BoxedString* self, Box* elt) { Box* strStartswith(BoxedString* self, Box* elt, Box* start, Box** _args) {
Box* end = _args[0];
if (self->cls != str_cls) if (self->cls != str_cls)
raiseExcHelper(TypeError, "descriptor 'startswith' requires a 'str' object but received a '%s'", raiseExcHelper(TypeError, "descriptor 'startswith' requires a 'str' object but received a '%s'",
getTypeName(self)->c_str()); getTypeName(self)->c_str());
...@@ -1502,12 +1504,44 @@ Box* strStartswith(BoxedString* self, Box* elt) { ...@@ -1502,12 +1504,44 @@ Box* strStartswith(BoxedString* self, Box* elt) {
if (elt->cls != str_cls) if (elt->cls != str_cls)
raiseExcHelper(TypeError, "expected a character buffer object"); raiseExcHelper(TypeError, "expected a character buffer object");
Py_ssize_t istart = 0, iend = PY_SSIZE_T_MAX;
if (start) {
int r = _PyEval_SliceIndex(start, &istart);
if (!r)
throwCAPIException();
}
if (end) {
int r = _PyEval_SliceIndex(end, &iend);
if (!r)
throwCAPIException();
}
BoxedString* sub = static_cast<BoxedString*>(elt); BoxedString* sub = static_cast<BoxedString*>(elt);
return boxBool(startswith(self->s, sub->s)); Py_ssize_t n = self->s.size();
iend = std::min(iend, n);
if (iend < 0)
iend += n;
if (iend < 0)
iend = 0;
if (istart < 0)
istart += n;
if (istart < 0)
istart = 0;
Py_ssize_t compare_len = iend - istart;
if (compare_len < 0)
return False;
if (sub->s.size() > compare_len)
return False;
return boxBool(self->s.compare(istart, sub->s.size(), sub->s) == 0);
} }
Box* strEndswith(BoxedString* self, Box* elt) { Box* strEndswith(BoxedString* self, Box* elt, Box* start, Box** _args) {
Box* end = _args[0];
if (self->cls != str_cls) if (self->cls != str_cls)
raiseExcHelper(TypeError, "descriptor 'endswith' requires a 'str' object but received a '%s'", raiseExcHelper(TypeError, "descriptor 'endswith' requires a 'str' object but received a '%s'",
getTypeName(self)->c_str()); getTypeName(self)->c_str());
...@@ -1515,9 +1549,41 @@ Box* strEndswith(BoxedString* self, Box* elt) { ...@@ -1515,9 +1549,41 @@ Box* strEndswith(BoxedString* self, Box* elt) {
if (elt->cls != str_cls) if (elt->cls != str_cls)
raiseExcHelper(TypeError, "expected a character buffer object"); raiseExcHelper(TypeError, "expected a character buffer object");
Py_ssize_t istart = 0, iend = PY_SSIZE_T_MAX;
if (start) {
int r = _PyEval_SliceIndex(start, &istart);
if (!r)
throwCAPIException();
}
if (end) {
int r = _PyEval_SliceIndex(end, &iend);
if (!r)
throwCAPIException();
}
BoxedString* sub = static_cast<BoxedString*>(elt); BoxedString* sub = static_cast<BoxedString*>(elt);
return boxBool(endswith(self->s, sub->s)); Py_ssize_t n = self->s.size();
iend = std::min(iend, n);
if (iend < 0)
iend += n;
if (iend < 0)
iend = 0;
if (istart < 0)
istart += n;
if (istart < 0)
istart = 0;
Py_ssize_t compare_len = iend - istart;
if (compare_len < 0)
return False;
if (sub->s.size() > compare_len)
return False;
// XXX: this line is the only difference between startswith and endswith:
istart += compare_len - sub->s.size();
return boxBool(self->s.compare(istart, sub->s.size(), sub->s) == 0);
} }
Box* strFind(BoxedString* self, Box* elt, Box* _start) { Box* strFind(BoxedString* self, Box* elt, Box* _start) {
...@@ -1790,8 +1856,10 @@ void setupStr() { ...@@ -1790,8 +1856,10 @@ void setupStr() {
str_cls->giveAttr("__contains__", new BoxedFunction(boxRTFunction((void*)strContains, BOXED_BOOL, 2))); str_cls->giveAttr("__contains__", new BoxedFunction(boxRTFunction((void*)strContains, BOXED_BOOL, 2)));
str_cls->giveAttr("startswith", new BoxedFunction(boxRTFunction((void*)strStartswith, BOXED_BOOL, 2))); str_cls->giveAttr("startswith",
str_cls->giveAttr("endswith", new BoxedFunction(boxRTFunction((void*)strEndswith, BOXED_BOOL, 2))); new BoxedFunction(boxRTFunction((void*)strStartswith, BOXED_BOOL, 4, 2, 0, 0), { NULL, NULL }));
str_cls->giveAttr("endswith",
new BoxedFunction(boxRTFunction((void*)strEndswith, BOXED_BOOL, 4, 2, 0, 0), { NULL, NULL }));
str_cls->giveAttr("find", str_cls->giveAttr("find",
new BoxedFunction(boxRTFunction((void*)strFind, BOXED_INT, 3, 1, false, false), { boxInt(0) })); new BoxedFunction(boxRTFunction((void*)strFind, BOXED_INT, 3, 1, false, false), { boxInt(0) }));
......
...@@ -81,3 +81,8 @@ print "hello world"[False:True:True] ...@@ -81,3 +81,8 @@ print "hello world"[False:True:True]
print "{hello}".format(hello="world") print "{hello}".format(hello="world")
print "%.3s" % "hello world" print "%.3s" % "hello world"
for i in xrange(-5, 15):
for j in xrange(-5, 15):
print i, j, "banana".startswith("ana", i, j), "banana".endswith("ana", i, j)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment