/*
* xtc - The eXTensible Compiler
* Copyright (C) 2011 Robert Grimm, 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 xtc.lang.cpp.ContextManager.Context;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Support for syntax objects. This class is not yet used.
*
* Each syntax object supports a number of flags, efficiently
* implemented through a bit mask. Their main usage is by the
* preprocessor to control macro expansion.
*
* @author Robert Grimm, Paul Gazzillo
* @version $Revision: 1.22 $
*/
public abstract class Syntax extends xtc.tree.Token {
/** The kinds of syntax objects. */
public static enum Kind {
/** Layout. */
LAYOUT,
/** A language token. */
LANGUAGE,
/** A preprocessor language token. */
PREPROCESSOR,
/** A preprocessor directive. */
DIRECTIVE,
/** The internal representation of a conditional. */
CONDITIONAL,
/** A self-contained conditional block. */
CONDITIONAL_BLOCK,
/** The EOF token. */
EOF
}
// --------------------------------------------------------------------------
/**
* The maximum flag number. Flags are numbered from 0 to this
* number.
*/
public static final int MAX_FLAGS = 31;
/** The flags. */
private int flags;
/** Create a new syntax object. */
public Syntax() {
// Nothing to do.
}
/**
* Create a copy of the specified syntax object, including its
* flags.
*
* @param other The syntax object to copy.
*/
protected Syntax(Syntax other) {
this.flags = other.flags;
this.setLocation(other.getLocation());
}
/**
* Copy this syntax object, including its flags.
*
* @return A copy.
*/
public abstract Syntax copy();
/**
* Get this syntax object's kind.
*
* @return The kind.
*/
public abstract Kind kind();
/**
* Convert this syntax object to layout.
*
* @return This syntax object as layout.
* @throws ClassCastException Signals that this syntax object is not
* layout.
*/
public Layout toLayout() {
throw new ClassCastException("Not layout: " + this);
}
/**
* Convert this syntax object to a language token.
*
* @return This syntax object as a language token.
* @throws ClassCastException Signals that this syntax object is not
* a language token.
*/
public Language<?> toLanguage() {
throw new ClassCastException("Not a language token: " + this);
}
/**
* Convert this syntax object to a preprocessor directive.
*
* @return This syntax object as a directive.
* @throws ClassCastException Signals that this syntax object is not
* a directive.
*/
public Directive toDirective() {
throw new ClassCastException("Not a directive: " + this);
}
/**
* Convert this syntax object to a conditional.
*
* @return This syntax object as a conditional.
* @throws ClassCastException Signals that this syntax object is not
* a conditional.
*/
public Conditional toConditional() {
throw new ClassCastException("Not a conditional: " + this);
}
private int toMask(int num) {
if (0 > num || MAX_FLAGS < num) {
throw new IllegalArgumentException("Invalid flag number: " + num);
}
return 1 << num;
}
/** Clear all flags. */
public void clearFlags() {
flags = 0;
}
/**
* Clear the specified flag.
*
* @param num The flag number.
* @throws IllegalArgumentException Signals an invalid flag number.
*/
public void clearFlag(int num) {
flags &= ~toMask(num);
}
/**
* Set the specified flag.
*
* @param num The flag number.
* @throws IllegalArgumentException Signals an invalid flag number.
*/
public void setFlag(int num) {
flags |= toMask(num);
}
/**
* Test the specified flag.
*
* @param num The flag number.
* @return true if the flag is set.
* @throws IllegalArgumentException Signals an invalid flag number.
*/
public boolean testFlag(int num) {
final int mask = toMask(num);
return (flags & mask) == mask;
}
// --------------------------------------------------------------------------
/** Layout. */
public static class Layout extends Syntax {
private final String text;
private final boolean newline;
/**
* Create a new layout object.
*
* @param text The text.
*/
public Layout(String text) {
this(text, text.contains("\n"));
}
/**
* Create a new layout object, given the text and whether that
* text contains a newline.
*
* @param text The text.
* @param newline Whether the text contains a newline.
*/
public Layout(String text, boolean newline) {
this.text = text;
this.newline = newline;
}
private Layout(Layout other) {
super(other);
this.text = other.text;
this.newline = other.newline;
}
public Layout copy() {
return new Layout(this);
}
public Kind kind() {
return Kind.LAYOUT;
}
public Layout toLayout() {
return this;
}
public boolean hasNewline() {
return newline;
}
public String getTokenText() {
return text;
}
public String toString() {
return getTokenText();
}
}
// --------------------------------------------------------------------------
public static enum PreprocessorTag {
NONE,
OPEN_PAREN,
CLOSE_PAREN,
COMMA,
ELLIPSIS,
HASH,
DOUBLE_HASH
}
/** The interface for language tags. */
public static interface LanguageTag {
/**
* Get the tag's ID. The returned number is used by the LR parser
* to identify the language token.
*
* @return The ID.
*/
public int getID();
/**
* Get the tag's text. For language tokens with a fixed text,
* this method returns that text. Otherwise, it returns null.
*
* @return The text.
*/
public String getText();
/**
* Determine whether the tag has a name. Keywords and identifiers
* have names and thus can be replaced by macro expansion in the
* preprocessor. Punctuation does not have a name.
*
* @return true if the tag has a name.
*/
public boolean hasName();
/**
* Return the preprocessor token tag.
*
* @return The preprocessor token tag.
*/
public PreprocessorTag ppTag();
}
/** A language token. */
public static class Language<Tag extends Enum<Tag> & LanguageTag>
extends Syntax {
protected final Tag tag;
/**
* Create a new language token.
*
* @param tag The tag.
*/
public Language(Tag tag) {
this.tag = tag;
}
/**
* Create a copy of another language token.
*
* @param other The other language token.
*/
protected Language(Language<Tag> other) {
super(other);
this.tag = other.tag;
}
public Language<Tag> copy() {
return new Language<Tag>(this);
}
public Kind kind() {
return Kind.LANGUAGE;
}
public Language<Tag> toLanguage() {
return this;
}
/**
* Get the tag.
*
* @return The tag.
*/
public Tag tag() {
return tag;
}
public String getTokenText() {
return tag.getText();
}
public String toString() {
return getTokenText();
}
}
/**
* A language token with variable text, such as identifiers and
* literals.
*/
public static class Text<Tag extends Enum<Tag> & LanguageTag>
extends Language<Tag> {
private final String text;
/**
* Create a new text token.
*
* @param tag The tag.
* @param text The text.
*/
public Text(Tag tag, String text) {
super(tag);
this.text = text;
}
private Text(Text<Tag> other) {
super(other);
this.text = other.text;
}
public Text<Tag> copy() {
return new Text<Tag>(this);
}
public String getTokenText() {
return text;
}
}
// --------------------------------------------------------------------------
/** The directive tag. */
public static enum DirectiveTag {
IF("#if"),
IFDEF("#ifdef"),
IFNDEF("#ifndef"),
ELIF("#elif"),
ELSE("#else"),
ENDIF("#endif"),
INCLUDE("#include"),
INCLUDE_NEXT("#include_next"),
DEFINE("#define"),
UNDEF("#undef"),
LINE("#line"),
LINEMARKER("#"),
ERROR("#error"),
WARNING("#warning"),
PRAGMA("#pragma");
private String text;
private DirectiveTag(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
/** A preprocessor directive. */
public static class Directive extends Syntax {
private final DirectiveTag tag;
private final List<Language<?>> tokens;
public Directive(DirectiveTag tag, List<Language<?>> tokens) {
this.tag = tag;
this.tokens = tokens;
}
private Directive(Directive other) {
super(other);
this.tag = other.tag;
this.tokens = new ArrayList<Language<?>>(other.tokens);
}
public Directive copy() {
return new Directive(this);
}
public Kind kind() {
return Kind.DIRECTIVE;
}
public Directive toDirective() {
return this;
}
public DirectiveTag tag() {
return tag;
}
public int size() {
return 1 + tokens.size();
}
public Object get(int index) {
if (0 == index) {
return tag.getText();
} else if (1 <= index && index < size()) {
return tokens.get(index - 1);
} else {
throw new IndexOutOfBoundsException("Index: " + index +
", Size: " + tokens.size());
}
}
public String getTokenText() {
StringBuilder buf = new StringBuilder();
buf.append(tag.getText());
Iterator<Language<?>> iter = tokens.iterator();
while (iter.hasNext()) {
buf.append(' ');
buf.append(iter.next().getTokenText());
}
buf.append('\n');
return buf.toString();
}
public String toString() {
return getTokenText();
}
}
// --------------------------------------------------------------------------
/** The conditional tag. */
public static enum ConditionalTag {
/** The start of a conditional. */
START,
/** The next branch of a conditional. */
NEXT,
/** The end of a conditional. */
END
}
/**
* A conditional. This class provides the internal, normalized
* representation of preprocessor conditional directives.
*/
public static class Conditional extends Syntax {
protected final ConditionalTag tag;
protected final Context context;
/**
* Create a new conditional token.
*
* @param tag The tag.
* @param context The context.
* @throws IllegalArgumentException Signals that the context is
* not null for an end conditional.
*/
public Conditional(ConditionalTag tag, Context context) {
if (ConditionalTag.END == tag && null != context) {
throw new IllegalArgumentException("End conditional with context");
}
this.tag = tag;
this.context = context;
}
private Conditional(Conditional other) {
super(other);
this.tag = other.tag;
this.context = other.context.addRef();
}
public Conditional copy() {
return new Conditional(this.tag, this.context);
}
public Kind kind() {
return Kind.CONDITIONAL;
}
public Conditional toConditional() {
return this;
}
/**
* Get the tag.
*
* @return The tag.
*/
public ConditionalTag tag() {
return tag;
}
/**
* Get the context.
*
* @return The context.
* @throws IllegalStateException Signals that this conditional
* token terminates a conditional.
*/
public Context context() {
if (ConditionalTag.END == tag) {
throw new IllegalStateException("End conditional has no context");
}
return context;
}
public String getTokenText() {
StringBuilder sb = new StringBuilder();
sb.append("#");
switch (tag()) {
case START:
sb.append("if ");
sb.append(context.toString());
break;
case NEXT:
sb.append("elif ");
sb.append(context.toString());
break;
case END:
sb.append("endif");
break;
default:
throw new UnsupportedOperationException("unsupported conditional tag");
}
sb.append("\n");
return sb.toString();
}
public String toString() {
return getTokenText();
}
}
/**
* Conditional blocks. SuperC is stream-based, but some operations
* require conditional blocks. Specifically, hoisting requires
* them.
*/
public static class ConditionalBlock extends Syntax {
List<List<Syntax>> branches;
List<Context> contexts;
public ConditionalBlock(List<List<Syntax>> branches,
List<Context> contexts) {
this.branches = branches;
this.contexts = contexts;
for (Context context : contexts) {
context.addRef();
}
}
public ConditionalBlock(ConditionalBlock c) {
super(c);
this.branches = c.branches;
this.contexts = c.contexts;
for (Context context : contexts) {
context.addRef();
}
}
/**
* Free the conditional contexts contained in this block. This is
* necessary because the implementation of conditional contexts is
* reference-counting BDDs.
*/
public void free() {
branches.clear();
for (Context c : contexts) {
c.delRef();
}
}
public Syntax copy() {
return new ConditionalBlock(this);
}
public Kind kind() {
return Kind.CONDITIONAL_BLOCK;
}
public String getTokenText() {
return "CONDITIONAL_BLOCK" + branches;
}
}
public static class EOF extends Syntax {
public Syntax copy() {
throw new UnsupportedOperationException();
}
public Kind kind() {
return Kind.EOF;
}
public String getTokenText() {
return "EOF";
}
}
}