001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 the ConQAT Project | 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 or implied. | 014| See the License for the specific language governing permissions and | 015| limitations under the License. | 016+-------------------------------------------------------------------------*/ 017package eu.cqse.check.framework.shallowparser.languages.ruby; 018 019import static eu.cqse.check.framework.scanner.ETokenType.BEGIN; 020import static eu.cqse.check.framework.scanner.ETokenType.CLASS; 021import static eu.cqse.check.framework.scanner.ETokenType.DEF; 022import static eu.cqse.check.framework.scanner.ETokenType.DO; 023import static eu.cqse.check.framework.scanner.ETokenType.DOT; 024import static eu.cqse.check.framework.scanner.ETokenType.ELSE; 025import static eu.cqse.check.framework.scanner.ETokenType.ELSIF; 026import static eu.cqse.check.framework.scanner.ETokenType.END; 027import static eu.cqse.check.framework.scanner.ETokenType.ENSURE; 028import static eu.cqse.check.framework.scanner.ETokenType.FOR; 029import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER; 030import static eu.cqse.check.framework.scanner.ETokenType.IF; 031import static eu.cqse.check.framework.scanner.ETokenType.INTERPOLATIONEND; 032import static eu.cqse.check.framework.scanner.ETokenType.INTERPOLATIONSTART; 033import static eu.cqse.check.framework.scanner.ETokenType.LBRACE; 034import static eu.cqse.check.framework.scanner.ETokenType.LPAREN; 035import static eu.cqse.check.framework.scanner.ETokenType.LSHIFT; 036import static eu.cqse.check.framework.scanner.ETokenType.MODULE; 037import static eu.cqse.check.framework.scanner.ETokenType.RBRACE; 038import static eu.cqse.check.framework.scanner.ETokenType.RESCUE; 039import static eu.cqse.check.framework.scanner.ETokenType.RPAREN; 040import static eu.cqse.check.framework.scanner.ETokenType.SELF; 041import static eu.cqse.check.framework.scanner.ETokenType.SEMICOLON; 042import static eu.cqse.check.framework.scanner.ETokenType.STRING_LITERAL; 043import static eu.cqse.check.framework.scanner.ETokenType.UNLESS; 044import static eu.cqse.check.framework.scanner.ETokenType.UNTIL; 045import static eu.cqse.check.framework.scanner.ETokenType.WHILE; 046import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.METHOD; 047import static eu.cqse.check.framework.shallowparser.languages.ruby.RubyShallowParser.ERubyParserStates.ANY; 048 049import java.util.EnumSet; 050 051import org.conqat.lib.commons.region.Region; 052 053import eu.cqse.check.framework.shallowparser.SubTypeNames; 054import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType; 055import eu.cqse.check.framework.shallowparser.framework.RecognizerBase; 056import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase; 057import eu.cqse.check.framework.shallowparser.languages.ruby.RubyShallowParser.ERubyParserStates; 058 059/** 060 * Shallow parser for JavaScript. The parser is aware of Google closure and 061 * supports special handling of the provide, require and inherits statements. 062 */ 063public class RubyShallowParser extends ShallowParserBase<ERubyParserStates> { 064 065 /** The states used in this parser. */ 066 public enum ERubyParserStates { 067 068 /** Single state, as any construct can occur at any place. */ 069 ANY 070 } 071 072 /** Constructor. */ 073 public RubyShallowParser() { 074 super(ERubyParserStates.class, ERubyParserStates.ANY); 075 076 createStringLiteralRules(); 077 createClassRules(); 078 createFunctionRules(); 079 createStatementRules(); 080 } 081 082 /** 083 * Stitches together string literals with interpolations. 084 */ 085 private void createStringLiteralRules() { 086 inState(ANY).sequence(STRING_LITERAL).skipNested(INTERPOLATIONSTART, INTERPOLATIONEND).optional(STRING_LITERAL); 087 } 088 089 /** Creates parsing rules for classes. */ 090 private void createClassRules() { 091 // class << self 092 inState(ANY).sequence(CLASS, LSHIFT, SELF).optional(SEMICOLON) 093 .createNode(EShallowEntityType.META, "static declarations").parseUntil(ANY).sequence(END).endNode(); 094 // class 095 inState(ANY).sequence(CLASS, IDENTIFIER).optional(SEMICOLON).createNode(EShallowEntityType.TYPE, "class", 1) 096 .parseUntil(ANY).sequence(END).endNode(); 097 // module 098 inState(ANY).sequence(MODULE, IDENTIFIER).optional(SEMICOLON).createNode(EShallowEntityType.TYPE, "module", 1) 099 .parseUntil(ANY).sequence(END).endNode(); 100 } 101 102 /** Creates parsing rules for functions. */ 103 private void createFunctionRules() { 104 // named function/method 105 inState(ANY).sequence(DEF, IDENTIFIER).optional(SEMICOLON).createNode(EShallowEntityType.METHOD, "method", 1) 106 .skipNested(LPAREN, RPAREN).parseUntil(ANY).sequence(END).endNode(); 107 // static function/method 108 inState(ANY).sequence(DEF, SELF, DOT, IDENTIFIER).optional(SEMICOLON) 109 .createNode(EShallowEntityType.METHOD, "method", new Region(1, 3)).skipNested(LPAREN, RPAREN) 110 .parseUntil(ANY).sequence(END).endNode(); 111 } 112 113 /** Creates parsing rules for statements. */ 114 private void createStatementRules() { 115 // empty statement 116 inState(ANY).sequence(SEMICOLON).createNode(EShallowEntityType.STATEMENT, SubTypeNames.EMPTY_STATEMENT) 117 .endNode(); 118 119 // blocks 120 inState(ANY).sequence(DO).createNode(METHOD, "block").parseUntil(ANY).sequence(END).endNode(); 121 inState(ANY).sequence(LBRACE).createNode(METHOD, "block").parseUntil(ANY).sequence(RBRACE).endNode(); 122 123 // if statements 124 RecognizerBase<ERubyParserStates> ifAlternative = inState(ANY).sequence(EnumSet.of(IF, ELSIF, UNLESS)) 125 .createNode(EShallowEntityType.STATEMENT, 0).subRecognizer(new RubyBlockStartRecognizer(), 1, 1) 126 .parseUntil(ANY); 127 appendTrailingConditionRecognizer(ifAlternative.sequence(END)).endNode(); 128 ifAlternative.sequenceBefore(EnumSet.of(ELSE, ELSIF)).endNodeWithContinuation(); 129 appendTrailingConditionRecognizer(inState(ANY).sequence(ELSE) 130 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.ELSE).parseUntil(ANY).sequence(END)).endNode(); 131 132 // while/for/until statements 133 appendTrailingConditionRecognizer( 134 inState(ANY).sequence(EnumSet.of(WHILE, UNTIL, FOR)).createNode(EShallowEntityType.STATEMENT, 0) 135 .subRecognizer(new RubyBlockStartRecognizer(), 1, 1).parseUntil(ANY).sequence(END)).endNode(); 136 137 // begin/end statements 138 RecognizerBase<ERubyParserStates> beginAlternative = inState(ANY).sequence(EnumSet.of(BEGIN)) 139 .createNode(EShallowEntityType.STATEMENT, 0).parseUntil(ANY); 140 141 appendTrailingConditionRecognizer(beginAlternative.sequence(END)).endNode(); 142 beginAlternative.sequenceBefore(EnumSet.of(RESCUE, ENSURE)).endNodeWithContinuation(); 143 144 appendTrailingConditionRecognizer( 145 inState(ANY).sequence(ENSURE).createNode(EShallowEntityType.STATEMENT, 0).parseUntil(ANY).sequence(END)) 146 .endNode(); 147 148 RecognizerBase<ERubyParserStates> rescueAlternative = inState(ANY).sequenceBefore(RESCUE) 149 .createNode(EShallowEntityType.STATEMENT, 0).subRecognizer(new RubyBlockStartRecognizer(), 1, 1) 150 .parseUntil(ANY); 151 152 appendTrailingConditionRecognizer(rescueAlternative.sequence(END)).endNode(); 153 rescueAlternative.sequenceBefore(EnumSet.of(RESCUE, ENSURE)).endNodeWithContinuation(); 154 155 // TODO case/when statements 156 157 // simple statement 158 inState(ANY).createNode(EShallowEntityType.STATEMENT, SubTypeNames.SIMPLE_STATEMENT, 0) 159 .subRecognizer(new RubySimpleStatementRecognizer(), 1, 1).endNode(); 160 } 161 162 private RecognizerBase<ERubyParserStates> appendTrailingConditionRecognizer( 163 RecognizerBase<ERubyParserStates> recognizer) { 164 return recognizer 165 .subRecognizer(createRecognizer(start -> start.sequence(EnumSet.of(FOR, IF, UNLESS, WHILE, UNTIL))) 166 .subRecognizer(new RubyTrailingConditionRecognizer(), 1, 1), 0, Integer.MAX_VALUE); 167 } 168}