diff --git a/src/Att.cpp b/src/Att.cpp index 400043ad0..81b5ffc0b 100644 --- a/src/Att.cpp +++ b/src/Att.cpp @@ -27,6 +27,7 @@ #include #include +#include #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; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/Att.h b/src/Att.h index 84a88371a..b185744b8 100644 --- a/src/Att.h +++ b/src/Att.h @@ -29,6 +29,7 @@ #include #include +#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&); diff --git a/src/Makefile.am b/src/Makefile.am index 9d4e032ff..c67c61ea7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/Nibbler.cpp b/src/Nibbler.cpp new file mode 100644 index 000000000..a97824517 --- /dev/null +++ b/src/Nibbler.cpp @@ -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 +#include +#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; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/Nibbler.h b/src/Nibbler.h new file mode 100644 index 000000000..bd4e00f52 --- /dev/null +++ b/src/Nibbler.h @@ -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 + +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 +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/Record.cpp b/src/Record.cpp index 32328cb3f..f419fd52f 100644 --- a/src/Record.cpp +++ b/src/Record.cpp @@ -25,9 +25,11 @@ // //////////////////////////////////////////////////////////////////////////////// +#include #include #include #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"); diff --git a/src/sandbox/Makefile b/src/sandbox/Makefile index 233b7de33..e897e7f1d 100644 --- a/src/sandbox/Makefile +++ b/src/sandbox/Makefile @@ -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) diff --git a/src/sandbox/TDB.cpp b/src/sandbox/TDB.cpp index a92803fa3..da96d2180 100644 --- a/src/sandbox/TDB.cpp +++ b/src/sandbox/TDB.cpp @@ -26,6 +26,7 @@ //////////////////////////////////////////////////////////////////////////////// #include +#include #include #include #include "text.h" @@ -155,46 +156,67 @@ void TDB::unlock () // Returns number of filtered tasks. int TDB::load (std::vector & 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 (); diff --git a/src/tests/.gitignore b/src/tests/.gitignore index d583ecc30..4d70d59a2 100644 --- a/src/tests/.gitignore +++ b/src/tests/.gitignore @@ -11,3 +11,4 @@ att.t mod.t record.t stringtable.t +nibbler.t diff --git a/src/tests/Makefile b/src/tests/Makefile index 76f9de225..bf6eac54b 100644 --- a/src/tests/Makefile +++ b/src/tests/Makefile @@ -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 + diff --git a/src/tests/att.t.cpp b/src/tests/att.t.cpp index 4b55186a5..d847caed9 100644 --- a/src/tests/att.t.cpp +++ b/src/tests/att.t.cpp @@ -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:\""\""); + n = Nibbler ("name:\""\""); + a7.parse (n); t.is (a7.composeF4 (), "name:\""\"", "Att::composeF4 (name:\""\")"); - a7.parse ("name:\"&tab;",&open;&close;:\""); + n = Nibbler ("name:\"&tab;",&open;&close;:\""); + a7.parse (n); t.is (a7.composeF4 (), "name:\"&tab;",&open;&close;:\"", "Att::composeF4 (name:\"&tab;",&open;&close;:\")"); - 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; diff --git a/src/tests/nibbler.t.cpp b/src/tests/nibbler.t.cpp new file mode 100644 index 000000000..cec8cec58 --- /dev/null +++ b/src/tests/nibbler.t.cpp @@ -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 +#include + +//////////////////////////////////////////////////////////////////////////////// +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; +} + +////////////////////////////////////////////////////////////////////////////////