1 /* 2 * Copyright (C) 2018 Google, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.escapevelocity; 17 18 import com.google.common.collect.ImmutableList; 19 import java.util.List; 20 21 /** 22 * A parsing node that will be deleted during the construction of the parse tree, to be replaced 23 * by a higher-level construct such as {@link DirectiveNode.IfNode}. See {@link Parser#parse()} 24 * for a description of the way these tokens work. 25 * 26 * @author emcmanus@google.com (Éamonn McManus) 27 */ 28 abstract class TokenNode extends Node { TokenNode(String resourceName, int lineNumber)29 TokenNode(String resourceName, int lineNumber) { 30 super(resourceName, lineNumber); 31 } 32 33 /** 34 * This method always throws an exception because a node like this should never be found in the 35 * final parse tree. 36 */ evaluate(EvaluationContext vars)37 @Override Object evaluate(EvaluationContext vars) { 38 throw new UnsupportedOperationException(getClass().getName()); 39 } 40 41 /** 42 * The name of the token, for use in parse error messages. 43 */ name()44 abstract String name(); 45 46 /** 47 * A synthetic node that represents the end of the input. This node is the last one in the 48 * initial token string and also the last one in the parse tree. 49 */ 50 static final class EofNode extends TokenNode { EofNode(String resourceName, int lineNumber)51 EofNode(String resourceName, int lineNumber) { 52 super(resourceName, lineNumber); 53 } 54 55 @Override name()56 String name() { 57 return "end of file"; 58 } 59 } 60 61 static final class EndTokenNode extends TokenNode { EndTokenNode(String resourceName, int lineNumber)62 EndTokenNode(String resourceName, int lineNumber) { 63 super(resourceName, lineNumber); 64 } 65 name()66 @Override String name() { 67 return "#end"; 68 } 69 } 70 71 /** 72 * A node in the parse tree representing a comment. Comments are introduced by {@code ##} and 73 * extend to the end of the line. The only reason for recording comment nodes is so that we can 74 * skip space between a comment and a following {@code #set}, to be compatible with Velocity 75 * behaviour. 76 */ 77 static class CommentTokenNode extends TokenNode { CommentTokenNode(String resourceName, int lineNumber)78 CommentTokenNode(String resourceName, int lineNumber) { 79 super(resourceName, lineNumber); 80 } 81 name()82 @Override String name() { 83 return "##"; 84 } 85 } 86 87 abstract static class IfOrElseIfTokenNode extends TokenNode { 88 final ExpressionNode condition; 89 IfOrElseIfTokenNode(ExpressionNode condition)90 IfOrElseIfTokenNode(ExpressionNode condition) { 91 super(condition.resourceName, condition.lineNumber); 92 this.condition = condition; 93 } 94 } 95 96 static final class IfTokenNode extends IfOrElseIfTokenNode { IfTokenNode(ExpressionNode condition)97 IfTokenNode(ExpressionNode condition) { 98 super(condition); 99 } 100 name()101 @Override String name() { 102 return "#if"; 103 } 104 } 105 106 static final class ElseIfTokenNode extends IfOrElseIfTokenNode { ElseIfTokenNode(ExpressionNode condition)107 ElseIfTokenNode(ExpressionNode condition) { 108 super(condition); 109 } 110 name()111 @Override String name() { 112 return "#elseif"; 113 } 114 } 115 116 static final class ElseTokenNode extends TokenNode { ElseTokenNode(String resourceName, int lineNumber)117 ElseTokenNode(String resourceName, int lineNumber) { 118 super(resourceName, lineNumber); 119 } 120 name()121 @Override String name() { 122 return "#else"; 123 } 124 } 125 126 static final class ForEachTokenNode extends TokenNode { 127 final String var; 128 final ExpressionNode collection; 129 ForEachTokenNode(String var, ExpressionNode collection)130 ForEachTokenNode(String var, ExpressionNode collection) { 131 super(collection.resourceName, collection.lineNumber); 132 this.var = var; 133 this.collection = collection; 134 } 135 name()136 @Override String name() { 137 return "#foreach"; 138 } 139 } 140 141 static final class NestedTokenNode extends TokenNode { 142 final ImmutableList<Node> nodes; 143 NestedTokenNode(String resourceName, ImmutableList<Node> nodes)144 NestedTokenNode(String resourceName, ImmutableList<Node> nodes) { 145 super(resourceName, 1); 146 this.nodes = nodes; 147 } 148 name()149 @Override String name() { 150 return "#parse(\"" + resourceName + "\")"; 151 } 152 } 153 154 static final class MacroDefinitionTokenNode extends TokenNode { 155 final String name; 156 final ImmutableList<String> parameterNames; 157 MacroDefinitionTokenNode( String resourceName, int lineNumber, String name, List<String> parameterNames)158 MacroDefinitionTokenNode( 159 String resourceName, int lineNumber, String name, List<String> parameterNames) { 160 super(resourceName, lineNumber); 161 this.name = name; 162 this.parameterNames = ImmutableList.copyOf(parameterNames); 163 } 164 name()165 @Override String name() { 166 return "#macro(" + name + ")"; 167 } 168 } 169 } 170