2012-03-14 2 views
0

После создания дерева синтаксиса я должен заполнить таблицу символов сейчас.Таблица символов Население после разбора; Компилятор здания

Я должен хранить информацию, как

Type, Scope, офсетные и т.д. для идентификаторов.

Теперь, как узнать тип, область действия идентификаторов, поскольку все, что я знаю, - это значение лексемы и номер строки для этого конкретного идентификатора (после лексического анализа).

Как мне получить все это. Благодарю.

ответ

2

Теперь, как делать я знать тип, объем идентификаторов, так как все я знать значение лексемы и номер строки для этого конкретного ID (после лексического анализа).

Как указано в EJP, необходимо пройти через дерево разбора. Ваше дерево должно быть создано так, чтобы вы могли совершать обход в порядке, посещая каждый узел в том же порядке, в котором вычисляются выражения и выражения исходного кода. Ваши узлы дерева также должны соответствовать определенной языковой конструкции, например. WhileStmtNode, MethodDeclNode и т. Д.

Предположим, что я создаю таблицу символов, рекурсивно перешагивая через дерево, и я только что ввел узел тела метода. Я мог бы иметь примерно следующее:

public void doAction(MethodBodyNode methodBody) { 
    currScope = 2; 
    methodBody.getExpr().applyAction(this); 
    currScope = 2; 
} 

Я сохраняю глобальную переменную для управления областью. Каждый раз, когда я вхожу в блок, где изменяется область, я увеличивал бы currScope. Аналогичным образом, я бы сохранил и currMethod переменные для хранения с именем, типом, смещением и т. Д. Для последующих этапов.

Update:

Теперь говорят, я обхода дерева, каждый раз, когда я наткнулся на ID я бы ввести значение в таблицу символов, а также тип, сферы и других, скажем, для области я проверяю, сталкивается ли я с именем функции '{' или , но как узнать, какой тип ID это.

Каждый узел дерева должен содержать всю необходимую информацию для всей конструкции. Если вы используете генератор парсера, например CUP или Bison, вы можете указать, как построить дерево в действиях грамматики. Например.

variableDeclaration::= identifier:i identifier:i2 SEMICOLON {: RESULT = new VarDeclNode(i, i2, null); :}; 
identifier::= ID:i {: RESULT = new IdNode(i.getLineNum(), i.getCharNum(), i.getStringValue()); :}; 

Эти произведения будут соответствовать Foo f; и добавить переменную узел декларации к дереву. Этот узел инкапсулирует два идентификационных узла, которые содержат номер строки, номер символа и строковое значение лексемы.Первый идентификатор - это тип, а второй - имя переменной. ID - это символ терминала, возвращаемый лексером при сопоставлении идентификатора. Я предполагаю, что вы знакомы с этим в некоторой степени.

public class VarDeclNode extends StmtNode { 

    private IdNode id; 
    private IdNode type; 
    private ExprNode expr; 

    public VarDeclNode(IdNode id, IdNode type, ExprNode expr) { 
     super(); 
     this.id = id; 
     this.type = type; 
     this.expr = expr; 
    } 

} 

Когда у вас есть дерево синтаксиса с такими узлами, у вас есть вся необходимая информация.

второе обновление:

Это не имеет значения, используете ли вы пользовательский анализатор или сгенерированные один, есть один отличный момент, когда вы добавляете узел в дерево на соответствие производства. И не имеет значения, какой язык вы используете. Структуры C будут делать все отлично.

если это не терминал имеет информацию, как имя нетерминалы, а если является терминал т.е. знак, то информация в лексемы, т.е. лексема значения, лексема имя и номер строки сохраняются

Вы должны иметь специализированные узлы в дереве, например ClassNode, TypeNode, MethodDeclNode, IfStmtNode, ExprNode. Вы не можете просто сохранить один тип узла и поместить в него нетерминалы и терминалы. Нетерминал представлен как узел дерева, нет никакой другой информации, чтобы хранить ее рядом с составляющими ее частями, которые обычно являются не-терминалами. Вы не будете хранить информацию о токенах. Есть только несколько примеров, когда вы фактически сохранили строковое значение lexeme: для идентификатора и для литерала string/boolean/integer.

