/* * xtc - The eXTensible Compiler * Copyright (C) 2009-2012 New York University * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package xtc.lang.cpp; import java.lang.StringBuilder; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.util.Iterator; import xtc.lang.cpp.Syntax.Kind; import xtc.lang.cpp.Syntax.LanguageTag; import xtc.lang.cpp.Syntax.PreprocessorTag; 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.tree.Location; /** * Parses directives into compound tokens. * * @author Paul Gazzillo * @version $Revision: 1.17 $ */ public class DirectiveParser implements Iterator<Syntax> { /** The input stream of tokens. */ Iterator<Syntax> stream; /** We are at the beginning of a newline. */ protected boolean newline; /** Create a new directive parser stream. */ public DirectiveParser(Iterator<Syntax> stream, String filename) { this.stream = stream; this.newline = true; } /** * This function parses preprocessor directives. The directive must * occur at the beginning of the new line, which is why we must keep * a flag indicating whether this is so. * * The function returns either the next Yytoken * from the lexer or a Directive which the function has parsed. * * The directive's location is taken from the location of the hash * symbol. * * @return the next token or compound token. */ public Syntax next() { Syntax syntax = stream.next(); // Parse the directive. if (newline && syntax.kind() == Kind.LANGUAGE && syntax.toLanguage().tag().ppTag() == PreprocessorTag.HASH) { List<Language<?>> list; String directiveName; DirectiveTag tag; boolean prevWhite = false; // Flag for preserving whitespace. Location location = syntax.getLocation(); list = new ArrayList<Language<?>>(); do { // Skip the whitespace after the #. syntax = stream.next(); if (syntax.kind() == Kind.LAYOUT && ((Layout) syntax).hasNewline()) { break; } else if (syntax.kind() == Kind.LANGUAGE) { break; } } while (true); if (syntax.kind() == Kind.LAYOUT && ((Layout) syntax).hasNewline() || syntax.kind() == Kind.EOF) { // It's an empty line marker. Directive empty = new Directive(DirectiveTag.LINEMARKER, list); empty.setLocation(location); return empty; } directiveName = syntax.toLanguage().getTokenText(); if (tagMap.containsKey(directiveName)) { tag = tagMap.get(directiveName); } else if (syntax.toLanguage().tag().ppTag() == PreprocessorTag.NUMBER) { tag = DirectiveTag.LINEMARKER; list.add(syntax.toLanguage()); } else { tag = DirectiveTag.INVALID; list.add(syntax.toLanguage()); } do { // Skip the whitespace after the directive name. syntax = stream.next(); if (syntax.kind() == Kind.LAYOUT && ((Layout) syntax).hasNewline()) { break; } else if (syntax.kind() == Kind.LANGUAGE) { break; } } while (true); if (syntax.kind() == Kind.LAYOUT && ((Layout) syntax).hasNewline() || syntax.kind() == Kind.EOF) { // It's an empty directive. Directive empty = new Directive(tag, list); empty.setLocation(location); return empty; } list.add(syntax.toLanguage()); do { // Collect the tokens in the directive. syntax = stream.next(); if (syntax.kind() == Kind.EOF) { // Don't swallow the EOF. break; } else if (syntax.kind() == Kind.LANGUAGE) { if (prevWhite) { syntax.setFlag(Preprocessor.PREV_WHITE); prevWhite = false; } list.add(syntax.toLanguage()); } else if (syntax.kind() == Kind.LAYOUT && ((Layout) syntax).hasNewline()) { break; } else if (syntax.kind() == Kind.LAYOUT) { // Set the PREV_WHITE flag when a token is preceded by // whitespace. This is used to preserve spacing in expanded // macros. if (syntax.getTokenText().length() > 0) { prevWhite = true; } } } while (true); newline = true; Directive directive = new Directive(tag, list); directive.setLocation(location); return directive; } else { // Check whether there is a newline. newline = (syntax.kind() == Kind.LAYOUT) && ((Layout) syntax).hasNewline(); return syntax; } } public boolean hasNext() { return stream.hasNext(); } private static final HashMap<String, DirectiveTag> tagMap = new HashMap<String, DirectiveTag>(); static { tagMap.put("if", DirectiveTag.IF); tagMap.put("ifdef", DirectiveTag.IFDEF); tagMap.put("ifndef", DirectiveTag.IFNDEF); tagMap.put("elif", DirectiveTag.ELIF); tagMap.put("else", DirectiveTag.ELSE); tagMap.put("endif", DirectiveTag.ENDIF); tagMap.put("include", DirectiveTag.INCLUDE); tagMap.put("include_next", DirectiveTag.INCLUDE_NEXT); tagMap.put("define", DirectiveTag.DEFINE); tagMap.put("undef", DirectiveTag.UNDEF); tagMap.put("line", DirectiveTag.LINE); tagMap.put("error", DirectiveTag.ERROR); tagMap.put("warning", DirectiveTag.WARNING); tagMap.put("pragma", DirectiveTag.PRAGMA); } public void remove() { throw new UnsupportedOperationException(); } }