package com.bitbakery.plugin.arc.psi; /* * Copyright (c) Kurt Christensen, 2009 * * Licensed under the Artistic 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.opensource.org/licenses/artistic-license-2.0.php * * 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.. */ import static com.bitbakery.plugin.arc.lexer.ArcTokenTypes.*; import static com.bitbakery.plugin.arc.psi.ArcElementTypes.*; import com.intellij.lang.ASTNode; import com.intellij.lang.PsiBuilder; import com.intellij.lang.PsiParser; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.util.containers.Stack; import org.jetbrains.annotations.NotNull; /** * Walk through the token stream of a Arc source file and generate the appropriate PSI tree. */ public class ArcParser implements PsiParser { private Stack<PsiBuilder.Marker> markers = new Stack<PsiBuilder.Marker>(); private PsiBuilder builder; @NotNull public ASTNode parse(IElementType root, PsiBuilder builder) { this.builder = builder; // TODO - Delete me! builder.setDebugMode(true); final PsiBuilder.Marker rootMarker = builder.mark(); try { while (!builder.eof()) { parseNext(); } } catch (EofException e) { while (!markers.empty()) { drop(); } } rootMarker.done(root); return builder.getTreeBuilt(); } private void parseNext() { if (isAt(LEFT_PAREN)) { parseExpression(); } else if (isAt(LEFT_SQUARE)) { parseSingleArgFn(); } else if (isAt(SYMBOL)) { markAndAdvance(VARIABLE_REFERENCE); } else if (isAtMacroTemplateToken()) { advance(); } else { // TODO - We can actually have error conditions at this point - for example, when parseNext gets called, we're not expecting a right paren, but we could get one! markAndAdvance(LITERAL); } } private void parseSingleArgFn() { markAndAdvance(); parseBody(RIGHT_SQUARE); done(SINGLE_ARG_ANONYMOUS_FUNCTION_DEFINITION); } private void parseExpression() { markAndAdvance(); IElementType type = getExpressionType(); if (isAt(DEF) || isAt(MAC)) { advance(); // Advance past def/mac token parseName(); parseParameters(); parseDocstring(); } else if (isAt(EQ)) { advance(); // Advance past = token if (isAt (SYMBOL)) { markAndAdvance(VARIABLE_DEFINITION); } else { type = EXPRESSION; } } else if (isAt(IF)) { advance(); // Advance past if token } else if (isAt(LET)) { advance(); // Advance past let token parseBinding(); } else if (isAt(WITH)) { advance(); // Advance past with token if (isAt(LEFT_PAREN)) { advance(); while (!isAt(RIGHT_PAREN)) { parseBinding(); } advance(); } } parseBody(RIGHT_PAREN); done(type); } private boolean isAtMacroTemplateToken() { return isAt(BACKQUOTE) || isAt(QUOTE) || isAt(COMMA) || isAt(COMMA_AT); } private void parseBinding() { if (isAtMacroTemplateToken()) { advance(); } if (isAt(LEFT_PAREN)) { parseExpression(); } else { parseName(); } parseNext(); } private void parseBody(IElementType teminator) { while (!isAt(teminator)) { parseNext(); } advance(); } private void parseName() { if (isAtMacroTemplateToken()) { advance(); } if (isAt (SYMBOL) || isIn(KEYWORDS) || isIn(SPECIAL_CHARACTERS)) { // Arc will blissfully let us redefine... anything! markAndAdvance(VARIABLE_DEFINITION); } else { builder.error("Expected identifier"); // TODO - Prop-ify me!! } } private void parseParameters() { mark(); if (isAt(SYMBOL)) { markAndAdvance(REST_PARAMETER); } else if (isAt(LEFT_PAREN)) { advance(); while (!isAt(RIGHT_PAREN)) { parseParameter(); } advance(); } else { advance(); builder.error("Expected parameter"); } done(PARAMETER_LIST); } private void parseParameter() { if (isAtMacroTemplateToken()) { advance(); } if (isAt(SYMBOL)) { markAndAdvance(PARAMETER); } else if (isAt(LEFT_PAREN)) { parseOptionalParameter(); } else if (isAt(DOT)) { advance(); markAndAdvance(REST_PARAMETER); } else { advance(); builder.error("Expected parameter"); } } private void parseOptionalParameter() { markAndAdvance(); // Advance past left paren advance(); // Advance past 'o' token parseName(); parseBody(RIGHT_PAREN); done(OPTIONAL_PARAMETER); } private void parseDocstring() { if (isAt(STRING_LITERAL)) { markAndAdvance(); // If the string is the *entire* body of a def/mac/fn, then it is *not* a docstring... if (isAt(RIGHT_PAREN)) { drop(); } else { done(DOCSTRING); } } } private void done(IElementType type) { try { markers.pop().done(type); } catch (RuntimeException e) { throw new RuntimeException(builder.getTokenText(), e); } } private void drop() { try { markers.pop().drop(); } catch (RuntimeException e) { throw new RuntimeException(builder.getTokenText(), e); } } private void mark() { markers.push(builder.mark()); } private void advance() { if (builder.eof()) { throw new EofException(); } builder.advanceLexer(); } private void markAndAdvance(IElementType type) { markAndAdvance(); done(type); } private void markAndAdvance() { mark(); advance(); } private boolean isAt(IElementType token) { return builder.getTokenType() == token; } private boolean isIn(TokenSet set) { return set.contains(builder.getTokenType()); } private IElementType getExpressionType() { if (isAt(DEF)) { return FUNCTION_DEFINITION; } else if (isAt(MAC)) { return MACRO_DEFINITION; } else if (isAt(FN)) { return ANONYMOUS_FUNCTION_DEFINITION; } else if (isAt(EQ)) { return VARIABLE_ASSIGNMENT; } else if (isAt(IF)) { return IF_BLOCK; } else if (isAt(LET)) { return LET_BLOCK; } else if (isAt(WITH)) { return WITH_BLOCK; } return EXPRESSION; } private class EofException extends RuntimeException { } }