blob: b4005d35d2509cdfcdccc82cd578ec8075ad4745 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2007, 2020 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.r.core.rsource.ast;
import static org.eclipse.statet.ltk.ast.core.AstNode.NA_OFFSET;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS123_SYNTAX_SEQREL_UNEXPECTED;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS12_SYNTAX_TOKEN_UNEXPECTED;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS12_SYNTAX_TOKEN_UNKNOWN;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_CC_NOT_CLOSED;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_CONDITION_MISSING;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_CONDITION_NOT_CLOSED;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_ELEMENTNAME_MISSING;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_EXPR_AS_BODY_MISSING;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_EXPR_AS_CONDITION_MISSING;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_EXPR_BEFORE_OP_MISSING;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_FCALL_NOT_CLOSED;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_FDEF_ARGS_MISSING;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_FDEF_ARGS_NOT_CLOSED;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_IF_MISSING;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_IN_MISSING;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_OPERATOR_MISSING;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_SUBINDEXED_NOT_CLOSED;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS2_SYNTAX_SYMBOL_MISSING;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS3_FDEF;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS3_FOR;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS3_IF;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS3_WHILE;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUSFLAG_ERROR_IN_CHILD;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUSFLAG_REAL_ERROR;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUSFLAG_SUBSEQUENT;
import static org.eclipse.statet.r.core.rsource.RSourceConstants.STATUS_RUNTIME_ERROR;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.string.BasicStringFactory;
import org.eclipse.statet.jcommons.string.StringFactory;
import org.eclipse.statet.jcommons.text.core.input.TextParserInput;
import org.eclipse.statet.internal.r.core.RCorePlugin;
import org.eclipse.statet.ltk.ast.core.AstInfo;
import org.eclipse.statet.ltk.ast.core.AstNode;
import org.eclipse.statet.r.core.rlang.RTerminal;
import org.eclipse.statet.r.core.rsource.RLexer;
import org.eclipse.statet.r.core.rsource.ast.RAstNode.Assoc;
/**
* Scanner to create a R AST.
*/
public final class RScanner {
private static final byte LINE_MODE_CONSOLE= 1;
private static final byte LINE_MODE_BLOCK= 2;
private static final byte LINE_MODE_EAT= 3;
public static final int LEXER_CONFIG= RLexer.SKIP_WHITESPACE;
private static final class ExprContext {
final RAstNode rootNode;
final Expression rootExpr;
RAstNode lastNode;
Expression openExpr;
final byte lineMode;
public ExprContext(final RAstNode node, final Expression expr, final byte eatLines) {
this.rootNode= this.lastNode= node;
this.rootExpr= this.openExpr= expr;
this.lineMode= eatLines;
}
final void update(final RAstNode lastNode, final Expression openExpr) {
this.lastNode= lastNode;
if (openExpr == null || openExpr.node != null) {
this.openExpr= null;
}
else {
this.openExpr= openExpr;
}
}
}
private static final class RoxygenCollector {
private Comment[] lines= new Comment[64];
private int lineCount;
private DocuComment current;
void init() {
this.lineCount= 0;
this.current= null;
}
void add(final Comment comment) {
if (this.current == null) {
this.current= new DocuComment();
}
comment.rParent= this.current;
if (this.lineCount == this.lines.length) {
this.lines= Arrays.copyOf(this.lines, this.lineCount + 64);
}
this.lines[this.lineCount++]= comment;
}
boolean hasComment() {
return (this.current != null);
}
DocuComment finish(final RLexer lexer) {
final DocuComment comment= new DocuComment();
final Comment[] lines= Arrays.copyOf(this.lines, this.lineCount);
comment.lines= lines;
comment.startOffset= lines[0].startOffset;
comment.endOffset= lines[this.lineCount - 1].endOffset;
comment.nextOffset= (lexer != null && lexer.getType() != RTerminal.EOF) ? lexer.getOffset() : NA_OFFSET;
this.lineCount= 0;
this.current= null;
return comment;
}
}
private final static RScannerPostExprVisitor POST_VISITOR= new RScannerPostExprVisitor();
private final RLexer lexer;
private final int level;
private RTerminal nextType;
private boolean wasLinebreak;
private List<RAstNode> comments;
private RoxygenCollector roxygen;
private int commentsLevel;
private final boolean createText;
private final StringFactory symbolTextFactory;
public RScanner(final int level) {
this(level, (StringFactory) null);
}
public RScanner(final int level, final StringFactory symbolTextFactory) {
this(level, new RLexer((level == AstInfo.LEVEL_MINIMAL) ?
(RLexer.DEFAULT | LEXER_CONFIG | RLexer.ENABLE_QUICK_CHECK) :
(RLexer.DEFAULT | LEXER_CONFIG) ),
symbolTextFactory );
}
public RScanner(final int level, final RLexer lexer, final StringFactory symbolTextFactory) {
if (lexer == null) {
throw new NullPointerException("lexer"); //$NON-NLS-1$
}
this.symbolTextFactory= (symbolTextFactory != null) ? symbolTextFactory : BasicStringFactory.INSTANCE;
this.createText= ((level & AstInfo.DEFAULT_LEVEL_MASK) > AstInfo.LEVEL_MINIMAL);
this.level= level;
this.lexer= lexer;
}
public void setCommentLevel(final int level) {
this.commentsLevel= level;
if (level > 0) {
this.comments= new ArrayList<>();
if (level > 0x4) {
this.roxygen= new RoxygenCollector();
}
}
}
public int getAstLevel() {
return this.level;
}
@NonNullByDefault
public SourceComponent scanSourceUnit(final TextParserInput input) {
return scanSourceRange(input, null, false);
}
@NonNullByDefault
public SourceComponent scanSourceRange(final TextParserInput input,
final @Nullable AstNode parent, final boolean expand) {
try {
this.lexer.reset(input);
init();
final SourceComponent rootNode= scanSourceUnit((RAstNode)null, expand);
rootNode.parent= parent;
return rootNode;
}
catch (final Exception e) {
RCorePlugin.logError("Error occured while parsing R code", e);
final SourceComponent dummy= new SourceComponent();
dummy.status= STATUS_RUNTIME_ERROR;
if (this.commentsLevel > 0) {
dummy.comments= Collections.emptyList();
}
return dummy;
}
}
@NonNullByDefault
public SourceComponent scanSourceRange(final TextParserInput input,
final @Nullable AstNode parent) {
return scanSourceRange(input, parent, false);
}
@NonNullByDefault
public @Nullable RAstNode scanExpr(final TextParserInput input) {
try {
this.lexer.reset(input);
init();
final SourceComponent rootNode= scanSourceUnit((RAstNode) null, false);
if (rootNode.getChildCount() == 1) {
return rootNode.getChild(0);
}
}
catch (final Exception e) {
RCorePlugin.logError("Error occured while parsing R code", e);
}
return null;
}
@NonNullByDefault
public @Nullable FDef scanFDef(final TextParserInput input) {
try {
this.lexer.reset(input);
init();
if (this.nextType == RTerminal.FUNCTION) {
return scanFDef((ExprContext) null);
}
}
catch (final Exception e) {
RCorePlugin.logError("Error occured while parsing R code", e);
}
return null;
}
@NonNullByDefault
public FCall. @Nullable Args scanFCallArgs(final TextParserInput input, final boolean expand) {
try {
this.lexer.reset(input);
init();
final FCall call= new FCall();
call.endOffset= NA_OFFSET;
scanInSpecArgs(call.args);
if (expand) {
call.args.startOffset= input.getStartIndex();
call.args.endOffset= input.getIndex();
}
return call.args;
}
catch (final Exception e) {
RCorePlugin.logError("Error occured while parsing R code", e);
}
return null;
}
private void init() {
if (this.roxygen != null) {
this.roxygen.init();
}
this.nextType= RTerminal.LINEBREAK;
consumeToken();
}
final SourceComponent scanSourceUnit(final RAstNode parent, final boolean expand) {
final SourceComponent node= new SourceComponent();
node.rParent= parent;
scanInExprList(node, true);
// if (this.nextType == RTerminal.EOF) {
// this.next.type= null;
// }
if (this.commentsLevel > 0) {
node.comments= Collections.unmodifiableList(this.comments);
}
if (expand) {
node.startOffset= this.lexer.getInput().getStartIndex();
node.endOffset= this.lexer.getInput().getStopIndex();
}
else if (node.getChildCount() > 0) {
node.startOffset= node.getChild(0).startOffset;
node.endOffset= node.getChild(node.getChildCount() - 1).endOffset;
}
else {
node.startOffset= this.lexer.getInput().getStartIndex();
node.endOffset= node.startOffset;
}
return node;
}
final void scanInExprList(final ExpressionList node, final boolean script) {
ITER_TOKEN: while (true) {
switch (this.nextType) {
case EOF:
break ITER_TOKEN;
case LINEBREAK:
consumeToken();
continue ITER_TOKEN;
default:
{
Expression expr= node.appendNewExpr();
final ExprContext context= new ExprContext(node, expr,
script ? LINE_MODE_CONSOLE : LINE_MODE_BLOCK );
scanInExpression(context);
if (expr.node == null) {
node.expressions.remove(context.rootExpr);
expr= null;
}
else {
checkExpression(context);
}
switch (this.nextType) {
case SEMI:
if (expr != null) {
node.setSeparator(this.lexer.getOffset());
consumeToken();
continue ITER_TOKEN;
}
// else error like comma
//$FALL-THROUGH$
case COMMA:
{
expr= node.appendNewExpr();
expr.node= errorFromNext(node);
}
continue ITER_TOKEN;
case SUB_INDEXED_CLOSE:
case BLOCK_CLOSE:
case GROUP_CLOSE:
if (script) {
expr= node.appendNewExpr();
expr.node= errorFromNext(node);
continue ITER_TOKEN;
}
break ITER_TOKEN;
}
}
}
}
}
final int scanInGroup(final RAstNode node, final Expression expr) {
final ExprContext context= new ExprContext(node, expr, LINE_MODE_EAT);
scanInExpression(context);
return checkExpression(context);
}
final void scanInExpression(final ExprContext context) {
this.wasLinebreak= false;
ITER_TOKEN : while(true) {
if (this.wasLinebreak && context.lineMode < LINE_MODE_EAT && context.openExpr == null) {
break ITER_TOKEN;
}
switch (this.nextType) {
case LINEBREAK:
if (context.lineMode < LINE_MODE_EAT && context.openExpr == null) {
break ITER_TOKEN;
}
consumeToken();
continue ITER_TOKEN;
case SYMBOL:
case SYMBOL_G:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, createSymbol(null));
continue ITER_TOKEN;
case TRUE:
case FALSE:
case NUM_NUM:
case NUM_INT:
case NUM_CPLX:
case NA:
case NA_REAL:
case NA_INT:
case NA_CPLX:
case NA_CHAR:
case NAN:
case INF:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, createNumberConst(null));
continue ITER_TOKEN;
case NULL:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, createNullConst(null));
continue ITER_TOKEN;
case STRING_D:
case STRING_S:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, createStringConst(null));
continue ITER_TOKEN;
case ARROW_LEFT_S:
case ARROW_LEFT_D:
case ARROW_RIGHT_S:
case ARROW_RIGHT_D:
case EQUAL:
case COLON_EQUAL:
appendOp(context, createAssignment());
continue ITER_TOKEN;
case TILDE:
if (context.openExpr != null) {
appendNonOp(context, createModel());
}
else {
appendOp(context, createModel());
}
continue ITER_TOKEN;
case PLUS:
case MINUS:
if (context.openExpr != null) {
appendNonOp(context, createSign());
}
else {
appendOp(context, createArithmetic());
}
continue ITER_TOKEN;
case MULT:
case DIV:
appendOp(context, createArithmetic());
continue ITER_TOKEN;
case POWER:
appendOp(context, createPower());
continue ITER_TOKEN;
case SEQ:
appendOp(context, createSeq());
continue ITER_TOKEN;
case SPECIAL:
appendOp(context, createSpecial());
continue ITER_TOKEN;
case REL_LT:
case REL_LE:
case REL_EQ:
case REL_GE:
case REL_GT:
case REL_NE:
appendOp(context, createRelational());
continue ITER_TOKEN;
case OR:
case OR_D:
case AND:
case AND_D:
appendOp(context, createLogical());
continue ITER_TOKEN;
case NOT:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, createSign());
continue ITER_TOKEN;
case IF:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, scanCIf(context));
continue ITER_TOKEN;
case ELSE:
if (context.rootNode.getNodeType() == NodeType.C_IF) {
break ITER_TOKEN;
}
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, scanCElse(context));
continue ITER_TOKEN;
case FOR:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, scanCForLoop(context));
continue ITER_TOKEN;
case REPEAT:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, scanCRepeatLoop(context));
continue ITER_TOKEN;
case WHILE:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, scanCWhileLoop(context));
continue ITER_TOKEN;
case BREAK:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, createLoopCommand());
continue ITER_TOKEN;
case NEXT:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, createLoopCommand());
continue ITER_TOKEN;
case FUNCTION:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, scanFDef(context));
continue ITER_TOKEN;
case GROUP_OPEN:
if (context.openExpr != null) {
appendNonOp(context, scanGroup());
}
else {
appendOp(context, scanFCall());
}
continue ITER_TOKEN;
case BLOCK_OPEN:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, scanBlock());
continue ITER_TOKEN;
case EOF:
break ITER_TOKEN;
case NS_GET:
case NS_GET_INT:
if (this.wasLinebreak && context.openExpr == null) {
break ITER_TOKEN;
}
appendNonOp(context, scanNSGet(context));
continue ITER_TOKEN;
case SUB_INDEXED_S_OPEN:
case SUB_INDEXED_D_OPEN:
appendOp(context, scanSubIndexed(context));
continue ITER_TOKEN;
case SUB_NAMED_PART:
case SUB_NAMED_SLOT:
appendOp(context, scanSubNamed(context));
continue ITER_TOKEN;
case QUESTIONMARK:
if (context.openExpr != null) {
appendNonOp(context, createHelp());
continue ITER_TOKEN;
}
else {
appendOp(context, createHelp());
continue ITER_TOKEN;
}
case UNKNOWN:
case IN:
appendNonOp(context, errorFromNext(null));
continue ITER_TOKEN;
case COMMA:
case SEMI:
case SUB_INDEXED_CLOSE:
case BLOCK_CLOSE:
case GROUP_CLOSE:
break ITER_TOKEN;
default:
throw new IllegalStateException("Unhandled token in expr-scanner: "+this.nextType.name());
}
}
}
final Group scanGroup() {
final Group node= new Group();
setupFromSourceToken(node);
consumeToken();
scanInGroup(node, node.expr);
if (this.nextType == RTerminal.GROUP_CLOSE) {
node.groupCloseOffset= this.lexer.getOffset();
node.endOffset= node.groupCloseOffset + 1;
consumeToken();
return node;
}
else {
node.endOffset= this.lexer.getOffset();
node.status |= STATUS2_SYNTAX_CC_NOT_CLOSED;
return node;
}
}
final Block scanBlock() {
final Block node= new Block();
setupFromSourceToken(node);
consumeToken();
scanInExprList(node, false);
if (this.nextType == RTerminal.BLOCK_CLOSE) {
node.blockCloseOffset= this.lexer.getOffset();
node.endOffset= node.blockCloseOffset + 1;
consumeToken();
return node;
}
else {
node.endOffset= this.lexer.getOffset();
node.status |= STATUS2_SYNTAX_CC_NOT_CLOSED;
return node;
}
}
final NSGet scanNSGet(final ExprContext context) {
final NSGet node;
switch (this.nextType) {
case NS_GET:
node= new NSGet.Std();
break;
case NS_GET_INT:
node= new NSGet.Internal();
break;
default:
throw new IllegalStateException();
}
setupFromSourceToken(node);
node.operatorOffset= this.lexer.getOffset();
consumeToken();
// setup ns
switch (context.lastNode.getNodeType()) {
case SYMBOL:
case STRING_CONST:
{
node.namespace= (SingleValue) context.lastNode;
final RAstNode base= context.lastNode.rParent;
node.namespace.rParent= node;
final Expression expr= base.getExpr(node.namespace);
if (expr != null) {
expr.node= null;
}
else {
throw new IllegalStateException(); // ?
}
context.update(base, expr);
node.startOffset= node.namespace.startOffset;
break;
}
default:
node.namespace= errorNonExistingSymbol(node, node.startOffset, STATUS2_SYNTAX_ELEMENTNAME_MISSING);
break;
}
// element
switch (this.nextType) {
case STRING_S:
case STRING_D:
node.element= createStringConst(node);
node.endOffset= node.element.endOffset;
return node;
case SYMBOL:
case SYMBOL_G:
node.element= createSymbol(node);
node.endOffset= node.element.endOffset;
return node;
default:
node.element= errorNonExistingSymbol(node, node.endOffset, STATUS2_SYNTAX_ELEMENTNAME_MISSING);
return node;
}
}
final SubNamed scanSubNamed(final ExprContext context) {
final SubNamed node;
switch (this.nextType) {
case SUB_NAMED_PART:
node= new SubNamed.Named();
break;
case SUB_NAMED_SLOT:
node= new SubNamed.Slot();
break;
default:
throw new IllegalStateException();
}
setupFromSourceToken(node);
node.operatorOffset= this.lexer.getOffset();
consumeToken();
readLines();
switch (this.nextType) {
case STRING_S:
case STRING_D:
node.subname= createStringConst(node);
node.endOffset= node.subname.endOffset;
return node;
case SYMBOL:
case SYMBOL_G:
node.subname= createSymbol(node);
node.endOffset= node.subname.endOffset;
return node;
default:
node.subname= errorNonExistingSymbol(node, node.endOffset, STATUS2_SYNTAX_ELEMENTNAME_MISSING);
return node;
}
}
final SubIndexed scanSubIndexed(final ExprContext context) {
final SubIndexed node;
switch (this.nextType) {
case SUB_INDEXED_S_OPEN:
node= new SubIndexed.S();
break;
case SUB_INDEXED_D_OPEN:
node= new SubIndexed.D();
break;
default:
throw new IllegalStateException();
}
setupFromSourceToken(node);
node.openOffset= this.lexer.getOffset();
consumeToken();
readLines();
scanInSpecArgs(node.sublist);
if (this.nextType == RTerminal.SUB_INDEXED_CLOSE) {
node.closeOffset= this.lexer.getOffset();
consumeToken();
if (node.getNodeType() == NodeType.SUB_INDEXED_D) {
if (this.nextType == RTerminal.SUB_INDEXED_CLOSE) {
node.close2Offset= this.lexer.getOffset();
node.endOffset= node.close2Offset + 1;
consumeToken();
return node;
}
else {
node.endOffset= node.closeOffset + 1;
node.status |= STATUS2_SYNTAX_SUBINDEXED_NOT_CLOSED;
return node;
}
}
else {
node.endOffset= node.closeOffset + 1;
return node;
}
}
else {
node.endOffset= node.sublist.endOffset;
node.status |= STATUS2_SYNTAX_SUBINDEXED_NOT_CLOSED;
return node;
}
}
final CIfElse scanCIf(final ExprContext context) {
final CIfElse node= new CIfElse();
setupFromSourceToken(node);
consumeToken();
int ok= 0;
readLines();
if (this.nextType == RTerminal.GROUP_OPEN) {
node.condOpenOffset= this.lexer.getOffset();
node.endOffset= this.lexer.getOffset() + 1;
consumeToken();
readLines();
// condition
ok+= scanInGroup(node, node.condExpr);
if (this.nextType == RTerminal.GROUP_CLOSE) {
node.condCloseOffset= this.lexer.getOffset();
node.endOffset= node.condCloseOffset + 1;
consumeToken();
ok= 1;
readLines();
}
else {
node.endOffset= node.condExpr.node.endOffset;
node.status |= STATUS2_SYNTAX_CONDITION_NOT_CLOSED;
}
}
else {
node.status= STATUS2_SYNTAX_CONDITION_MISSING;
node.condExpr.node= errorNonExistExpression(node, node.endOffset,
(STATUS2_SYNTAX_EXPR_AS_CONDITION_MISSING | STATUSFLAG_SUBSEQUENT | STATUS3_IF));
}
// then
if (ok > 0 || recoverCCont()) {
final ExprContext thenContext= new ExprContext(node, node.thenExpr, context.lineMode);
scanInExpression(thenContext);
checkExpression(thenContext);
node.endOffset= node.thenExpr.node.endOffset;
if (context.lineMode >= LINE_MODE_BLOCK) {
readLines();
}
}
else {
node.thenExpr.node= errorNonExistExpression(node, node.condExpr.node.endOffset,
(STATUS2_SYNTAX_EXPR_AS_BODY_MISSING | STATUSFLAG_SUBSEQUENT | STATUS3_IF));
}
// else
if (this.nextType == RTerminal.ELSE) {
node.withElse= true;
node.elseOffset= this.lexer.getOffset();
consumeToken();
// else body is added via common expression processing
}
return node;
}
final CIfElse scanCElse(final ExprContext context) { // else without if
final CIfElse node= new CIfElse();
setupFromSourceToken(node);
node.status= STATUS2_SYNTAX_IF_MISSING;
node.condExpr.node= errorNonExistExpression(node, node.startOffset,
(STATUS2_SYNTAX_EXPR_AS_CONDITION_MISSING | STATUSFLAG_SUBSEQUENT | STATUS3_IF));
node.thenExpr.node= errorNonExistExpression(node, node.startOffset,
(STATUS2_SYNTAX_EXPR_AS_BODY_MISSING | STATUSFLAG_SUBSEQUENT | STATUS3_IF));
node.elseOffset= this.lexer.getOffset();
node.withElse= true;
consumeToken();
return node;
}
final CForLoop scanCForLoop(final ExprContext context) {
final CForLoop node= new CForLoop();
setupFromSourceToken(node);
consumeToken();
int ok= 0;
readLines();
if (this.nextType == RTerminal.GROUP_OPEN) {
node.condOpenOffset= this.lexer.getOffset();
consumeToken();
readLines();
// condition
switch (this.nextType) {
case SYMBOL:
case SYMBOL_G:
node.varSymbol= createSymbol(node);
readLines();
break;
default:
node.varSymbol= errorNonExistingSymbol(node, node.condOpenOffset + 1, STATUS2_SYNTAX_SYMBOL_MISSING);
ok--;
break;
}
if (this.nextType == RTerminal.IN) {
node.inOffset= this.lexer.getOffset();
node.endOffset= node.inOffset + 2;
consumeToken();
readLines();
ok+= scanInGroup(node, node.condExpr);
}
else {
node.endOffset= node.varSymbol.endOffset;
node.status |= (ok >= 0) ? STATUS2_SYNTAX_IN_MISSING :
(STATUS2_SYNTAX_IN_MISSING | STATUSFLAG_SUBSEQUENT);
node.condExpr.node= errorNonExistExpression(node, node.endOffset,
(STATUS2_SYNTAX_EXPR_AS_CONDITION_MISSING | STATUSFLAG_SUBSEQUENT));
}
if (this.nextType == RTerminal.GROUP_CLOSE) {
node.condCloseOffset= this.lexer.getOffset();
node.endOffset= node.condCloseOffset + 1;
consumeToken();
ok= 1;
readLines();
}
else {
node.endOffset= node.condExpr.node.endOffset;
if ((node.status & STATUSFLAG_REAL_ERROR) == 0) {
node.status |= (ok >= 0) ? STATUS2_SYNTAX_CONDITION_NOT_CLOSED :
(STATUS2_SYNTAX_CONDITION_NOT_CLOSED | STATUSFLAG_SUBSEQUENT);
}
}
}
else { // missing GROUP_OPEN
node.status= STATUS2_SYNTAX_CONDITION_MISSING;
node.varSymbol= errorNonExistingSymbol(node, node.endOffset,
STATUS2_SYNTAX_SYMBOL_MISSING | STATUSFLAG_SUBSEQUENT);
node.condExpr.node= errorNonExistExpression(node, node.endOffset,
(STATUS2_SYNTAX_EXPR_AS_CONDITION_MISSING | STATUSFLAG_SUBSEQUENT | STATUS3_FOR));
}
// loop
if (ok <= 0 && !recoverCCont()) {
node.loopExpr.node= errorNonExistExpression(node, node.endOffset,
(STATUS2_SYNTAX_EXPR_AS_BODY_MISSING | STATUSFLAG_SUBSEQUENT | STATUS3_FOR));
}
return node;
}
final CWhileLoop scanCWhileLoop(final ExprContext context) {
final CWhileLoop node= new CWhileLoop();
setupFromSourceToken(node);
consumeToken();
int ok= 0;
readLines();
if (this.nextType == RTerminal.GROUP_OPEN) {
node.condOpenOffset= this.lexer.getOffset();
node.endOffset= node.condOpenOffset + 1;
consumeToken();
readLines();
// condition
ok+= scanInGroup(node, node.condExpr);
if (this.nextType == RTerminal.GROUP_CLOSE) {
node.condCloseOffset= this.lexer.getOffset();
node.endOffset= node.condCloseOffset + 1;
consumeToken();
ok= 1;
readLines();
}
else {
node.endOffset= node.condExpr.node.endOffset;
node.status= (ok >= 0) ? STATUS2_SYNTAX_CONDITION_NOT_CLOSED :
(STATUS2_SYNTAX_CONDITION_NOT_CLOSED | STATUSFLAG_SUBSEQUENT);
}
}
else {
node.status= STATUS2_SYNTAX_CONDITION_MISSING;
node.condExpr.node= errorNonExistExpression(node, node.endOffset,
(STATUS2_SYNTAX_EXPR_AS_CONDITION_MISSING | STATUSFLAG_SUBSEQUENT | STATUS3_WHILE));
}
// loop
if (ok <= 0 && !recoverCCont()) {
node.loopExpr.node= errorNonExistExpression(node, node.endOffset,
(STATUS2_SYNTAX_EXPR_AS_BODY_MISSING | STATUSFLAG_SUBSEQUENT | STATUS3_WHILE));
}
return node;
}
final CRepeatLoop scanCRepeatLoop(final ExprContext context) {
final CRepeatLoop node= new CRepeatLoop();
setupFromSourceToken(node);
consumeToken();
return node;
}
final FDef scanFDef(final ExprContext context) {
final FDef node= new FDef();
setupFromSourceToken(node);
consumeToken();
int ok= 0;
readLines();
if (this.nextType == RTerminal.GROUP_OPEN) {
node.argsOpenOffset= this.lexer.getOffset();
node.endOffset= node.argsOpenOffset + 1;
consumeToken();
readLines();
// args
scanInFDefArgs(node.args);
if (this.nextType == RTerminal.GROUP_CLOSE) {
node.argsCloseOffset= this.lexer.getOffset();
node.endOffset= node.argsCloseOffset + 1;
consumeToken();
ok= 1;
readLines();
}
else {
node.endOffset= node.args.endOffset;
node.status |= STATUS2_SYNTAX_FDEF_ARGS_NOT_CLOSED;
}
}
else {
node.args.startOffset= node.args.endOffset= node.endOffset;
node.status= STATUS2_SYNTAX_FDEF_ARGS_MISSING;
}
// body
if (ok <= 0 && !recoverCCont()) {
node.expr.node= errorNonExistExpression(node, node.endOffset,
(STATUS2_SYNTAX_EXPR_AS_BODY_MISSING | STATUSFLAG_SUBSEQUENT | STATUS3_FDEF));
}
return node;
}
final FCall scanFCall() {
final FCall node= new FCall();
setupFromSourceToken(node);
node.argsOpenOffset= this.lexer.getOffset();
consumeToken();
readLines();
scanInSpecArgs(node.args);
if (this.nextType == RTerminal.GROUP_CLOSE) {
node.argsCloseOffset= this.lexer.getOffset();
node.endOffset= node.argsCloseOffset + 1;
consumeToken();
}
else {
node.endOffset= node.args.endOffset;
node.status |= STATUS2_SYNTAX_FCALL_NOT_CLOSED;
}
return node;
}
final void scanInFDefArgs(final FDef.Args args) {
args.startOffset= args.endOffset= args.rParent.endOffset;
ITER_ARGS : while (true) {
final FDef.Arg arg= new FDef.Arg(args);
switch(this.nextType) {
case SYMBOL:
case SYMBOL_G:
arg.argName= createSymbol(arg);
arg.startOffset= arg.argName.startOffset;
arg.endOffset= arg.argName.endOffset;
readLines();
break;
case EQUAL:
case COMMA:
arg.startOffset= arg.endOffset= this.lexer.getOffset();
break;
default:
if (args.specs.isEmpty()) {
return;
}
arg.startOffset= arg.endOffset= args.endOffset;
break;
}
if (arg.argName == null) {
arg.argName= errorNonExistingSymbol(arg, arg.endOffset, STATUS2_SYNTAX_SYMBOL_MISSING);
}
if (this.nextType == RTerminal.EQUAL) {
arg.endOffset= this.lexer.getOffset() + 1;
consumeToken();
final Expression expr= arg.addDefault();
scanInGroup(arg, expr);
arg.endOffset= arg.defaultExpr.node.endOffset;
}
args.specs.add(arg);
args.status= POST_VISITOR.checkTerminal(arg);
if (this.nextType == RTerminal.COMMA) {
args.endOffset= this.lexer.getOffset() + 1;
consumeToken();
readLines();
continue ITER_ARGS;
}
else {
args.startOffset= args.specs.get(0).startOffset;
args.endOffset= arg.endOffset;
return;
}
}
}
final void scanInSpecArgs(final FCall.Args args) {
args.startOffset= args.endOffset= args.rParent.endOffset;
ITER_ARGS : while (true) {
final FCall.Arg arg= new FCall.Arg(args);
arg.startOffset= this.lexer.getOffset();
switch(this.nextType) {
case SYMBOL:
arg.argName= createSymbol(arg);
readLines();
break;
case STRING_S:
case STRING_D:
arg.argName= createStringConst(arg);
readLines();
break;
case NULL:
arg.argName= createNullConst(arg);
readLines();
break;
case EQUAL:
arg.argName= errorNonExistingSymbol(arg, this.lexer.getOffset(), STATUS2_SYNTAX_ELEMENTNAME_MISSING);
break;
default:
break;
}
if (arg.argName != null) {
if (this.nextType == RTerminal.EQUAL) {
arg.equalsOffset= this.lexer.getOffset();
arg.endOffset= arg.equalsOffset + 1;
consumeToken();
final ExprContext valueContext= new ExprContext(arg, arg.valueExpr, LINE_MODE_EAT);
scanInExpression(valueContext);
if (arg.valueExpr.node != null) { // empty items are allowed
checkExpression(valueContext);
arg.endOffset= arg.valueExpr.node.endOffset;
}
}
else {
// argName -> valueExpr
arg.valueExpr.node= arg.argName;
arg.argName= null;
final ExprContext valueContext= new ExprContext(arg, arg.valueExpr, LINE_MODE_EAT);
valueContext.update(arg.valueExpr.node, null);
scanInExpression(valueContext);
checkExpression(valueContext);
arg.endOffset= arg.valueExpr.node.endOffset;
}
}
else {
final ExprContext valueContext= new ExprContext(arg, arg.valueExpr, LINE_MODE_EAT);
scanInExpression(valueContext);
if (arg.valueExpr.node != null) { // empty items are allowed
checkExpression(valueContext);
arg.endOffset= arg.valueExpr.node.endOffset;
}
else {
arg.startOffset= arg.endOffset= args.endOffset;
}
}
if (this.nextType == RTerminal.COMMA) {
args.specs.add(arg);
args.status= POST_VISITOR.checkTerminal(arg);
args.sepList.add(this.lexer.getOffset());
args.endOffset= this.lexer.getOffset() + 1;
consumeToken();
readLines();
continue ITER_ARGS;
}
// last arg before )
if (args.specs.isEmpty() && !arg.hasChildren()) {
return;
}
args.specs.add(arg);
args.status= POST_VISITOR.checkTerminal(arg);
args.startOffset= args.specs.get(0).startOffset;
args.endOffset= arg.endOffset;
return;
}
}
final void scanInSpecArgs(final SubIndexed.Args args) {
args.startOffset= args.endOffset= args.rParent.endOffset;
ITER_ARGS : while (true) {
final SubIndexed.Arg arg= new SubIndexed.Arg(args);
arg.startOffset= this.lexer.getOffset();
switch(this.nextType) {
case SYMBOL:
arg.argName= createSymbol(arg);
readLines();
break;
case STRING_S:
case STRING_D:
arg.argName= createStringConst(arg);
readLines();
break;
case NULL:
arg.argName= createNullConst(arg);
readLines();
break;
case EQUAL:
arg.argName= errorNonExistingSymbol(arg, this.lexer.getOffset(), STATUS2_SYNTAX_ELEMENTNAME_MISSING);
break;
default:
break;
}
if (arg.argName != null) {
if (this.nextType == RTerminal.EQUAL) {
arg.equalsOffset= this.lexer.getOffset();
arg.endOffset= arg.equalsOffset + 1;
consumeToken();
final ExprContext valueContext= new ExprContext(arg, arg.valueExpr, LINE_MODE_EAT);
scanInExpression(valueContext);
if (arg.valueExpr.node != null) { // empty items are allowed
checkExpression(valueContext);
arg.endOffset= arg.valueExpr.node.endOffset;
}
}
else {
// argName -> valueExpr
arg.valueExpr.node= arg.argName;
arg.argName= null;
final ExprContext valueContext= new ExprContext(arg, arg.valueExpr, LINE_MODE_EAT);
valueContext.update(arg.valueExpr.node, null);
scanInExpression(valueContext);
checkExpression(valueContext);
arg.endOffset= arg.valueExpr.node.endOffset;
}
}
else {
final ExprContext valueContext= new ExprContext(arg, arg.valueExpr, LINE_MODE_EAT);
scanInExpression(valueContext);
if (arg.valueExpr.node != null) { // empty items are allowed
checkExpression(valueContext);
arg.endOffset= arg.valueExpr.node.endOffset;
}
else {
arg.startOffset= arg.endOffset= args.endOffset;
}
}
if (this.nextType == RTerminal.COMMA) {
args.specs.add(arg);
args.status= POST_VISITOR.checkTerminal(arg);
args.endOffset= this.lexer.getOffset() + 1;
consumeToken();
readLines();
continue ITER_ARGS;
}
// last arg before )
if (args.specs.isEmpty() && !arg.hasChildren()) {
return;
}
args.specs.add(arg);
args.status= POST_VISITOR.checkTerminal(arg);
args.startOffset= args.specs.get(0).startOffset;
args.endOffset= arg.endOffset;
return;
}
}
final boolean recoverCCont() {
return !this.wasLinebreak
&& (this.nextType == RTerminal.SYMBOL || this.nextType == RTerminal.SYMBOL_G || this.nextType == RTerminal.BLOCK_OPEN);
}
final void appendNonOp(final ExprContext context, final RAstNode newNode) {
if (context.openExpr != null) {
newNode.rParent= context.lastNode;
context.openExpr.node= newNode;
}
else {
// setup missing op
final Dummy.Operator error= new Dummy.Operator(STATUS2_SYNTAX_OPERATOR_MISSING);
error.rParent= context.rootNode;
error.leftExpr.node= context.rootExpr.node;
error.startOffset= error.endOffset= newNode.startOffset;
context.rootExpr.node= error;
// append news
newNode.rParent= error;
error.rightExpr.node= newNode;
context.rootExpr.node= error;
}
context.update(newNode, newNode.getRightExpr());
return;
}
final void appendOp(final ExprContext context, final RAstNode newNode) {
if (context.openExpr != null) {
context.openExpr.node= errorNonExistExpression(context.lastNode, newNode.startOffset, STATUS2_SYNTAX_EXPR_BEFORE_OP_MISSING);
context.update(context.openExpr.node, null);
}
final int newP= newNode.getNodeType().opPrec;
RAstNode left= context.lastNode;
RAstNode cand= context.lastNode;
ITER_CAND : while (cand != null && cand != context.rootNode) {
final NodeType candType= cand.getNodeType();
if (candType.opPrec == newP) {
switch (candType.opAssoc) {
case Assoc.NOSTD:
left= cand;
if ((newNode.status & STATUSFLAG_REAL_ERROR) == 0) {
newNode.status= STATUS123_SYNTAX_SEQREL_UNEXPECTED;
}
break ITER_CAND;
case Assoc.LEFTSTD:
left= cand;
break ITER_CAND;
case Assoc.RIGHTSTD:
default:
break ITER_CAND;
}
}
if (candType.opPrec > newP) {
break ITER_CAND;
}
left= cand;
cand= cand.rParent;
}
final RAstNode baseNode= left.rParent;
if (baseNode == null) {
throw new IllegalStateException(); // DEBUG
}
final Expression baseExpr= baseNode.getExpr(left);
newNode.getLeftExpr().node= left;
left.rParent= newNode;
baseExpr.node= newNode;
newNode.rParent= baseNode;
context.update(newNode, newNode.getRightExpr());
return;
}
Dummy.Terminal errorNonExistExpression(final RAstNode parent, final int stopHint, final int status) {
final Dummy.Terminal error= new Dummy.Terminal(status);
error.rParent= parent;
error.startOffset= error.endOffset= (stopHint != NA_OFFSET) ? stopHint : parent.endOffset;
error.text= ""; //$NON-NLS-1$
parent.status |= STATUSFLAG_ERROR_IN_CHILD;
return error;
}
Dummy.Terminal errorFromNext(final RAstNode parent) {
final Dummy.Terminal error= new Dummy.Terminal((this.nextType == RTerminal.UNKNOWN) ?
STATUS12_SYNTAX_TOKEN_UNKNOWN : STATUS12_SYNTAX_TOKEN_UNEXPECTED);
error.rParent= parent;
error.startOffset= this.lexer.getOffset();
error.endOffset= this.lexer.getOffset() + this.lexer.getLength();
if (this.createText) {
error.text= this.lexer.getText();
}
consumeToken();
if (parent != null) {
parent.status |= STATUSFLAG_ERROR_IN_CHILD;
}
return error;
}
Symbol errorNonExistingSymbol(final RAstNode parent, final int offset, final int status) {
final Symbol error= new Symbol.Std();
error.rParent= parent;
error.startOffset= error.endOffset= offset;
error.text= ""; //$NON-NLS-1$
error.status= status;
parent.status |= STATUSFLAG_ERROR_IN_CHILD;
return error;
}
protected Symbol createSymbol(final RAstNode parent) {
final Symbol symbol;
switch (this.nextType) {
case SYMBOL_G:
symbol= new Symbol.G();
break;
case SYMBOL:
symbol= new Symbol.Std();
break;
default:
throw new IllegalStateException();
}
symbol.rParent= parent;
setupFromSourceToken(symbol);
if (parent != null) {
parent.status |= POST_VISITOR.checkTerminal(symbol);
}
consumeToken();
return symbol;
}
protected NumberConst createNumberConst(final RAstNode parent) {
final NumberConst num= new NumberConst(this.nextType);
num.rParent= parent;
setupFromSourceToken(num);
consumeToken();
return num;
}
protected NullConst createNullConst(final RAstNode parent) {
final NullConst num= new NullConst();
num.rParent= parent;
setupFromSourceToken(num);
consumeToken();
return num;
}
protected StringConst createStringConst(final RAstNode parent) {
final StringConst str;
switch (this.nextType) {
case STRING_D:
str= new StringConst.D();
break;
case STRING_S:
str= new StringConst.S();
break;
default:
throw new IllegalStateException();
}
str.rParent= parent;
setupFromSourceToken(str);
consumeToken();
return str;
}
protected Assignment createAssignment() {
Assignment node;
switch (this.nextType) {
case ARROW_LEFT_S:
node= new Assignment.LeftS();
break;
case ARROW_LEFT_D:
node= new Assignment.LeftD();
break;
case ARROW_RIGHT_S:
node= new Assignment.RightS();
break;
case ARROW_RIGHT_D:
node= new Assignment.RightD();
break;
case EQUAL:
node= new Assignment.LeftE();
break;
case COLON_EQUAL:
node= new Assignment.LeftC();
break;
default:
throw new IllegalStateException();
}
setupFromSourceToken(node);
consumeToken();
return node;
}
protected Model createModel() {
final Model node= new Model();
setupFromSourceToken(node);
consumeToken();
return node;
}
protected CLoopCommand createLoopCommand() {
final CLoopCommand node;
switch (this.nextType) {
case NEXT:
node= new CLoopCommand.Next();
break;
case BREAK:
node= new CLoopCommand.Break();
break;
default:
throw new IllegalStateException();
}
setupFromSourceToken(node);
consumeToken();
return node;
}
protected Sign createSign() {
final Sign node;
switch (this.nextType) {
case PLUS:
node= new Sign.PlusSign();
break;
case MINUS:
node= new Sign.MinusSign();
break;
case NOT:
node= new Sign.Not();
break;
default:
throw new IllegalStateException();
}
setupFromSourceToken(node);
consumeToken();
return node;
}
protected Arithmetic createArithmetic() {
final Arithmetic node;
switch (this.nextType) {
case PLUS:
node= new Arithmetic.Plus();
break;
case MINUS:
node= new Arithmetic.Minus();
break;
case MULT:
node= new Arithmetic.Mult();
break;
case DIV:
node= new Arithmetic.Div();
break;
default:
throw new IllegalStateException();
}
setupFromSourceToken(node);
consumeToken();
return node;
}
protected Power createPower() {
final Power node= new Power();
setupFromSourceToken(node);
consumeToken();
return node;
}
protected Seq createSeq() {
final Seq node= new Seq();
setupFromSourceToken(node);
consumeToken();
return node;
}
protected Special createSpecial() {
final Special node= new Special();
setupFromSourceToken(node);
if (this.createText) {
node.qualifier= this.lexer.getText(this.symbolTextFactory);
}
consumeToken();
return node;
}
protected Relational createRelational() {
final Relational node;
switch (this.nextType) {
case REL_LT:
node= new Relational.LT();
break;
case REL_LE:
node= new Relational.LE();
break;
case REL_EQ:
node= new Relational.EQ();
break;
case REL_GE:
node= new Relational.GE();
break;
case REL_GT:
node= new Relational.GT();
break;
case REL_NE:
node= new Relational.NE();
break;
default:
throw new IllegalStateException();
}
setupFromSourceToken(node);
consumeToken();
return node;
}
protected Logical createLogical() {
final Logical node;
switch (this.nextType) {
case AND:
node= new Logical.And();
break;
case AND_D:
node= new Logical.AndD();
break;
case OR:
node= new Logical.Or();
break;
case OR_D:
node= new Logical.OrD();
break;
default:
throw new IllegalStateException();
}
setupFromSourceToken(node);
consumeToken();
return node;
}
protected Help createHelp() {
final Help node= new Help();
setupFromSourceToken(node);
consumeToken();
return node;
}
private final void setupFromSourceToken(final RAstNode node) {
node.startOffset= this.lexer.getOffset();
node.endOffset= this.lexer.getOffset() + this.lexer.getLength();
node.status= this.lexer.getFlags();
}
private final void setupFromSourceToken(final Symbol node) {
node.startOffset= this.lexer.getOffset();
node.endOffset= this.lexer.getOffset() + this.lexer.getLength();
if (this.createText) {
node.text= this.lexer.getText(this.symbolTextFactory);
if (this.lexer.getStatusDetail() != null) {
node.addAttachment(this.lexer.getStatusDetail());
}
}
node.status= this.lexer.getFlags();
}
private final void setupFromSourceToken(final SingleValue node) {
node.startOffset= this.lexer.getOffset();
node.endOffset= this.lexer.getOffset() + this.lexer.getLength();
if (this.createText) {
node.text= this.lexer.getText();
if (this.lexer.getStatusDetail() != null) {
node.addAttachment(this.lexer.getStatusDetail());
}
}
node.status= this.lexer.getFlags();
}
private final int checkExpression(final ExprContext context) {
int state= 0;
if (context.openExpr != null && context.openExpr.node == null) {
context.openExpr.node= errorNonExistExpression(context.lastNode, context.lastNode.endOffset,
context.lastNode.getMissingExprStatus(context.openExpr));
state= -1;
}
context.rootNode.status |= POST_VISITOR.check(context.rootExpr.node);
return state;
}
private final void readLines() {
while (this.nextType == RTerminal.LINEBREAK) {
consumeToken();
}
}
private final void consumeToken() {
this.wasLinebreak= (this.nextType == RTerminal.LINEBREAK);
this.nextType= this.lexer.next();
switch (this.nextType) {
case COMMENT:
case ROXYGEN_COMMENT:
if (this.commentsLevel > 0x4) {
consumeCommentWithRoxygen();
}
else {
consumeComment();
}
return;
default:
return;
}
}
private void consumeCommentWithRoxygen() {
while (true) {
final Comment comment;
switch (this.nextType) {
case COMMENT:
if (this.roxygen.hasComment()) {
this.comments.add(this.roxygen.finish(this.lexer));
}
comment= new Comment.CommonLine();
setupFromSourceToken(comment);
this.comments.add(comment);
this.nextType= this.lexer.next();
continue;
case ROXYGEN_COMMENT:
comment= new Comment.RoxygenLine();
setupFromSourceToken(comment);
this.roxygen.add(comment);
this.nextType= this.lexer.next();
continue;
case LINEBREAK:
this.nextType= this.lexer.next();
if (this.nextType == RTerminal.LINEBREAK && this.roxygen.hasComment()) {
this.comments.add(this.roxygen.finish(null));
}
continue;
default:
if (this.roxygen.hasComment()) {
this.comments.add(this.roxygen.finish(this.lexer));
}
this.wasLinebreak= true;
return;
}
}
}
private void consumeComment() {
while (true) {
switch (this.nextType) {
case COMMENT:
case ROXYGEN_COMMENT:
if (this.commentsLevel > 0) {
final Comment comment= (this.nextType == RTerminal.ROXYGEN_COMMENT) ?
new Comment.RoxygenLine() :
new Comment.CommonLine();
setupFromSourceToken(comment);
this.comments.add(comment);
} // no break
this.nextType= this.lexer.next();
continue;
case LINEBREAK:
this.nextType= this.lexer.next();
continue;
default:
this.wasLinebreak= true;
return;
}
}
}
}