[構文解析]変数を識別するアナライザ


変数というもの自体は簡単で、識別子の先頭にすぎず、演算の正反対かっこをいくつか挟んでいるのに対し、演算ノードを分析するタスクは専門の解析器に任せることができるので、変数を識別する解析器自体には何もする必要はありません.次はデータ構造です.
struct VariableAnalyser {
    memberSyntaxAnalyser
    struct VariableNode* var; //          
};

次はその各メンバー関数の実装であり,ここでは興味深い実装方式を用いた.
/* variable-analyser.c */
#include<stdlib.h>
#include"datastruct.h"
#include"syntax-analyser.h"
#include"syntax-node.h"

#include"COOL/MemoryAllocationDebugger.h"

extern struct Stack* analyserStack;
extern struct SyntaxAnalyser* newOperationAnalyser(void);

#define wrapname(name) name ## _VariableAnalyser
void wrapname(consumeIdentifier)(void*, struct Token*);
void wrapname(consumeLBracket)(void*, struct Token*);
void wrapname(consumeRBracket)(void*, struct Token*);

void wrapname(consumeIdentifier)(void* self, struct Token* t)
{
    ((struct VariableAnalyser*)self)->var = newVariableNode(t->image);
    ((struct VariableAnalyser*)self)->consumeToken = wrapname(consumeLBracket);
}

void wrapname(consumeLBracket)(void* self, struct Token* t)
{
    if(LBRACKET == t->type) {
        analyserStack->push(analyserStack, newOperationAnalyser());
    } else {
//        printf("Varaible analyser returns.
"); struct VariableNode* var = ((struct VariableAnalyser*)self)->var; revert(analyserStack->pop(analyserStack)); struct SyntaxAnalyser* analyser = analyserStack->peek(analyserStack); analyser->consumeNonTerminal(analyser, (struct AbstractSyntaxNode*)var); analyser = analyserStack->peek(analyserStack); analyser->consumeToken(analyser, t); } } void wrapname(consumeRBracket)(void* self, struct Token* t) { if(RBRACKET == t->type) { ((struct VariableAnalyser*)self)->consumeToken = wrapname(consumeLBracket); } else { printf("Expecting `]', but %s
", t->image); // TODO } } void wrapname(consumeNonTerminal)(void* self, struct AbstractSyntaxNode* op) { struct VariableAnalyser* varAna = (struct VariableAnalyser*)self; varAna->var->dimInfor->enqueue(varAna->var->dimInfor, op); varAna->consumeToken = wrapname(consumeRBracket); } struct SyntaxAnalyser* newVariableAnalyser(void) { struct VariableAnalyser* varAna = (struct VariableAnalyser*) allocate(sizeof(struct VariableAnalyser)); varAna->consumeToken = wrapname(consumeIdentifier); varAna->consumeNonTerminal = wrapname(consumeNonTerminal); return (struct SyntaxAnalyser*)varAna; } #undef wrapname

OperationAnalyserの実装では、現在必要とされている演算子か数かを区別するために、consumeToken関数は、現在のOperationAnalyserのneedFactorメンバーに基づいてconsumeFactorを呼び出すかconsumeOperatorを呼び出すかを判断する分流関数を指していることを覚えています.それは悪い実装です.マルチステートで実現できるはずの部分が、分岐文を乱用しているのは、設計モードで問題があります.VariableAnalyserはこれを修正しました.VariableAnalyserが新しく作成されたばかりの頃、そのconsumeToken関数はwrapname(consumeIdentifiier)関数を指し、その関数内で自身のconsumeToken関数をwrapname(consumeLbracket)関数に変更した.他の関数でも似たような小さな動作が見られます.これらの小さな動作はデザインをより明確にした.
添付:構文解析モジュールをテストするドライブ
もちろん、文法分析が終わったわけではありませんし、エラー処理が完了していません.
これは構文解析ヘッダファイルです.アナライザ構造定義をdatastruct.に入れるのを忘れないでください.h中.
/* syntax-analyser.h */
#ifndef _SYNTAX_ANALYSER_H
#define _SYNTAX_ANALYSER_H

#include"datastruct.h"

struct SyntaxAnalyser* newOperationAnalyser(void);

void initialLRStates(void);
void destructLRStates(void);
struct SyntaxAnalyser* newLRAnalyser(void);

struct SyntaxAnalyser* newVariableAnalyser(void);

#ifdef _DEBUG_MODE
#include"operation-analyser.c"
#include"lr-analyser.c"
#include"variable-analyser.c"
#endif /* _DEBUG_MODE */

#endif /* _SYNTAX_ANALYSER_H */

次のものはOperationAnalyserのテストドライブから外れています.しかし、行番号も処理されていません.文法分析のテストと組み合わせて修正することができます.
#include<stdio.h>

#ifndef _DEBUG_MODE
#define _DEBUG_MODE
#endif /* _DEBUG_MODE */
#include"COOL/MemoryAllocationDebugger.h"

#include"datastruct.h"
#include"syntax-node.h"
#include"syntax-analyser.h"
#include"dfa.h"
#include"dfa.c"

FILE* treeout;

struct FakeDefaultAnalyser {
    memberSyntaxAnalyser
};

struct SyntaxAnalyser* newFakeDefaultAnalyser(void);
void FakeDefaultAnalyser_ConsumeNT(void*, struct AbstractSyntaxNode*);
void FakeDefaultAnalyser_ConsumeT(void*, struct Token*);

struct Stack* analyserStack; //     
FILE* input; //     

//      
char buffer[64];
struct Token token = {
    0, SKIP, NULL, buffer
};

int main()
{
    treeout = fopen("syntax.xml", "w"); //    xml
    input = fopen("testin", "r");
    analyserStack = newStack();
    analyserStack->push(analyserStack, newFakeDefaultAnalyser());

    initialLRStates();
    tokenize();
    destructLRStates();

    printf("Test done.
"); revert(analyserStack->pop(analyserStack)); analyserStack->finalize(analyserStack); showAlloc; fclose(input); fclose(treeout); return 0; } struct SyntaxAnalyser* newFakeDefaultAnalyser(void) { struct SyntaxAnalyser* defAna = (struct SyntaxAnalyser*) allocate(sizeof(struct FakeDefaultAnalyser)); defAna->consumeToken = FakeDefaultAnalyser_ConsumeT; defAna->consumeNonTerminal = FakeDefaultAnalyser_ConsumeNT; return defAna; } void FakeDefaultAnalyser_ConsumeNT(void* self, struct AbstractSyntaxNode* node) { printf("
Returned.
"); if(NULL == node) { fprintf(treeout, "NULL returned.
"); } else { fprintf(treeout, "<Jerry>
"); node->printree(node, 1); fprintf(treeout, "</Jerry>
"); node->delNode(node); } } void FakeDefaultAnalyser_ConsumeT(void* self, struct Token* t) { if(NULL == t->image) { printf("Token passed: %2d / NULL image
", t->type); } else { printf("Error before %s
", t->image); exit(1); } } int nextChar(void) { return fgetc(input); } struct Token* firstToken(void) { analyserStack->push(analyserStack, newLRAnalyser()); return &token; } struct Token* nextToken(void) { if(SKIP != token.type) { struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*) (analyserStack->peek(analyserStack)); analyser->consumeToken(analyser, &token); } return &token; } void eofToken(void) { nextToken(); struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*) (analyserStack->peek(analyserStack)); struct Token endToken = {0, END, NULL, NULL}; analyser->consumeToken(analyser, &endToken); }