Enhancements - Nibbler + parsing

- New Nibbler object greatly assists in FF4 parsing.
- Unit tests for Nibbler.
- Record now parses itself.
- Att now parses itself.
This commit is contained in:
Paul Beckingham 2009-05-29 01:47:39 -04:00
parent 7c0aee4a5f
commit 5263147c83
12 changed files with 623 additions and 77 deletions

View file

@ -27,6 +27,7 @@
#include <sstream>
#include <stdlib.h>
#include <text.h>
#include "Att.h"
////////////////////////////////////////////////////////////////////////////////
@ -85,26 +86,45 @@ Att::~Att ()
}
////////////////////////////////////////////////////////////////////////////////
// Parse the following forms:
// name [[.mod] ...] : " [value] "
void Att::parse (const std::string& input)
//
// start --> name --> . --> mod --> : --> " --> value --> " --> end
// ^ |
// |__________|
//
bool Att::parse (Nibbler& n)
{
// Ensure a clean object first.
mName = "";
mValue = "";
mMods.clear ();
std::string::size_type colon = input.find (":");
if (colon != std::string::npos)
if (n.getUntilChars (".:", mName))
{
std::string name = input.substr (0, colon);
// TODO Are there mods?
mName = name;
while (n.skip ('.'))
{
std::string mod;
if (n.getUntilChars (".:", mod))
mMods.push_back (mod);
else
throw std::string ("Missing . or : after modifier");
}
std::string value = input.substr (colon + 1, std::string::npos);
dequote (value);
decode (value);
mValue = value;
if (n.skip (':'))
{
if (n.getQuoted ('"', mValue))
return true;
else if (n.getUntilChar (' ', mValue))
return true;
throw std::string ("Missing attribute value");
}
else
throw std::string ("Missing : after attribute name");
}
else
throw std::string ("Missing : after attribute name");
return false;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -29,6 +29,7 @@
#include <string>
#include <vector>
#include "Nibbler.h"
#include "Mod.h"
class Att
@ -41,7 +42,7 @@ public:
Att& operator= (const Att&); // Assignment operator
~Att (); // Destructor
void parse (const std::string&);
bool parse (Nibbler&);
std::string composeF4 () const;
void addMod (const Mod&);

View file

@ -2,9 +2,9 @@ bin_PROGRAMS = task
task_SOURCES = Config.cpp Date.cpp Record.cpp T.cpp TDB.cpp Att.cpp Mod.cpp \
Filter.cpp Sequence.cpp Table.cpp Grid.cpp Timer.cpp \
Duration.cpp StringTable.cpp Location.cpp Subst.cpp Keymap.cpp \
color.cpp parse.cpp task.cpp command.cpp edit.cpp report.cpp \
util.cpp text.cpp rules.cpp import.cpp Config.h Date.h Record.h \
T.h TDB.h Att.h Mod.h Filter.h Sequence.h Table.h Grid.h \
Timer.h Duration.h StringTable.h Location.h Subst.h Keymap.h \
color.h task.h
Nibbler.cpp color.cpp parse.cpp task.cpp command.cpp edit.cpp \
report.cpp util.cpp text.cpp rules.cpp import.cpp Config.h \
Date.h Record.h T.h TDB.h Att.h Mod.h Filter.h Sequence.h \
Table.h Grid.h Timer.h Duration.h StringTable.h Location.h \
Subst.h Keymap.h Nibbler.h color.h task.h

282
src/Nibbler.cpp Normal file
View file

@ -0,0 +1,282 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2009, Paul Beckingham.
// All rights reserved.
//
// This program 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 of the License, or (at your option) any later
// version.
//
// This program 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; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <ctype.h>
#include "Nibbler.h"
////////////////////////////////////////////////////////////////////////////////
Nibbler::Nibbler ()
: mInput ("")
, mCursor (0)
{
}
////////////////////////////////////////////////////////////////////////////////
Nibbler::Nibbler (const char* input)
: mInput (input)
, mCursor (0)
{
}
////////////////////////////////////////////////////////////////////////////////
Nibbler::Nibbler (const std::string& input)
: mInput (input)
, mCursor (0)
{
}
////////////////////////////////////////////////////////////////////////////////
Nibbler::Nibbler (const Nibbler& other)
{
mInput = other.mInput;
mCursor = other.mCursor;
}
////////////////////////////////////////////////////////////////////////////////
Nibbler& Nibbler::operator= (const Nibbler& other)
{
if (this != &other)
{
mInput = other.mInput;
mCursor = other.mCursor;
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////
Nibbler::~Nibbler ()
{
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getUntilChar (char c, std::string& result)
{
if (mCursor < mInput.length ())
{
std::string::size_type i = mInput.find (c, mCursor);
if (i != std::string::npos)
{
result = mInput.substr (mCursor, i - mCursor);
mCursor = i;
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getUntilChars (const std::string& chars, std::string& result)
{
if (mCursor < mInput.length ())
{
std::string::size_type i = mInput.find_first_of (chars, mCursor);
if (i != std::string::npos)
{
result = mInput.substr (mCursor, i - mCursor);
mCursor = i;
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getUntilString (const std::string& terminator, std::string& result)
{
if (mCursor < mInput.length ())
{
std::string::size_type i = mInput.find (terminator, mCursor);
if (i != std::string::npos)
{
result = mInput.substr (mCursor, i - mCursor);
mCursor = i;
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::skip (const int quantity /* = 1 */)
{
if (mCursor <= mInput.length () - quantity)
{
mCursor += quantity;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::skip (char c)
{
if (mCursor < mInput.length () &&
mInput[mCursor] == c)
{
++mCursor;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::skipAll (char c)
{
std::string::size_type i = mCursor;
while (i < mInput.length () && mInput[i] == c)
++i;
if (i != mCursor)
{
mCursor = i;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::skipAllChars (const std::string& chars)
{
if (mCursor < mInput.length ())
{
std::string::size_type i = mInput.find_first_not_of (chars, mCursor);
if (i == mCursor)
return false;
if (i == std::string::npos)
mCursor = mInput.length (); // Yes, off the end.
else
mCursor = i;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getQuoted (char c, std::string& result)
{
std::string::size_type start = mCursor;
if (start < mInput.length () - 1 && mInput[start] == c)
{
++start;
if (start < mInput.length ())
{
std::string::size_type end = mInput.find (c, start);
if (end != std::string::npos)
{
result = mInput.substr (start, end - start);
mCursor = end + 1;
return true;
}
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getInt (int& result)
{
std::string::size_type i = mCursor;
if (i < mInput.length ())
{
if (mInput[i] == '-')
++i;
else if (mInput[i] == '+')
++i;
}
while (i < mInput.length () && ::isdigit (mInput[i]))
++i;
if (i > mCursor)
{
result = ::atoi (mInput.substr (mCursor, i - mCursor).c_str ());
mCursor = i;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getUnsignedInt (int& result)
{
std::string::size_type i = mCursor;
while (i < mInput.length () && ::isdigit (mInput[i]))
++i;
if (i > mCursor)
{
result = ::atoi (mInput.substr (mCursor, i - mCursor).c_str ());
mCursor = i;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getUntilEOL (std::string& result)
{
return getUntilChar ('\n', result);
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getUntilEOS (std::string& result)
{
if (mCursor < mInput.length ())
{
result = mInput.substr (mCursor, std::string::npos);
mCursor = mInput.length ();
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::depleted ()
{
if (mCursor >= mInput.length ())
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////

62
src/Nibbler.h Normal file
View file

@ -0,0 +1,62 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2009, Paul Beckingham.
// All rights reserved.
//
// This program 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 of the License, or (at your option) any later
// version.
//
// This program 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; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_NIBBLER
#define INCLUDED_NIBBLER
#include <string>
class Nibbler
{
public:
Nibbler (); // Default constructor
Nibbler (const char*); // Constructor
Nibbler (const std::string&); // Constructor
Nibbler (const Nibbler&); // Copy constructor
Nibbler& operator= (const Nibbler&); // Assignment operator
~Nibbler (); // Destructor
bool getUntilChar (char, std::string&);
bool getUntilChars (const std::string&, std::string&);
bool getUntilString (const std::string&, std::string&);
bool skip (const int quantity = 1);
bool skip (char);
bool skipAll (char);
bool skipAllChars (const std::string&);
bool getQuoted (char, std::string&);
bool getInt (int&);
bool getUnsignedInt (int&i);
bool getUntilEOL (std::string&);
bool getUntilEOS (std::string&);
bool depleted ();
private:
std::string mInput;
std::string::size_type mCursor;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -25,9 +25,11 @@
//
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include "util.h"
#include "Nibbler.h"
#include "Record.h"
////////////////////////////////////////////////////////////////////////////////
@ -84,19 +86,34 @@ std::string Record::composeF4 ()
////////////////////////////////////////////////////////////////////////////////
//
// start --> [ --> name --> : --> " --> value --> " --> ] --> end
// ^ |
// |________________________________|
// start --> name --> : --> " --> value --> " --> end
// ^ |
// |________________________________|
//
void Record::parse (const std::string& input)
{
if (input[0] == '[' && input[input.length () - 1] == ']')
Nibbler n (input);
std::string line;
if (n.skip ('[') && n.getUntilChar (']', line))
{
Nibbler nl (line);
bool first = true;
Att a;
while (a.parse (nl))
{
if (first)
first = false;
else
nl.skip (' ');
(*this)[a.name ()] = a;
}
throw std::string ("unimplemented Record:parse");
std::string remainder;
nl.getUntilEOS (remainder);
if (remainder.length ())
throw std::string ("Unrecognized garbage at end of line");
}
else
throw std::string ("Record not recognized as FF4");

View file

@ -4,7 +4,7 @@ LFLAGS =
LIBS =
OBJECTS = main.o Context.o TDB.o T.o ../Sequence.o ../Filter.o ../Att.o \
../Keymap.o ../Record.o ../Mod.o ../StringTable.o ../util.o \
../text.o ../Date.o ../Config.o ../Location.o ../Subst.o
../text.o ../Date.o ../Config.o ../Location.o ../Subst.o ../Nibbler.o
all: $(PROJECT)

View file

@ -26,6 +26,7 @@
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <sstream>
#include <string>
#include <sys/file.h>
#include "text.h"
@ -155,46 +156,67 @@ void TDB::unlock ()
// Returns number of filtered tasks.
int TDB::load (std::vector <T>& tasks, Filter& filter)
{
char line[T_LINE_MAX];
foreach (location, mLocations)
std::string file;
int line_number;
try
{
std::cout << "# location.path: " << location->path << std::endl;
while (fgets (line, T_LINE_MAX, location->pending))
char line[T_LINE_MAX];
foreach (location, mLocations)
{
int length = ::strlen (line);
if (length > 1)
std::cout << "# location.path: " << location->path << std::endl;
line_number = 1;
file = location->path + "/pending.data";
while (fgets (line, T_LINE_MAX, location->pending))
{
line[length - 1] = '\0'; // Kill \n
std::cout << "# line: " << line << std::endl;
T task (line);
if (filter.pass (task))
int length = ::strlen (line);
if (length > 1)
{
// TODO Add hidden attribute indicating source.
tasks.push_back (task);
line[length - 1] = '\0'; // Kill \n
std::cout << "# line: " << line << std::endl;
T task (line);
if (filter.pass (task))
{
// TODO Add hidden attribute indicating source.
tasks.push_back (task);
}
}
++line_number;
}
line_number = 1;
file = location->path + "/completed.data";
while (fgets (line, T_LINE_MAX, location->completed))
{
int length = ::strlen (line);
if (length > 1)
{
line[length - 1] = '\0'; // Kill \n
std::cout << "# line: " << line << std::endl;
T task (line);
if (filter.pass (task))
{
// TODO Add hidden attribute indicating source.
tasks.push_back (task);
}
}
++line_number;
}
}
}
while (fgets (line, T_LINE_MAX, location->completed))
{
int length = ::strlen (line);
if (length > 1)
{
line[length - 1] = '\0'; // Kill \n
std::cout << "# line: " << line << std::endl;
T task (line);
if (filter.pass (task))
{
// TODO Add hidden attribute indicating source.
tasks.push_back (task);
}
}
}
catch (std::string& e)
{
std::stringstream s;
s << " int " << file << " at line " << line_number;
throw e + s.str ();
}
return tasks.size ();

View file

@ -11,3 +11,4 @@ att.t
mod.t
record.t
stringtable.t
nibbler.t

View file

@ -1,10 +1,10 @@
PROJECT = t.t tdb.t date.t duration.t t.benchmark.t text.t autocomplete.t \
parse.t seq.t att.t mod.t stringtable.t record.t # subst.t
parse.t seq.t att.t mod.t stringtable.t record.t nibbler.t # subst.t
CFLAGS = -I. -I.. -Wall -pedantic -ggdb3 -fno-rtti
LFLAGS = -L/usr/local/lib
OBJECTS = ../TDB.o ../T.o ../parse.o ../text.o ../Date.o ../Duration.o \
../util.o ../Config.o ../Sequence.o ../Att.o ../Record.o ../Mod.o \
../StringTable.o ../Subst.o
../StringTable.o ../Subst.o ../Nibbler.o
all: $(PROJECT)
@ -62,3 +62,6 @@ stringtable.t: stringtable.t.o $(OBJECTS) test.o
subst.t: subst.t.o $(OBJECTS) test.o
g++ subst.t.o $(OBJECTS) test.o $(LFLAGS) -o subst.t
nibbler.t: nibbler.t.o $(OBJECTS) test.o
g++ nibbler.t.o $(OBJECTS) test.o $(LFLAGS) -o nibbler.t

View file

@ -74,57 +74,75 @@ int main (int argc, char** argv)
if (good) t.fail ("Att::addMod (fartwizzle)");
// Att::parse
Nibbler n ("");
Att a7;
a7.parse ("");
t.is (a7.composeF4 (), "", "Att::composeF4 ()");
good = true;
try {a7.parse (n);} catch (...) {t.pass ("Att::compose () -> throw"); good = false;}
if (good) t.fail ("Att::composeF4 () -> throw");
a7.parse ("name:value");
n = Nibbler ("name:value");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"value\"", "Att::composeF4 (name:value)");
a7.parse ("name:\"value\"");
n = Nibbler ("name:\"value\"");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"value\"", "Att::composeF4 (name:\"value\")");
a7.parse ("name:\"one two\"");
n = Nibbler ("name:\"one two\"");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"one two\"", "Att::composeF4 (name:\"one two\")");
a7.parse ("name:\"&quot;\"");
n = Nibbler ("name:\"&quot;\"");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"&quot;\"", "Att::composeF4 (name:\"&quot;\")");
a7.parse ("name:\"&tab;&quot;&comma;&open;&close;&colon;\"");
n = Nibbler ("name:\"&tab;&quot;&comma;&open;&close;&colon;\"");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"&tab;&quot;&comma;&open;&close;&colon;\"",
"Att::composeF4 (name:\"&tab;&quot;&comma;&open;&close;&colon;\")");
a7.parse ("total gibberish");
n = Nibbler ("total gibberish");
a7.parse (n);
t.is (a7.composeF4 (), "", "Att::composeF4 (total gibberish)");
a7.parse ("malformed");
n = Nibbler ("malformed");
a7.parse (n);
t.is (a7.composeF4 (), "", "Att::composeF4 (malformed)");
a7.parse (":malformed");
n = Nibbler (":malformed");
a7.parse (n);
t.is (a7.composeF4 (), "", "Att::composeF4 (:malformed)");
a7.parse (":\"\"");
n = Nibbler (":\"\"");
a7.parse (n);
t.is (a7.composeF4 (), "", "Att::composeF4 (:\"\")");
a7.parse (":\"");
n = Nibbler (":\"");
a7.parse (n);
t.is (a7.composeF4 (), "", "Att::composeF4 (:\")");
a7.parse ("name:");
n = Nibbler ("name:");
a7.parse (n);
t.is (a7.composeF4 (), "", "Att::composeF4 (name:)");
a7.parse ("name:\"value");
n = Nibbler ("name:\"value");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"value\"", "Att::composeF4 (name:\"value)");
a7.parse ("name:value\"");
n = Nibbler ("name:value\"");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"value\"", "Att::composeF4 (name:value\")");
a7.parse ("name:val\"ue");
n = Nibbler ("name:val\"ue");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"value\"", "Att::composeF4 (name:val\"ue)");
a7.parse ("name:\"\"va\"\"\"\"\"lu\"\"\"e\"\"");
n = Nibbler ("name:\"\"va\"\"\"\"\"lu\"\"\"e\"\"");
a7.parse (n);
t.is (a7.composeF4 (), "name:\"value\"", "Att::composeF4 (name:\"\"va\"\"\"\"\"lu\"\"\"e\"\")");
a7.parse ("name\"");
n = Nibbler ("name\"");
a7.parse (n);
t.is (a7.composeF4 (), "", "Att::composeF4 (name\")");
return 0;

120
src/tests/nibbler.t.cpp Normal file
View file

@ -0,0 +1,120 @@
////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2009, Paul Beckingham.
// All rights reserved.
//
// This program 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 of the License, or (at your option) any later
// version.
//
// This program 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; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <Nibbler.h>
#include <test.h>
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest t (52);
Nibbler n ("this is 'a test' of 123the,nibbler");
std::string s;
int i;
t.ok (n.getUntilChar (' ', s), "nibble word");
t.is (s, "this", "found 'this'");
t.ok (n.skip (' '), "skip ws");
t.ok (n.getUntilChar (' ', s), "nibble word");
t.is (s, "is", "found 'is'");
t.ok (n.skip (' '), "skip ws");
t.ok (n.getQuoted ('\'', s), "nibble quoted");
t.is (s, "a test", "found 'a test'");
t.ok (n.skip (' '), "skip ws");
t.ok (n.getUntilChar (' ', s), "nibble word");
t.is (s, "of", "found 'of'");
t.ok (n.skip (' '), "skip ws");
t.ok (n.getInt (i), "nibble integer");
t.is (i, 123, "found '123'");
t.ok (n.getUntilChar (',', s), "nibble word");
t.ok (n.skip (','), "skip ,");
t.is (s, "the", "found 'the'");
t.ok (n.getUntilEOS (s), "nibble remainder");
t.is (s, "nibbler", "found 'nibbler'");
// Test EOS handling.
n = Nibbler ("xx");
t.ok (n.skip ('x'), "skip x");
t.ok (n.skip ('x'), "skip x");
t.notok (n.skip ('x'), "skip x");
n = Nibbler ("aaaaaX");
t.ok (n.skip (5), "aaaaaX -> skip 5 pass");
t.notok (n.skip(2), "X -> skip 2 fail");
t.ok (n.skip (), "X -> skip pass");
n = Nibbler ("aaaaaa");
t.ok (n.skipAll ('a'), "aaaaaa -> skipAll 'a' pass");
n = Nibbler ("aabbabab");
t.ok (n.skipAllChars ("ab"), "aabbabab -> skipAllChars 'ab' pass");
n = Nibbler ("abcX");
t.ok (n.skipAllChars ("abc"), "abcX -> skipChars abc pass");
t.notok (n.skipAllChars ("abc"), "X -> skipChars abc fail");
n = Nibbler ("-123+456 789");
t.ok (n.getInt (i), "-123+456 789 -> getInt pass");
t.is (i, -123, "-123+456 789 -> -123 pass");
t.notok (n.getUnsignedInt (i), "+456 789 -> getUnsignedInt fail");
t.ok (n.getInt (i), "+456 789 -> getInt pass");
t.is (i, 456, "+456 789 -> getInt pass");
t.ok (n.skip (' '), "\s789 -> skip ' ' pass");
t.ok (n.getUnsignedInt (i), "789 -> getUnsignedInt pass");
t.is (i, 789, "789 -> getInt pass");
n = Nibbler ("123");
t.ok (n.getInt (i), "123 -> getInt pass");
t.is (i, 123, "123 -> getInt 123 pass");
n = Nibbler ("abc\nd");
t.ok (n.getUntilEOL (s), "abc\\nd -> getUntilEOL pass");
t.is (s, "abc", "abc\\nd -> getUntilEOL abc pass");
n = Nibbler ("abcba'foo");
t.ok (n.getQuoted ('a', s), "abcba'foo -> getQuoted 'a' pass");
t.is (s, "bcb", "abcba'foo -> getQuoted 'a' bcb pass");
t.notok (n.getQuoted ('\'', s), "'foo -> getQuoted '\\'' fail");
n = Nibbler ("abcde");
t.ok (n.getUntilChars ("ed", s), "abcde -> getUntilChars 'ed' pass");
t.is (s, "abc", "abcde -> getUntilChars 'ed abc pass");
n = Nibbler ("abcde");
t.ok (n.getUntilString ("de", s), "abcde -> getUntilString 'de' pass");
t.is (s, "abc", "abcde -> getUntilString 'de abc pass");
n = Nibbler ("aa");
t.notok (n.depleted (), "'aa' -> not depleted pass");
t.ok (n.skip ('a'), "aa -> skip 'a' pass");
t.ok (n.skip ('a'), "aa -> skip 'a' pass");
t.ok (n.depleted (), "'' -> depleted pass");
return 0;
}
////////////////////////////////////////////////////////////////////////////////