/*
* xtc - The eXTensible Compiler
* Copyright (C) 2009-2012 New York University
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package xtc.lang.cpp;
import java.util.List;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import xtc.lang.cpp.Syntax.LanguageTag;
import xtc.lang.cpp.Syntax.Layout;
import xtc.lang.cpp.Syntax.Language;
import xtc.lang.cpp.Syntax.Text;
import xtc.lang.cpp.Syntax.Directive;
import xtc.lang.cpp.Syntax.Conditional;
import xtc.lang.cpp.PresenceConditionManager.PresenceCondition;
/** The conditional macro table.
*
* A few properties:
* (1) no entry means the macro is free,
* (2) the union of all entry's conditions is TRUE,
* (3) each entry's definitions' conditions are disjoint,
* (4) there is at most one free and one undefined element
* (5) the table is optimized by having no duplicate definitions
*
* @author Paul Gazzillo
* @version $Revision: 1.63 $
*/
public class MacroTable {
/** The token creator. */
TokenCreator tokenCreator;
/** The mapping from names to lists of (condition, definition) pairs */
Map<String, List<Entry>> table;
/** Track enabled/disabled macro names */
Set<String> disabled;
/** Whether to collect configuration variable names or not. */
private boolean getConfigurationVariables = false;
/** Records unique configuration variable names. */
public Set<String> configurationVariables = null;
/** Whether to collect header guard names or not. */
private boolean getHeaderGuards = false;
/** Records unique header guard names. */
public Set<String> headerGuards = null;
/** Only allow macros with a given prefix to be free macros. */
private boolean restrictPrefix = false;
/** The restricted prefix. */
private String prefix = null;
/** Make a new empty macro table */
public MacroTable(TokenCreator tokenCreator) {
this.tokenCreator = tokenCreator;
table = new HashMap<String, List<Entry>>();
disabled = new HashSet<String>();
}
public void getConfigurationVariables(boolean b) {
getConfigurationVariables = b;
if (getConfigurationVariables && null == configurationVariables) {
configurationVariables = new HashSet<String>();
getHeaderGuards(true);
}
}
public boolean getConfigurationVariables() {
return getConfigurationVariables;
}
public void getHeaderGuards(boolean b) {
getHeaderGuards = b;
if (getHeaderGuards && null == headerGuards) {
headerGuards = new HashSet<String>();
}
}
public boolean getHeaderGuards() {
return getHeaderGuards;
}
/**
* Only allow macros with a given prefix to be free macros. Pass
* null to turn this feature off. It is off by default.
*
* @param prefix Give a string to turn restricted prefix on. Give
* null to turn it off.
*/
public void restrictPrefix(String prefix) {
if (null == prefix) {
restrictPrefix = false;
this.prefix = null;
} else {
restrictPrefix = true;
this.prefix = prefix;
}
}
/** Define a macro under a given presenceCondition. This function will
* ensure that all conditions are disjoint and that there are no more
* than one of each unique definition.
*/
public void define(String name, Macro macro, PresenceConditionManager presenceConditionManager) {
PresenceCondition presenceCondition;
if (presenceConditionManager.isFalse()) return; //invalid configuration
/* If this is a boolean variable already, then remove FREE and
* replace with equivalent defined M and ! defined M
*/
if (contains(name)
&& presenceConditionManager.getVariableManager().hasDefinedVariable(name)) {
PresenceCondition var;
PresenceCondition not;
List<Entry> entries;
var = presenceConditionManager.new PresenceCondition(presenceConditionManager.getVariableManager().getDefinedVariable(name));
not = var.not();
entries = table.get(name);
for (int i = 0; i < entries.size(); i++) {
Entry e;
e = entries.get(i);
if (Macro.State.FREE == e.macro.state) {
List<Syntax> definition;
PresenceCondition andvar, andnot;
andvar = e.presenceCondition.and(var);
andnot = e.presenceCondition.and(not);
definition = new LinkedList<Syntax>();
{ // Mark an unknown definition.
Language<?> deftoken;
deftoken = tokenCreator.createIdentifier(name);
deftoken.setFlag(Preprocessor.NO_EXPAND);
deftoken.setFlag(Preprocessor.UNKNOWN_DEF);
definition.add(deftoken);
}
_define(name, new Macro.Object(definition), andvar);
_define(name, Macro.undefined, andnot);
andvar.delRef();
andnot.delRef();
}
}
}
presenceCondition = presenceConditionManager.reference();
_define(name, macro, presenceCondition);
presenceCondition.delRef();
}
/** Update the symbol table with a new entry. The presenceCondition passed
* will gain a reference, so dereference it yourself if you no
* longer need it.
*/
protected void _define(String name, Macro macro, PresenceCondition presenceCondition) {
if (presenceCondition.isFalse()) return;
if (! contains(name)) { //new macro
List<Entry> defs;
defs = new LinkedList<Entry>();
table.put(name, defs);
if (! presenceCondition.isTrue()) {
PresenceCondition negation;
negation = presenceCondition.not();
if (restrictPrefix && !name.startsWith(prefix)) {
// Assume the macro is undefined.
defs.add(new Entry(Macro.undefined, negation));
} else {
// Let the macro be free.
defs.add(new Entry(Macro.free, negation));
}
negation.delRef();
}
defs.add(new Entry(macro, presenceCondition));
} else { //update macro entries
Entry duplicate;
List<Entry> defs;
duplicate = null;
defs = table.get(name);
//make entries disjoint
for (int d = 0; d < defs.size(); d++) {
Entry entry = defs.get(d);
PresenceCondition newPresenceCondition;
newPresenceCondition = entry.presenceCondition.andNot(presenceCondition);
entry.presenceCondition.delRef();
entry.presenceCondition = newPresenceCondition;
if (newPresenceCondition.isFalse()) {
newPresenceCondition.delRef();
defs.remove(d);
d--;
}
}
// Deduplication macro entries. First find the duplicate entry
// if there is one.
for (Entry e : defs) {
if (e.macro.state != macro.state) continue;
switch (macro.state) {
case UNDEFINED:
// Fall through.
case FREE:
// Only allow one entry for UNDEFINED and FREE.
duplicate = e;
break;
case DEFINED:
if (null == macro.definition && null == e.macro.definition) {
duplicate = e;
} else if (null != macro.definition && null != e.macro.definition) {
if (macro.definition.size() == e.macro.definition.size()) {
boolean isdup;
isdup = true;
for (int i = 0; i < macro.definition.size(); i++) {
Syntax a, b;
a = macro.definition.get(i);
b = e.macro.definition.get(i);
if (! a.getTokenText().equals(b.getTokenText())
|| ! a.flagsEqual(b)) {
isdup = false;
}
}
if (isdup) {
duplicate = e;
break;
}
}
}
break;
}
}
if (null == duplicate) {
// Add new entry if there is no duplicate entry.
table.get(name).add(new Entry(macro, presenceCondition));
} else {
// Instead of adding a new entry, update the presence
// condition of the existing duplicate entry.
PresenceCondition oldc = duplicate.presenceCondition;
PresenceCondition newc = oldc.or(presenceCondition);
oldc.delRef();
duplicate.presenceCondition = newc;
}
}
}
/**
* Update the presenceConditions of macros that use a guard macro. We assume
* that guard macros for headers are undefined, not free.
*
* This method assumes that there is no entry in the macro table for
* the guard macro. Since it is only called by
* HeaderFileManager.openHeader right now, that method checks this
* condition.
*
* @param guardMacro The name of the guard macro.
* @param presenceConditionManager The presenceCondition manager.
*/
public void rectifyGuard(String guardMacro, PresenceConditionManager presenceConditionManager) {
PresenceCondition var, not;
PresenceCondition presenceCondition;
// presenceCondition = presenceConditionManager.reference();
// var = presenceConditionManager.new PresenceCondition(presenceConditionManager.getVariableManager()
// .getDefinedVariable(guardMacro));
// not = var.not();
// for (String name : table.keySet()) {
// List<Entry> entries;
// entries = table.get(name);
// for (int i = 0; i < entries.size(); i++) {
// Entry e;
// PresenceCondition restrictnot;
// PresenceCondition c;
// e = entries.get(i);
// c = presenceCondition.and(e.presenceCondition);
// // Check whether the macro entry's presence condition
// // (e.presenceCondition) depends on the guard macro (! (defined var)).
// restrictnot = e.presenceCondition.restrict(not);
// if (! restrictnot.is(e.presenceCondition)) {
// e.presenceCondition.delRef();
// if (restrictnot.isFalse()) {
// entries.remove(i);
// i--;
// } else {
// e.presenceCondition = restrictnot;
// e.presenceCondition.addRef();
// }
// }
// restrictnot.delRef();
// }
// }
// presenceCondition.delRef();
// var.delRef();
// not.delRef();
// This following line doesn't check whether the header macro is
// already defined! In openHeader, we make sure the header guard
// is not already contained in the macro table.
if (getHeaderGuards) {
headerGuards.add(guardMacro);
}
_define(guardMacro, Macro.undefined, presenceConditionManager.new PresenceCondition(true));
}
/**
* Undefine a macro under the given presenceCondition.
*
* @param name The name of the macro to undefine.
* @param presenceConditionManager The presenceCondition manager holding the current
* presence condition in which the macro should be undefined.
*/
public void undefine(String name, PresenceConditionManager presenceConditionManager) {
define(name, Macro.undefined, presenceConditionManager);
}
/**
* Gets the macro table entries of the given macro under the given
* presenceCondition. Any entries not valid in the presenceCondition will not be
* returned. This creates new Entry objects for the entries with
* the presence conditions ANDed with the current presence
* condition.
*
* @param name The name of the macro.
* @param presenceConditionManager The manager containing the current presence
* condition.
* @return The list of entries valid under the current presence
* condition or null if there are none.
*/
public List<Entry> get(String name, PresenceConditionManager presenceConditionManager) {
List<Entry> all;
List<Entry> valid;
PresenceCondition presenceCondition;
if (! contains(name)) return null;
presenceCondition = presenceConditionManager.reference();
all = table.get(name);
valid = null;
for (Entry e : all) {
PresenceCondition and;
and = presenceCondition.and(e.presenceCondition);
if (! and.isFalse()) {
if (null == valid) {
valid = new LinkedList<Entry>();
}
valid.add(new Entry(e.macro, e.presenceCondition));
}
and.delRef();
}
presenceCondition.delRef();
return valid;
}
public void free(List<Entry> entries) {
for (Entry e : entries) {
e.presenceCondition.delRef();
}
}
/** Whether the table already contains the macro name. */
public boolean contains(String name) {
return table.containsKey(name);
}
/** String representation */
public String toString() {
StringBuilder sb;
sb = new StringBuilder();
for (String name : table.keySet()) {
sb.append(name);
sb.append("\n");
sb.append("-------------------------------------------");
sb.append("\n");
for (Entry e : table.get(name)) {
sb.append(e);
sb.append("\n");
}
sb.append("\n");
}
return sb.toString();
}
/** Disable a macro */
public void disable(String macro) {
disabled.add(macro);
}
/** Enable a macro */
public void enable(String macro) {
disabled.remove(macro);
}
/** Test whether a macro is enabled */
public boolean isEnabled(String macro) {
return ! disabled.contains(macro);
}
/**
* Count the number of definitions a given macro has. Does not
* include FREE or UNDEFINED entries in the macro table.
*
* @param name The macro name.
* @return The number of definitions of the macro.
*/
public int countDefinitions(String name) {
if (! table.containsKey(name)) return 0;
int count = 0;
for (Entry e : table.get(name)) {
if (Macro.State.DEFINED == e.macro.state) {
count++;
}
}
return count;
}
/** A macro definition */
public static class Macro {
/** The macro definition */
protected List<Syntax> definition;
/** The state of the macro */
protected State state;
/** An undefined macro */
public static Macro undefined = new Macro(State.UNDEFINED);
/** A free macro */
public static Macro free = new Macro(State.FREE);
public enum State {
FREE, UNDEFINED, DEFINED
}
/** Only subclasses are instantiable */
protected Macro(List<Syntax> definition, State state) {
this.definition = definition;
this.state = state;
}
protected Macro(State state) {
this(null, state);
}
public boolean isObject() {
return false;
}
public boolean isFunction() {
return false;
}
public static class Object extends Macro {
public Object(List<Syntax> definition) {
super(definition, State.DEFINED);
}
public boolean isObject() {
return true;
}
}
public static class Function extends Macro {
protected List<String> formals;
protected String variadic;
public Function(List<String> formals, List<Syntax> definition,
String variadic) {
super(definition, State.DEFINED);
this.formals = formals;
this.variadic = variadic;
}
public boolean isFunction() {
return true;
}
public boolean isVariadic() {
return null != variadic;
}
}
}
/** A macro table entry. */
public static class Entry {
protected Macro macro;
protected PresenceCondition presenceCondition;
public Entry(Macro macro, PresenceCondition presenceCondition) {
this.macro = macro;
this.presenceCondition = presenceCondition;
this.presenceCondition.addRef();
}
public String toString() {
StringBuilder sb;
sb = new StringBuilder();
sb.append(macro.state);
if (Macro.State.DEFINED == macro.state && null != macro.definition) {
sb.append("(");
for (Syntax t : macro.definition) {
if (t.testFlag(Preprocessor.PREV_WHITE)) {
sb.append(" ");
}
sb.append(t.getTokenText());
}
sb.append(")");
}
sb.append("\n");
sb.append(presenceCondition);
sb.append("\n");
return sb.toString();
}
}
}