111be35a1SLionel Sambuc // Copyright 2012 Google Inc.
211be35a1SLionel Sambuc // All rights reserved.
311be35a1SLionel Sambuc //
411be35a1SLionel Sambuc // Redistribution and use in source and binary forms, with or without
511be35a1SLionel Sambuc // modification, are permitted provided that the following conditions are
611be35a1SLionel Sambuc // met:
711be35a1SLionel Sambuc //
811be35a1SLionel Sambuc // * Redistributions of source code must retain the above copyright
911be35a1SLionel Sambuc // notice, this list of conditions and the following disclaimer.
1011be35a1SLionel Sambuc // * Redistributions in binary form must reproduce the above copyright
1111be35a1SLionel Sambuc // notice, this list of conditions and the following disclaimer in the
1211be35a1SLionel Sambuc // documentation and/or other materials provided with the distribution.
1311be35a1SLionel Sambuc // * Neither the name of Google Inc. nor the names of its contributors
1411be35a1SLionel Sambuc // may be used to endorse or promote products derived from this software
1511be35a1SLionel Sambuc // without specific prior written permission.
1611be35a1SLionel Sambuc //
1711be35a1SLionel Sambuc // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1811be35a1SLionel Sambuc // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1911be35a1SLionel Sambuc // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2011be35a1SLionel Sambuc // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2111be35a1SLionel Sambuc // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2211be35a1SLionel Sambuc // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2311be35a1SLionel Sambuc // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2411be35a1SLionel Sambuc // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2511be35a1SLionel Sambuc // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2611be35a1SLionel Sambuc // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2711be35a1SLionel Sambuc // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2811be35a1SLionel Sambuc
2911be35a1SLionel Sambuc #include "utils/config/lua_module.hpp"
3011be35a1SLionel Sambuc
3111be35a1SLionel Sambuc #include <lutok/stack_cleaner.hpp>
3211be35a1SLionel Sambuc #include <lutok/state.ipp>
3311be35a1SLionel Sambuc
3411be35a1SLionel Sambuc #include "utils/config/exceptions.hpp"
3511be35a1SLionel Sambuc #include "utils/config/tree.ipp"
3611be35a1SLionel Sambuc
3711be35a1SLionel Sambuc namespace config = utils::config;
3811be35a1SLionel Sambuc
3911be35a1SLionel Sambuc
4011be35a1SLionel Sambuc namespace {
4111be35a1SLionel Sambuc
4211be35a1SLionel Sambuc
4311be35a1SLionel Sambuc /// Gets the tree singleton stored in the Lua state.
4411be35a1SLionel Sambuc ///
45*84d9c625SLionel Sambuc /// \param state The Lua state. The registry must contain a key named
4611be35a1SLionel Sambuc /// "tree" with a pointer to the singleton.
4711be35a1SLionel Sambuc ///
4811be35a1SLionel Sambuc /// \return A reference to the tree associated with the Lua state.
4911be35a1SLionel Sambuc ///
5011be35a1SLionel Sambuc /// \throw syntax_error If the tree cannot be located.
5111be35a1SLionel Sambuc config::tree&
get_global_tree(lutok::state & state)5211be35a1SLionel Sambuc get_global_tree(lutok::state& state)
5311be35a1SLionel Sambuc {
5411be35a1SLionel Sambuc lutok::stack_cleaner cleaner(state);
5511be35a1SLionel Sambuc
56*84d9c625SLionel Sambuc state.push_value(lutok::registry_index);
57*84d9c625SLionel Sambuc state.push_string("tree");
58*84d9c625SLionel Sambuc state.get_table(-2);
59*84d9c625SLionel Sambuc if (state.is_nil())
6011be35a1SLionel Sambuc throw config::syntax_error("Cannot find tree singleton; global state "
6111be35a1SLionel Sambuc "corrupted?");
62*84d9c625SLionel Sambuc config::tree& tree = **state.to_userdata< config::tree* >();
63*84d9c625SLionel Sambuc state.pop(1);
64*84d9c625SLionel Sambuc return tree;
6511be35a1SLionel Sambuc }
6611be35a1SLionel Sambuc
6711be35a1SLionel Sambuc
6811be35a1SLionel Sambuc /// Gets a fully-qualified tree key from the state.
6911be35a1SLionel Sambuc ///
7011be35a1SLionel Sambuc /// \param state The Lua state.
7111be35a1SLionel Sambuc /// \param table_index An index to the Lua stack pointing to the table being
7211be35a1SLionel Sambuc /// accessed. If this table contains a tree_key metadata property, this is
7311be35a1SLionel Sambuc /// considered to be the prefix of the tree key.
7411be35a1SLionel Sambuc /// \param field_index An index to the Lua stack pointing to the entry
7511be35a1SLionel Sambuc /// containing the name of the field being indexed.
7611be35a1SLionel Sambuc ///
7711be35a1SLionel Sambuc /// \return A dotted key.
7811be35a1SLionel Sambuc ///
7911be35a1SLionel Sambuc /// \throw invalid_key_error If the name of the key is invalid.
8011be35a1SLionel Sambuc static std::string
get_tree_key(lutok::state & state,const int table_index,const int field_index)8111be35a1SLionel Sambuc get_tree_key(lutok::state& state, const int table_index, const int field_index)
8211be35a1SLionel Sambuc {
8311be35a1SLionel Sambuc PRE(state.is_string(field_index));
8411be35a1SLionel Sambuc const std::string field = state.to_string(field_index);
8511be35a1SLionel Sambuc if (!field.empty() && field[0] == '_')
8611be35a1SLionel Sambuc throw config::invalid_key_error(
8711be35a1SLionel Sambuc F("Configuration key cannot have an underscore as a prefix; "
8811be35a1SLionel Sambuc "found %s") % field);
8911be35a1SLionel Sambuc
9011be35a1SLionel Sambuc std::string tree_key;
9111be35a1SLionel Sambuc if (state.get_metafield(table_index, "tree_key")) {
9211be35a1SLionel Sambuc tree_key = state.to_string(-1) + "." + state.to_string(field_index - 1);
9311be35a1SLionel Sambuc state.pop(1);
9411be35a1SLionel Sambuc } else
9511be35a1SLionel Sambuc tree_key = state.to_string(field_index);
9611be35a1SLionel Sambuc return tree_key;
9711be35a1SLionel Sambuc }
9811be35a1SLionel Sambuc
9911be35a1SLionel Sambuc
10011be35a1SLionel Sambuc static int redirect_newindex(lutok::state&);
10111be35a1SLionel Sambuc static int redirect_index(lutok::state&);
10211be35a1SLionel Sambuc
10311be35a1SLionel Sambuc
10411be35a1SLionel Sambuc /// Creates a table for a new configuration inner node.
10511be35a1SLionel Sambuc ///
10611be35a1SLionel Sambuc /// \post state(-1) Contains the new table.
10711be35a1SLionel Sambuc ///
10811be35a1SLionel Sambuc /// \param state The Lua state in which to push the table.
10911be35a1SLionel Sambuc /// \param tree_key The key to which the new table corresponds.
11011be35a1SLionel Sambuc static void
new_table_for_key(lutok::state & state,const std::string & tree_key)11111be35a1SLionel Sambuc new_table_for_key(lutok::state& state, const std::string& tree_key)
11211be35a1SLionel Sambuc {
11311be35a1SLionel Sambuc state.new_table();
11411be35a1SLionel Sambuc {
11511be35a1SLionel Sambuc state.new_table();
11611be35a1SLionel Sambuc {
11711be35a1SLionel Sambuc state.push_string("__index");
11811be35a1SLionel Sambuc state.push_cxx_function(redirect_index);
11911be35a1SLionel Sambuc state.set_table(-3);
12011be35a1SLionel Sambuc
12111be35a1SLionel Sambuc state.push_string("__newindex");
12211be35a1SLionel Sambuc state.push_cxx_function(redirect_newindex);
12311be35a1SLionel Sambuc state.set_table(-3);
12411be35a1SLionel Sambuc
12511be35a1SLionel Sambuc state.push_string("tree_key");
12611be35a1SLionel Sambuc state.push_string(tree_key);
12711be35a1SLionel Sambuc state.set_table(-3);
12811be35a1SLionel Sambuc }
12911be35a1SLionel Sambuc state.set_metatable(-2);
13011be35a1SLionel Sambuc }
13111be35a1SLionel Sambuc }
13211be35a1SLionel Sambuc
13311be35a1SLionel Sambuc
13411be35a1SLionel Sambuc /// Sets the value of an configuration node.
13511be35a1SLionel Sambuc ///
13611be35a1SLionel Sambuc /// \pre state(-3) The table to index. If this is not _G, then the table
13711be35a1SLionel Sambuc /// metadata must contain a tree_key property describing the path to
13811be35a1SLionel Sambuc /// current level.
13911be35a1SLionel Sambuc /// \pre state(-2) The field to index into the table. Must be a string.
14011be35a1SLionel Sambuc /// \pre state(-1) The value to set the indexed table field to.
14111be35a1SLionel Sambuc ///
14211be35a1SLionel Sambuc /// \param state The Lua state in which to operate.
14311be35a1SLionel Sambuc ///
14411be35a1SLionel Sambuc /// \return The number of result values on the Lua stack; always 0.
14511be35a1SLionel Sambuc ///
14611be35a1SLionel Sambuc /// \throw invalid_key_error If the provided key is invalid.
14711be35a1SLionel Sambuc /// \throw unknown_key_error If the key cannot be located.
14811be35a1SLionel Sambuc /// \throw value_error If the value has an unsupported type or cannot be
14911be35a1SLionel Sambuc /// set on the key, or if the input table or index are invalid.
15011be35a1SLionel Sambuc static int
redirect_newindex(lutok::state & state)15111be35a1SLionel Sambuc redirect_newindex(lutok::state& state)
15211be35a1SLionel Sambuc {
15311be35a1SLionel Sambuc if (!state.is_table(-3))
15411be35a1SLionel Sambuc throw config::value_error("Indexed object is not a table");
15511be35a1SLionel Sambuc if (!state.is_string(-2))
15611be35a1SLionel Sambuc throw config::value_error("Invalid field in configuration object "
15711be35a1SLionel Sambuc "reference; must be a string");
15811be35a1SLionel Sambuc
15911be35a1SLionel Sambuc const std::string dotted_key = get_tree_key(state, -3, -2);
16011be35a1SLionel Sambuc try {
16111be35a1SLionel Sambuc config::tree& tree = get_global_tree(state);
16211be35a1SLionel Sambuc tree.set_lua(dotted_key, state, -1);
16311be35a1SLionel Sambuc } catch (const config::value_error& e) {
16411be35a1SLionel Sambuc throw config::value_error(F("Invalid value for key '%s' (%s)") %
16511be35a1SLionel Sambuc dotted_key % e.what());
16611be35a1SLionel Sambuc }
16711be35a1SLionel Sambuc
16811be35a1SLionel Sambuc // Now really set the key in the Lua table, but prevent direct accesses from
16911be35a1SLionel Sambuc // the user by prefixing it. We do this to ensure that re-setting the same
17011be35a1SLionel Sambuc // key of the tree results in a call to __newindex instead of __index.
17111be35a1SLionel Sambuc state.push_string("_" + state.to_string(-2));
17211be35a1SLionel Sambuc state.push_value(-2);
17311be35a1SLionel Sambuc state.raw_set(-5);
17411be35a1SLionel Sambuc
17511be35a1SLionel Sambuc return 0;
17611be35a1SLionel Sambuc }
17711be35a1SLionel Sambuc
17811be35a1SLionel Sambuc
17911be35a1SLionel Sambuc /// Indexes a configuration node.
18011be35a1SLionel Sambuc ///
18111be35a1SLionel Sambuc /// \pre state(-3) The table to index. If this is not _G, then the table
18211be35a1SLionel Sambuc /// metadata must contain a tree_key property describing the path to
18311be35a1SLionel Sambuc /// current level. If the field does not exist, a new table is created.
18411be35a1SLionel Sambuc /// \pre state(-1) The field to index into the table. Must be a string.
18511be35a1SLionel Sambuc ///
18611be35a1SLionel Sambuc /// \param state The Lua state in which to operate.
18711be35a1SLionel Sambuc ///
18811be35a1SLionel Sambuc /// \return The number of result values on the Lua stack; always 1.
18911be35a1SLionel Sambuc ///
19011be35a1SLionel Sambuc /// \throw value_error If the input table or index are invalid.
19111be35a1SLionel Sambuc static int
redirect_index(lutok::state & state)19211be35a1SLionel Sambuc redirect_index(lutok::state& state)
19311be35a1SLionel Sambuc {
19411be35a1SLionel Sambuc if (!state.is_table(-2))
19511be35a1SLionel Sambuc throw config::value_error("Indexed object is not a table");
19611be35a1SLionel Sambuc if (!state.is_string(-1))
19711be35a1SLionel Sambuc throw config::value_error("Invalid field in configuration object "
19811be35a1SLionel Sambuc "reference; must be a string");
19911be35a1SLionel Sambuc
20011be35a1SLionel Sambuc // Query if the key has already been set by a call to redirect_newindex.
20111be35a1SLionel Sambuc state.push_string("_" + state.to_string(-1));
20211be35a1SLionel Sambuc state.raw_get(-3);
20311be35a1SLionel Sambuc if (!state.is_nil(-1))
20411be35a1SLionel Sambuc return 1;
20511be35a1SLionel Sambuc state.pop(1);
20611be35a1SLionel Sambuc
20711be35a1SLionel Sambuc state.push_value(-1); // Duplicate the field name.
20811be35a1SLionel Sambuc state.raw_get(-3); // Get table[field] to see if it's defined.
20911be35a1SLionel Sambuc if (state.is_nil(-1)) {
21011be35a1SLionel Sambuc state.pop(1);
21111be35a1SLionel Sambuc
21211be35a1SLionel Sambuc // The stack is now the same as when we entered the function, but we
21311be35a1SLionel Sambuc // know that the field is undefined and thus have to create a new
21411be35a1SLionel Sambuc // configuration table.
21511be35a1SLionel Sambuc INV(state.is_table(-2));
21611be35a1SLionel Sambuc INV(state.is_string(-1));
21711be35a1SLionel Sambuc
21811be35a1SLionel Sambuc const config::tree& tree = get_global_tree(state);
21911be35a1SLionel Sambuc const std::string tree_key = get_tree_key(state, -2, -1);
22011be35a1SLionel Sambuc if (tree.is_set(tree_key)) {
22111be35a1SLionel Sambuc // Publish the pre-recorded value in the tree to the Lua state,
22211be35a1SLionel Sambuc // instead of considering this table key a new inner node.
22311be35a1SLionel Sambuc tree.push_lua(tree_key, state);
22411be35a1SLionel Sambuc } else {
22511be35a1SLionel Sambuc state.push_string("_" + state.to_string(-1));
22611be35a1SLionel Sambuc state.insert(-2);
22711be35a1SLionel Sambuc state.pop(1);
22811be35a1SLionel Sambuc
22911be35a1SLionel Sambuc new_table_for_key(state, tree_key);
23011be35a1SLionel Sambuc
23111be35a1SLionel Sambuc // Duplicate the newly created table and place it deep in the stack
23211be35a1SLionel Sambuc // so that the raw_set below leaves us with the return value of this
23311be35a1SLionel Sambuc // function at the top of the stack.
23411be35a1SLionel Sambuc state.push_value(-1);
23511be35a1SLionel Sambuc state.insert(-4);
23611be35a1SLionel Sambuc
23711be35a1SLionel Sambuc state.raw_set(-3);
23811be35a1SLionel Sambuc state.pop(1);
23911be35a1SLionel Sambuc }
24011be35a1SLionel Sambuc }
24111be35a1SLionel Sambuc return 1;
24211be35a1SLionel Sambuc }
24311be35a1SLionel Sambuc
24411be35a1SLionel Sambuc
24511be35a1SLionel Sambuc } // anonymous namespace
24611be35a1SLionel Sambuc
24711be35a1SLionel Sambuc
24811be35a1SLionel Sambuc /// Install wrappers for globals to set values in the configuration tree.
24911be35a1SLionel Sambuc ///
25011be35a1SLionel Sambuc /// This function installs wrappers to capture all accesses to global variables.
25111be35a1SLionel Sambuc /// Such wrappers redirect the reads and writes to the out_tree, which is the
25211be35a1SLionel Sambuc /// entity that defines what configuration variables exist.
25311be35a1SLionel Sambuc ///
25411be35a1SLionel Sambuc /// \param state The Lua state into which to install the wrappers.
25511be35a1SLionel Sambuc /// \param out_tree The tree with the layout definition and where the
25611be35a1SLionel Sambuc /// configuration settings will be collected.
25711be35a1SLionel Sambuc void
redirect(lutok::state & state,tree & out_tree)25811be35a1SLionel Sambuc config::redirect(lutok::state& state, tree& out_tree)
25911be35a1SLionel Sambuc {
26011be35a1SLionel Sambuc lutok::stack_cleaner cleaner(state);
26111be35a1SLionel Sambuc
262*84d9c625SLionel Sambuc state.get_global_table();
26311be35a1SLionel Sambuc {
26411be35a1SLionel Sambuc state.push_string("__index");
26511be35a1SLionel Sambuc state.push_cxx_function(redirect_index);
26611be35a1SLionel Sambuc state.set_table(-3);
26711be35a1SLionel Sambuc
26811be35a1SLionel Sambuc state.push_string("__newindex");
26911be35a1SLionel Sambuc state.push_cxx_function(redirect_newindex);
27011be35a1SLionel Sambuc state.set_table(-3);
271*84d9c625SLionel Sambuc }
272*84d9c625SLionel Sambuc state.set_metatable(-1);
27311be35a1SLionel Sambuc
274*84d9c625SLionel Sambuc state.push_value(lutok::registry_index);
27511be35a1SLionel Sambuc state.push_string("tree");
27611be35a1SLionel Sambuc config::tree** tree = state.new_userdata< config::tree* >();
27711be35a1SLionel Sambuc *tree = &out_tree;
27811be35a1SLionel Sambuc state.set_table(-3);
279*84d9c625SLionel Sambuc state.pop(1);
28011be35a1SLionel Sambuc }
281