All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mitchellbosecke.pebble.parser.ParserImpl Maven / Gradle / Ivy

/*******************************************************************************
 * 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.List;
import java.util.Map;
import java.util.Stack;

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 Stack 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 Stack<>();

		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