cgal/Manual_tools/src/macro_dictionary.C

478 lines
13 KiB
C

/**************************************************************************
macro_dictionary.C
=============================================================
Project : Tools for the CC manual writing task around cc_manual.sty.
Function : Dictionary of TeX macro definitions.
System : bison, flex, C++ (g++)
Author : (c) 1998 Lutz Kettner
as of version 3.3 (Sept. 1999) maintained by Susan Hert
Revision : $Id$
Date : $Date$
**************************************************************************/
#include <macro_dictionary.h>
#include <iostream>
#include <lexer.h>
#include <input.h>
#include <string_conversion.h>
#include <error.h>
#include <config.h>
#include <list>
#include <string.h>
// #include <hash_map.h> // get hash_map from mstring.h
using namespace std;
typedef hash_map < string, Macro_item > Macro_dictionary_scope;
class Active_char_scope {
bool on[256];
public:
Active_char_scope() {
memset(on, 0, 256 * sizeof(bool));
} bool & operator[] (char c) {
return on[c];
}
bool operator[] (char c) const {
return on[c];
}
};
typedef
std::list < Macro_dictionary_scope > Macro_dictionary;
typedef
std::list < Active_char_scope > Active_char;
Macro_dictionary macro_dictionary(1);
Active_char active_char(1);
void pushMacroScope()
{
macro_dictionary.push_front(Macro_dictionary_scope());
active_char.push_front(active_char.front());
}
void popMacroScope()
{
if (macro_dictionary.size() > 1) {
macro_dictionary.pop_front();
active_char.pop_front();
} else
printErrorMessage(MacroStackUnderflowError);
}
bool is_active_char(char c)
{
return active_char.front()[c];
}
void set_all_active_char(char c, bool tag)
{
Active_char::iterator i = active_char.begin();
for (; i != active_char.end(); ++i) {
(*i)[c] = tag;
}
}
inline void set_active_char(char c, bool tag)
{
active_char.front()[c] = tag;
}
void localEraseMacro(const string & macro)
{
if (macro[0] != '\0' && macro[1] == '\0')
set_all_active_char(macro[0], false);
Macro_dictionary::iterator i = macro_dictionary.begin();
while (i != macro_dictionary.end()) {
Macro_dictionary_scope::iterator j = (*i).find(macro);
if (j != (*i).end())
(*i).erase(j);
++i;
}
}
void
insertMacro(const string & macro,
const string & filename,
size_t line, const string & body, size_t n_param)
{
if (macro_def_switch) {
cerr << "Macro definition: macro `" << macro << "' = `"
<< body << "'." << endl;
}
CC_Assert(macro_dictionary.size() > 0);
if (macro[0] != '\0' && macro[1] == '\0')
set_active_char(macro[0], true);
checkMacroOptEnd(macro);
macro_dictionary.front()[macro] =
Macro_item(filename, line, body, n_param);
}
void
insertInternalMacro(const string & macro, ExpandFunction fct,
size_t n_param)
{
if (macro_def_switch) {
cerr << "Internal def. : macro `" << macro << "'." << endl;
}
CC_Assert(macro_dictionary.size() > 0);
if (macro[0] != '\0' && macro[1] == '\0')
set_active_char(macro[0], true);
checkMacroOptEnd(macro);
macro_dictionary.front()[macro] =
Macro_item("<internal macro>", 0, fct, n_param);
}
void
insertInternalMacro(const string & macro,
const string & body, size_t n_param)
{
if (macro_def_switch) {
cerr << "Internal def. : macro `" << macro << "' = `"
<< body << "'." << endl;
}
CC_Assert(macro_dictionary.size() > 0);
if (macro[0] != '\0' && macro[1] == '\0')
set_active_char(macro[0], true);
checkMacroOptEnd(macro);
macro_dictionary.front()[macro] =
Macro_item("<internal macro>", 0, body, n_param);
}
void
insertGlobalMacro(const string & macro,
const string & filename,
size_t line, const string & body, size_t n_param)
{
if (macro_def_switch) {
cerr << "Macro global def: macro `" << macro << "' = `"
<< body << "'." << endl;
}
CC_Assert(macro_dictionary.size() > 0);
localEraseMacro(macro);
if (macro[0] != '\0' && macro[1] == '\0')
set_all_active_char(macro[0], true);
checkMacroOptEnd(macro);
macro_dictionary.back()[macro] =
Macro_item(filename, line, body, n_param);
}
void
insertInternalGlobalMacro(const string & macro,
ExpandFunction fct, size_t n_param)
{
if (macro_def_switch) {
cerr << "Internal gdef. : macro `" << macro << "'." << endl;
}
CC_Assert(macro_dictionary.size() > 0);
localEraseMacro(macro);
if (macro[0] != '\0' && macro[1] == '\0')
set_all_active_char(macro[0], true);
checkMacroOptEnd(macro);
macro_dictionary.back()[macro] =
Macro_item("<internal macro>", 0, fct, n_param);
}
void
insertInternalGlobalMacro(const string & macro,
const string & body, size_t n_param)
{
if (macro_def_switch) {
cerr << "Internal gdef. : macro `" << macro << "' = `"
<< body << "'." << endl;
}
CC_Assert(macro_dictionary.size() > 0);
localEraseMacro(macro);
if (macro[0] != '\0' && macro[1] == '\0')
set_all_active_char(macro[0], true);
checkMacroOptEnd(macro);
macro_dictionary.back()[macro] =
Macro_item("<internal macro>", 0, body, n_param);
}
bool queryMacro(const string & macro, Macro_dictionary_scope::iterator & j)
{
Macro_dictionary::iterator i = macro_dictionary.begin();
while (i != macro_dictionary.end()) {
j = (*i).find(macro);
if (j != (*i).end())
return true;
++i;
}
return false;
}
const Macro_item & fetchMacro(const string & macro)
{
static Macro_item undefd_macro("undefined", 0, "");
Macro_dictionary_scope::iterator j;
if (queryMacro(macro, j))
return (*j).second;
undefd_macro.body = macro;
return undefd_macro;
}
bool definedMacro(const string & macro)
{
Macro_dictionary_scope::iterator j;
return queryMacro(macro, j);
}
bool macroIsTrue(const string & macro)
{
Macro_dictionary_scope::iterator j;
if (queryMacro(macro, j)) {
if ((*j).second.fct)
return false;
string body = (*j).second.body;
crop_string(body);
return (body == "\\lcTrue" || body == "\\ccTrue");
}
return false;
}
void eraseMacro(const string & macro)
{
if (macro_def_switch) {
cerr << "Macro erasion: macro `" << macro << "'." << endl;
}
localEraseMacro(macro);
}
string expandFirstMacro(string body, bool expand_only_once )
{
if (macro_exp_switch)
cerr << " #X expansion of: `" << body << "'" << endl;
while (1) {
while (!body.empty() && isspace(body[0])) // remove whitespaces
body.replace(0, 1, "");
string::const_iterator i = body.begin();
if (body.empty() || *i != '\\')
break;
++i;
if (i == body.end())
break;
if (isalpha(*i)) {
++i;
while (i != body.end() && isalpha(*i))
++i;
} else {
if (*i == '\\') {
++i;
if (i == body.end() || *i != '*')
--i;
}
}
if (!definedMacro(body.substr(0, i - body.begin())))
break;
const Macro_item & item = fetchMacro(body.substr(0, i - body.begin()));
if (item.n_param > 0 || item.n_opt_at_end > 0)
break;
// skip whitespaces behind macro
string::const_iterator j = i;
while (j != body.end() && isspace(*j) && *j != SEPARATOR)
++j;
if (item.fct)
body.replace(0, j - body.begin(),
item.fct(body.substr(0, i - body.begin()),
&body /* dummy */ , 0, 0));
else
body.replace(0, j - body.begin(), item.body);
if( expand_only_once )
break;
}
if (macro_exp_switch)
cerr << " results in: `" << body << "'." << endl;
return body;
}
string
expandMacro(const string & macro,
const Macro_item & item,
string parameters[], size_t n_parameters, size_t n_options)
{
const size_t cache_size = 9;
static string expand_cache[cache_size];
static bool cache_valid[cache_size];
memset(cache_valid, 0, sizeof(bool) * cache_size);
bool macro_exp_switch2 = macro_exp_switch
&& !(macro == "\\newcommand") && !(macro == "\\newcommand@mom");
if (macro_exp_switch2) {
cerr << '`' << macro << "' Expanded using parameters:" << endl;
for (size_t i = 0; i < n_parameters + n_options; ++i)
cerr << " #" << i + 1 << ": `" << parameters[i] << "'" << endl;
if (!item.fct)
cerr << " with body `" << item.body << "'" << endl;
}
if (item.fct) {
if (macro_exp_switch2) {
string s = item.fct(macro, parameters, n_parameters, n_options);
cerr << " internally expanded to: `" << s << "'." << endl;
return s;
}
return item.fct(macro, parameters, n_parameters, n_options);
}
string s = item.body;
string::size_type i = 0;
bool expand_only_once = false;
if (!s.empty()) {
while (i + 1 < s.size()) { // Expansion loop. At least two characters
// remain to be inspected.
if (s[i] == '#') {
if (s[i + 1] == '#')
s.replace(i, 1, "");
else {
int hash_len = 2;
int j = i;
bool glue_tag = false;
if (s[i + 1] == 'G' && i + 2 < s.size()) {
glue_tag = true;
++i;
++hash_len;
}
bool expand_tag = false;
if (s[i + 1] == 'X' && i + 2 < s.size()) {
expand_tag = true;
++i;
++hash_len;
}
if (s[i + 1] == 'Y' && i + 2 < s.size()) {
expand_tag = true;
expand_only_once = true;
++i;
++hash_len;
}
bool skip_tag = false;
if (s[i + 1] == 'S' && i + 2 < s.size()) {
skip_tag = true;
++i;
++hash_len;
}
bool length_tag = false;
if (s[i + 1] == 'L' && i + 2 < s.size()) {
length_tag = true;
++i;
++hash_len;
}
bool crop_tag = false;
if (s[i + 1] == 'C' && i + 2 < s.size()) {
crop_tag = true;
++i;
++hash_len;
}
if ((s[i + 1] >= '1' && s[i + 1] <= '9') ||
(s[i + 1] >= 'a' && s[i + 1] <= 'z')) {
size_t index = size_t(s[i + 1]) - size_t('1');
if (s[i + 1] >= 'a' && s[i + 1] <= 'z')
index = size_t(s[i + 1]) - size_t('a') + 9; //'a'==9
if (index >= n_parameters + n_options) {
printErrorMessage(ParamIndexError);
std::cerr << "macro: " << s << " i=" << i << std::endl << std::endl;
std::cerr << "index: " << index << " n_parameters: " << n_parameters << std::endl;
++i;
} else {
string repl;
if (expand_tag) {
if (glue_tag) {
repl = parameters[index];
remove_separator(repl);
repl = expandFirstMacro(repl,expand_only_once);
} else {
if (index < cache_size && cache_valid[index])
repl = expand_cache[index];
else {
repl = expandFirstMacro(parameters[index],expand_only_once);
expand_cache[index] = repl;
cache_valid[index] = true;
}
}
} else {
repl = parameters[index];
}
if (skip_tag) {
repl.replace(0, 1, "");
}
if (crop_tag) {
crop_string(repl);
}
if (length_tag) {
repl = int_to_string(repl.size());
}
if (expand_tag || skip_tag || crop_tag
|| length_tag || glue_tag) {
if (glue_tag) {
//cerr << "AAA" << convert_quoted_string_seps(repl) << "BBB" << endl;
remove_separator(repl);
//cerr << "CCC" << convert_quoted_string_seps(repl) << "BBB" << endl;
while (j > 0 && s[j - 1] == SEPARATOR) {
--j;
++hash_len;
}
while (j + hash_len < s.size()
&& s[j + hash_len] == SEPARATOR) {
++hash_len;
}
}
s.replace(j, hash_len, repl);
i = j - 1 + repl.size();
} else {
s.replace(j, hash_len, repl + SEPARATOR);
i = j - 1 + repl.size() + 1;
}
}
} else
printErrorMessage(ParamIndexError);
}
}
++i;
}
}
if (macro_exp_switch2)
cerr << " expanded to: `" << s << "'." << endl;
return s;
}
void checkMacroOptEnd(const string & macro)
{
if (macro.size() > 2) {
int cnt = 0;
string::const_iterator i = macro.end();
--i;
while (i != macro.begin() && *i == 'o') {
--i;
++cnt;
}
while (i != macro.begin() && (*i == 'o' || *i == 'm'))
--i;
if (*i != '@' || cnt == 0)
return;
string basename(macro.begin(), i);
Macro_dictionary_scope::iterator j;
if (queryMacro(basename, j)) {
if (cnt > int ((*j).second.n_opt_at_end))
(*j).second.n_opt_at_end = cnt;
} else {
cerr << endl << "Error: Define macro " << basename
<< " before defining optional arguments " << macro
<< " in `" << in_string->name()
<< " in line " << in_string->line() << "'.";
if (stack_trace_switch)
printErrorMessage(MacroUndefinedError);
}
}
}
// EOF //