/**
* 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.compiler;
import java.util.Iterator;
import java.util.LinkedHashMap;
import org.apache.log4j.Logger;
import de.codesourcery.jasm16.ast.ASTNode;
import de.codesourcery.jasm16.ast.ASTUtils;
import de.codesourcery.jasm16.ast.ISimpleASTNodeVisitor;
import de.codesourcery.jasm16.ast.SymbolReferenceNode;
import de.codesourcery.jasm16.ast.TermNode;
import de.codesourcery.jasm16.parser.Identifier;
import de.codesourcery.jasm16.utils.ITextRegion;
import de.codesourcery.jasm16.utils.TextRegion;
/**
* A variable assignment (.equ).
*
* @author tobias.gierke@code-sourcery.de
*/
public class Equation extends AbstractSymbol implements IValueSymbol {
private static Logger LOG = Logger.getLogger(Equation.class);
private TermNode expression;
public Equation(ICompilationUnit unit,
ITextRegion location,
Identifier identifier,
TermNode expression)
{
super(unit, location, identifier,null);
if ( expression == null ) {
throw new IllegalArgumentException("expression must not be NULL");
}
this.expression = expression;
}
@Override
public ISymbol createCopy()
{
return new Equation( getCompilationUnit() , getLocation() , getName() , expression == null ? null : (TermNode) expression.createCopy(false) );
}
@Override
public ISymbol withIdentifier(Identifier newIdentifier)
{
final TextRegion newLocation = new TextRegion( getLocation().getStartingOffset() , newIdentifier.getRawValue().length() );
return new Equation( getCompilationUnit() , newLocation , newIdentifier , expression );
}
@Override
public ISymbol withScope(ISymbol newScope)
{
if ( newScope != null ) {
throw new IllegalArgumentException(".equ definitions always have global scope");
}
return new Equation( getCompilationUnit() , getLocation() , getName() , expression );
}
/**
* Invoked by ASTValidationPhase1 to prevent later
* compilation phases from choking on circular equation dependencies.
*/
public void clearExpression() {
expression = null;
}
@Override
public Long getValue(ISymbolTable symbolTable)
{
if ( expression == null ) {
return null;
}
return expression.calculate( symbolTable );
}
public TermNode getExpression() {
return expression;
}
@Override
public void setValue(Long value) {
throw new UnsupportedOperationException( "cannot set value of constant equation");
}
private static class CircularEquationsException extends RuntimeException {
public CircularEquationsException(String message) {
super(message);
}
}
public static void checkCyclicDependencies(final Identifier id,ISymbolTable symbolTable)
{
final LinkedHashMap<Identifier, Equation> symbolsSeen = new LinkedHashMap<Identifier, Equation>();
checkCyclicDependencies( id , symbolTable , symbolsSeen );
}
private static void checkCyclicDependencies(final Identifier id,
ISymbolTable symbolTable, LinkedHashMap<Identifier, Equation> symbolsSeen)
{
final ISymbol symbol = symbolTable.getSymbol( id , null );
if ( symbol instanceof Equation )
{
try {
checkCyclicDependencies( (Equation) symbol , symbolTable , symbolsSeen );
}
catch (CircularEquationsException e)
{
((Equation) symbol).clearExpression();
final ICompilationUnit unit = symbol.getCompilationUnit();
unit.addMarker(
new CompilationError( "Equation '"+symbol.getFullyQualifiedName()+"' has circular dependency: "+
e.getMessage() , unit , symbol.getLocation() )
);
}
}
}
private static void checkCyclicDependencies(Equation symbol,final ISymbolTable symbolTable,
final LinkedHashMap<Identifier,Equation> symbolsSeen) throws CircularEquationsException
{
if ( symbolsSeen.containsKey( symbol.getName() ) )
{
symbol.clearExpression();
failWithException(symbolsSeen);
}
symbolsSeen.put( symbol.getName() , symbol );
if ( symbol.getExpression() != null )
{
final ISimpleASTNodeVisitor<ASTNode> checkingVisitor = new ISimpleASTNodeVisitor<ASTNode>()
{
@Override
public boolean visit(ASTNode node)
{
if ( node instanceof SymbolReferenceNode)
{
final SymbolReferenceNode refNode = (SymbolReferenceNode) node;
if ( refNode.getIdentifier() != null ) {
checkCyclicDependencies( refNode.getIdentifier() , symbolTable , symbolsSeen );
}
}
return true;
}
};
ASTUtils.visitInOrder( symbol.getExpression() , checkingVisitor );
}
}
private static void failWithException(LinkedHashMap<Identifier, Equation> symbolsSeen) throws CircularEquationsException
{
final StringBuilder cycle = new StringBuilder();
for (Iterator<Identifier> it = symbolsSeen.keySet().iterator(); it.hasNext();) {
Identifier id = it.next();
cycle.append( id );
if ( it.hasNext() ) {
cycle.append(" <-> ");
}
}
cycle.append(" <-> ").append( symbolsSeen.keySet().iterator().next() );
final String errorMsg ="Equations have circular dependency: "+cycle;
LOG.error("createParseContextForInclude(): "+errorMsg);
throw new CircularEquationsException( cycle.toString() );
}
}