001/*
002 * Anarres C Preprocessor
003 * Copyright (c) 2007-2015, Shevek
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
014 * or implied.  See the License for the specific language governing
015 * permissions and limitations under the License.
016 */
017package org.anarres.cpp;
018
019import java.io.Closeable;
020import java.io.IOException;
021import java.io.Reader;
022import javax.annotation.Nonnull;
023import static org.anarres.cpp.Token.CCOMMENT;
024import static org.anarres.cpp.Token.CPPCOMMENT;
025import static org.anarres.cpp.Token.EOF;
026
027/**
028 * A Reader wrapper around the Preprocessor.
029 *
030 * This is a utility class to provide a transparent {@link Reader}
031 * which preprocesses the input text.
032 *
033 * @see Preprocessor
034 * @see Reader
035 */
036public class CppReader extends Reader implements Closeable {
037
038    private final Preprocessor cpp;
039    private String token;
040    private int idx;
041
042    public CppReader(@Nonnull final Reader r) {
043        cpp = new Preprocessor(new LexerSource(r, true) {
044            @Override
045            public String getName() {
046                return "<CppReader Input@"
047                        + System.identityHashCode(r) + ">";
048            }
049        });
050        token = "";
051        idx = 0;
052    }
053
054    public CppReader(@Nonnull Preprocessor p) {
055        cpp = p;
056        token = "";
057        idx = 0;
058    }
059
060    /**
061     * Returns the Preprocessor used by this CppReader.
062     */
063    @Nonnull
064    public Preprocessor getPreprocessor() {
065        return cpp;
066    }
067
068    /**
069     * Defines the given name as a macro.
070     *
071     * This is a convnience method.
072     */
073    public void addMacro(@Nonnull String name)
074            throws LexerException {
075        cpp.addMacro(name);
076    }
077
078    /**
079     * Defines the given name as a macro.
080     *
081     * This is a convnience method.
082     */
083    public void addMacro(@Nonnull String name, @Nonnull String value)
084            throws LexerException {
085        cpp.addMacro(name, value);
086    }
087
088    private boolean refill()
089            throws IOException {
090        try {
091            assert cpp != null : "cpp is null : was it closed?";
092            if (token == null)
093                return false;
094            while (idx >= token.length()) {
095                Token tok = cpp.token();
096                switch (tok.getType()) {
097                    case EOF:
098                        token = null;
099                        return false;
100                    case CCOMMENT:
101                    case CPPCOMMENT:
102                        if (!cpp.getFeature(Feature.KEEPCOMMENTS)) {
103                            token = " ";
104                            break;
105                        }
106                    default:
107                        token = tok.getText();
108                        break;
109                }
110                idx = 0;
111            }
112            return true;
113        } catch (LexerException e) {
114            // new IOException(String, Throwable) is since 1.6
115            IOException _e = new IOException(String.valueOf(e));
116            _e.initCause(e);
117            throw _e;
118        }
119    }
120
121    @Override
122    public int read()
123            throws IOException {
124        if (!refill())
125            return -1;
126        return token.charAt(idx++);
127    }
128
129    @Override
130    /* XXX Very slow and inefficient. */
131    public int read(char cbuf[], int off, int len)
132            throws IOException {
133        if (token == null)
134            return -1;
135        for (int i = 0; i < len; i++) {
136            int ch = read();
137            if (ch == -1)
138                return i;
139            cbuf[off + i] = (char) ch;
140        }
141        return len;
142    }
143
144    @Override
145    public void close()
146            throws IOException {
147        cpp.close();
148        token = null;
149    }
150
151}