/**
* 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.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
import junit.framework.TestCase;
import de.codesourcery.jasm16.OpCode;
import de.codesourcery.jasm16.ast.ASTNode;
import de.codesourcery.jasm16.ast.ASTUtils;
import de.codesourcery.jasm16.ast.ASTVisitor;
import de.codesourcery.jasm16.ast.IIterationContext;
import de.codesourcery.jasm16.ast.ObjectCodeOutputNode;
import de.codesourcery.jasm16.ast.UnparsedContentNode;
import de.codesourcery.jasm16.compiler.CompilationContext;
import de.codesourcery.jasm16.compiler.CompilationListener;
import de.codesourcery.jasm16.compiler.CompilationUnit;
import de.codesourcery.jasm16.compiler.Compiler;
import de.codesourcery.jasm16.compiler.CompilerPhase;
import de.codesourcery.jasm16.compiler.DebugInfo;
import de.codesourcery.jasm16.compiler.ICompilationContext;
import de.codesourcery.jasm16.compiler.ICompilationListener;
import de.codesourcery.jasm16.compiler.ICompilationUnit;
import de.codesourcery.jasm16.compiler.ICompilationUnitResolver;
import de.codesourcery.jasm16.compiler.ICompiler;
import de.codesourcery.jasm16.compiler.ICompiler.CompilerOption;
import de.codesourcery.jasm16.compiler.ICompilerPhase;
import de.codesourcery.jasm16.compiler.IMarker;
import de.codesourcery.jasm16.compiler.IParentSymbolTable;
import de.codesourcery.jasm16.compiler.ParentSymbolTable;
import de.codesourcery.jasm16.compiler.io.AbstractResourceResolver;
import de.codesourcery.jasm16.compiler.io.ByteArrayObjectCodeWriterFactory;
import de.codesourcery.jasm16.compiler.io.IObjectCodeWriterFactory;
import de.codesourcery.jasm16.compiler.io.IResource;
import de.codesourcery.jasm16.compiler.io.IResourceResolver;
import de.codesourcery.jasm16.compiler.io.NullObjectCodeWriterFactory;
import de.codesourcery.jasm16.exceptions.ResourceNotFoundException;
import de.codesourcery.jasm16.lexer.ILexer;
import de.codesourcery.jasm16.lexer.IToken;
import de.codesourcery.jasm16.lexer.Lexer;
import de.codesourcery.jasm16.lexer.TokenType;
import de.codesourcery.jasm16.parser.IParser.ParserOption;
import de.codesourcery.jasm16.scanner.Scanner;
import de.codesourcery.jasm16.utils.DebugCompilationListener;
import de.codesourcery.jasm16.utils.FormattingVisitor;
import de.codesourcery.jasm16.utils.ITextRegion;
import de.codesourcery.jasm16.utils.Misc;
public abstract class TestHelper extends TestCase implements ICompilationUnitResolver
{
protected IParentSymbolTable symbolTable;
protected DebugInfo debugInfo;
protected static final Set<CompilerOption> OPTIONS = Collections.singleton( CompilerOption.DEBUG_MODE );
protected static final IResourceResolver RESOURCE_RESOLVER = new IResourceResolver() {
@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");
}
};
protected final class MyResolver implements ICompilationUnitResolver {
private final ICompilationUnit unit;
public MyResolver(ICompilationUnit unit) {
this.unit = unit;
}
@Override
public ICompilationUnit getOrCreateCompilationUnit(IResource resource) throws IOException
{
if ( unit.getResource().getIdentifier().equals( resource.getIdentifier() ) ) {
return unit;
}
throw new UnsupportedOperationException("Don't know how to create ICompilationUnit for "+resource);
}
@Override
public ICompilationUnit getCompilationUnit(IResource resource) throws IOException
{
throw new UnsupportedOperationException("Not implemented");
}
}
protected File getTempDir() throws IOException {
File f = File.createTempFile("blubb"," blah");
File parent = f.getParentFile();
assertNotNull( parent );
f.delete();
return parent;
}
protected static final IObjectCodeWriterFactory NOP_WRITER = new NullObjectCodeWriterFactory();
protected void assertTextRegion(ITextRegion expected,ITextRegion actual,String source) {
if ( ! expected.isSame( actual ) ) {
System.out.println("Expected: >"+expected.apply( source )+"< ("+expected+")");
System.out.println("Got: >"+actual.apply( source )+"< ("+actual+")");
fail( "expected: "+expected+" , got "+actual);
}
}
protected IParseContext createParseContext(String source)
{
final ICompilationUnit unit = CompilationUnit.createInstance("string input" , source );
return new ParseContext( unit , symbolTable , new Lexer(new Scanner( source ) ) , RESOURCE_RESOLVER ,this, Collections.singleton( ParserOption.DEBUG_MODE ) , null );
}
protected IParseContext createParseContext(ICompilationUnit unit) throws IOException
{
final String source = Misc.readSource( unit.getResource() );
return new ParseContext( unit , symbolTable , new Lexer(new Scanner( source ) ) , RESOURCE_RESOLVER ,this, Collections.singleton( ParserOption.DEBUG_MODE ) , null );
}
@Override
protected void setUp() throws Exception
{
debugInfo = new DebugInfo();
symbolTable = new ParentSymbolTable("dummy");
}
protected final IToken assertToken(ILexer lexer , TokenType type , String contents ) {
return assertToken( lexer , type , contents , -1 );
}
protected final ICompilationUnit assertCompiles(String source) throws Exception {
final ICompilationUnit unit = compile( source );
Misc.printCompilationErrors( unit , source , true );
assertFalse( unit.hasErrors() );
assertNotNull( unit.getAST() );
assertFalse( unit.getAST().hasErrors() );
assertSourceCode( source , unit.getAST() );
return unit;
}
protected final ICompilationUnit compile(String source) throws Exception {
return compile(source, new DebugCompilationListener(true) );
}
protected final ICompilationUnit compile(String source,ICompilationListener listener) throws Exception {
final Compiler compiler = new Compiler();
compiler.setCompilerOption( CompilerOption.DEBUG_MODE , true );
compiler.setCompilerOption( CompilerOption.LOCAL_LABELS_SUPPORTED , true );
compiler.setObjectCodeWriterFactory( NOP_WRITER );
final ICompilationUnit unit = CompilationUnit.createInstance("string input" , source );
compiler.insertCompilerPhaseAfter( new CompilerPhase("pry-on-symbols") {
@Override
protected void run(ICompilationUnit unit, ICompilationContext context) throws IOException
{
symbolTable = context.getSymbolTable();
}
} , ICompilerPhase.PHASE_GENERATE_CODE );
compiler.compile( Collections.singletonList( unit ) , listener );
Misc.printCompilationErrors( unit , source , true );
assertSourceCode( source , unit.getAST() );
return unit;
}
protected byte[] compileToByteCode(String source)
{
final ICompiler c = new de.codesourcery.jasm16.compiler.Compiler();
final ByteArrayObjectCodeWriterFactory factory = new ByteArrayObjectCodeWriterFactory();
c.setObjectCodeWriterFactory( factory );
final ICompilationUnit unit = CompilationUnit.createInstance("string" , source );
c.compile( Collections.singletonList( unit ) );
if ( unit.hasErrors() ) {
Misc.printCompilationErrors( unit , source , true );
throw new RuntimeException("Internal error, compilation failed.");
}
return factory.getBytes();
}
protected final void assertDoesNotCompile(String source) {
final ICompilationUnit unit = CompilationUnit.createInstance("string input" , source );
final Compiler c = new Compiler();
c.setObjectCodeWriterFactory( NOP_WRITER );
c.compile(Collections.singletonList( unit ) , new CompilationListener() );
final ASTNode result = unit.getAST();
boolean hasErrors = ! unit.getErrors().isEmpty();
assertTrue( "Compilation should have failed", hasErrors );
assertSourceCode( source , result );
}
protected final IToken assertToken(ILexer lexer , TokenType type , String contents , int parseOffset)
{
assertFalse( lexer.eof() );
IToken tok = lexer.read();
assertNotNull( tok );
assertEquals( type , tok.getType() );
assertEquals( contents , tok.getContents() );
if ( parseOffset != -1 ) {
assertEquals( parseOffset , tok.getStartingOffset() );
}
return tok;
}
protected final IToken assertToken(ILexer lexer , OpCode opCode , String contents)
{
return assertToken(lexer,opCode,contents,-1);
}
protected final ICompilationContext createCompilationContext(ICompilationUnit unit) throws IOException
{
return new CompilationContext( unit , symbolTable , new NullObjectCodeWriterFactory() ,
new AbstractResourceResolver() {
@Override
public IResource resolveRelative(String identifier, IResource parent) throws ResourceNotFoundException
{
throw new UnsupportedOperationException();
}
@Override
public IResource resolve(String identifier) throws ResourceNotFoundException
{
throw new UnsupportedOperationException();
}
} , this , OPTIONS );
}
protected final IToken assertToken(ILexer lexer , OpCode opCode , String contents , int parseOffset)
{
assertFalse( lexer.eof() );
IToken tok = lexer.read();
assertNotNull( tok );
assertEquals( TokenType.INSTRUCTION , tok.getType() );
final OpCode actual = OpCode.fromIdentifier( contents );
assertEquals( opCode , actual );
assertEquals( contents , tok.getContents() );
if ( parseOffset != -1 ) {
assertEquals( parseOffset , tok.getStartingOffset() );
}
return tok;
}
protected final String getErrors(final String source,ASTNode node) {
final StringBuilder result = new StringBuilder();
final ASTVisitor visitor = new ASTVisitor() {
@Override
public void visit(UnparsedContentNode node, IIterationContext context)
{
final String msg = Misc.toPrettyString( node.getError() , node.getErrorOffset() , source );
System.out.println( msg );
result.append("\n").append( msg ).append("\n");
}
};
ASTUtils.visitInOrder( node , visitor );
return result.toString();
}
protected final void assertSourceCode(String expected,String source , ASTNode node)
{
assertEquals( expected , toSourceCode( node , source ) );
}
protected final void assertSourceCode(String source,ASTNode node)
{
assertEquals( source , toSourceCode( node , source ) );
}
protected final String toSourceCode(ASTNode node,String source)
{
if ( node.getTextRegion() == null ) {
return "<no text range available>";
}
return node.getTextRegion().apply( source );
}
protected void resolveSymbols( final ICompilationUnit unit , ASTNode node )
{
ICompilationContext context = new ICompilationContext() {
@Override
public ICompilationUnit getCurrentCompilationUnit() {
return unit;
}
@Override
public IObjectCodeWriterFactory getObjectCodeWriterFactory() {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public IParentSymbolTable getSymbolTable() {
return symbolTable;
}
@Override
public boolean hasCompilerOption(CompilerOption option) {
return false;
}
@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");
}
@Override
public ICompilationUnit getOrCreateCompilationUnit(
IResource resource) throws IOException
{
return CompilationUnit.createInstance( resource.getIdentifier() , resource );
}
@Override
public ICompilationUnit getCompilationUnit(IResource resource)
throws IOException
{
return null;
}
@Override
public DebugInfo getDebugInfo()
{
return debugInfo;
}
@Override
public void addMarker(IMarker marker) {
}
@Override
public void addCompilationError(String message, ASTNode node) {
}
};
if ( node instanceof ObjectCodeOutputNode )
{
((ObjectCodeOutputNode) node).symbolsResolved( context );
}
}
@Override
public ICompilationUnit getCompilationUnit(IResource resource)
throws IOException {
return null;
}
@Override
public ICompilationUnit getOrCreateCompilationUnit(IResource resource)
throws IOException
{
return CompilationUnit.createInstance( resource.getIdentifier() , resource );
}
protected void assertEquals(File expected,File actual) {
assertEquals( expected.getAbsolutePath() , actual.getAbsolutePath() );
}
}