/* * 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.lang.StringBuilder; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; 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.11 $ */ public class DirectiveParser implements Stream { /** The input stream of tokens. */ Stream stream; // TODO need to handle saving the filename better. /** The filename. */ String filename; /** We are at the beginning of a newline. */ protected boolean newline; /** Create a new directive parser stream. */ public DirectiveParser(Stream stream, String filename) { this.stream = stream; this.filename = filename; 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 scan() throws java.io.IOException { Syntax syntax = stream.scan(); // 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<?>>(); // TODO if the directive does not have a newline character and // it's the last thing in the file, the EOF character gets // dropped, even though the EOF character is necessary for the // LR parser to finish. do { // Skip the whitespace after the #. syntax = stream.scan(); 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 { tag = DirectiveTag.LINEMARKER; list.add(syntax.toLanguage()); } do { // Skip the whitespace after the directive name. syntax = stream.scan(); 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.scan(); if (syntax.kind() == Kind.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 done() { return stream.done(); } 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); } }