/**
* 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 de.codesourcery.jasm16.AddressingMode;
import de.codesourcery.jasm16.ast.AST;
import de.codesourcery.jasm16.ast.ASTNode;
import de.codesourcery.jasm16.ast.ASTUtils;
import de.codesourcery.jasm16.ast.CommentNode;
import de.codesourcery.jasm16.ast.InitializedMemoryNode;
import de.codesourcery.jasm16.ast.InstructionNode;
import de.codesourcery.jasm16.ast.InvokeMacroNode;
import de.codesourcery.jasm16.ast.LabelNode;
import de.codesourcery.jasm16.ast.OperandNode;
import de.codesourcery.jasm16.ast.StartMacroNode;
import de.codesourcery.jasm16.ast.StatementNode;
import de.codesourcery.jasm16.ast.UninitializedMemoryNode;
import de.codesourcery.jasm16.ast.UnparsedContentNode;
import de.codesourcery.jasm16.compiler.CompilationUnit;
import de.codesourcery.jasm16.compiler.ICompilationUnit;
import de.codesourcery.jasm16.exceptions.ParseException;
import de.codesourcery.jasm16.utils.Misc;
public class ParserTest extends TestHelper {
public void testParseEmptyFile()
{
final Parser p = new Parser(this);
AST ast = p.parse( "" );
assertFalse( ast.hasErrors() );
assertFalse( ast.hasChildren() );
}
public void testParseBlankFile()
{
final Parser p = new Parser(this);
final String source = " ";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseLineWithInstructionAndLabel() {
final String source = " :loop SET [0x2000], [A] ; 2161 2000\n";
final Parser p = new Parser(this);
final ICompilationUnit unit = CompilationUnit.createInstance("dummy" , source );
AST ast = p.parse( unit , symbolTable , source , RESOURCE_RESOLVER, null );
Misc.printCompilationErrors( unit , source , true );
assertFalse( ast.hasErrors() );
assertFalse( unit.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( " :loop SET [0x2000], [A] ; 2161 2000\n" , toSourceCode( root , source ) );
}
public void testParseCommentLine()
{
final Parser p = new Parser(this);
final String source = " ; Try some basic stuff\n";
final ICompilationUnit unit = CompilationUnit.createInstance("dummy" , source );
AST ast = p.parse( unit , symbolTable , source , RESOURCE_RESOLVER, null );
Misc.printCompilationErrors( unit , source , true );
assertFalse( ast.hasErrors() );
assertFalse( unit.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( " ; Try some basic stuff\n" , toSourceCode( root , source ) );
}
public void testParseInitializedMemoryWithOneByte()
{
final Parser p = new Parser(this);
final String source = ".dat 1";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( InitializedMemoryNode.class , root.child(0).getClass() );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseInitializedMemoryWithBinaryValues() throws ParseException
{
final Parser p = new Parser(this);
String source = ".dat b1,b10";
ICompilationUnit unit = CompilationUnit.createInstance("string",source);
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( InitializedMemoryNode.class , root.child(0).getClass() );
final InitializedMemoryNode node = (InitializedMemoryNode) root.child(0);
resolveSymbols( unit , node );
final byte[] data = node.getBytes();
assertEquals( 4 , data.length );
assertEquals( 0 , data[0] );
assertEquals( 1 , data[1] );
assertEquals( 0 , data[2] );
assertEquals( 2 , data[3] );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseInitializedMemoryWithInvalidNumberLiteral() throws Exception
{
ICompilationUnit unit = compile( ".dat 65536" );
assertTrue( unit.hasErrors() );
}
public void testParseInitializedMemoryWithInvalidNumberLiteral2() throws Exception
{
ICompilationUnit unit = compile( ".dat 0xfffff" );
assertTrue( unit.hasErrors() );
}
public void testParseInitializedMemoryWithOneCharacterCharacterLiteral() throws ParseException
{
final Parser p = new Parser(this);
String source = ".dat \"a\"";
ICompilationUnit unit = CompilationUnit.createInstance("string",source);
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( InitializedMemoryNode.class , root.child(0).getClass() );
assertEquals( source , toSourceCode( root , source ) );
final InitializedMemoryNode node = (InitializedMemoryNode) root.child(0);
resolveSymbols( unit , node );
final byte[] data = node.getBytes();
assertEquals( 2 , data.length );
assertEquals( 0 , data[0] );
assertEquals( 97 , data[1] );
}
public void testParseInitializedMemoryWithMixedQuotingCharacterLiteral() throws ParseException
{
final Parser p = new Parser(this);
final String source = ".dat \"I'm great\"";
final ICompilationUnit unit = CompilationUnit.createInstance("string input" , source );
AST ast = p.parse( unit , symbolTable , source , RESOURCE_RESOLVER, null );
Misc.printCompilationErrors( unit , source , true );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( InitializedMemoryNode.class , root.child(0).getClass() );
final InitializedMemoryNode node = (InitializedMemoryNode) root.child(0);
resolveSymbols( unit , node );
final byte[] data = node.getBytes();
// a = 97
// b = 98
// c = 99
// d = 100
// e = 101
// f = 102
assertEquals( 0 , data[0] );
assertEquals( 73 , data[1] );
assertEquals( 0 , data[2] );
assertEquals( 39 , data[3] );
assertEquals( 0 , data[4] );
assertEquals( 109 , data[5] );
assertEquals( 0 , data[6] );
assertEquals( 32 , data[7] );
assertEquals( 0 , data[8] );
assertEquals( 103 , data[9] );
assertEquals( 0 , data[10] );
assertEquals( 114 , data[11] );
assertEquals( 0 , data[12] );
assertEquals( 101 , data[13] );
assertEquals( 0 , data[14] );
assertEquals( 97 , data[15] );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseInitializedMemoryWithCharacterLiteral() throws ParseException
{
final Parser p = new Parser(this);
final String source = ".dat \"dead\"";
final ICompilationUnit unit = CompilationUnit.createInstance("string input" , source );
AST ast = p.parse( unit , symbolTable , source , RESOURCE_RESOLVER , null );
Misc.printCompilationErrors( unit , source , true );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( InitializedMemoryNode.class , root.child(0).getClass() );
final InitializedMemoryNode node = (InitializedMemoryNode) root.child(0);
resolveSymbols( unit , node );
final byte[] data = node.getBytes();
// a = 97
// b = 98
// c = 99
// d = 100
// e = 101
// f = 102
assertEquals( 0 , data[0] );
assertEquals( 100 , data[1] );
assertEquals( 0 , data[2] );
assertEquals( 101 , data[3] );
assertEquals( 0 , data[4] );
assertEquals( 97 , data[5] );
assertEquals( 0 , data[6] );
assertEquals( 100 , data[7] );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseInitializedMemoryWithTwoBytes()
{
final Parser p = new Parser(this);
String source = ".dat 1,2";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( InitializedMemoryNode.class , root.child(0).getClass() );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseInitializedMemoryWithTwoBytesAndWhitespace()
{
final Parser p = new Parser(this);
String source = " .dat 1 , 2 ";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( InitializedMemoryNode.class , root.child(0).getClass() );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseMacroWithEmptyBody()
{
final Parser p = new Parser(this);
final String macroBody="";
String source = ".macro brk\n"+
".endmacro";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
assertEquals(2 , ast.getChildCount() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( StartMacroNode.class , root.child(0).getClass() );
StartMacroNode n = (StartMacroNode) root.child(0);
assertEquals("brk" , n.getMacroName().getRawValue() );
assertTrue( n.getArgumentNames().isEmpty() );
assertEquals( macroBody , n.getMacroBody() );
assertEquals( source , toSourceCode( ast , source ) );
}
public void testParseMacroWithoutArguments()
{
final Parser p = new Parser(this);
final String macroBody=":loop SET PC , loop";
final String source = ".macro brk\n"+
macroBody+"\n"+
".endmacro";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
assertEquals(2 , ast.getChildCount() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( StartMacroNode.class , root.child(0).getClass() );
StartMacroNode n = (StartMacroNode) root.child(0);
assertEquals("brk" , n.getMacroName().getRawValue() );
assertTrue( n.getArgumentNames().isEmpty() );
assertEquals( macroBody , n.getMacroBody() );
assertEquals( source , toSourceCode( ast , source ) );
ASTUtils.debugPrintTextRegions(ast,source);
}
public void testParseMacroInvocationWithoutArguments() throws ParseException
{
final Parser p = new Parser(this);
final String macroBody=":loop SET PC , loop";
String source = ".macro brk\n"+
macroBody+"\n"+
".endmacro\n"+
"brk\n";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
assertEquals(3 , ast.getChildCount() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( StartMacroNode.class , root.child(0).getClass() );
StartMacroNode n = (StartMacroNode) root.child(0);
assertEquals("brk" , n.getMacroName().getRawValue() );
assertTrue( n.getArgumentNames().isEmpty() );
assertEquals( macroBody , n.getMacroBody() );
assertEquals( source , toSourceCode( ast , source ) );
assertTrue( "Expected MacroInvocationNode but got "+ast.child(2).child(0).getClass() , ast.child(2).child(0) instanceof InvokeMacroNode );
final InvokeMacroNode invocation = (InvokeMacroNode) ast.child(2).child(0);
assertEquals( new Identifier("brk") , invocation.getMacroName() );
assertEquals( 0 , invocation.getArgumentCount() );
}
public void testParseMacroInvocationWithOneArgument() throws ParseException
{
final Parser p = new Parser(this);
final String macroBody="ADD A,arg1";
String source = ".macro inc(arg1)\n"+
macroBody+"\n"+
".endmacro\n"+
"inc (1)\n";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
assertEquals(3 , ast.getChildCount() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( StartMacroNode.class , root.child(0).getClass() );
StartMacroNode n = (StartMacroNode) root.child(0);
assertEquals("inc" , n.getMacroName().getRawValue() );
assertEquals( 1, n.getArgumentNames().size() );
assertEquals( new Identifier("arg1") , n.getArgumentNames().get(0) );
assertEquals( macroBody , n.getMacroBody() );
assertEquals( source , toSourceCode( ast , source ) );
assertTrue( "Expected MacroInvocationNode but got "+ast.child(2).child(0).getClass() , ast.child(2).child(0) instanceof InvokeMacroNode );
final InvokeMacroNode invocation = (InvokeMacroNode) ast.child(2).child(0);
assertEquals( new Identifier("inc") , invocation.getMacroName() );
assertEquals( 1 , invocation.getArgumentCount() );
}
public void testParseMacroWithOneArgument() throws ParseException
{
final Parser p = new Parser(this);
final String macroBody="ADD arg , 1";
String source = ".macro inc(arg)\n"+
macroBody+"\n"+
".endmacro";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
assertEquals( 2 , ast.getChildCount() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( StartMacroNode.class , root.child(0).getClass() );
StartMacroNode n = (StartMacroNode) root.child(0);
assertEquals("inc" , n.getMacroName().getRawValue() );
assertEquals( 1 , n.getArgumentNames().size() );
assertEquals( new Identifier("arg"), n.getArgumentNames().get(0) );
assertEquals( macroBody , n.getMacroBody() );
assertEquals( source , toSourceCode( ast , source ) );
}
public void testParseMacroWithTwoArguments() throws ParseException
{
final Parser p = new Parser(this);
final String macroBody="ADD arg , 1";
String source = ".macro inc(arg1,arg2)\n"+
macroBody+"\n"+
".endmacro";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
assertEquals( 2 , ast.getChildCount() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( StartMacroNode.class , root.child(0).getClass() );
StartMacroNode n = (StartMacroNode) root.child(0);
assertEquals("inc" , n.getMacroName().getRawValue() );
assertEquals( 2 , n.getArgumentNames().size() );
assertEquals( new Identifier("arg1"), n.getArgumentNames().get(0) );
assertEquals( new Identifier("arg2"), n.getArgumentNames().get(1) );
assertEquals( macroBody , n.getMacroBody() );
assertEquals( source , toSourceCode( ast , source ) );
}
public void testParseMacroWithTwoArgumentsAndWhitespae() throws ParseException
{
final Parser p = new Parser(this);
final String macroBody="ADD arg , 1";
String source = ".macro inc ( arg1 , arg2 ) \n"+
macroBody+"\n"+
".endmacro";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
assertEquals( 2 , ast.getChildCount() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( StartMacroNode.class , root.child(0).getClass() );
StartMacroNode n = (StartMacroNode) root.child(0);
assertEquals("inc" , n.getMacroName().getRawValue() );
assertEquals( 2 , n.getArgumentNames().size() );
assertEquals( new Identifier("arg1"), n.getArgumentNames().get(0) );
assertEquals( new Identifier("arg2"), n.getArgumentNames().get(1) );
assertEquals( macroBody , n.getMacroBody() );
assertEquals( source , toSourceCode( ast , source ) );
}
public void testParseMacroInvocationWithTwoArguments() throws ParseException
{
final Parser p = new Parser(this);
final String macroBody="ADD arg1,arg2";
String source = ".macro inc(arg1,arg2)\n"+
macroBody+"\n"+
".endmacro\n"+
"inc (A,1)\n";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
assertEquals(3 , ast.getChildCount() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertEquals( StartMacroNode.class , root.child(0).getClass() );
StartMacroNode n = (StartMacroNode) root.child(0);
assertEquals( 2, n.getArgumentNames().size() );
assertEquals( new Identifier("arg1") , n.getArgumentNames().get(0) );
assertEquals( macroBody , n.getMacroBody() );
assertEquals( source , toSourceCode( ast , source ) );
assertTrue( "Expected MacroInvocationNode but got "+ast.child(2).child(0).getClass() , ast.child(2).child(0) instanceof InvokeMacroNode );
final InvokeMacroNode invocation = (InvokeMacroNode) ast.child(2).child(0);
assertEquals( new Identifier("inc") , invocation.getMacroName() );
assertEquals( 2 , invocation.getArgumentCount() );
}
public void testParseUninitializedMemory()
{
final Parser p = new Parser(this);
String source = ".bss 100";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertTrue( root.child(0) instanceof UninitializedMemoryNode);
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseUninitializedMemoryWithLabel()
{
final Parser p = new Parser(this);
String source = ":label .bss 100";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 2 , root.getChildCount() );
assertEquals( LabelNode.class , root.child(0).getClass() );
assertEquals( UninitializedMemoryNode.class , root.child(1).getClass() );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseUninitializedMemoryWithLabelAndComment()
{
final Parser p = new Parser(this);
String source = ":label .bss 100 ; do weird stuff ";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 3 , root.getChildCount() );
assertEquals( LabelNode.class , root.child(0).getClass() );
assertEquals( UninitializedMemoryNode.class , root.child(1).getClass() );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseLineWithLabel()
{
final Parser p = new Parser(this);
String source = ":label";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( 1 , root.getChildCount() );
assertTrue( root.child(0) instanceof LabelNode );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseLineWithWhitespaceBeforeLabel()
{
final Parser p = new Parser(this);
String source = " :label";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertTrue( root.child(0) instanceof LabelNode );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseLineWithWhitespaceAfterLabel()
{
final Parser p = new Parser(this);
String source = ":label ";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertTrue( root.child(0) instanceof LabelNode );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseLineWithCommentAfterLabel()
{
final Parser p = new Parser(this);
String source = ":label;comment";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertTrue( root.child(0) instanceof LabelNode );
assertTrue( root.child(1) instanceof CommentNode );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseLineWithComment()
{
final Parser p = new Parser(this);
String source = ";comment";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertTrue( root.child(0) instanceof CommentNode );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseLineWithOpCode1()
{
final Parser p = new Parser(this);
String source = "SET a,10";
AST ast = p.parse( source );
assertFalse( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( InstructionNode.class , root.child(0).getClass() );
final InstructionNode instruction = (InstructionNode) root.child(0);
assertEquals( OperandNode.class , instruction.child(0).getClass() );
assertEquals( OperandNode.class , instruction.child(1).getClass() );
assertEquals( AddressingMode.IMMEDIATE , instruction.getOperand(1).getAddressingMode() );
assertEquals( AddressingMode.REGISTER , instruction.getOperand(0).getAddressingMode() );
assertEquals( source , toSourceCode( root , source ) );
}
public void testParseLineWithOpCode2()
{
final Parser p = new Parser(this);
String source = "SET 10,a";
AST ast = p.parse( source ); // invalid addressing mode
assertTrue( ast.hasErrors() );
ASTNode root = ast.child(0);
assertNotNull( root );
assertTrue( root instanceof StatementNode );
assertEquals( InstructionNode.class , root.child(0).getClass() );
assertEquals( UnparsedContentNode.class , root.child(0).child(0).getClass() );
assertEquals(source , toSourceCode( root , source ) );
}
}