Теперь, как делать я знать тип, объем идентификаторов, так как все я знать значение лексемы и номер строки для этого конкретного 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;
}
И так далее ...
Итак, я использую C, мои узлы parsetree имеют значение информации lexeme и токен NAme i.e if/IF или имя/идентификатор или 12/INTEGER.Теперь скажите, я обхожу дерево, каждый раз, когда я сталкиваюсь с идентификатором, мне нужно будет ввести значение в таблицу символов вместе с типом, областью видимости и другими, скажем, для области я проверяю, сталкивается ли я с именем «{» или имени функции, но как я знаю, какой тип ID это. – Kraken
@Kraken: Звучит так, будто вы не правильно строили дерево. См. Обновление. – blackcompe
Позвольте мне передать вам все, что я сделал до сих пор. 1. Лексический анализ: правильно подделать лексемы, сохранить всю информацию, собранную во время прохождения исходного кода (то есть значение lexeme, tokeName, lineno.). 2. Анализ синтаксиса: используется предсказательный синтаксический анализатор (с использованием таблицы синтаксического анализа), правила грамматики, которые я поддерживаю как правила массива, причем каждая запись массива является связанным списком и поддерживает каждое правило. Аналогично выполняются первые и последующие наборы. Теперь я поддерживаю стек со стартовым символом, проверяю следующий токен из списка токенов, генерируемого лексическим анализатором и (coninued) .. – Kraken