Implement static/lexical scoping for 1.5. Refs #2034.

[SVN r55908]
This commit is contained in:
Daniel James
2009-08-31 11:36:47 +00:00
parent c0661a9991
commit d2e0488349
9 changed files with 119 additions and 13 deletions

View File

@@ -512,7 +512,7 @@ namespace quickbook
actions.template_info.push_back(std::string(first, last));
actions.templates.add(
actions.template_info[0]
, boost::make_tuple(actions.template_info, first.get_position()));
, template_symbol(actions.template_info, first.get_position()));
actions.template_info.clear();
}
@@ -565,6 +565,7 @@ namespace quickbook
get_arguments(
std::vector<std::string>& template_info
, std::vector<std::string> const& template_
, template_scope const& scope
, boost::spirit::classic::file_position const& pos
, quickbook::actions& actions
)
@@ -578,7 +579,7 @@ namespace quickbook
std::vector<std::string> tinfo;
tinfo.push_back(*tpl);
tinfo.push_back(*arg);
template_symbol template_(tinfo, pos);
template_symbol template_(tinfo, pos, &scope);
if (actions.templates.find_top_scope(*tpl))
{
@@ -665,6 +666,17 @@ namespace quickbook
return;
}
// The template arguments should have the scope that the template was
// called from, not the template's own scope.
//
// Note that for quickbook 1.4- this value is just ignored when the
// arguments are expanded.
template_scope const& call_scope = actions.templates.top_scope();
template_symbol const* symbol =
actions.templates.find(actions.template_info[0]);
BOOST_ASSERT(symbol);
std::string result;
actions.push(); // scope the actions' states
{
@@ -672,6 +684,13 @@ namespace quickbook
actions.templates.find(actions.template_info[0]);
BOOST_ASSERT(symbol);
// Quickbook 1.4-: When expanding the tempalte continue to use the
// current scope (the dynamic scope).
// Quickbook 1.5+: Use the scope the template was defined in
// (the static scope).
if (qbk_version_n >= 105)
actions.templates.set_parent_scope(*boost::get<2>(*symbol));
std::vector<std::string> template_ = boost::get<0>(*symbol);
boost::spirit::classic::file_position template_pos = boost::get<1>(*symbol);
@@ -693,7 +712,8 @@ namespace quickbook
bool get_arg_result;
std::vector<std::string>::const_iterator tpl;
boost::tie(get_arg_result, tpl) =
get_arguments(template_info, template_, pos, actions);
get_arguments(template_info, template_,
call_scope, pos, actions);
if (!get_arg_result)
{
@@ -1069,7 +1089,7 @@ namespace quickbook
std::vector<std::string> tinfo;
tinfo.push_back(id);
tinfo.push_back(snippet);
storage.push_back(boost::make_tuple(tinfo, first.get_position()));
storage.push_back(template_symbol(tinfo, first.get_position()));
callout_id += callouts.size();
callouts.clear();

View File

@@ -23,7 +23,7 @@ namespace quickbook
template_symbol* template_stack::find(std::string const& symbol) const
{
for (deque::const_iterator i = scopes.begin(); i != scopes.end(); ++i)
for (template_scope const* i = &*scopes.begin(); i; i = i->parent_scope)
{
if (template_symbol* ts = boost::spirit::classic::find(i->symbols, symbol.c_str()))
return ts;
@@ -41,22 +41,38 @@ namespace quickbook
BOOST_ASSERT(!scopes.empty());
return scopes.front().symbols;
}
template_scope const& template_stack::top_scope() const
{
BOOST_ASSERT(!scopes.empty());
return scopes.front();
}
// TODO: Should symbols defined by '[import]' use the current scope?
void template_stack::add(std::string const& symbol, template_symbol const& ts)
{
BOOST_ASSERT(!scopes.empty());
boost::spirit::classic::add(scopes.front().symbols, symbol.c_str(), ts);
}
boost::spirit::classic::add(scopes.front().symbols, symbol.c_str(),
boost::get<2>(ts) ? ts :
template_symbol(boost::get<0>(ts), boost::get<1>(ts), &top_scope()));
}
void template_stack::push()
{
template_scope const& old_front = scopes.front();
scopes.push_front(template_scope());
set_parent_scope(old_front);
}
void template_stack::pop()
{
scopes.pop_front();
}
void template_stack::set_parent_scope(template_scope const& parent)
{
scopes.front().parent_scope = &parent;
}
}

View File

@@ -21,6 +21,8 @@
namespace quickbook
{
struct template_scope;
// template symbols with N arguments are stored as follows:
//
// vector<std::string>
@@ -28,21 +30,34 @@ namespace quickbook
// 1: template param name[0]
// 2: template param name[1]
// ...
// template param name[N]
// template body
// N: template param name[N-1]
// N+1: template body
// file position
// template scope (only used for 1.5+, 1.4- uses the dynamic scope)
typedef boost::tuple<
std::vector<std::string>
, boost::spirit::classic::file_position>
, boost::spirit::classic::file_position
, template_scope const*>
template_symbol;
typedef boost::spirit::classic::symbols<template_symbol> template_symbols;
// template scope
//
// 1.4-: parent_scope is the previous scope on the stack
// (the template's dynamic parent).
// 1.5+: parent_scope is the template's lexical parent.
//
// This means that a search along the parent_scope chain will follow the
// correct lookup chain for that version of quickboook.
//
// symbols contains the templates defined in this scope.
struct template_scope
{
template_scope() : parent_scope() {}
template_scope const* parent_scope;
template_symbols symbols;
};
@@ -64,8 +79,7 @@ namespace quickbook
// search all scopes for the longest matching symbol.
typename Scanner::iterator_t f = scan.first;
std::ptrdiff_t len = -1;
for (template_stack::deque::const_iterator i = ts.scopes.begin();
i != ts.scopes.end(); ++i)
for (template_scope const* i = &*ts.scopes.begin(); i; i = i->parent_scope)
{
boost::spirit::classic::match<> m = i->symbols.parse(scan);
if (m.length() > len)
@@ -84,10 +98,16 @@ namespace quickbook
template_symbol* find(std::string const& symbol) const;
template_symbol* find_top_scope(std::string const& symbol) const;
template_symbols const& top() const;
template_scope const& top_scope() const;
// Add the given template symbol to the current scope.
// If it doesn't have a scope, sets the symbol's scope to the current scope.
void add(std::string const& symbol, template_symbol const& ts);
void push();
void pop();
// Set the current scope's parent.
void set_parent_scope(template_scope const&);
boost::spirit::classic::functor_parser<parser> scope;
private:

View File

@@ -176,6 +176,7 @@ Features include:
* Improved handling of unmatched escape in code blocks.
* Support for python snippets.
* `teletype` source mode.
* Use static scoping in templates, should be a lot more intuitive.
[endsect]

View File

@@ -22,6 +22,7 @@ test-suite quickbook.test :
[ quickbook-test escape ]
[ quickbook-test templates ]
[ quickbook-test templates_1_4 ]
[ quickbook-test templates_1_5 ]
[ quickbook-test xinclude ]
[ quickbook-test import ]
[ quickbook-fail-test fail-include ]
@@ -33,6 +34,7 @@ test-suite quickbook.test :
[ quickbook-fail-test fail-post-process ]
[ quickbook-fail-test fail-parse-error1 ]
[ quickbook-fail-test fail-parse-error2 ]
[ quickbook-fail-test fail-template-lookup1 ]
;

View File

@@ -0,0 +1,7 @@
[article Fail Template Lookup 1
[quickbook 1.5]
]
[template test1[] [a]]
[template test2[a] [test1]]
[test2 1]

View File

@@ -13,4 +13,4 @@
[template y new]
[template foo3[a y] [a]]
[foo3 [y] old]
[foo3 [y] old]

17
test/templates_1_5.gold Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
<article id="template_1_5" last-revision="DEBUG MODE Date: 2000/12/20 12:00:00 $"
xmlns:xi="http://www.w3.org/2001/XInclude">
<title>Template 1.5</title>
<articleinfo>
</articleinfo>
<para>
static scoping
</para>
<para>
new
</para>
<para>
foo foo
</para>
</article>

View File

@@ -0,0 +1,23 @@
[article Template 1.5
[quickbook 1.5]
]
[/ 1.5 uses static scoping ]
[template x static scoping]
[template foo1[] [x]]
[template foo2[x] [foo1]]
[foo2 dynamic scoping]
[/ In 1.5 template arguments are scoped at the point they are defined]
[template y new]
[template foo3[a y] [a]]
[foo3 [y] old]
[/ From https://svn.boost.org/trac/boost/ticket/2034 ]
[template same[x] [x]]
[template echo[a b] [a] [b]]
[template echo_twice[x] [echo [same [x]]..[same [x]]]]
[echo_twice foo]