public enum SymbolType
{
    Unknown,
    Callback,
    Command,
    UserFunction,
    Variable,
    Literal,
    Expression,
    PreprocessorSymbol,
}; public
class ASTArrayIndex extends SimpleNode {
  public ASTArrayIndex(int id) {
    super(id);
  }

  public ASTArrayIndex(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTArrayInitializer extends SimpleNode {

  /** トークン ":="" を含んでいたかどうか。UIの初期化子の場合分け用 */
  public boolean hasAssign;

  public ASTArrayInitializer(int id) {
    super(id);
  }

  public ASTArrayInitializer(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTAssignment extends SimpleNode {
  public ASTAssignment(int id) {
    super(id);
  }

  public ASTAssignment(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTBlock extends SimpleNode {
  public ASTBlock(int id) {
    super(id);
  }

  public ASTBlock(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTCallCommand extends SimpleNode {
  public ASTCallCommand(int id) {
    super(id);
  }

  public ASTCallCommand(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTCallUserFunctionStatement extends SimpleNode {
  public ASTCallUserFunctionStatement(int id) {
    super(id);
  }

  public ASTCallUserFunctionStatement(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTCallbackDeclaration extends SimpleNode {
  public ASTCallbackDeclaration(int id) {
    super(id);
  }

  public ASTCallbackDeclaration(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTCaseCondition extends SimpleNode {
  public ASTCaseCondition(int id) {
    super(id);
  }

  public ASTCaseCondition(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTCommandArgumentList extends SimpleNode {
  public ASTCommandArgumentList(int id) {
    super(id);
  }

  public ASTCommandArgumentList(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTIfStatement extends SimpleNode {
  public ASTIfStatement(int id) {
    super(id);
  }

  public ASTIfStatement(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTPrimitiveInititalizer extends SimpleNode {

  /** トークン ":="" を含んでいたかどうか。UIの初期化子の場合分け用 */
  public boolean hasAssign;

  public ASTPrimitiveInititalizer(int id) {
    super(id);
  }

  public ASTPrimitiveInititalizer(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTRefVariable extends SimpleNode {
  public ASTRefVariable(int id) {
    super(id);
  }

  public ASTRefVariable(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTSelectStatement extends SimpleNode {
  public ASTSelectStatement(int id) {
    super(id);
  }

  public ASTSelectStatement(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTUserFunctionDeclaration extends SimpleNode {
  public ASTUserFunctionDeclaration(int id) {
    super(id);
  }

  public ASTUserFunctionDeclaration(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTVariableDeclaration extends SimpleNode {
  public ASTVariableDeclaration(int id) {
    super(id);
  }

  public ASTVariableDeclaration(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTVariableInitializer extends SimpleNode {
  public ASTVariableInitializer(int id) {
    super(id);
  }

  public ASTVariableInitializer(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class ASTWhileStatement extends SimpleNode {
  public ASTWhileStatement(int id) {
    super(id);
  }

  public ASTWhileStatement(KSPParser p, int id) {
    super(p, id);
  }


  /** Accept the visitor. **/
  public Object jjtAccept(KSPParserVisitor visitor, Object data) {

    return
    visitor.visit(this, data);
  }
} public
class SimpleNode extends net.rkoubou.kspparser.javacc.ASTKSPNode implements Node
{

    protected Node parent;
    protected Node[] children;
    protected int id;
    protected Object value;
    protected KSPParser parser;

    public SimpleNode( int i )
    {
        id = i;
    }

    public SimpleNode( KSPParser p, int i )
    {
        this( i );
        parser = p;
    }

    public void jjtOpen(){}

    public void jjtClose(){}

    public void jjtSetParent( Node n ) { parent = n; }
    public Node jjtGetParent() { return parent; }

    public void jjtAddChild( Node n, int i )
    {
        if ( children == null )
        {
            children = new Node[i + 1];
        }
        else if ( i >= children.length )
        {
            Node c[] = new Node[i + 1];
            System.arraycopy( children, 0, c, 0, children.length );
            children = c;
        }
        children[i] = n;
    }

    public Node jjtGetChild( int i )
    {
        return children[i];
    }

    public int jjtGetNumChildren()
    {
        return ( children == null ) ? 0 : children.length;
    }

    public void jjtSetValue( Object value ) { this.value = value; }
    public Object jjtGetValue() { return value; }

    /** Accept the visitor. **/
    public Object jjtAccept( KSPParserVisitor visitor, Object data )
    {
        return visitor.visit( this, data );
    }

    /** Accept the visitor. **/
    public Object childrenAccept( KSPParserVisitor visitor, Object data )
    {
        if ( children != null )
        {
            for ( int i = 0; i < children.length; ++i )
            {
                children[i].jjtAccept( visitor, data );
            }
        }
        return data;
    }

/*
    You can override these two methods in subclasses of SimpleNode to
    customize the way the node appears when the tree is dumped.  If
    your output uses more than one line you should override
    toString(String), otherwise overriding toString() is probably all
    you need to do.
*/
    public String toString()
    {
        return KSPParserTreeConstants.jjtNodeName[ id ];
    }
    public String toString( String prefix ) { return prefix + toString(); }

/*
Override this method if you want to customize how the node dumps out its children.
*/
    public void dump( String prefix )
    {
        System.out.println( toString( prefix ) );
        if ( children != null )
        {
            for ( int i = 0; i < children.length; ++i )
            {
                SimpleNode n = ( SimpleNode )children[i];
                if ( n != null )
                {
                    n.dump( prefix + " " );
                }
            }
        }
    }

    public int getId()
    {
        return id;
    }

    /**
     * 追記：このノードを指定された新規ノードに置き換える
     */
    public <T extends SimpleNode> T reset( T newNode, Node[] newChildren, Object newValue, SymbolDefinition newSymbol )
    {
        if( parent == null )
        {
            newNode.id       = newNode.id;
            newNode.value    = newValue;
            newNode.parser   = parser;
            newNode.children = newChildren;
            newSymbol.value  = newValue;
            SymbolDefinition.copy( newSymbol, newNode.symbol );
            return newNode;
        }
        for( int i = 0; i < parent.jjtGetNumChildren(); i++ )
        {
            SimpleNode n = ( SimpleNode )parent.jjtGetChild( i );
            if( n != this )
            {
                continue;
            }
            newNode.parent   = parent;
            newNode.id       = newNode.id;
            newNode.value    = newValue;
            newNode.parser   = n.parser;
            newNode.children = newChildren;
            newSymbol.value  = newValue;
            SymbolDefinition.copy( newSymbol, newNode.symbol );
            ( ( SimpleNode )parent ).children[ i ]  = newNode;
            break;
        }
        return newNode;
    }

    /**
     * 親ノードが算術演算子ノードかどうかを判定する
     */
    public boolean isParentBinaryOperator()
    {
        if( parent == null )
        {
            return false;
        }
        switch( parent.getId() )
        {
            case JJTSTRADD:
            case JJTBITWISEOR:
            case JJTBITWISEAND:
            case JJTEQUAL:
            case JJTNOTEQUAL:
            case JJTLT:
            case JJTGT:
            case JJTLE:
            case JJTGE:
            case JJTADD:
            case JJTSUB:
            case JJTMUL:
            case JJTDIV:
            case JJTMOD:
            case JJTNEG:
            case JJTNOT:
                return true;
            default:
                return false;
        }
    }

    /**
     * このノードが配列変数で、親ノード次第で添え字の有無の検証が必要かどうかを判定する
     */
    public boolean isNecessaryValidArraySubscribe()
    {
        if( parent == null )
        {
            return false;
        }
        if( !symbol.isArray() )
        {
            return false;
        }
        if( isParentBinaryOperator() )
        {
            return true;
        }
        switch( parent.getId() )
        {
            case JJTASSIGNMENT:
                return true;
            default:
                return false;
        }
    }

//------------------------------------------------------------------------------
// 特定ノード検索
//------------------------------------------------------------------------------

    /**
     * hasNode メソッドに渡すノード検索条件のための基底インタフェース
     */
    static interface HasNodeCondition<T>
    {
        boolean evalCondition( SimpleNode node, T v );
    }

    /**
     * 特定ノードIDを持つノード検索用の条件
     */
    static public final HasNodeCondition<Integer> HasNodeIdCondition = new HasNodeCondition<Integer>() {
        @Override
        public boolean evalCondition( SimpleNode node, Integer id )
        {
            return node.getId() == id;
        }
    };

    /**
     * 特定ノードのシンボルが予約済み変数ノードかどうかの検索用の条件
     */
    static public final HasNodeCondition<Boolean> ReservedSymbolCondition = new HasNodeCondition<Boolean>() {
        @Override
        public boolean evalCondition( SimpleNode node, Boolean reserved )
        {
            return node.symbol.reserved == reserved;
        }
    };

    /**
     * 指定されたノード以下に指定ノードIDを持つノードが存在するかどうかを再帰的に調べる
     * @param node ノード。自身が起点ノードの場合は null
     * @param cond 条件インスタンス
     * @param v 条件比較値
     */
    public <T> boolean hasNode( SimpleNode node, HasNodeCondition<T> cond, T v )
    {
        int len;
        SimpleNode root = this;
        if( node != null )
        {
            root = node;
        }
        len = root.jjtGetNumChildren();
        for( int i = 0; i < len; i++ )
        {
            SimpleNode n = (SimpleNode)root.jjtGetChild( i );
            if( cond.evalCondition( n, v ) )
            {
                return true;
            }
            if( hasNode( n, cond, v ) )
            {
                return true;
            }
        }
        return false;
    }

} public class CommandlineOptions
{
    /** コマンドラインオプションを格納している Bean */
    static public final KSPParserOptions options = new KSPParserOptions();

    /**
     * ctor.
     */
    private CommandlineOptions(){}

    /**
     * 渡されたコマンドライン引数を元にオプションを構成する
     */
    static public CmdLineParser setup( String[] args ) throws CmdLineException
    {
        CmdLineParser parser = new CmdLineParser( options );
        parser.parseArgument( args );

        if( options.strict )
        {
            options.unused = true;
        }
        return parser;
    }
}
import java.util.ArrayList;
import net.rkoubou.kspparser.analyzer.SymbolDefinition.SymbolType;
import net.rkoubou.kspparser.javacc.generated.ASTArrayIndex;
import net.rkoubou.kspparser.javacc.generated.ASTArrayInitializer;
import net.rkoubou.kspparser.javacc.generated.ASTAssignment;
import net.rkoubou.kspparser.javacc.generated.ASTBlock;
import net.rkoubou.kspparser.javacc.generated.ASTCallCommand;
import net.rkoubou.kspparser.javacc.generated.ASTCallUserFunctionStatement;
import net.rkoubou.kspparser.javacc.generated.ASTCallbackDeclaration;
import net.rkoubou.kspparser.javacc.generated.ASTCaseCondition;
import net.rkoubou.kspparser.javacc.generated.ASTCommandArgumentList;
import net.rkoubou.kspparser.javacc.generated.ASTIfStatement;
import net.rkoubou.kspparser.javacc.generated.ASTPrimitiveInititalizer;
import net.rkoubou.kspparser.javacc.generated.ASTRefVariable;
import net.rkoubou.kspparser.javacc.generated.ASTSelectStatement;
import net.rkoubou.kspparser.javacc.generated.ASTUserFunctionDeclaration;
import net.rkoubou.kspparser.javacc.generated.ASTVariableDeclaration;
import net.rkoubou.kspparser.javacc.generated.ASTVariableInitializer;
import net.rkoubou.kspparser.javacc.generated.ASTWhileStatement;
import net.rkoubou.kspparser.javacc.generated.SimpleNode;
import net.rkoubou.kspparser.options.CommandlineOptions;
/* =========================================================================

    SemanticAnalyzer.java
    Copyright (c) R-Koubou

   ======================================================================== */

package net.rkoubou.kspparser.analyzer;



/**
 * 意味解析実行クラス
 */
public class SemanticAnalyzer extends BasicEvaluationAnalyzerTemplate
{

    // 局所的にのみ使用することを前提としたワークエリア
    private final SymbolDefinition tempSymbol = new SymbolDefinition();

    /**
     * ctor
     */
    public SemanticAnalyzer( SymbolCollector symbolCollector )
    {
        super( symbolCollector.astRootNode, symbolCollector );
    }

    /**
     * 意味解析の実行
     */
    @Override
    public void analyze() throws Exception
    {
        astRootNode.jjtAccept( this, null );

        //--------------------------------------------------------------------------
        // 解析後の未使用・未初期化シンボルの洗い出し
        //--------------------------------------------------------------------------

        if( CommandlineOptions.options.unused )
        {
            for( SymbolDefinition s : variableTable.toArray() )
            {
                Variable v = (Variable)s;
                // 参照
                if( !v.referenced )
                {
                    MessageManager.printlnW( MessageManager.PROPERTY_WARNING_SEMANTIC_UNUSE_VARIABLE, v );
                    AnalyzeErrorCounter.w();
                }
                // 初期化（１度も値が格納されていない）
                // ただし、UI型変数は除外
                if( CommandlineOptions.options.strict && v.state == SymbolState.UNLOADED && !v.isUIVariable() )
                {
                    MessageManager.printlnW( MessageManager.PROPERTY_WARNING_SEMANTIC_VARIABLE_INIT, v );
                    AnalyzeErrorCounter.w();
                }

            }
            for( SymbolDefinition v : userFunctionTable.toArray() )
            {
                if( !v.referenced )
                {
                    MessageManager.printlnW( MessageManager.PROPERTY_WARNING_SEMANTIC_UNUSE_FUNCTION, v );
                    AnalyzeErrorCounter.w();
                }
            }
        }
        if( CommandlineOptions.options.strict )
        {
            for( SymbolDefinition s : variableTable.toArray() )
            {
                if( REGEX_NUMERIC_PREFIX.matcher( s.getName() ).find() )
                {
                    MessageManager.printlnW( MessageManager.PROPERTY_WARNING_SEMANTIC_INFO_VARNAME, s );
                    AnalyzeErrorCounter.w();
                }
            }
        }

    }

//--------------------------------------------------------------------------
// ユーティリティ
//--------------------------------------------------------------------------

    /**
     * 上位ノードに返す評価式のテンプレを生成する
     */
    public SimpleNode createEvalNode( SimpleNode src, int nodeId )
    {
        SimpleNode ret = new SimpleNode( nodeId );
        SymbolDefinition.copy( src.symbol, ret.symbol );
        return ret;
    }

//--------------------------------------------------------------------------
// 変数宣言
//--------------------------------------------------------------------------

    /**
     * 変数宣言
     */
    @Override
    public Object visit( ASTVariableDeclaration node, Object data)
    {
/*
    VariableDeclaration                     // NOW
            -> ASTVariableInitializer
                -> [
                      ArrayInitializer
                    | UIInitializer
                    | PrimitiveInititalizer
                ]
*/

        // 初期化式なし
        if( node.jjtGetNumChildren() == 0 )
        {
            final Variable v    = variableTable.search( node.symbol );
            final UIType uiType = uiTypeTable.search( v.uiTypeName );

            // const 修飾子がついているかどうかの検証
            if( v.isConstant() )
            {
                // 変数宣言時に初期化式が必須
                MessageManager.printlnE( MessageManager.PROPERTY_ERROR_SEMANTIC_VARIABLE_REQUIRED_INITIALIZER, v );
                AnalyzeErrorCounter.e();
            }
            // UI型変数の初期値代入必須の検証
            else if( v.isUIVariable() )
            {
                if( uiType == null )
                {
                    // KSP（data/symbol/uitypes.txt）で未定義のUIタイプ
                    // シンボル収集フェーズで警告出力済みなので何もしない
                    v.state = SymbolState.INITIALIZED;
                }
                else if( uiType.initializerRequired )
                {
                    // 変数宣言時に初期化式が必須
                    MessageManager.printlnE( MessageManager.PROPERTY_ERROR_SEMANTIC_VARIABLE_REQUIRED_INITIALIZER, v );
                    AnalyzeErrorCounter.e();
                }
            }

            return node;
        }
        node.childrenAccept( this, node );
        return node;
    }

    /**
     * 変数宣言(+初期値代入)
     */
    @Override
    public Object visit( ASTVariableInitializer node, Object data )