/**
* 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.ast;
import java.util.List;
import de.codesourcery.jasm16.Address;
import de.codesourcery.jasm16.compiler.CompilationError;
import de.codesourcery.jasm16.compiler.ISymbol;
import de.codesourcery.jasm16.compiler.MacroNameSymbol;
import de.codesourcery.jasm16.exceptions.ParseException;
import de.codesourcery.jasm16.lexer.IToken;
import de.codesourcery.jasm16.lexer.TokenType;
import de.codesourcery.jasm16.parser.IParseContext;
import de.codesourcery.jasm16.parser.Identifier;
import de.codesourcery.jasm16.utils.ITextRegion;
import de.codesourcery.jasm16.utils.TextRegion;
/**
* AST node representing a statement.
*
* @author tobias.gierke@code-sourcery.de
*/
public class StatementNode extends ASTNode
{
private boolean parseStartOfLine(IParseContext context) throws ParseException {
/*
* LINE_START := <WS> |
* <LABEL> <WS>
*/
if ( ! context.eof() && context.peek().isWhitespace() ) {
mergeWithAllTokensTextRegion( context.parseWhitespace() );
}
if ( ! context.eof() )
{
if ( context.peek( TokenType.CHARACTERS ) && Identifier.isValidIdentifier( context.peek().getContents() ))
{
final Identifier id = new Identifier(context.peek().getContents());
if ( context.getSymbolTable().getSymbol( id , null ) instanceof MacroNameSymbol )
{
int offset = context.currentParseIndex();
context.mark();
try {
addChild( new InvokeMacroNode().parse( context ) , context );
}
catch(Exception e)
{
final ITextRegion range;
if ( e instanceof ParseException) {
range = ((ParseException) e).getTextRegion();
} else {
range = new TextRegion( offset , context.currentParseIndex()-offset );
}
addCompilationErrorAndAdvanceParser( new CompilationError(
"Failed to parse label: "+e.getMessage() ,
context.getCompilationUnit(),
range,e ) , new TokenType[]{TokenType.WHITESPACE,TokenType.EOL} , context );
} finally {
context.clearMark();
}
return false;
}
}
if ( context.peek().hasType( TokenType.COLON ) ||
context.peek().hasType( TokenType.DOT ) ||
context.peek().hasType( TokenType.CHARACTERS ) )
{
int offset = context.currentParseIndex();
try {
context.mark();
addChild( new LabelNode().parseInternal( context ) , context );
}
catch(Exception e)
{
final ITextRegion range;
if ( e instanceof ParseException) {
range = ((ParseException) e).getTextRegion();
} else {
range = new TextRegion( offset , context.currentParseIndex()-offset );
}
addCompilationErrorAndAdvanceParser( new CompilationError(
"Failed to parse label: "+e.getMessage() ,
context.getCompilationUnit(),
range,e ) , new TokenType[]{TokenType.WHITESPACE,TokenType.EOL} , context );
} finally {
context.clearMark();
}
}
}
if ( ! context.eof() && context.peek().isWhitespace() ) {
mergeWithAllTokensTextRegion( context.parseWhitespace() );
}
return ! context.eof() && ! context.peek().isEOL();
}
public InstructionNode getInstructionNode()
{
for ( ASTNode n : getChildren() ) {
if ( n instanceof InstructionNode) {
return (InstructionNode) n;
}
}
return null;
}
public boolean hasInstruction() {
return getInstructionNode() != null;
}
public LabelNode getLabelNode()
{
for ( ASTNode n : getChildren() )
{
LabelNode result = searchLabelNode(n);
if ( result != null ) {
return result;
}
}
return null;
}
private LabelNode searchLabelNode(ASTNode toCheck)
{
if ( toCheck instanceof LabelNode) {
return (LabelNode) toCheck;
}
if ( toCheck instanceof InvokeMacroNode) {
for ( ASTNode child : toCheck.getChildren() ) {
LabelNode result = searchLabelNode( child );
if ( result != null ) {
return result;
}
}
}
return null;
}
public List<ObjectCodeOutputNode> getObjectOutputNodes() {
return ASTUtils.getNodesByType( this , ObjectCodeOutputNode.class , false );
}
private void parseEndOfLine(IParseContext context) throws ParseException {
/*
* LINE_END:= <EOL> |
* <WS> <EOL>
* <SINGLE_LINE_COMMENT>
*/
if ( ! context.eof() && context.peek().hasType( TokenType.WHITESPACE ) )
{
mergeWithAllTokensTextRegion( context.read() );
}
if ( context.eof() ) {
return;
}
if ( context.peek().isEOL() ) {
mergeWithAllTokensTextRegion( context.read() );
return;
}
if ( context.peek().hasType( TokenType.SINGLE_LINE_COMMENT ) )
{
addChild( new CommentNode().parse( context ) , context );
} else {
throw new ParseException("Unexpected character '"+context.peek().getContents()+"' at end of statement", context.peek() );
}
}
@Override
protected ASTNode parseInternal(IParseContext context) throws ParseException
{
if ( parseStartOfLine( context ) && ! context.peek().hasType( TokenType.SINGLE_LINE_COMMENT ) )
{
try {
context.mark();
parseStatementBody( context );
} catch(Exception e) {
addCompilationErrorAndAdvanceParser( e , context );
} finally {
context.clearMark();
}
}
parseEndOfLine( context );
return this;
}
protected TokenType getParseRecoveryTokenType() {
return TokenType.SINGLE_LINE_COMMENT;
}
private void parseStatementBody(IParseContext context) throws ParseException
{
final IToken tok = context.peek();
switch( tok.getType() )
{
case START_MACRO:
addChild( new StartMacroNode().parseInternal( context ) , context );
break;
case END_MACRO:
addChild( new EndMacroNode().parseInternal( context ) , context );
break;
case EQUATION:
addChild( new EquationNode().parseInternal( context ) , context );
break;
case INITIALIZED_MEMORY_PACK:
// $//$FALL-THROUGH$
case INITIALIZED_MEMORY_BYTE:
// $FALL-THROUGH$
case INITIALIZED_MEMORY_WORD:
addChild( new InitializedMemoryNode().parseInternal( context ) , context );
break;
case UNINITIALIZED_MEMORY_WORDS:
case UNINITIALIZED_MEMORY_BYTES:
addChild( new UninitializedMemoryNode().parseInternal( context ) , context );
break;
case INSTRUCTION:
addChild( new InstructionNode().parseInternal( context ) , context );
break;
case INCLUDE_SOURCE:
addChild( new IncludeSourceFileNode().parseInternal( context ) , context );
break;
case INCLUDE_BINARY:
addChild( new IncludeBinaryFileNode().parseInternal( context ) , context );
break;
case ORIGIN:
final ASTNode origin = addChild( new OriginNode().parseInternal( context ) , context );
if ( origin instanceof OriginNode )
{
final Address newOffset = ((OriginNode) origin).getAddress();
final Address currentOffset = context.getCompilationUnit().getObjectCodeStartOffset();
if ( Address.ZERO.equals( currentOffset ) )
{
context.getCompilationUnit().setObjectCodeStartOffset( newOffset );
}
}
break;
default:
// check for macro invocation
boolean isMacroInvocation = false;
context.mark();
try
{
context.skipWhitespace(false);
if ( context.peek( TokenType.CHARACTERS ) )
{
IToken identifierToken = context.read();
if ( Identifier.isValidIdentifier( identifierToken.getContents() ) )
{
ISymbol symbol = context.getSymbolTable().getSymbol( new Identifier( identifierToken.getContents() ) , null );
isMacroInvocation = ( symbol instanceof MacroNameSymbol );
}
}
} finally {
context.reset();
context.clearMark();
}
if ( isMacroInvocation ) {
addChild( new InvokeMacroNode().parseInternal( context ) , context );
}
throw new ParseException( "Unexpected token '"+tok.getContents()+"' in statement, expected an instruction" , context.peek() );
}
}
@Override
protected StatementNode copySingleNode()
{
return new StatementNode();
}
@Override
public boolean supportsChildNodes() {
return true;
}
}