Посмотрите на пример this. Во время первого сокращения, когда S сокращен до (S + F), вы добавляете ParenExprNode к корню дерева. Вы также добавляете AddExprNode в качестве дочернего элемента ParenExprNode. Эта логика должна быть жестко закодирована в ваш парсер при применении сокращения по правилу 2 грамматики.

Дерево:

ExprNode (root) 
     | 
    ParenExprNode 
     | 
    AddExprNode 
/  \ 
ExprNode ExprNode 

Код:

struct ExprNode { void* exprNode; }; 
struct ParenExprNode { void* exprNode; }; 
struct AddExprNode { void* op1, * op2; }; 
struct IdNode { char* val; int line; int charNum; }; 
struct IntLiteralNode { int val; int line; int charNum; }; 

void reduce_rule_2(ExprNode* expr) { 

    //remove stack symbols 

    //create nodes 
    struct ParenExprNode* parenExpr = malloc(sizeof(struct ParenExprNode)); 
    struct AddExprNode* addExpr = malloc(sizeof(struct AddExprNode)); 
    addExpr->op1 = malloc(sizeof(struct ExprNode)); 
    addExpr->op2 = malloc(sizeof(struct ExprNode)); 

    //link them 
    parenExpr->exprNode = (void*)addExpr; 
    expr->exprNode = (void*)parenExpr; 
} 

На следующем этапе, левая скобка удаляется от входа. После этого S находится поверх стека и сводится к F по правилу 1. Поскольку F является нетерминальным для идентификатора, он представлен IdNode.

Дерево:

ExprNode 
     | 
    ParenExprNode 
     | 
    AddExprNode 
/  \ 
ExprNode ExprNode 
    | 
IdNode 

Код:

reduce_rule_2(addExpr->op1); 

void reduce_rule_1(ExprNode* expr) { 
    //reduce stack symbols 
    struct IdNode* id = malloc(sizeof(struct IdNode)); 
    id->val = parser_matched_text(); 
    id->lineNum = parser_line_num(); 
    id->charNum = parser_char_num(); 
    expr->exprNode = (void*)id; 
} 

И так далее ...

+0

Итак, я использую C, мои узлы parsetree имеют значение информации lexeme и токен NAme i.e if/IF или имя/идентификатор или 12/INTEGER.Теперь скажите, я обхожу дерево, каждый раз, когда я сталкиваюсь с идентификатором, мне нужно будет ввести значение в таблицу символов вместе с типом, областью видимости и другими, скажем, для области я проверяю, сталкивается ли я с именем «{» или имени функции, но как я знаю, какой тип ID это. – Kraken

+0

@Kraken: Звучит так, будто вы не правильно строили дерево. См. Обновление. – blackcompe

+0

Позвольте мне передать вам все, что я сделал до сих пор. 1. Лексический анализ: правильно подделать лексемы, сохранить всю информацию, собранную во время прохождения исходного кода (то есть значение lexeme, tokeName, lineno.). 2. Анализ синтаксиса: используется предсказательный синтаксический анализатор (с использованием таблицы синтаксического анализа), правила грамматики, которые я поддерживаю как правила массива, причем каждая запись массива является связанным списком и поддерживает каждое правило. Аналогично выполняются первые и последующие наборы. Теперь я поддерживаю стек со стартовым символом, проверяю следующий токен из списка токенов, генерируемого лексическим анализатором и (coninued) .. – Kraken

0

все, что я знаю, это значение лексемы и номер строки для этого конкретного ID

Это не так. Вы знаете, где он объявлен в дереве синтаксического разбора, который сообщает вам все, что вам нужно. Вы делаете этот шаг на обработки дерева синтаксического разбора.

+0

обработкой вы имеете в виду, создание грамматики атрибутов (наследуемые атрибуты) [я только что пришел через эти термины, следовательно, я мог бы быть совершенно неправильным], и для области видимости я могу просто проверить, встречена ли новая пара скобок, или новая функция. – Kraken

+0

@Kraken Да, вот что я имею в виду. Вы никогда не обрабатываете символы только с лексемой и номером строки. – EJP