/* * Copyright (c) Joachim Ansorg, [email protected] * * 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 com.ansorgit.plugins.bash.lang.parser.shellCommand; import com.ansorgit.plugins.bash.lang.parser.BashPsiBuilder; import com.ansorgit.plugins.bash.lang.parser.Parsing; import com.ansorgit.plugins.bash.lang.parser.ParsingFunction; import com.ansorgit.plugins.bash.lang.parser.arithmetic.ArithmeticFactory; import com.ansorgit.plugins.bash.lang.parser.misc.ShellCommandParsing; import com.ansorgit.plugins.bash.lang.parser.util.ParserUtil; import com.intellij.lang.PsiBuilder; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.tree.IElementType; /** * Parsing function for for loops statements. * <br> * * @author jansorg */ public class ForLoopParsingFunction implements ParsingFunction { private static final Logger log = Logger.getInstance("#bash.ForLoopParsingFunction"); private static final IElementType[] ARITH_FOR_LOOP_START = {FOR_KEYWORD, EXPR_ARITH}; public boolean isValid(BashPsiBuilder builder) { return builder.getTokenType() == FOR_KEYWORD; } private boolean isArithmeticForLoop(PsiBuilder builder) { return ParserUtil.hasNextTokens(builder, false, ARITH_FOR_LOOP_START); } public boolean parse(BashPsiBuilder builder) { /* The grammar: for_command: FOR WORD newline_list DO compound_list DONE | FOR WORD newline_list '{' compound_list '}' | FOR WORD ';' newline_list DO compound_list DONE | FOR WORD ';' newline_list '{' compound_list '}' | FOR WORD newline_list IN word_list list_terminator newline_list DO compound_list DONE | FOR WORD newline_list IN word_list list_terminator newline_list '{' compound_list '}' | FOR WORD newline_list IN list_terminator newline_list DO compound_list DONE | FOR WORD newline_list IN list_terminator newline_list '{' compound_list '}' */ return isArithmeticForLoop(builder) ? parseArithmeticForLoop(builder) : parseForLoop(builder); } private boolean parseForLoop(BashPsiBuilder builder) { final PsiBuilder.Marker forLoop = builder.mark(); builder.advanceLexer();//after the "for" //now just a single word if (ParserUtil.isIdentifier(builder.getTokenType())) { //mark the word as var ParserUtil.remapMarkAdvance(builder, WORD, VAR_DEF_ELEMENT); } else { forLoop.drop(); ParserUtil.error(builder, "parser.shell.for.expectedWord"); return false; } builder.readOptionalNewlines(); //now either do, a block {} or IN final IElementType afterLoopValue = builder.getTokenType(); if (afterLoopValue == SEMI) { builder.advanceLexer(); builder.readOptionalNewlines(); } else if ((afterLoopValue == WORD || afterLoopValue == IN_KEYWORD_REMAPPED) && "in".equals(builder.getTokenText())) { builder.remapCurrentToken(IN_KEYWORD_REMAPPED); builder.advanceLexer(); //in keyword //parse the optional word list if (builder.getTokenType() == SEMI) { builder.advanceLexer(); } else if (!Parsing.word.parseWordListIfValid(builder, true, false).isParsedSuccessfully()) {//fixme validate forLoop.drop();//fixme return false; } builder.readOptionalNewlines(); } if (!LoopParserUtil.parseLoopBody(builder, true, false)) { forLoop.drop(); return false; } forLoop.done(ShellCommandParsing.FOR_COMMAND); return true; } /** * Helper function to parse an arithmetic for loop. * * @param builder The builder to use. * @return True if the arithmetic for loop has succesfully been parsed. */ boolean parseArithmeticForLoop(BashPsiBuilder builder) { /* arith_for_command: FOR ARITH_FOR_EXPRS list_terminator newline_list DO compound_list DONE | FOR ARITH_FOR_EXPRS list_terminator newline_list '{' compound_list '}' | FOR ARITH_FOR_EXPRS DO compound_list DONE | FOR ARITH_FOR_EXPRS '{' compound_list '}' ; */ PsiBuilder.Marker marker = builder.mark(); builder.advanceLexer();//after the "for" keyword //parse arithmetic expressions, it's a block like (( a; b; c )) with a,b,c //being arithmetic expressions if (builder.getTokenType() != EXPR_ARITH) { ParserUtil.error(marker, "parser.unexpected.token"); return false; } builder.advanceLexer();//after the "((" token if (!parseArithmeticExpression(builder, SEMI)) { ParserUtil.error(marker, "parser.unexpected.token"); return false; } if (!parseArithmeticExpression(builder, SEMI)) { ParserUtil.error(marker, "parser.unexpected.token"); return false; } if (!parseArithmeticExpression(builder, _EXPR_ARITH)) { ParserUtil.error(marker, "parser.unexpected.token"); return false; } if (Parsing.list.isListTerminator(builder.getTokenType())) { builder.advanceLexer(); builder.readOptionalNewlines(); } if (!LoopParserUtil.parseLoopBody(builder, true, false)) { marker.drop(); return false; } marker.done(ShellCommandParsing.FOR_COMMAND); return true; } /** * parses an optional arithmetic expression with an expected end token * * @param builder The builder which provides the tokens. * @param endToken The token which is at the end of the arithmetic expression. * @return True if the parsing has been successfull. */ private boolean parseArithmeticExpression(BashPsiBuilder builder, IElementType endToken) { if (builder.getTokenType() == endToken) {//the expression can be empty builder.advanceLexer(); return true; } while ((builder.getTokenType() != endToken) && !builder.eof()) { if (!ArithmeticFactory.entryPoint().parse(builder)) { return false; } } final IElementType foundEndToken = ParserUtil.getTokenAndAdvance(builder); return (!builder.eof() && foundEndToken == endToken); } }