Mercurial > hg > octave-nkf
changeset 5582:6bf56668b01a
[project @ 2005-12-15 01:08:20 by jwe]
author | jwe |
---|---|
date | Thu, 15 Dec 2005 01:10:05 +0000 |
parents | 79ecf64976ce |
children | 8eebdcfde94e |
files | doc/ChangeLog doc/interpreter/Makefile.in doc/interpreter/octave.texi doc/interpreter/strings.txi doc/interpreter/testfun.txi src/ChangeLog src/DLD-FUNCTIONS/dispatch.cc src/DLD-FUNCTIONS/regexp.cc src/Makefile.in src/error.cc src/oct-stream.h src/ov-struct.cc |
diffstat | 12 files changed, 1113 insertions(+), 26 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,10 @@ +2005-12-14 David Bateman <dbateman@free.fr> + + * interpreter/testfun.txi: New test/demo documentation. + * interpreter/strings.txi: Include regexp/regexi docstrings. + * interpreter/octave.texi: Include test/demo appendix. + * interpreter/Makefile.in (SUB_SOURCE): Include test.txi. + 2005-12-13 David Bateman <dbateman@free.fr> * interpreter/sparse.txi: Add new gplot, etreeplot and treeplot
--- a/doc/interpreter/Makefile.in +++ b/doc/interpreter/Makefile.in @@ -27,7 +27,7 @@ op-idx.txi optim.txi plot.txi poly.txi preface.txi \ quad.txi quaternion.txi set.txi signal.txi sparse.txi stats.txi \ stmt.txi stream.txi strings.txi struct.txi system.txi \ - tips.txi var.txi vr-idx.txi + testfun.txi tips.txi var.txi vr-idx.txi SOURCES := $(SUB_SOURCE)
--- a/doc/interpreter/octave.texi +++ b/doc/interpreter/octave.texi @@ -154,6 +154,7 @@ * Audio Processing:: * Quaternions:: * System Utilities:: +* Test and Demo Functions:: * Tips:: * Trouble:: If you have trouble installing Octave. * Installation:: How to configure, compile and install Octave. @@ -454,6 +455,11 @@ * Group Database Functions:: * System Information:: +Test and Demo Functions + +* Test Functions:: +* Demonstration Functions:: + Tips and Standards * Style Tips:: Writing clean and robust programs. @@ -546,6 +552,7 @@ @c for them, and there appears to be no way to go back to the original @c set of indices once a redirection has taken place. +@include testfun.texi @include tips.texi @include bugs.texi @include install.texi
--- a/doc/interpreter/strings.txi +++ b/doc/interpreter/strings.txi @@ -162,6 +162,10 @@ @DOCSTRING(substr) +@DOCSTRING(regexp) + +@DOCSTRING(regexpi) + @node String Conversions @section String Conversions
new file mode 100644 --- /dev/null +++ b/doc/interpreter/testfun.txi @@ -0,0 +1,283 @@ +@c Copyright (C) 2005 David Bateman +@c Copyright (C) 2002-2005 Paul Kienzle +@c This is part of the Octave manual. +@c For copying conditions, see the file gpl.texi. + +@node Test and Demo Functions +@appendix Test and Demo Functions +@cindex test functions + +Octave includes a number of functions to allow the integration of testing +and demonstration code in the source code of the functions themselves. + +@menu +* Test Functions:: +* Demonstration Functions:: +@end menu + +@node Test Functions +@section Test Functions + +@DOCSTRING(test) + +@code{test} scans the named script file looking for lines which +start with @code{%!}. The prefix is stripped off and the rest of the +line is processed through the octave interpreter. If the code +generates an error, then the test is said to fail. + +Since @code{eval()} will stop at the first error it encounters, you must +divide your tests up into blocks, with anything in a separate +block evaluated separately. Blocks are introduced by the keyword +@code{test} immediately following the @code{%!}. For example, + +@example +@group + %!test error("this test fails!"); + %!test "this test doesn't fail since it doesn't generate an error"; +@end group +@end example + +When a test fails, you will see something like: + +@example +@group + ***** test error('this test fails!') + !!!!! test failed + this test fails! +@end group +@end example + +Generally, to test if something works, you want to assert that it +produces a correct value. A real test might look something like + +@example +@group + %!test + %! @var{a} = [1, 2, 3; 4, 5, 6]; B = [1; 2]; + %! expect = [ @var{a} ; 2*@var{a} ]; + %! get = kron (@var{b}, @var{a}); + %! if (any(size(expect) != size(get))) + %! error ("wrong size: expected %d,%d but got %d,%d", + %! size(expect), size(get)); + %! elseif (any(any(expect!=get))) + %! error ("didn't get what was expected."); + %! endif +@end group +@end example + +To make the process easier, use the @code{assert} function. For example, +with @code{assert} the previous test is reduced to: + +@example +@group + %!test + %! @var{a} = [1, 2, 3; 4, 5, 6]; @var{b} = [1; 2]; + %! assert (kron (@var{b}, @var{a}), [ @var{a}; 2*@var{a} ]); +@end group +@end example + +@code{assert} can accept a tolerance so that you can compare results +absolutely or relatively. For example, the following all succeed: + +@example +@group + %!test assert (1+eps, 1, 2*eps) # absolute error + %!test assert (100+100*eps, 100, -2*eps) # relative error +@end group +@end example + +You can also do the comparison yourself, but still have assert +generate the error: + +@example +@group + %!test assert (isempty([])) + %!test assert ([ 1,2; 3,4 ] > 0) +@end group +@end example + +Because @code{assert} is so frequently used alone in a test block, there +is a shorthand form: + +@example + %!assert (@dots{}) +@end example + +which is equivalent to: + +@example + %!test assert (@dots{}) +@end example + +Each block is evaluated in its own function environment, which means +that variables defined in one block are not automatically shared +with other blocks. If you do want to share variables, then you +must declare them as @code{shared} before you use them. For example, the +following declares the variable @var{a}, gives it an initial value (default +is empty), then uses it in several subsequent tests. + +@example +@group + %!shared @var{a} + %! @var{a} = [1, 2, 3; 4, 5, 6]; + %!assert (kron ([1; 2], @var{a}), [ @var{a}; 2*@var{a} ]); + %!assert (kron ([1, 2], @var{a}), [ @var{a}, 2*@var{a} ]); + %!assert (kron ([1,2; 3,4], @var{a}), [ @var{a},2*@var{a}; 3*@var{a},4*@var{a} ]); +@end group +@end example + +You can share several variables at the same time: + +@example + %!shared @var{a}, @var{b} +@end example + +You can also share test functions: + +@example +@group + %!function @var{a} = fn(@var{b}) + %! @var{a} = 2*@var{b}; + %!assert (@var{a}(2),4); +@end group +@end example + +Note that all previous variables and values are lost when a new +shared block is declared. + +Error and warning blocks are like test blocks, but they only succeed +if the code generates an error. You can check the text of the error +is correct using an optional regular expression @code{<pattern>}. +For example: + +@example + %!error <passes!> error('this test passes!'); +@end example + +If the code doesn't generate an error, the test fails. For example, + +@example + %!error "this is an error because it succeeds."; +@end example + +produces + +@example +@group + ***** error "this is an error because it succeeds."; + !!!!! test failed: no error +@end group +@end example + +It is important to automate the tests as much as possible, however +some tests require user interaction. These can be isolated into +demo blocks, which if you are in batch mode, are only run when +called with @code{demo} or @code{verbose}. The code is displayed before +it is executed. For example, + +@example +@group + %!demo + %! @var{t}=[0:0.01:2*pi]; @var{x}=sin(@var{t}); + %! plot(@var{t},@var{x}); + %! you should now see a sine wave in your figure window +@end group +@end example + +produces + +@example +@group + > @var{t}=[0:0.01:2*pi]; @var{x}=sin(@var{t}); + > plot(@var{t},@var{x}); + > you should now see a sine wave in your figure window + Press <enter> to continue: +@end group +@end example + +Note that demo blocks cannot use any shared variables. This is so +that they can be executed by themselves, ignoring all other tests. + +If you want to temporarily disable a test block, put @code{#} in place +of the block type. This creates a comment block which is echoed +in the log file, but is not executed. For example: + +@example +@group + %!#demo + %! @var{t}=[0:0.01:2*pi]; @var{x}=sin(@var{t}); + %! plot(@var{t},@var{x}); + %! you should now see a sine wave in your figure window +@end group +@end example + +Block type summary: + +@table @code +@item %!test +check that entire block is correct +@item %!error +check for correct error message +@item %!warning +check for correct warning message +@item %!demo +demo only executes in interactive mode +@item %!# +comment: ignore everything within the block +@item %!shared x,y,z +declares variables for use in multiple tests +@item %!function +defines a function value for a shared variable +@item %!assert (x, y, tol) +shorthand for %!test assert (x, y, tol) +@end table + +You can also create test scripts for builtins and your own C++ +functions. Just put a file of the function name on your path without +any extension and it will be picked up by the test procedure. You +can even embed tests directly in your C++ code: + +@example +@group + #if 0 + %!test disp('this is a test') + #endif +@end group +@end example + +or + +@example +@group + /* + %!test disp('this is a test') + */ +@end group +@end example + +but then the code will have to be on the load path and the user +will have to remember to type test('name.cc'). Conversely, you +can separate the tests from normal octave script files by putting +them in plain files with no extension rather than in script files. +@c DO I WANT TO INCLUDE THE EDITOR SPECIFIC STATEMENT BELOW??? +@c Don't forget to tell emacs that the plain text file you are using +@c is actually octave code, using something like: +@c -*-octave-*- + +@DOCSTRING(assert) + +@DOCSTRING(fail) + +@node Demonstration Functions +@section Demonstration Functions + +@DOCSTRING(demo) + +@DOCSTRING(example) + +@DOCSTRING(speed) + + +@c Local Variables: *** +@c Mode: texinfo *** +@c End: ***
--- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,26 @@ +2005-12-14 John W. Eaton <jwe@octave.org> + + * oct-stream.cc (octave_stream::invalid_stream_error): Delete. + * oct-stream.h (octave_stream::stream_ok): Don't fail with error. + +2005-12-14 David Bateman <dbateman@free.fr> + + * DLD-FUNCTIONS/regexp.cc: New file. + + * DLD-FUNCTIONS/dispatch.cc: Update tests for string/sq_string + differences. + + * error.cc (Vquiet_warning): New variable. + (vwarning): Use Vquiet_warning to prevent warning output. + (Fwarning): Include "quiet" option to Fwarning function. + Assign retval when using "query". Typo in error message. + (Flastwarn): Clear warning_state when using Flastwarn to probe warning + message. + + * ov-struct.cc: Update Fstruct tests for change in error output. + + * Makefile.in: Include regexp when needed with appropriate libraries. + 2005-12-13 David Bateman <dbateman@free.fr> * Makefile.in: Change references to gplot.l to __gnuplot_raw__.l.
--- a/src/DLD-FUNCTIONS/dispatch.cc +++ b/src/DLD-FUNCTIONS/dispatch.cc @@ -485,12 +485,12 @@ %!test # builtin function replacement %! dispatch('sin','length','string') -%! assert(sin('abc'),3) +%! assert(sin("abc"),3) %! assert(sin(0),0,10*eps); %!test # 'any' function %! dispatch('sin','exp','any') %! assert(sin(0),1,eps); -%! assert(sin('abc'),3); +%! assert(sin("abc"),3); %!test # 'builtin' function %! assert(builtin('sin',0),0,eps); %! builtin('eval','x=1;'); @@ -502,25 +502,25 @@ %!test # oct-file replacement %! dispatch('fft','length','string') %! assert(fft([1,1]),[2,0]); -%! assert(fft('abc'),3) +%! assert(fft("abc"),3) %! dispatch('fft','string'); %!test # m-file replacement %! dispatch('hamming','length','string') %! assert(hamming(1),1) -%! assert(hamming('abc'),3) +%! assert(hamming("abc"),3) %! dispatch('hamming','string') %!test # override preloaded builtin %! evalin('base','cos(1);'); %! dispatch('cos','length','string') -%! evalin('base',"assert(cos('abc'),3)"); -%! evalin('base',"assert(cos(0),1,eps)"); +%! evalin('base','assert(cos("abc"),3)'); +%! evalin('base','assert(cos(0),1,eps)'); %! dispatch('cos','string') %!test # override pre-loaded oct-file %! evalin('base','qr(1);'); %! dispatch('qr','length','string') -%! evalin('base',"assert(qr('abc'),3)"); -%! evalin('base',"assert(qr(1),1)"); +%! evalin('base','assert(qr("abc"),3)'); +%! evalin('base','assert(qr(1),1)'); %! dispatch('qr','string'); %!test # override pre-loaded m-file %! evalin('base','hanning(1);'); @@ -536,11 +536,11 @@ %! system("echo 'function a=dispatch_x(a)'>dispatch_x.m"); %! dispatch('dispatch_x','length','string') %! assert(dispatch_x(3),3) -%! assert(dispatch_x('a'),1) +%! assert(dispatch_x("a"),1) %! pause(1); %! system("echo 'function a=dispatch_x(a),++a;'>dispatch_x.m"); %! assert(dispatch_x(3),4) -%! assert(dispatch_x('a'),1) +%! assert(dispatch_x("a"),1) %!test %! system("rm dispatch_x.m");
new file mode 100644 --- /dev/null +++ b/src/DLD-FUNCTIONS/regexp.cc @@ -0,0 +1,741 @@ +/* + +Copyright (C) 2005 David Bateman +Copyright (C) 2002-2005 Paul Kienzle + +Octave is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +Octave is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. + +*/ + +// XXX FIXME XXX +// regexprep should be written as an m-file based on regexp + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "defun-dld.h" +#include "error.h" +#include "gripes.h" +#include "oct-obj.h" +#include "utils.h" + +#include "Cell.h" +#include "oct-map.h" +#include "str-vec.h" + +#ifdef HAVE_PCRE +#include <pcre.h> +#else +#ifdef HAVE_REGEX +#ifdef __MINGW32__ +#define __restrict +#endif +#include <regex.h> +#endif +#endif + +static octave_value_list +octregexp (const octave_value_list &args, int nargout, const std::string &nm, + bool case_insensitive) +{ + octave_value_list retval; +#if defined (HAVE_REGEX) || defined (HAVE_PCRE) + int nargin = args.length(); + int nopts = nargin - 2; + bool once = false; + + if (nargin < 2) + { + print_usage(nm); + return retval; + } + + std::string buffer = args(0).string_value (); + if (error_state) + { + gripe_wrong_type_arg (nm.c_str(), args(0)); + return retval; + } + + std::string pattern = args(1).string_value (); + if (error_state) + { + gripe_wrong_type_arg (nm.c_str(), args(1)); + return retval; + } + + for (int i = 2; i < nargin; i++) + { + std::string str = args(i).string_value(); + if (error_state) + { + error ("%s: optional arguments must be strings", nm.c_str()); + break; + } + std::transform (str.begin (), str.end (), str.begin (), tolower); + if (str.find("once", 0) == 0) + { + once = true; + nopts--; + } +#ifdef HAVE_PCRE + // XXX FIXME XXX named tokens still broken. Disable for now +#if 0 + else if (str.find("start", 0) && str.find("end", 0) && + str.find("tokenextents", 0) && str.find("match", 0) && + str.find("tokens", 0) && str.find("names", 0)) + error ("%s: unrecognized option", nm.c_str()); +#else + else if (str.find("names", 0) == 0) + error ("%s: named tokens not implemented in this version", nm.c_str()); + else if (str.find("start", 0) && str.find("end", 0) && + str.find("tokenextents", 0) && str.find("match", 0) && + str.find("tokens", 0)) + error ("%s: unrecognized option", nm.c_str()); +#endif +#else + else if (str.find("names", 0) == 0) + error ("%s: named tokens not implemented in this version", nm.c_str()); + else if (str.find("start", 0) && str.find("end", 0) && + str.find("tokenextents", 0) && str.find("match", 0) && + str.find("tokens", 0)) + error ("%s: unrecognized option", nm.c_str()); +#endif + } + + if (!error_state) + { + Octave_map nmap; + Cell t, m, te; + NDArray s, e; + + // named tokens "(?<name>...)" are only treated with PCRE not regex. +#if HAVE_PCRE + // The syntax of named tokens in pcre is "(?P<name>...)" while we need + // a syntax "(?<name>...)", so fix that here. Also an expression like + // "(?<first>\w+)\s+(?<last>\w+)|(?<last>\w+),\s+(?<first>\w+)" should + // be perfectly legal, while pcre does not allow the same named token + // name of both sides of the alternative. Also fix that here by replacing + // duplicate name tokens by dummy names, and dealing with the dummy names + // later. + + + + + // Compile expression + pcre *re; + const char *err; + int erroffset; + re = pcre_compile(pattern.c_str(), (case_insensitive ? PCRE_CASELESS : 0), + &err, &erroffset, NULL); + + if (re == NULL) { + error("%s: %s at position %d of expression", nm.c_str(), + err, erroffset); + return retval; + } + + int subpatterns; + int namecount; + int nameentrysize; + char *nametable; + int idx = 0; + int sz = 0; + + pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &subpatterns); + pcre_fullinfo(re, NULL, PCRE_INFO_NAMECOUNT, &namecount); + pcre_fullinfo(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &nameentrysize); + pcre_fullinfo(re, NULL, PCRE_INFO_NAMETABLE, &nametable); + + OCTAVE_LOCAL_BUFFER(int, ovector, (subpatterns+1)*3); + OCTAVE_LOCAL_BUFFER(int, nidx, namecount); + string_vector names (namecount); + + for (int i = 0; i < namecount; i++) + { + // Index of subpattern in first two bytes MSB first of name. + // Extract name and index. + nidx[i] = ((int)nametable[i*nameentrysize]) << 8 | + (int)nametable[i*nameentrysize+1]; + names(i) = std::string((&(nametable[i*nameentrysize+2]))); + } + + Cell named_tokens(dim_vector(namecount,1)); + + while(true) + { + int matches = pcre_exec(re, NULL, buffer.c_str(), + buffer.length(), idx, + (idx ? PCRE_NOTBOL : 0), + ovector, (subpatterns+1)*3); + + if (matches < 0 && matches != PCRE_ERROR_NOMATCH) + { + error ("%s: internal error calling pcre_exec", nm.c_str()); + pcre_free(re); + return retval; + } + else if (matches == PCRE_ERROR_NOMATCH) + break; + else + { + s.resize (dim_vector(1, sz+1)); + s(sz) = double (ovector[0]+1); + e.resize (dim_vector(1, sz+1)); + e(sz) = double (ovector[1]); + te.resize(dim_vector(1, sz+1)); + Matrix mat_te(matches-1,2); + for (int i = 1; i < matches; i++) + { + mat_te(i-1,0) = double (ovector[2*i]+1); + mat_te(i-1,1) = double (ovector[2*i+1]); + } + te(sz) = mat_te; + + const char **listptr; + int status = pcre_get_substring_list(buffer.c_str(), ovector, + matches, &listptr); + + if (status == PCRE_ERROR_NOMEMORY) { + error("%s: cannot allocate memory in pcre_get_substring_list", + nm.c_str()); + pcre_free(re); + return retval; + } + + m.resize (dim_vector(1, sz+1)); + m(sz) = std::string(*listptr); + + t.resize (dim_vector(1, sz+1)); + Cell cell_t (dim_vector(1,matches-1)); + for (int i = 1; i < matches; i++) + cell_t(i-1) = std::string(*(listptr+i)); + t(sz) = cell_t; + + for (int i = 0; i < namecount; i++) + { + Cell tmp = named_tokens(i); + tmp.resize(dim_vector(1,sz+1)); + tmp(sz) = + std::string(*(listptr+nidx[i])); + named_tokens(i) = tmp; + } + + pcre_free_substring_list(listptr); + + if (once) + break; + + idx = ovector[1]; + sz++; + } + } + + for (int i = 0; i < namecount; i++) + nmap.assign (names(i), named_tokens(i)); + + pcre_free(re); +#else + regex_t compiled; + int err=regcomp(&compiled, pattern.c_str(), REG_EXTENDED | + (case_insensitive ? REG_ICASE : 0)); + if (err) + { + int len = regerror(err, &compiled, NULL, 0); + char *errmsg = (char *)malloc(len); + if (errmsg) + { + regerror(err, &compiled, errmsg, len); + error("%s: %s in pattern (%s)", nm.c_str(), errmsg, + pattern.c_str()); + free(errmsg); + } + else + { + error("out of memory"); + } + regfree(&compiled); + return retval; + } + + int subexpr = 1; + int idx = 0; + int sz = 0; + for (unsigned int i=0; i < pattern.length(); i++) + subexpr += ( pattern[i] == '(' ? 1 : 0 ); + OCTAVE_LOCAL_BUFFER (regmatch_t, match, subexpr ); + + while(true) + { + if (regexec(&compiled, buffer.c_str() + idx, subexpr, + match, (idx ? REG_NOTBOL : 0)) == 0) + { + // Count actual matches + int matches = 0; + while (matches < subexpr && match[matches].rm_so >= 0) + matches++; + + s.resize (dim_vector(1, sz+1)); + s(sz) = double (match[0].rm_so+1+idx); + e.resize (dim_vector(1, sz+1)); + e(sz) = double (match[0].rm_eo+idx); + te.resize(dim_vector(1, sz+1)); + Matrix mat_te(matches-1,2); + for (int i = 1; i < matches; i++) + { + mat_te(i-1,0) = double (match[i].rm_so+1+idx); + mat_te(i-1,1) = double (match[i].rm_eo+idx); + } + te(sz) = mat_te; + + m.resize (dim_vector(1, sz+1)); + m(sz) = buffer.substr (match[0].rm_so+idx, + match[0].rm_eo-match[0].rm_so); + + t.resize (dim_vector(1, sz+1)); + Cell cell_t (dim_vector(1,matches-1)); + for (int i = 1; i < matches; i++) + cell_t(i-1) = buffer.substr (match[i].rm_so+idx, + match[i].rm_eo-match[i].rm_so); + t(sz) = cell_t; + + idx += match[0].rm_eo; + sz++; + + if (once) + break; + } + else + break; + } + regfree(&compiled); +#endif + + retval(5) = nmap; + retval(4) = t; + retval(3) = m; + retval(2) = te; + retval(1) = e; + retval(0) = s; + + // Alter the order of the output arguments + if (nopts > 0) + { + int n = 0; + octave_value_list new_retval; + new_retval.resize(nargout); + + OCTAVE_LOCAL_BUFFER (int, arg_used, 6); + for (int i = 0; i < 6; i++) + arg_used[i] = false; + + for (int i = 2; i < nargin; i++) + { + int k = 0; + std::string str = args(i).string_value(); + std::transform (str.begin (), str.end (), str.begin (), tolower); + if (str.find("once", 0) == 0) + continue; + else if (str.find("start", 0) == 0) + k = 0; + else if (str.find("end", 0) == 0) + k = 1; + else if (str.find("tokenextents", 0) == 0) + k = 2; + else if (str.find("match", 0) == 0) + k = 3; + else if (str.find("tokens", 0) == 0) + k = 4; + else if (str.find("names", 0) == 0) + k = 5; + + new_retval(n++) = retval(k); + arg_used[k] = true; + + if (n == nargout) + break; + } + + // Fill in the rest of the arguments + if (n < nargout) + { + for (int i = 0; i < 6; i++) + { + if (! arg_used[i]) + new_retval(n++) = retval(i); + } + } + + retval = new_retval; + } + } + +#else + error ("%s: not available in this version of Octave", nm); +#endif + return retval; +} + +DEFUN_DLD (regexp, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {[@var{s}, @var{e}, @var{te}, @var{m}, @var{t}, @var{nm}] =} regexp (@var{str}, @var{pat})\n\ +@deftypefnx {Loadable Function} {[@dots{}] =} regexp (@var{str}, @var{pat}, @var{opts}, @dots{})\n\ +\n\ +Regular expression string matching. Matches @var{pat} in @var{str} and\n\ +returns the position and matching substrings or empty values if there are\n\ +none.\n\ +\n\ +The matched pattern @var{pat} can include any of the standard regex\n\ +operators, including:\n\ +\n\ +@table @code\n\ +@item .\n\ +Match any character\n\ +@item * + ? @{@}\n\ +Repetition operators, representing\n\ +@table @code\n\ +@item *\n\ +Match zero or more times\n\ +@item +\n\ +Match one or more times\n\ +@item ?\n\ +Match zero or one times\n\ +@item @{@}\n\ +Match range operator, which is of the form @code{@{@var{n}@}} to match exactly\n\ +@var{n} times, @code{@{@var{m},@}} to match @var{m} or more times,\n\ +@code{@{@var{m},@var{n}@}} to match between @var{m} and @var{n} times.\n\ +@end table\n\ +@item [@dots{}] [^@dots{}]\n\ +List operators, where for example @code{[ab]c} matches @code{ac} and @code{bc}\n\ +@item ()\n\ +Grouping operator\n\ +@item |\n\ +Alternation operator. Match one of a choice of regular expressions. The\n\ +alternatives must be delimited by the grouoing operator @code{()} above\n\ +@item ^ $\n\ +Anchoring operator. @code{^} matches the start of the string @var{str} and\n\ +@code{$} the end\n\ +@end table\n\ +\n\ +In addition the following escaped characters have special meaning. It should\n\ +be noted that it is recommended to quote @var{pat} in single quotes rather\n\ +than double quotes, to avoid the escape sequences being interpreted by octave\n\ +before being passed to @code{regexp}.\n\ +\n\ +@table @code\n\ +@item \\b\n\ +Match a word boundary\n\ +@item \\B\n\ +Match within a word\n\ +@item \\w\n\ +Matches any word character\n\ +@item \\W\n\ +Matches any non word character\n\ +@item \\<\n\ +Matches the beginning of a word\n\ +@item \\>\n\ +Matches the end of a word\n\ +@item \\s\n\ +Matches any whitespace character\n\ +@item \\S\n\ +Matches any non whitespace character\n\ +@item \\d\n\ +Matches any digit\n\ +@item \\D\n\ +Matches any non-digit\n\ +@end table\n\ +\n\ +The outputs of @code{regexp} by default are in the order as given below\n\ +\n\ +@table @asis\n\ +@item @var{s}\n\ +The start indices of each of the matching substrings\n\ +\n\ +@item @var{e}\n\ +The end indices of each matching substring\n\ +\n\ +@item @var{te}\n\ +The extents of each of the matched token surrounded by @code{(@dots{})} in\n\ +@var{pat}.\n\ +\n\ +@item @var{m}\n\ +A cell array of the text of each match.\n\ +\n\ +@item @var{t}\n\ +A cell array of the text of each token matched.\n\ +\n\ +@item @var{nm}\n\ +A structure containing the text of each matched named token, with the name\n\ +being used as the fieldname. A named token is denoted as\n\ +@code{(?<name>@dots{})}\n\ +@end table\n\ +\n\ +Particular output arguments or the order of the output arguments can be\n\ +selected by additional @var{opts} arguments. These are strings and the\n\ +correspondence between the output arguments and the optional argument\n\ +are\n\ +\n\ +@multitable @columnfractions 0.2 0.3 0.3 0.2\n\ +@item @tab 'start' @tab @var{s} @tab\n\ +@item @tab 'end' @tab @var{e} @tab\n\ +@item @tab 'tokenExtents' @tab @var{te} @tab\n\ +@item @tab 'match' @tab @var{m} @tab\n\ +@item @tab 'tokens' @tab @var{t} @tab\n\ +@item @tab 'names' @tab @var{nm} @tab\n\ +@end multitable\n\ +\n\ +A further optional argument is 'once', that limits the number of returned\n\ +matches to the first match.\n\ +@end deftypefn") +{ + return octregexp (args, nargout, "regexp", false); +} + +/* + +## seg-fault test +%!assert(regexp("abcde","."),[1,2,3,4,5]) + +## Check that anchoring of pattern works correctly +%!assert(regexp('abcabc','^abc'),1); +%!assert(regexp('abcabc','abc$'),4); +%!assert(regexp('abcabc','^abc$'),[]); + +%!test +%! [s, e, te, m, t] = regexp(' No Match ', 'f(.*)uck'); +%! assert (s,[]) +%! assert (e,[]) +%! assert (te,{}) +%! assert (m, {}) +%! assert (t, {}) + +%!test +%! [s, e, te, m, t] = regexp(' FiRetrUck ', 'f(.*)uck'); +%! assert (s,[]) +%! assert (e,[]) +%! assert (te,{}) +%! assert (m, {}) +%! assert (t, {}) + +%!test +%! [s, e, te, m, t] = regexp(' firetruck ', 'f(.*)uck'); +%! assert (s,2) +%! assert (e,10) +%! assert (te{1},[3,7]) +%! assert (m{1}, 'firetruck') +%! assert (t{1}{1}, 'iretr') + +%!test +%! [s, e, te, m, t] = regexp('short test string','\w*r\w*'); +%! assert (s,[1,12]) +%! assert (e,[5,17]) +%! assert (size(te), [1,2]) +%! assert (isempty(te{1})) +%! assert (isempty(te{2})) +%! assert (m{1},'short') +%! assert (m{2},'string') +%! assert (size(t), [1,2]) +%! assert (isempty(t{1})) +%! assert (isempty(t{2})) + +%!test +%! [s, e, te, m, t] = regexp('short test string','\w*r\w*','once'); +%! assert (s,1) +%! assert (e,5) +%! assert (size(te), [1,1]) +%! assert (isempty(te{1})) +%! assert (m{1},'short') +%! ## Matlab gives [1,0] here but that seems wrong. +%! assert (size(t), [1,1]) + +%!test +%! [m, te, e, s, t] = regexp('short test string','\w*r\w*','once', 'match', 'tokenExtents', 'end', 'start', 'tokens'); +%! assert (s,1) +%! assert (e,5) +%! assert (size(te), [1,1]) +%! assert (isempty(te{1})) +%! assert (m{1},'short') +%! ## Matlab gives [1,0] here but that seems wrong. +%! assert (size(t), [1,1]) + +## XXX FIXME XXX Disable test for now as PCRE version not written +%!#test +%! ## This test is expected to fail if PCRE is not installed +%! [s, e, te, m, t, nm] = regexp('short test string','(?<word1>\w*t)\s*(?<word2>\w*t)'); +%! assert (s,1) +%! assert (e,10) +%! assert (size(te), [1,1]) +%! assert (te{1}, [1 5; 7, 10]) +%! assert (m{1},'short test') +%! assert (size(t),[1,1]) +%! assert (t{1}{1},'short') +%! assert (t{1}{2},'test') +%! assert (size(nm), [1,1]) +%! assert (isempty(fieldnames(nm))) +%! assert (sort(fieldnames(nm)),{'word1','word2'}) +%! assert (nm.word1,'short') +%! assert (nm.word2,'test') + +## XXX FIXME XXX Disable test for now as PCRE version not written +%!#test +%! ## This test is expected to fail if PCRE is not installed +%! [nm, m, te, e, s, t] = regexp('short test string','(?<word1>\w*t)\s*(?<word2>\w*t)', 'names', 'match', 'tokenExtents', 'end', 'start', 'tokens'); +%! assert (s,1) +%! assert (e,10) +%! assert (size(te), [1,1]) +%! assert (te{1}, [1 5; 7, 10]) +%! assert (m{1},'short test') +%! assert (size(t),[1,1]) +%! assert (t{1}{1},'short') +%! assert (t{1}{2},'test') +%! assert (size(nm), [1,1]) +%! assert (isempty(fieldnames(nm))) +%! assert (sort(fieldnames(nm)),{'word1','word2'}) +%! assert (nm.word1,'short') +%! assert (nm.word2,'test') + +%!error regexp('string', 'tri', 'BadArg'); +%!error regexp('string'); + +*/ + +DEFUN_DLD(regexpi, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {[@var{s}, @var{e}, @var{te}, @var{m}, @var{t}, @var{nm}] =} regexpi (@var{str}, @var{pat})\n\ +@deftypefnx {Loadable Function} {[@dots{}] =} regexpi (@var{str}, @var{pat}, @var{opts}, @dots{})\n\ +\n\ +Case insensitive regular expression string matching. Matches @var{pat} in\n\ +@var{str} and returns the position and matching substrings or empty values\n\ +if there are none. See @code{regexp} for more details\n\ +@end deftypefn") +{ + return octregexp (args, nargout, "regexp", true); +} + +/* + +## seg-fault test +%!assert(regexpi("abcde","."),[1,2,3,4,5]) + +## Check that anchoring of pattern works correctly +%!assert(regexpi('abcabc','^abc'),1); +%!assert(regexpi('abcabc','abc$'),4); +%!assert(regexpi('abcabc','^abc$'),[]); + +%!test +%! [s, e, te, m, t] = regexpi(' No Match ', 'f(.*)uck'); +%! assert (s,[]) +%! assert (e,[]) +%! assert (te,{}) +%! assert (m, {}) +%! assert (t, {}) + +%!test +%! [s, e, te, m, t] = regexpi(' FiRetrUck ', 'f(.*)uck'); +%! assert (s,2) +%! assert (e,10) +%! assert (te{1},[3,7]) +%! assert (m{1}, 'FiRetrUck') +%! assert (t{1}{1}, 'iRetr') + +%!test +%! [s, e, te, m, t] = regexpi(' firetruck ', 'f(.*)uck'); +%! assert (s,2) +%! assert (e,10) +%! assert (te{1},[3,7]) +%! assert (m{1}, 'firetruck') +%! assert (t{1}{1}, 'iretr') + +%!test +%! [s, e, te, m, t] = regexpi('ShoRt Test String','\w*r\w*'); +%! assert (s,[1,12]) +%! assert (e,[5,17]) +%! assert (size(te), [1,2]) +%! assert (isempty(te{1})) +%! assert (isempty(te{2})) +%! assert (m{1},'ShoRt') +%! assert (m{2},'String') +%! assert (size(t), [1,2]) +%! assert (isempty(t{1})) +%! assert (isempty(t{2})) + +%!test +%! [s, e, te, m, t] = regexpi('ShoRt Test String','\w*r\w*','once'); +%! assert (s,1) +%! assert (e,5) +%! assert (size(te), [1,1]) +%! assert (isempty(te{1})) +%! assert (m{1},'ShoRt') +%! ## Matlab gives [1,0] here but that seems wrong. +%! assert (size(t), [1,1]) + +%!test +%! [m, te, e, s, t] = regexpi('ShoRt Test String','\w*r\w*','once', 'match', 'tokenExtents', 'end', 'start', 'tokens'); +%! assert (s,1) +%! assert (e,5) +%! assert (size(te), [1,1]) +%! assert (isempty(te{1})) +%! assert (m{1},'ShoRt') +%! ## Matlab gives [1,0] here but that seems wrong. +%! assert (size(t), [1,1]) + +## XXX FIXME XXX Disable test for now as PCRE version not written +%!#test +%! ## This test is expected to fail if PCRE is not installed +%! [s, e, te, m, t, nm] = regexpi('ShoRt Test String','(?<word1>\w*t)\s*(?<word2>\w*t)'); +%! assert (s,1) +%! assert (e,10) +%! assert (size(te), [1,1]) +%! assert (te{1}, [1 5; 7, 10]) +%! assert (m{1},'ShoRt Test') +%! assert (size(t),[1,1]) +%! assert (t{1}{1},'ShoRt') +%! assert (t{1}{2},'Test') +%! assert (size(nm), [1,1]) +%! assert (isempty(fieldnames(nm))) +%! assert (sort(fieldnames(nm)),{'word1','word2'}) +%! assert (nm.word1,'ShoRt') +%! assert (nm.word2,'Test') + +## XXX FIXME XXX Disable test for now as PCRE version not written +%!#test +%! ## This test is expected to fail if PCRE is not installed +%! [nm, m, te, e, s, t] = regexpi('ShoRt Test String','(?<word1>\w*t)\s*(?<word2>\w*t)', 'names', 'match', 'tokenExtents', 'end', 'start', 'tokens'); +%! assert (s,1) +%! assert (e,10) +%! assert (size(te), [1,1]) +%! assert (te{1}, [1 5; 7, 10]) +%! assert (m{1},'ShoRt Test') +%! assert (size(t),[1,1]) +%! assert (t{1}{1},'ShoRt') +%! assert (t{1}{2},'Test') +%! assert (size(nm), [1,1]) +%! assert (isempty(fieldnames(nm))) +%! assert (sort(fieldnames(nm)),{'word1','word2'}) +%! assert (nm.word1,'ShoRt') +%! assert (nm.word2,'Test') + +%!error regexpi('string', 'tri', 'BadArg'); +%!error regexpi('string'); + +*/ + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; End: *** +*/
--- a/src/Makefile.in +++ b/src/Makefile.in @@ -49,9 +49,10 @@ filter.cc find.cc fsolve.cc gammainc.cc gcd.cc getgrent.cc \ getpwent.cc getrusage.cc givens.cc hess.cc inv.cc kron.cc \ lpsolve.cc lsode.cc lu.cc luinc.cc matrix_type.cc minmax.cc \ - pinv.cc qr.cc quad.cc qz.cc rand.cc schur.cc sort.cc sparse.cc \ - spchol.cc spdet.cc spkron.cc splu.cc spparms.cc sqrtm.cc svd.cc \ - syl.cc time.cc __gnuplot_raw__.l __glpk__.cc __qp__.cc + pinv.cc qr.cc quad.cc qz.cc rand.cc regexp.cc schur.cc sort.cc \ + sparse.cc spchol.cc spdet.cc spkron.cc splu.cc spparms.cc \ + sqrtm.cc svd.cc syl.cc time.cc \ + __gnuplot_raw__.l __glpk__.cc __qp__.cc DLD_SRC := $(addprefix DLD-FUNCTIONS/, $(DLD_XSRC)) @@ -241,7 +242,7 @@ $(LIBPLPLOT) $(LIBGLOB) $(LIBDLFCN) else OCTAVE_LIBS = $(LIBOCTINTERP) $(LIBOCTAVE) \ - $(GLPK_LIBS) $(SPECIAL_MATH_LIB) $(LIBCRUFT) \ + $(GLPK_LIBS) $(REGEX_LIBS) $(SPECIAL_MATH_LIB) $(LIBCRUFT) \ $(LIBPLPLOT) $(LIBGLOB) $(LIBDLFCN) endif @@ -569,9 +570,13 @@ ifeq ($(ENABLE_DYNAMIC_LINKING), true) ifdef CXXPICFLAG + regexp.oct : pic/regexp.o octave$(EXEEXT) + $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(REGEX_LIBS) __glpk__.oct : pic/__glpk__.o octave$(EXEEXT) $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(GLPK_LIBS) else + regexp.oct : regexp.o octave$(EXEEXT) + $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(REGEX_LIBS) __glpk__.oct : __glpk__.o octave$(EXEEXT) $(DL_LD) $(DL_LDFLAGS) -o $@ $< $(OCT_LINK_DEPS) $(GLPK_LIBS) endif
--- a/src/error.cc +++ b/src/error.cc @@ -67,6 +67,10 @@ // TRUE means that Octave will print a verbose warning. Currently unused. static bool Vverbose_warning; +// TRUE means that Octave will print no warnings, but lastwarn will be +//updated +static bool Vquiet_warning = false; + // A structure containing (most of) the current state of warnings. static Octave_map warning_options; @@ -169,9 +173,12 @@ Vlast_warning_message = msg_string; } - octave_diary << msg_string; + if (! Vquiet_warning) + { + octave_diary << msg_string; - std::cerr << msg_string; + std::cerr << msg_string; + } } static void @@ -857,6 +864,14 @@ done = true; } } + else if (arg2 == "quiet") + { + if (arg1 != "error") + { + Vquiet_warning = (arg1 == "on"); + done = true; + } + } else { if (arg2 == "last") @@ -917,7 +932,7 @@ if (arg2 == "all") retval = warning_options; else if (arg2 == "backtrace" || arg2 == "debug" - || arg2 == "verbose") + || arg2 == "verbose" || arg2 == "quiet") { Octave_map tmp; tmp.assign ("identifier", arg2); @@ -925,8 +940,12 @@ tmp.assign ("state", Vbacktrace_on_warning ? "on" : "off"); else if (arg2 == "debug") tmp.assign ("state", Vdebug_on_warning ? "on" : "off"); + else if (arg2 == "verbose") + tmp.assign ("state", Vverbose_warning ? "on" : "off"); else - tmp.assign ("state", Vverbose_warning ? "on" : "off"); + tmp.assign ("state", Vquiet_warning ? "on" : "off"); + + retval = tmp; } else { @@ -1071,7 +1090,7 @@ } } else - error ("lastwarn: expecting arguments to be character strings"); + error ("lasterr: expecting arguments to be character strings"); } else print_usage ("lasterr"); @@ -1112,6 +1131,7 @@ if (argc == 1 || nargout > 0) { + warning_state = 0; retval(1) = prev_warning_id; retval(0) = prev_warning_message; }
--- a/src/oct-stream.h +++ b/src/oct-stream.h @@ -603,10 +603,7 @@ rep->clear (); } else - { - retval = false; - invalid_stream_error (who); - } + retval = false; return retval; }
--- a/src/ov-struct.cc +++ b/src/ov-struct.cc @@ -498,8 +498,8 @@ %!assert(isstruct(x)); %!assert(isempty(fieldnames(x))); %!fail("struct('a',{1,2},'b',{1,2,3})","dimensions of parameter 2 do not match those of parameter 4") -%!fail("struct(1,2,3,4)","struct expects alternating 'field',value pairs"); -%!fail("struct('1',2,'3')","struct expects alternating 'field',value pairs"); +%!fail("struct(1,2,3,4)","struct expects alternating \"field\", VALUE pairs"); +%!fail("struct('1',2,'3')","struct expects alternating \"field\", VALUE pairs"); */ DEFUN (struct, args, ,