* [gentoo-commits] proj/libbash:master commit in: /, src/core/, src/core/tests/
@ 2011-03-25 9:24 Petteri Räty
0 siblings, 0 replies; 2+ messages in thread
From: Petteri Räty @ 2011-03-25 9:24 UTC (permalink / raw
To: gentoo-commits
commit: 74e5523b8f672c046ad455cb8ed2d2eed1b0513e
Author: Mu Qiao <qiaomuf <AT> gentoo <DOT> org>
AuthorDate: Wed Mar 16 02:15:14 2011 +0000
Commit: Petteri Räty <betelgeuse <AT> gentoo <DOT> org>
CommitDate: Fri Mar 25 09:08:24 2011 +0000
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/libbash.git;a=commit;h=74e5523b
Implement symbols and symbol table
Using unordered_map to implement symbol table. boost::variant is
used to implement the type system. Simplify unit test by Petteri
Räty.
---
.gitignore | 1 +
Makefile.am | 10 ++-
src/core/interpreter.h | 4 +
src/core/interpreter_exception.h | 41 ++++++++
src/core/symbols.hpp | 198 ++++++++++++++++++++++++++++++++++++++
src/core/tests/symbols_test.cpp | 73 ++++++++++++++
6 files changed, 325 insertions(+), 2 deletions(-)
diff --git a/.gitignore b/.gitignore
index 81aa67f..a0936bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@ install-sh
missing
Makefile
autom4te.cache
+core_unittests
builtin_unittests
post_check
bashast.g
diff --git a/Makefile.am b/Makefile.am
index 2554482..7cb508d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -56,8 +56,12 @@ AM_CXXFLAGS=$(BOOST_CPPFLAGS) -std=c++0x -I$(top_srcdir)/src/
AM_CFLAGS=-x c++ $(AM_CXXFLAGS)
if HAVE_GTEST
-TESTS += builtin_unittests post_check
-check_PROGRAMS = builtin_unittests post_check
+TESTS += core_unittests builtin_unittests post_check
+check_PROGRAMS = core_unittests builtin_unittests post_check
+core_unittests_SOURCES = test/run_tests.cpp
+core_unittests_SOURCES += src/core/tests/symbols_test.cpp
+core_unittests_LDADD = ${GTEST_LIBS}
+
builtin_unittests_SOURCES = test/run_tests.cpp
builtin_unittests_SOURCES += src/builtins/tests/echo_tests.cpp
builtin_unittests_SOURCES += src/builtins/tests/boolean_tests.cpp
@@ -96,8 +100,10 @@ libcppbash_la_SOURCES = src/cppbash_builtin.cpp \
src/builtins/echo_builtin.h \
src/builtins/boolean_builtins.h \
$(GENERATED_PARSER_C) \
+ src/core/interpreter_exception.h \
src/core/interpreter.cpp \
src/core/interpreter.h \
+ src/core/symbols.hpp \
$(GENERATED_WALKER_C)
EXTRA_DIST = bashast/bashast.g \
diff --git a/src/core/interpreter.h b/src/core/interpreter.h
index a0bb344..3c0c23f 100644
--- a/src/core/interpreter.h
+++ b/src/core/interpreter.h
@@ -32,6 +32,7 @@
#include <antlr3basetree.h>
#include "bashastLexer.h"
+#include "core/symbols.hpp"
///
/// \class interpreter
@@ -39,6 +40,9 @@
///
class interpreter{
public:
+ /// \var public::members
+ /// \brief global symbol table
+ scope members;
/// \brief parse the text value of a tree to integer
/// \param the target tree
diff --git a/src/core/interpreter_exception.h b/src/core/interpreter_exception.h
new file mode 100644
index 0000000..40f3d90
--- /dev/null
+++ b/src/core/interpreter_exception.h
@@ -0,0 +1,41 @@
+/*
+ Copyright 2011 Mu Qiao
+
+ This file is part of libbash.
+
+ libbash 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.
+
+ libbash 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 libbash. If not, see <http://www.gnu.org/licenses/>.
+*/
+///
+/// \file interpreter_exception.h
+/// \author Mu Qiao
+/// \brief implementation for interpreter_exception
+///
+
+#ifndef INTERPRETER_EXCEPTION_H_
+#define INTERPRETER_EXCEPTION_H_
+
+#include <stdexcept>
+#include <string>
+
+///
+/// \class interpreter_exception
+/// \brief runtime exception occured during interpreting
+///
+class interpreter_exception: public std::runtime_error
+{
+public:
+ explicit interpreter_exception(const std::string& err_msg):
+ runtime_error(err_msg){}
+};
+
+#endif
diff --git a/src/core/symbols.hpp b/src/core/symbols.hpp
new file mode 100644
index 0000000..c613cbf
--- /dev/null
+++ b/src/core/symbols.hpp
@@ -0,0 +1,198 @@
+/*
+ Copyright 2011 Mu Qiao
+
+ This file is part of libbash.
+
+ libbash 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.
+
+ libbash 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 libbash. If not, see <http://www.gnu.org/licenses/>.
+*/
+///
+/// \file symbols.hpp
+/// \author Mu Qiao
+/// \brief template implementation for symbols and symbol table
+///
+
+#ifndef SYMBOLS_HPP_
+#define SYMBOLS_HPP_
+
+#include <memory>
+#include <string>
+#include <sstream>
+#include <unordered_map>
+#include <vector>
+
+#include <boost/variant.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include "core/interpreter_exception.h"
+
+///
+/// \class symbol
+/// \brief base class for symbols such as variables and functions
+///
+class symbol
+{
+ /// \var private::name
+ /// \brief symbol name
+ std::string name;
+
+public:
+ symbol(){}
+
+ symbol(const std::string& n): name(n){}
+
+ /// \brief retrieve symbol name
+ /// \return const string value of symbol name
+ const std::string& get_name() const
+ {
+ return name;
+ }
+
+};
+
+///
+/// \class converter
+/// \brief template class of converter
+///
+template<typename T>
+class converter: public boost::static_visitor<T>
+{};
+
+///
+/// \class converter
+/// \brief specialized converter for int
+///
+template<>
+class converter<int>: public boost::static_visitor<int>
+{
+public:
+ /// \brief converter for int value
+ /// \param the value to be converted
+ /// \return the converted int
+ int operator() (const int value) const
+ {
+ return value;
+ }
+
+ /// \brief converter for string value
+ /// \param the value to be converted
+ /// \return the converted int
+ int operator() (const std::string& value) const
+ {
+ int result = 0;
+ try
+ {
+ result = boost::lexical_cast<int>(value);
+ }
+ catch(boost::bad_lexical_cast& e)
+ {
+ throw interpreter_exception("can't cast " + value + " to int");
+ }
+ return result;
+ }
+};
+
+///
+/// \class converter
+/// \brief specialized converter for string
+///
+template<>
+class converter<std::string>:
+ public boost::static_visitor<std::string>
+{
+public:
+ /// \brief converter for int value
+ /// \param the value to be converted
+ /// \return the converted string
+ std::string operator() (const int value) const
+ {
+ return boost::lexical_cast<std::string>(value);
+ }
+
+ /// \brief converter for string value
+ /// \param the value to be converted
+ /// \return the converted string
+ std::string operator() (const std::string& value) const
+ {
+ return value;
+ }
+};
+
+///
+/// \class variable
+/// \brief implementation for all variable types
+///
+class variable: public symbol
+{
+ /// \var private::value
+ /// \brief actual value of the symbol
+ boost::variant<int, std::string> value;
+
+ /// \var private::readonly
+ /// \brief whether the symbol is readonly
+ bool readonly;
+
+public:
+ template <typename T>
+ variable(const std::string& name, T v, bool ro=false)
+ : symbol(name), value(v), readonly(ro){}
+
+ /// \brief retrieve actual value of the symbol
+ /// \return the value of the symbol
+ template<typename T>
+ T get_value() const
+ {
+ static converter<T> visitor;
+ return boost::apply_visitor(visitor, value);
+ }
+
+ /// \brief set the value of the symbol, raise exception if it's readonly
+ /// \param the new value to be set
+ template <typename T>
+ void set_value(T new_value)
+ {
+ if(readonly)
+ throw interpreter_exception(get_name() + " is readonly variable");
+ value = new_value;
+ }
+};
+
+///
+/// class scope
+/// \brief implementation for symbol table
+///
+class scope
+{
+public:
+ typedef std::unordered_map<std::string, std::shared_ptr<symbol>>
+ table_type;
+
+ /// \brief define a new symbol
+ /// \param the new symbol
+ void define(std::shared_ptr<symbol> s)
+ {
+ members[s->get_name()] = s;
+ }
+
+ /// \brief resolve a symbol
+ /// \param the symbol name
+ /// \return target symbol passed by reference
+ std::shared_ptr<symbol> resolve(const std::string& name)
+ {
+ return members[name];
+ }
+protected:
+ /// \var protected::member
+ /// \brief symbol table data structure
+ table_type members;
+};
+#endif
diff --git a/src/core/tests/symbols_test.cpp b/src/core/tests/symbols_test.cpp
new file mode 100644
index 0000000..aaaf66d
--- /dev/null
+++ b/src/core/tests/symbols_test.cpp
@@ -0,0 +1,73 @@
+/*
+Copyright 2011 Mu Qiao
+
+This file is part of libbash.
+
+libbash 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.
+
+libbash 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 libbash. If not, see <http://www.gnu.org/licenses/>.
+*/
+///
+/// \file symbols_test.cpp
+/// \author Mu Qiao
+/// \brief series of unit tests for symbols and symbol table.
+///
+
+#include <gtest/gtest.h>
+
+#include "core/symbols.hpp"
+
+using namespace std;
+
+TEST(symbol_test, int_variable)
+{
+ // readonly integer
+ variable ro_integer("integer", 10, true);
+ EXPECT_STREQ("integer", ro_integer.get_name().c_str());
+ EXPECT_EQ(10, ro_integer.get_value<int>());
+ EXPECT_THROW(ro_integer.set_value(100), interpreter_exception);
+ EXPECT_EQ(10, ro_integer.get_value<int>());
+
+ // normal only integer
+ variable normal_integer("integer", 10);
+ normal_integer.set_value(100);
+ EXPECT_EQ(100, normal_integer.get_value<int>());
+
+ // get string value of an integer
+ EXPECT_STREQ("100", normal_integer.get_value<string>().c_str());
+}
+
+TEST(symbol_test, string_variable)
+{
+ // readonly string
+ variable ro_string("string", "hello", true);
+ EXPECT_STREQ("string", ro_string.get_name().c_str());
+ EXPECT_STREQ("hello", ro_string.get_value<string>().c_str());
+ EXPECT_THROW(ro_string.set_value("hello world"), interpreter_exception);
+ EXPECT_STREQ("hello", ro_string.get_value<string>().c_str());
+
+ // normal string
+ variable normal_string("string", "hello");
+ normal_string.set_value("hello world");
+ EXPECT_STREQ("hello world", normal_string.get_value<string>().c_str());
+
+ // string contains integer value
+ variable int_string("string", "123");
+ EXPECT_EQ(123, int_string.get_value<int>());
+}
+
+TEST(scope_test, define_resolve)
+{
+ scope members;
+ auto an_int = shared_ptr<variable>(new variable("integer_symbol", 100));
+ members.define(an_int);
+ EXPECT_EQ(an_int, members.resolve("integer_symbol"));
+}
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [gentoo-commits] proj/libbash:master commit in: /, src/core/, src/core/tests/
@ 2011-05-27 23:03 Petteri Räty
0 siblings, 0 replies; 2+ messages in thread
From: Petteri Räty @ 2011-05-27 23:03 UTC (permalink / raw
To: gentoo-commits
commit: a6e8bcfd05d72ca16ea786aa78106111b7a4d15e
Author: Mu Qiao <qiaomuf <AT> gentoo <DOT> org>
AuthorDate: Thu May 26 10:42:50 2011 +0000
Commit: Petteri Räty <betelgeuse <AT> gentoo <DOT> org>
CommitDate: Fri May 27 08:45:41 2011 +0000
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/libbash.git;a=commit;h=a6e8bcfd
Core: adjust interpreter to support unset
We didn't not handle local variables and the interface was not
helpful. Now the implementation is adjusted to support unset
built-in. As we don't support read only functions, unsetting
functions will always return true.
---
Makefile.am | 1 +
src/core/interpreter.cpp | 60 ++++++++++++++++++++++++++--------
src/core/interpreter.h | 4 ++
src/core/tests/interpreter_test.cpp | 59 +++++++++++++++++++++++++++++-----
src/core/unset_exception.h | 44 +++++++++++++++++++++++++
5 files changed, 145 insertions(+), 23 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 86c2583..3b72d46 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -193,6 +193,7 @@ libcppbash_la_SOURCES = src/common.h \
$(GENERATED_PARSER_C) \
$(GENERATED_PARSER_H) \
src/core/interpreter_exception.h \
+ src/core/unset_exception.h \
src/core/interpreter.cpp \
src/core/interpreter.h \
src/core/symbols.hpp \
diff --git a/src/core/interpreter.cpp b/src/core/interpreter.cpp
index 2337e87..41bc7b5 100644
--- a/src/core/interpreter.cpp
+++ b/src/core/interpreter.cpp
@@ -22,8 +22,6 @@
/// \brief implementations for bash interpreter (visitor pattern).
///
-#include "core/interpreter.h"
-
#include <cctype>
#include <functional>
@@ -37,8 +35,11 @@
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/copy.hpp>
+#include "core/unset_exception.h"
#include "libbashWalker.h"
+#include "core/interpreter.h"
+
interpreter::interpreter(): out(&std::cout), err(&std::cerr), in(&std::cin), bash_options(
{
{"autocd", false},
@@ -341,25 +342,56 @@ void interpreter::get_all_function_names(std::vector<std::string>& function_name
boost::copy(functions | boost::adaptors::map_keys, back_inserter(function_names));
}
+namespace
+{
+ void check_unset_positional(const std::string& name)
+ {
+ // Unsetting positional parameters is not allowed
+ if(isdigit(name[0]))
+ throw unset_exception("unset: not a valid identifier");
+ }
+}
+
void interpreter::unset(const std::string& name)
{
- auto iter = members.find(name);
- if(iter == members.end())
- return;
- else if(iter->second->is_readonly())
- throw interpreter_exception("Can't unset readonly variable " + name);
- else
- members.erase(name);
+ check_unset_positional(name);
+
+ auto unsetter = [&](scope& frame) -> bool {
+ auto iter_local = frame.find(name);
+ if(iter_local != frame.end())
+ {
+ if(iter_local->second->is_readonly())
+ throw unset_exception("unset a readonly variable");
+ frame.erase(iter_local);
+ return true;
+ }
+ return false;
+ };
+
+ if(std::none_of(local_members.rbegin(), local_members.rend(), unsetter))
+ unsetter(members);
+}
+
+// We need to return false when unsetting readonly functions in future
+void interpreter::unset_function(const std::string& name)
+{
+ auto function = functions.find(name);
+ if(function != functions.end())
+ functions.erase(name);
}
void interpreter::unset(const std::string& name,
const unsigned index)
{
- auto iter = members.find(name);
- if(iter == members.end())
- return;
- else
- iter->second->unset_value(index);
+ check_unset_positional(name);
+
+ auto var = resolve_variable(name);
+ if(var)
+ {
+ if(var->is_readonly())
+ throw unset_exception("unset a readonly variable");
+ var->unset_value(index);
+ }
}
bool interpreter::get_option(const std::string& name) const
diff --git a/src/core/interpreter.h b/src/core/interpreter.h
index f35b8ff..3420ee4 100644
--- a/src/core/interpreter.h
+++ b/src/core/interpreter.h
@@ -502,6 +502,10 @@ public:
/// \param the name of the variable
void unset(const std::string& name);
+ /// \brief unset a function
+ /// \param the name of the function
+ void unset_function(const std::string& name);
+
/// \brief unset a array member
/// \param the name of the array
/// \param the index of the member
diff --git a/src/core/tests/interpreter_test.cpp b/src/core/tests/interpreter_test.cpp
index ab24449..d64866f 100644
--- a/src/core/tests/interpreter_test.cpp
+++ b/src/core/tests/interpreter_test.cpp
@@ -25,6 +25,7 @@
#include <gtest/gtest.h>
#include "core/interpreter.h"
+#include "core/unset_exception.h"
using namespace std;
@@ -143,31 +144,71 @@ TEST(interpreter, get_array_values)
EXPECT_FALSE(walker.resolve_array("undefined", array_values));
}
-TEST(interpreter, unset_values)
+TEST(interpreter, unset_arrays)
{
interpreter walker;
std::map<int, std::string> values = {{0, "1"}, {1, "2"}, {2, "3"}};
walker.define("array", values);
walker.define("ro_array", values, true);
- walker.define("var", "123");
- walker.define("ro_var", "123", true);
-
+ interpreter::local_scope temp_scope(walker);
+ values[0] = "local";
+ walker.define_local("array", values);
+ walker.define_local("ro_local_array", values, true);
+
+ // unset arrays
+ EXPECT_STREQ("local", walker.resolve<string>("array", 0).c_str());
+ // unset local
+ walker.unset("array", 0);
+ EXPECT_STREQ("", walker.resolve<string>("array", 0).c_str());
+ // unset local
+ walker.unset("array");
+ // resolve to global
+ EXPECT_STREQ("1", walker.resolve<string>("array", 0).c_str());
EXPECT_STREQ("2", walker.resolve<string>("array", 1).c_str());
- walker.unset("array", 1);
- EXPECT_STREQ("", walker.resolve<string>("array", 1).c_str());
+ EXPECT_STREQ("3", walker.resolve<string>("array", 2).c_str());
+ // unset global
walker.unset("array");
EXPECT_STREQ("", walker.resolve<string>("array", 0).c_str());
EXPECT_STREQ("", walker.resolve<string>("array", 1).c_str());
EXPECT_STREQ("", walker.resolve<string>("array", 2).c_str());
+ walker.unset("array");
+
+ EXPECT_THROW(walker.unset("ro_array", 1), unset_exception);
+ EXPECT_THROW(walker.unset("ro_local_array", 1), unset_exception);
+ EXPECT_THROW(walker.unset("ro_array"), unset_exception);
+ EXPECT_THROW(walker.unset("ro_local_array"), unset_exception);
+
+ EXPECT_THROW(walker.unset("1", 1), interpreter_exception);
+}
- EXPECT_THROW(walker.unset("ro_array", 1), interpreter_exception);
- EXPECT_THROW(walker.unset("ro_array"), interpreter_exception);
+TEST(interpreter, unset_variables)
+{
+ interpreter walker;
+ walker.define("var", "123");
+ walker.define("ro_var", "123", true);
+ interpreter::local_scope temp_scope(walker);
+ walker.define_local("var", 456);
+ walker.define_local("ro_local_var", 456, true);
+ EXPECT_STREQ("456", walker.resolve<string>("var").c_str());
+ walker.unset("var");
EXPECT_STREQ("123", walker.resolve<string>("var").c_str());
walker.unset("var");
EXPECT_STREQ("", walker.resolve<string>("var").c_str());
+ walker.unset("var");
+
+ EXPECT_THROW(walker.unset("ro_var"), unset_exception);
+ EXPECT_THROW(walker.unset("ro_local_var"), unset_exception);
+ EXPECT_THROW(walker.unset("1"), interpreter_exception);
+}
- EXPECT_THROW(walker.unset("ro_var"), interpreter_exception);
+TEST(interpreter, unset_functions)
+{
+ interpreter walker;
+ walker.define_function("foo", 0);
+ EXPECT_TRUE(walker.has_function("foo"));
+ walker.unset_function("foo");
+ EXPECT_FALSE(walker.has_function("foo"));
}
TEST(interperter, substring_expansion_exception)
diff --git a/src/core/unset_exception.h b/src/core/unset_exception.h
new file mode 100644
index 0000000..7ee6c03
--- /dev/null
+++ b/src/core/unset_exception.h
@@ -0,0 +1,44 @@
+/*
+ Please use git log for copyright holder and year information
+
+ This file is part of libbash.
+
+ libbash 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.
+
+ libbash 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 libbash. If not, see <http://www.gnu.org/licenses/>.
+*/
+///
+/// \file unset_exception.h
+/// \author Mu Qiao
+/// \brief implementation for unset_exception
+///
+
+#ifndef LIBBASH_CORE_UNSET_EXCEPTION_H_
+#define LIBBASH_CORE_UNSET_EXCEPTION_H_
+
+#include <stdexcept>
+#include <string>
+
+#include "interpreter_exception.h"
+
+///
+/// \class unset_exception
+/// \brief exception for unsetting variables
+///
+class unset_exception: public interpreter_exception
+{
+public:
+ explicit unset_exception(const std::string& err_msg):
+ interpreter_exception(err_msg){}
+};
+
+#endif
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2011-05-27 23:03 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-05-27 23:03 [gentoo-commits] proj/libbash:master commit in: /, src/core/, src/core/tests/ Petteri Räty
-- strict thread matches above, loose matches on Subject: below --
2011-03-25 9:24 Petteri Räty
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox