/**
* Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de>
*
* Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package de.codesourcery.jasm16.parser;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import de.codesourcery.jasm16.ast.AST;
import de.codesourcery.jasm16.ast.StartMacroNode;
import de.codesourcery.jasm16.compiler.CompilationUnit;
import de.codesourcery.jasm16.compiler.ICompilationContext;
import de.codesourcery.jasm16.compiler.ICompilationUnit;
import de.codesourcery.jasm16.compiler.ICompilationUnitResolver;
import de.codesourcery.jasm16.compiler.ISymbolTable;
import de.codesourcery.jasm16.compiler.SymbolTable;
import de.codesourcery.jasm16.compiler.io.AbstractResourceResolver;
import de.codesourcery.jasm16.compiler.io.IResource;
import de.codesourcery.jasm16.compiler.io.IResourceResolver;
import de.codesourcery.jasm16.exceptions.ResourceNotFoundException;
import de.codesourcery.jasm16.lexer.ILexer;
import de.codesourcery.jasm16.lexer.ILexer.LexerOption;
import de.codesourcery.jasm16.lexer.Lexer;
import de.codesourcery.jasm16.lexer.Lexer.ParseOffset;
import de.codesourcery.jasm16.scanner.Scanner;
import de.codesourcery.jasm16.utils.Misc;
/**
* Default {@link IParser} implementation.
*
* <p>Here's not much to look at since the actual parsing is
* performed by the AST nodes (recursive-descent parsing).</p>
*
* @author tobias.gierke@code-sourcery.de
*/
public class Parser implements IParser
{
private final Set<ParserOption> options = new HashSet<ParserOption>();
private final ICompilationUnitResolver compilationUnitResolver;
private final ParseOffset parseOffset;
public Parser(ICompilationUnitResolver compilationUnitResolver,ParseOffset parseOffset) {
if (compilationUnitResolver == null) {
throw new IllegalArgumentException("compilationUnitResolver must not be NULL");
}
this.compilationUnitResolver= compilationUnitResolver;
this.parseOffset = parseOffset;
}
public Parser(ICompilationUnitResolver compilationUnitResolver) {
this(compilationUnitResolver,new ParseOffset() );
}
@Override
public AST parse(ICompilationContext context) throws IOException
{
final String source = Misc.readSource( context.getCurrentCompilationUnit() );
return parse(context.getCurrentCompilationUnit(),context.getSymbolTable(),source,context,null);
}
// do not use except for unit-testing
protected AST parse(final String source)
{
final ICompilationUnit unit = CompilationUnit.createInstance( "string input" , source );
final IResourceResolver resolver = new AbstractResourceResolver() {
@Override
public IResource resolve(String identifier) throws ResourceNotFoundException
{
throw new UnsupportedOperationException("Not implemented");
}
@Override
public IResource resolveRelative(String identifier, IResource parent) throws ResourceNotFoundException
{
throw new UnsupportedOperationException("Not implemented");
}
};
return parse( unit , new SymbolTable("Parser#parse()") , source , resolver , null );
}
@Override
public AST parse(ICompilationUnit unit , ISymbolTable symbolTable , String source , IResourceResolver resolver,StartMacroNode currentlyExpandingMacro)
{
final Scanner scanner = new Scanner( source );
final ILexer lexer = new Lexer( scanner , this.parseOffset );
if ( hasParserOption( ParserOption.RELAXED_PARSING ) ) {
lexer.setLexerOption( LexerOption.CASE_INSENSITIVE_OPCODES , true );
}
final ParseContext context = new ParseContext( unit , symbolTable, lexer , resolver , compilationUnitResolver, this.options , currentlyExpandingMacro );
parseContextCreated(context);
final AST result = (AST) new AST().parse( context );
if ( ! context.eof() || ( context.currentParseIndex() - this.parseOffset.baseOffset() ) != source.length() ) {
throw new RuntimeException("Internal error, parsing at EOF but not all input tokens consumed ?");
}
return result;
}
protected void parseContextCreated(IParseContext context) {
}
@Override
public void setParserOption(ParserOption option, boolean onOff)
{
if ( option == null ) {
throw new IllegalArgumentException("option must not be NULL.");
}
if ( onOff ) {
options.add( option );
} else {
options.remove( option );
}
}
@Override
public boolean hasParserOption(ParserOption option)
{
if (option == null) {
throw new IllegalArgumentException("option must not be NULL.");
}
return options.contains( option );
}
}