org.anarres.cpp.Source Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcpp Show documentation
Show all versions of jcpp Show documentation
An embeddable C Preprocessor for the JVM.
/*
* Anarres C Preprocessor
* Copyright (c) 2007-2008, Shevek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.anarres.cpp;
import java.io.Closeable;
import java.io.IOException;
import java.util.Iterator;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import static org.anarres.cpp.Token.*;
/**
* An input to the Preprocessor.
*
* Inputs may come from Files, Strings or other sources. The
* preprocessor maintains a stack of Sources. Operations such as
* file inclusion or token pasting will push a new source onto
* the Preprocessor stack. Sources pop from the stack when they
* are exhausted; this may be transparent or explicit.
*
* BUG: Error messages are not handled properly.
*/
public abstract class Source implements Iterable, Closeable {
private Source parent;
private boolean autopop;
private PreprocessorListener listener;
private boolean active;
private boolean werror;
/* LineNumberReader */
/*
// We can't do this, since we would lose the LexerException
private class Itr implements Iterator {
private Token next = null;
private void advance() {
try {
if (next != null)
next = token();
}
catch (IOException e) {
throw new UnsupportedOperationException(
"Failed to advance token iterator: " +
e.getMessage()
);
}
}
public boolean hasNext() {
return next.getType() != EOF;
}
public Token next() {
advance();
Token t = next;
next = null;
return t;
}
public void remove() {
throw new UnsupportedOperationException(
"Cannot remove tokens from a Source."
);
}
}
*/
public Source() {
this.parent = null;
this.autopop = false;
this.listener = null;
this.active = true;
this.werror = false;
}
/**
* Sets the parent source of this source.
*
* Sources form a singly linked list.
*/
/* pp */ void setParent(Source parent, boolean autopop) {
this.parent = parent;
this.autopop = autopop;
}
/**
* Returns the parent source of this source.
*
* Sources form a singly linked list.
*/
/* pp */ final Source getParent() {
return parent;
}
// @OverrideMustInvoke
/* pp */ void init(Preprocessor pp) {
setListener(pp.getListener());
this.werror = pp.getWarnings().contains(Warning.ERROR);
}
/**
* Sets the listener for this Source.
*
* Normally this is set by the Preprocessor when a Source is
* used, but if you are using a Source as a standalone object,
* you may wish to call this.
*/
public void setListener(PreprocessorListener pl) {
this.listener = pl;
}
/**
* Returns the File currently being lexed.
*
* If this Source is not a {@link FileLexerSource}, then
* it will ask the parent Source, and so forth recursively.
* If no Source on the stack is a FileLexerSource, returns null.
*/
@CheckForNull
public String getPath() {
Source parent = getParent();
if (parent != null)
return parent.getPath();
return null;
}
/**
* Returns the human-readable name of the current Source.
*/
@CheckForNull
public String getName() {
Source parent = getParent();
if (parent != null)
return parent.getName();
return null;
}
/**
* Returns the current line number within this Source.
*/
@Nonnegative
public int getLine() {
Source parent = getParent();
if (parent == null)
return 0;
return parent.getLine();
}
/**
* Returns the current column number within this Source.
*/
public int getColumn() {
Source parent = getParent();
if (parent == null)
return 0;
return parent.getColumn();
}
/**
* Returns true if this Source is expanding the given macro.
*
* This is used to prevent macro recursion.
*/
/* pp */ boolean isExpanding(Macro m) {
Source parent = getParent();
if (parent != null)
return parent.isExpanding(m);
return false;
}
/**
* Returns true if this Source should be transparently popped
* from the input stack.
*
* Examples of such sources are macro expansions.
*/
/* pp */ boolean isAutopop() {
return autopop;
}
/**
* Returns true if this source has line numbers.
*/
/* pp */ boolean isNumbered() {
return false;
}
/* This is an incredibly lazy way of disabling warnings when
* the source is not active. */
/* pp */ void setActive(boolean b) {
this.active = b;
}
/* pp */ boolean isActive() {
return active;
}
/**
* Returns the next Token parsed from this input stream.
*
* @see Token
*/
@Nonnull
public abstract Token token()
throws IOException,
LexerException;
/**
* Returns a token iterator for this Source.
*/
@Override
public Iterator iterator() {
return new SourceIterator(this);
}
/**
* Skips tokens until the end of line.
*
* @param white true if only whitespace is permitted on the
* remainder of the line.
* @return the NL token.
*/
@Nonnull
public Token skipline(boolean white)
throws IOException,
LexerException {
for (;;) {
Token tok = token();
switch (tok.getType()) {
case EOF:
/* There ought to be a newline before EOF.
* At least, in any skipline context. */
/* XXX Are we sure about this? */
warning(tok.getLine(), tok.getColumn(),
"No newline before end of file");
return new Token(NL,
tok.getLine(), tok.getColumn(),
"\n");
// return tok;
case NL:
/* This may contain one or more newlines. */
return tok;
case CCOMMENT:
case CPPCOMMENT:
case WHITESPACE:
break;
default:
/* XXX Check white, if required. */
if (white)
warning(tok.getLine(), tok.getColumn(),
"Unexpected nonwhite token");
break;
}
}
}
protected void error(int line, int column, String msg)
throws LexerException {
if (listener != null)
listener.handleError(this, line, column, msg);
else
throw new LexerException("Error at " + line + ":" + column + ": " + msg);
}
protected void warning(int line, int column, String msg)
throws LexerException {
if (werror)
error(line, column, msg);
else if (listener != null)
listener.handleWarning(this, line, column, msg);
else
throw new LexerException("Warning at " + line + ":" + column + ": " + msg);
}
public void close()
throws IOException {
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy