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

org.eclipse.epsilon.egl.parse.EglParser Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2008 The University of York.
 * 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/
 * 
 * Contributors:
 *     Louis Rose - initial API and implementation
 ******************************************************************************/
package org.eclipse.epsilon.egl.parse;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.epsilon.common.parse.AST;
import org.eclipse.epsilon.common.parse.EpsilonTreeAdaptor;
import org.eclipse.epsilon.common.parse.problem.ParseProblem;
import org.eclipse.epsilon.egl.parse.EglToken.TokenType;

public class EglParser {
	
	private final EglLexer lexer;
	private final List problems = new LinkedList<>();
	
	private final EpsilonTreeAdaptor astFactory;
	
	private ParseProblem lastError = null;
	private EglToken current; 
	
	private AST ast;
	
	
	public EglParser(EglLexer lexer, EpsilonTreeAdaptor astFactory) {
		if (lexer==null)
			throw new NullPointerException("program cannot be null");
		
		this.lexer      = lexer;
		this.astFactory = astFactory;
		
		ast = createAST(TokenType.PROGRAM, "", 1, 1);
	}
	
	public AST getAST() {
		return ast;
	}
	
	public List getParseProblems() {
		return Collections.unmodifiableList(problems);
	}
	
	public boolean parse() {
		try {
			nextToken();
			return parseProgram();
		
		} catch (EglRecognitionException e) {
			problems.add(new ParseProblem(e.getLineNumber(), e.getColumnNumber(), e.getMessage()));
			return false;
		}
	}
	
	
	private void nextToken() throws EglRecognitionException {
		current = lexer.nextToken();
	}
	
	private boolean parseProgram() throws EglRecognitionException {
		while(true) {
			if (current.getTokenType() == TokenType.EOF)
				return true;

			final AST commentedAst = parseCommented();
			
			if (commentedAst != null) {
			
				ast.addChild(commentedAst);
				
			} else {
				final AST taggedAst = parseTagged();
				
				if (taggedAst != null) {
					ast.addChild(taggedAst);
				
				} else {
				
					final AST textAst = parseText();
					
					if (textAst != null) {
						ast.addChild(textAst);
					
					} else {
						return false;
					}
				}
			}
		}
	}
	
	private AST parseText() throws EglRecognitionException {
		StringBuilder text = new StringBuilder();
		
		int line = -1;
		int col  = -1;
		
		while (current.getTokenType() == TokenType.PLAIN_TEXT) {
			if (line == -1) {
				line = current.getLine();
				col  = current.getColumn();
			}
			text.append(current.getText());
			nextToken();
		}
		
		if (text.toString().length() > 0) {
			return createAST(TokenType.PLAIN_TEXT, text.toString(), line, col);
			
		} else {
			if (current.getTokenType() == TokenType.NEW_LINE) {
				
				AST newLineAST = createAST(current); 
				nextToken();
				return newLineAST;
			
			} else {
				reportError(TokenType.PLAIN_TEXT);
				return null;
			}
		} 
	}
	
	private AST parseTagged() throws EglRecognitionException {
		final EglToken startTagToken = current; 
		
		if (current.getTokenType() != TokenType.START_TAG &&
		    current.getTokenType() != TokenType.START_OUTPUT_TAG)
			return null;
		
		nextToken();
		
		// Create a data structure to hold all of the text sections
		// that this tagged section contains
		final List textAsts = new LinkedList<>();
		
		// Parse as many text tokens as possible
		AST next = null;
		do {
			next = parseText();
			if (next != null) textAsts.add(next);
		} while(next != null && current.getTokenType() != TokenType.END_TAG && current.getTokenType() != TokenType.END_OUTDENT_TAG);
			
		// A tagged section must contain at least one text section
		if (textAsts.size() == 0) return null;
		
		// Ensure end tag is present
		if (current.getTokenType() == TokenType.END_TAG || current.getTokenType() == TokenType.END_OUTDENT_TAG) {
			final AST endTag = createAST(current);
			
			final AST taggedAst = createAST(startTagToken);
			for (AST textAst : textAsts) {
				taggedAst.addChild(textAst);
			}
			taggedAst.addChild(endTag);
			
			nextToken();
			
			return taggedAst;
		}

		return null;
	}
	
	private AST parseCommented() throws EglRecognitionException {
		final EglToken startCommentTagToken = current; 
		
		if (current.getTokenType() != TokenType.START_COMMENT_TAG &&
			current.getTokenType() != TokenType.START_MARKER_TAG)
			return null;
		
		nextToken();
		
		final StringBuilder commentContents = new StringBuilder();
		
		// Parse all tokens that between this and the end comment token
		while(current != null &&
			  current.getTokenType() != TokenType.END_COMMENT_TAG && 
			  current.getTokenType() != TokenType.EOF) {
			commentContents.append(current.getText());
			nextToken();
		}
		
		// Ensure end comment tag is present
		if (current.getTokenType() == TokenType.END_COMMENT_TAG) {
			final AST endCommentTag = createAST(current);
			final AST commentedAst = createAST(startCommentTagToken);
			
			commentedAst.addChild(createAST(TokenType.PLAIN_TEXT,
			                                commentContents.toString(),
			                                startCommentTagToken.getLine(),
			                                startCommentTagToken.getColumn() + startCommentTagToken.getText().length()));
		
			commentedAst.addChild(endCommentTag);
			
			nextToken();
			
			return commentedAst;
		}

		return null;
	}
	
	private AST createAST(EglToken t) {
		return astFactory.create(t);
	}
	
	private AST createAST(TokenType type, String text, int line, int col) {
		return astFactory.create(new EglToken(type, text, line, col));
	}

	private void reportError(TokenType expectedType) {

		if (lastError == null ||
		    lastError.getColumn() != current.getColumn() ||
		    lastError.getLine()   != current.getLine()) {
		
			lastError = new ParseProblem(current.getLine(),
			                             current.getColumn(),
			                             "Unexpected token '" + current.getTokenType() + "' " + 
			                              "(was expecting '" + expectedType + "')");
			                               	
			problems.add(lastError);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy