![JAR search and dependency download from the Maven repository](/logo.png)
com.mitchellbosecke.pebble.parser.ParserImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pebble Show documentation
Show all versions of pebble Show documentation
Templating engine for Java.
/*******************************************************************************
* This file is part of Pebble.
*
* Copyright (c) 2014 by Mitchell Bösecke
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
******************************************************************************/
package com.mitchellbosecke.pebble.parser;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.mitchellbosecke.pebble.PebbleEngine;
import com.mitchellbosecke.pebble.error.ParserException;
import com.mitchellbosecke.pebble.lexer.Token;
import com.mitchellbosecke.pebble.lexer.TokenStream;
import com.mitchellbosecke.pebble.node.BodyNode;
import com.mitchellbosecke.pebble.node.PrintNode;
import com.mitchellbosecke.pebble.node.RenderableNode;
import com.mitchellbosecke.pebble.node.RootNode;
import com.mitchellbosecke.pebble.node.TextNode;
import com.mitchellbosecke.pebble.node.expression.Expression;
import com.mitchellbosecke.pebble.tokenParser.TokenParser;
public class ParserImpl implements Parser {
/**
* A reference to the main engine.
*/
private final PebbleEngine engine;
/**
* An expression parser.
*/
private ExpressionParser expressionParser;
/**
* The TokenStream that we are converting into an Abstract Syntax Tree.
*/
private TokenStream stream;
/**
* TokenParser objects provided by the extensions.
*/
private Map tokenParsers;
/**
* used to keep track of the name of the block that we are currently inside
* of. This is purely just for the parent() function.
*/
private LinkedList blockStack;
/**
* Constructor
*
* @param engine
* The main PebbleEngine that this parser is working for
*/
public ParserImpl(PebbleEngine engine) {
this.engine = engine;
}
@Override
public RootNode parse(TokenStream stream) throws ParserException {
// token parsers which have come from the extensions
this.tokenParsers = engine.getTokenParsers();
// expression parser
this.expressionParser = new ExpressionParser(this, engine.getBinaryOperators(), engine.getUnaryOperators());
this.stream = stream;
this.blockStack = new LinkedList<>();
BodyNode body = subparse();
RootNode root = new RootNode(body);
return root;
}
@Override
public BodyNode subparse() throws ParserException {
return subparse(null);
}
@Override
/**
* The main method for the parser. This method does the work of converting
* a TokenStream into a Node
*
* @param stopCondition A stopping condition provided by a token parser
* @return Node The root node of the generated Abstract Syntax Tree
*/
public BodyNode subparse(StoppingCondition stopCondition) throws ParserException {
// these nodes will be the children of the root node
List nodes = new ArrayList<>();
Token token;
while (!stream.isEOF()) {
switch (stream.current().getType()) {
case TEXT:
/*
* The current token is a text token. Not much to do here other
* than convert it to a text Node.
*/
token = stream.current();
nodes.add(new TextNode(token.getValue(), token.getLineNumber()));
stream.next();
break;
case PRINT_START:
/*
* We are entering a print delimited region at this point. These
* regions will contain some sort of expression so let's pass
* control to our expression parser.
*/
// go to the next token because the current one is just the
// opening delimiter
token = stream.next();
Expression> expression = this.expressionParser.parseExpression();
nodes.add(new PrintNode(expression, token.getLineNumber()));
// we expect to see a print closing delimiter
stream.expect(Token.Type.PRINT_END, engine.getLexer().getPrintCloseDelimiter());
break;
case EXECUTE_START:
// go to the next token because the current one is just the
// opening delimiter
stream.next();
token = stream.current();
/*
* We expect a name token at the beginning of every block.
*
* We do not use stream.expect() because it consumes the current
* token. The current token may be needed by a token parser
* which has provided a stopping condition. Ex. the 'if' token
* parser may need to check if the current token is either
* 'endif' or 'else' and act accordingly, thus we should not
* consume it.
*/
if (!Token.Type.NAME.equals(token.getType())) {
throw new ParserException(null, "A block must start with a tag name.", token.getLineNumber(),
stream.getFilename());
}
// If this method was executed using a TokenParser and
// that parser provided a stopping condition (ex. checking
// for the 'endif' token) let's check for that condition
// now.
if (stopCondition != null && stopCondition.evaluate(token)) {
return new BodyNode(token.getLineNumber(), nodes);
}
// find an appropriate parser for this name
TokenParser tokenParser = tokenParsers.get(token.getValue());
if (tokenParser == null) {
throw new ParserException(null, String.format("Unexpected tag name \"%s\"", token.getValue()),
token.getLineNumber(), stream.getFilename());
}
tokenParser.setParser(this);
RenderableNode node = tokenParser.parse(token);
// node might be null (ex. "extend" token parser)
if (node != null) {
nodes.add(node);
}
break;
default:
throw new ParserException(null, "Parser ended in undefined state.", stream.current().getLineNumber(),
stream.getFilename());
}
}
// create the root node with the children that we have found
return new BodyNode(stream.current().getLineNumber(), nodes);
}
@Override
public TokenStream getStream() {
return stream;
}
public void setStream(TokenStream stream) {
this.stream = stream;
}
@Override
public ExpressionParser getExpressionParser() {
return this.expressionParser;
}
@Override
public String peekBlockStack() {
return blockStack.peek();
}
@Override
public String popBlockStack() {
return blockStack.pop();
}
@Override
public void pushBlockStack(String blockName) {
blockStack.push(blockName);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy