/*
* 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.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.HashMap;
import xtc.tree.Node;
import xtc.util.Pair;
import xtc.util.Runtime;
import xtc.lang.cpp.Syntax.Kind;
import xtc.lang.cpp.Syntax.LanguageTag;
import xtc.lang.cpp.Syntax.ConditionalTag;
import xtc.lang.cpp.Syntax.DirectiveTag;
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.ContextManager.Context;
import xtc.lang.cpp.ForkMergeParser.OrderedSyntax;
import xtc.lang.cpp.ForkMergeParser.Lookahead;
import xtc.lang.cpp.ForkMergeParser.StateStack;
/**
* This class implements the generated CActionsBase class.
*
* @author Paul Gazzillo
* @version $Revision: 1.3 $
*/
public class CParsingContext implements Actions.Context {
/** Output bindings and scope changes. */
protected static boolean DEBUG = false;
/** The symbol table for this parsing context. */
protected SymbolTable symtab;
/**
* The parent parsing context, corresponding to the parent
* scope.
*/
protected CParsingContext parent;
/**
* Whether the scope this parsing context is associated with is
* reentrant. This is used to parse function definitions.
*/
protected boolean reentrant;
/** The xtc runtime. */
Runtime runtime;
/** Whether to display language statistics. */
boolean languageStatistics;
/**
* A three-bit digit. This is used to capture typedef/var ambiguity
* when one token can be both.
*/
public static enum trit {
TRUE,
FALSE,
TRUEFALSE
}
/**
* Create a new initial C parsing contex.
*/
public CParsingContext(Runtime runtime) {
this(new SymbolTable(), null, runtime);
}
/**
* Create a new C parsing context.
*
* @param symtab The symbol table for this parsing context and scope.
* @param parent The parent parsing context and scope.
*/
public CParsingContext(SymbolTable symtab, CParsingContext parent,
Runtime runtime) {
this.symtab = symtab;
this.parent = parent;
this.runtime = runtime;
this.reentrant = false;
languageStatistics = runtime.test("statisticsLanguage");
}
/**
* Copy a C parsing context. Used for forking the parsing context.
*
* @scope The parsing context to copy.
*/
public CParsingContext(CParsingContext scope, Runtime runtime) {
this.symtab = scope.symtab.addRef();
if (scope.parent != null) {
this.parent = new CParsingContext(scope.parent, runtime);
} else {
this.parent = null;
}
this.reentrant = scope.reentrant;
this.runtime = runtime;
}
public Actions.Context fork() {
return new CParsingContext(this, runtime);
}
public boolean shouldFork(Lookahead n) {
return true;
}
public Collection<Lookahead> reclassify(Collection<Lookahead> set) {
// Apply the parsing context to tokens in the follow set. This
// may reclassify tokens and handle implicit conditionals,
// e.g. due to C typedef ambiguities.
// The list of new tokens if there are any from implicit
// conditionals due to the typedef/var name ambiguity.
Collection<Lookahead> newTokens = null;
for (Lookahead n : set) {
trit isTypedef = trit.FALSE;
if (n.t.syntax.kind() == Kind.LANGUAGE
&& n.t.syntax.toLanguage().tag() == CTag.IDENTIFIER) {
isTypedef = isType(n.t.syntax.getTokenText(),
n.c);
}
// Check for implicit conditional.
switch (isTypedef) {
case TRUEFALSE:
// Find the presence conditions of each token.
Context typedefContext
= this.typedefContext(n.t.syntax.getTokenText(), n.c);
// Update the token's presence condition.
n.c.delRef();
n.c = typedefContext;
// We need to replace the token with two new one because there
// is a typedef/var amgiguity.
// Split up the presence conditions.
Context varContext = typedefContext.not();
Lookahead identifier = new Lookahead(n.t, varContext);
// Add a new token to the list.
if (null == newTokens) {
newTokens = new LinkedList<Lookahead>();
}
newTokens.add(identifier);
// Fall through to reclassify the token as a TYPEDEFname.
case TRUE:
// Replace the IDENTIFIER with a new one classified as a
// TYPEDEFname.
Language<CTag> newToken = new Text<CTag>(CTag.TYPEDEFname,
n.t.syntax.getTokenText());
newToken.setLocation(n.t.syntax.getLocation());
OrderedSyntax newOrdered;
try {
newOrdered = n.t.copy(newToken);
} catch (java.io.IOException e) {
// OrderedSyntax.copy() can throw an IOException because it
// may have to read from the input stream. The Actions
// interface does not throw an IOException, so we throw a
// RuntimeException instead.
throw new RuntimeException(e);
}
// Update the token part of the next token object.
n.t = newOrdered;
break;
case FALSE:
// No reclassification necessary.
break;
}
}
return newTokens;
}
/**
* This method determines whether an identifier is a typedef name,
* var name, or both by inspecting the symbol table in this scope
* and any parent scopes.
*
* @param ident The identifier.
* @param context The presence condition.
*/
public trit isType(String ident, Context context) {
CParsingContext scope;
scope = this;
while (true) {
while (scope.reentrant) scope = scope.parent;
if ( scope.symtab.map.containsKey(ident)
&& scope.symtab.map.get(ident).t != null
) {
break;
}
if (null == scope.parent) {
return trit.FALSE;
}
scope = scope.parent;
}
scope = this;
do { //find the symbol in local scope or parent scope
while (scope.reentrant) scope = scope.parent;
if (scope.symtab.map.containsKey(ident)) {
Entry e;
boolean typedef;
boolean var;
e = scope.symtab.map.get(ident);
typedef = false;
var = false;
//TODO make sure logic is correct and optimize branching
if (null != e.t) {
Context and;
and = e.t.and(context);
if (! and.isFalse()) {
typedef = true;
}
and.delRef();
}
if (null != e.f) {
Context and;
and = e.f.and(context);
if (! and.isFalse()) {
var = true;
}
and.delRef();
}
if (typedef && var) {
if (DEBUG) System.err.println("isType: " + ident
+ " true/false in " /*+ context*/);
if (languageStatistics) {
String location = null; //TODO pass location to this
//function
runtime.errConsole().pln(String.format("typedef_ambiguity %s %s",
ident, location)).flush();
}
return trit.TRUEFALSE;
} else if (typedef) {
if (DEBUG) System.err.println("isType: " + ident
+ " true in " /*+ context*/);
return trit.TRUE;
} else if (var) {
if (DEBUG) System.err.println("isType: " + ident
+ " false in " /*+ context*/);
return trit.FALSE;
}
}
if (null == scope.parent) {
break;
}
scope = scope.parent;
} while (true);
if (DEBUG) System.err.println("isType: " + ident
+ " false in " /*+ context*/);
return trit.FALSE;
}
public boolean mayMerge(Actions.Context other) {
if (! (other instanceof CParsingContext)) return false;
return mergeable(this, (CParsingContext) other);
}
/**
* A helper method for testing mergeability.
*
* @param s The first parsing context.
* @param t The second parsing context.
*/
private static boolean mergeable(CParsingContext s, CParsingContext t) {
if ((null == s) && (null == t)) {
return true;
} else if ((null == s) || (null == t)) {
//System.err.println("TABLES NOT MERGEABLE 1");
return false;
} else if (s.symtab == t.symtab) {
return true;
} else if (s.reentrant != t.reentrant) {
//System.err.println("TABLES NOT MERGEABLE 2");
return false;
} else {
return mergeable(s.parent, t.parent);
}
}
public Actions.Context merge(Actions.Context other) {
CParsingContext scope = (CParsingContext) other;
if (this.symtab == scope.symtab) {
return this;
} else {
symtab.addAll(scope.symtab);
if (null != parent) {
return parent.merge(scope.parent);
} else {
return null;
}
}
}
public void free() {
symtab.delRef();
if (null != parent) {
parent.free();
}
}
/**
* Bind an identifier to a typedef or var name for a given presence
* condition.
*
* @param ident The identifier.
* @param typedef Whether its a typedef name or a var name.
* @param context The presence condition.
*/
public void bind(String ident, boolean typedef, Context context) {
CParsingContext scope;
if (DEBUG) {
System.err.println("bind: " + ident + " " + typedef);
}
scope = this;
while (scope.reentrant) scope = scope.parent;
scope.symtab.add(ident, typedef, context);
}
/**
* Return the presence condition under which an identifier is a
* typedef name.
*
* @param ident The identifier.
* @param context The current presence condition.
*/
public Context typedefContext(String ident, Context context) {
CParsingContext scope;
scope = this;
do { //find the symbol in local scope or parent scope
while (scope.reentrant) scope = scope.parent;
if (scope.symtab.map.containsKey(ident)) {
Entry e;
boolean typedef;
boolean var;
Context and;
e = scope.symtab.map.get(ident);
and = e.t.and(context);
if (! and.isFalse()) {
return and;
}
and.delRef();
}
if (null == scope.parent) {
break;
}
scope = scope.parent;
} while (true);
return null;
}
/**
* Enter a new scope.
*
* @param context The current presence condition.
* @return The parsing context of the new scope.
*/
public CParsingContext enterScope(Context context) {
CParsingContext scope;
if (DEBUG) System.err.println("enter scope");
scope = this;
while (scope.reentrant) {
scope.symtab.delRef();
scope.symtab = null;
scope = scope.parent;
}
scope = new CParsingContext(new SymbolTable(), new CParsingContext(scope,
runtime),
runtime);
return scope;
}
/**
* Exit the scope.
*
* @param context The current presence condition.
* @return The parsing context of the parent scope.
*/
public CParsingContext exitScope(Context context) {
CParsingContext scope;
if (DEBUG) System.err.println("exit scope");
scope = this;
while (scope.reentrant) {
scope.symtab.delRef();
scope.symtab = null;
scope = scope.parent;
}
scope.symtab.delRef();
scope.symtab = null;
scope = scope.parent;
return scope;
}
/**
* Exit a reentrant scope.
*
* @param context The current presence condition.
* @return The parsing context of the parent scope.
*/
public CParsingContext exitReentrantScope(Context context) {
CParsingContext scope;
if (DEBUG) System.err.println("exit reentrant scope");
scope = this;
while (scope.reentrant) {
scope.symtab.delRef();
scope.symtab = null;
scope = scope.parent;
}
scope.reentrant = true;
return scope;
}
/**
* Reenter a reentrant scope.
*
* @param context The current presence condition.
* @return The parsing context of the reentered scope.
*/
public CParsingContext reenterScope(Context context) {
if (DEBUG) System.err.println("reenter scope");
if (! reentrant) {
//if (cfg.errordetail) {
//System.err.println("NOT REENTRANT");
//}
}
else {
reentrant = false;
}
return this;
}
/**
* Kill a reentrant scope.
*
* @param context The current presence condition.
* @return The parsing context of the non-reentrant parent scope.
*/
public CParsingContext killReentrantScope(Context context) {
CParsingContext scope;
if (DEBUG) System.err.println("kill reentrant scope");
scope = this;
while (scope.reentrant) {
scope.symtab.delRef();
scope.symtab = null;
scope = scope.parent;
}
return scope;
}
/** The symbol table that store parsing context symbols. */
private static class SymbolTable {
/** The symbol table */
public HashMap<String, Entry> map;
/** The reference count for cleaning up the table BDDs */
public int refs;
/** New table */
public SymbolTable() {
this.map = new HashMap<String, Entry>();
this.refs = 1;
}
public SymbolTable addRef() {
refs++;
return this;
}
public void delRef() {
refs--;
if (0 == refs) { //clean up symbol table
for (String str : this.map.keySet()) {
Entry e = this.map.get(str);
if (null != e.t) {
e.t.delRef();
}
if (null != e.f) {
e.f.delRef();
}
}
}
}
public void add(String ident, boolean typedef, Context context) {
if (! map.containsKey(ident)) {
map.put(ident,
new Entry(typedef ? context : null, typedef ? null : context));
context.addRef();
}
else {
Entry e;
e = map.get(ident);
if (typedef) {
if (null == e.t) {
e.t = context;
context.addRef();
}
else {
Context or;
or = e.t.or(context);
e.t.delRef();
e.t = or;
}
}
else {
if (null == e.f) {
e.f = context;
context.addRef();
}
else {
Context or;
or = e.f.or(context);
e.f.delRef();
e.f = or;
}
}
}
}
public void addAll(SymbolTable symtab) {
for (String str : symtab.map.keySet()) {
if (! map.containsKey(str)) {
Entry e = symtab.map.get(str);
map.put(str, new Entry(e.t, e.f));
if (null != e.t) {
e.t.addRef();
}
if (null != e.f) {
e.f.addRef();
}
}
else {
Entry d = map.get(str);
Entry e = symtab.map.get(str);
if (null != e.t) {
if (null == d.t) {
d.t = e.t;
e.t.addRef();
}
else {
Context or;
or = d.t.or(e.t);
d.t.delRef();
d.t = or;
}
}
if (null != e.f) {
if (null == d.f) {
d.f = e.f;
e.f.addRef();
}
else {
Context or;
or = d.f.or(e.f);
d.f.delRef();
d.f = or;
}
}
}
}
}
}
/** An entry in the symbol table. */
private static class Entry {
/** The presence condition when the symbol is a typedef name. */
Context t;
/** The presence condition when the symbol is a var name. */
Context f;
/** Create a new entry.
*
* @param t The typedef name presence condition.
* @param f The var name presence condition.
*/
public Entry(Context t, Context f) {
this.t = t;
this.f = f;
}
}
}