/*
* xtc - The eXTensible Compiler
* Copyright (C) 2009-2011 New York University
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; 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.util.Runtime;
import xtc.lang.cpp.ContextManager.Context;
/** 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.52 $
*/
public class MacroTable {
/** The xtc runtime. */
Runtime runtime;
/** 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;
/** Make a new empty macro table */
public MacroTable(Runtime runtime, TokenCreator tokenCreator) {
this.runtime = runtime;
this.tokenCreator = tokenCreator;
table = new HashMap<String, List<Entry>>();
disabled = new HashSet<String>();
}
/** Define a macro under a given context. 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, ContextManager contextManager) {
Context context;
if (contextManager.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)
&& contextManager.getVariableManager().hasDefinedVariable(name)) {
Context var;
Context not;
List<Entry> entries;
var = contextManager.new Context(contextManager.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;
Context andvar, andnot;
andvar = e.context.and(var);
andnot = e.context.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();
}
}
}
context = contextManager.reference();
_define(name, macro, context);
context.delRef();
}
/** Update the symbol table with a new entry. The context passed
* will gain a reference, so dereference it yourself if you no
* longer need it.
*/
protected void _define(String name, Macro macro, Context context) {
if (context.isFalse()) return;
if (! contains(name)) { //new macro
List<Entry> defs;
defs = new LinkedList<Entry>();
table.put(name, defs);
if (! context.isTrue()) {
Context negation;
negation = context.not();
if (runtime.test("cppmode")
|| (null != runtime.getString("TypeChef-x")
&& ! name.startsWith(runtime.getString("TypeChef-x")))) {
// 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, context));
} 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);
Context newContext;
newContext = entry.context.andNot(context);
entry.context.delRef();
entry.context = newContext;
if (newContext.isFalse()) {
newContext.delRef();
defs.remove(d);
d--;
}
}
for (Entry e : defs) {
if (Macro.State.UNDEFINED == e.macro.state
&& Macro.State.UNDEFINED == macro.state
|| Macro.State.FREE == e.macro.state
&& Macro.State.FREE == macro.state
) {
//only allow one entry for UNDEFINED and FREE
duplicate = e;
}
if (/*! runtime.test("noDedup")*/ true) {
//TODO check dedup flag for optimization evaluation
if (Macro.State.DEFINED == e.macro.state
&& Macro.State.DEFINED == macro.state) {
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())) {
isdup = false;
}
}
if (isdup) {
duplicate = e;
break;
}
}
}
}
}
}
if (null != duplicate) {
Context oldc, newc;
oldc = duplicate.context;
newc = oldc.or(context);
oldc.delRef();
duplicate.context = newc;
} else {
//add new entry
table.get(name).add(new Entry(macro, context));
}
}
}
/**
* Update the contexts of macros that use a guard macro. We assume
* that guard macros for headers are undefined, not free.
*
* @param guardMacro The name of the guard macro.
* @param contextManager The context manager.
*/
public void rectifyGuard(String guardMacro, ContextManager contextManager) {
Context var, not;
Context context;
context = contextManager.reference();
var = contextManager.new Context(contextManager.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;
Context restrictnot;
Context c;
e = entries.get(i);
c = context.and(e.context);
// Check whether the macro entry's presence condition
// (e.context) depends on the guard macro (! (defined var)).
restrictnot = e.context.restrict(not);
if (! restrictnot.equals(e.context)) {
e.context.delRef();
if (restrictnot.isFalse()) {
entries.remove(i);
i--;
} else {
e.context = restrictnot;
e.context.addRef();
}
}
restrictnot.delRef();
}
}
context.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.
_define(guardMacro, Macro.undefined, contextManager.new Context(true));
}
/** Undefine a macro under the given context. */
public void undefine(String name, ContextManager contextManager) {
define(name, Macro.undefined, contextManager);
}
/** Gets the macro table entries of the given macro under the
* given context. Any entries not valid in the context will
* not be returned. The actual table entries are returned, so
* they shouldn't be modified.
*/
public List<Entry> get(String name, ContextManager contextManager) {
List<Entry> all;
List<Entry> valid;
Context context;
if (! contains(name)) return null;
context = contextManager.reference();
all = table.get(name);
valid = new LinkedList<Entry>();
for (Entry e : all) {
Context and;
and = context.and(e.context);
if (! and.isFalse()) {
valid.add(new Entry(e.macro, e.context));
}
and.delRef();
}
context.delRef();
return valid;
}
public void free(List<Entry> entries) {
for (Entry e : entries) {
e.context.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;
}
}
}
public static class Entry {
protected Macro macro;
protected Context context;
public Entry(Macro macro, Context context) {
this.macro = macro;
this.context = context;
this.context.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");
/*if (! runtime.test("noConditions"))*/ {
sb.append(context);
}
sb.append("\n");
return sb.toString();
}
}
}