/* Oort * Copyright 2007, Soren Sandmann Pedersen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "ast.h" static symbol_table_t * symbol_table_new (symbol_table_t *outer) { symbol_table_t *table = g_new0 (symbol_table_t, 1); table->outer = outer; table->symbols = g_hash_table_new (g_str_hash, g_str_equal); return table; } static gboolean symbol_table_insert (symbol_table_t *table, const char *name, gpointer value) { if (g_hash_table_lookup (table->symbols, name)) return FALSE; g_hash_table_insert (table->symbols, g_strdup (name), value); return TRUE; } static gpointer symbol_table_lookup (symbol_table_t *table, const char *key) { while (table) { gpointer result = g_hash_table_lookup (table->symbols, key); if (result) return result; table = table->outer; } return NULL; } /* * First pass to gather definitions so we can support mutual recursion */ static symbol_table_t * find_outer_table (ast_t *ast) { if (!ast) return NULL; if (ast->common.type == AST_PROGRAM) return ast->program.symbol_table; else if (ast_is (ast, AST_DEFINITION, AST_FUNCTION_DEFINITION)) return ast->definition.function.symbol_table; else if (ast_is (ast, AST_STATEMENT, AST_BLOCK_STATEMENT)) return ast->statement.block.symbol_table; else if (ast_is (ast, AST_EXPRESSION, AST_BLOCK_EXPRESSION)) return ast->expression.block.symbol_table; else if (ast_is (ast, AST_DEFINITION, AST_CLASS_DEFINITION)) return ast->definition.class.symbol_table; else return find_outer_table (ast->common.parent); } static gboolean already_defined (const char *name) { return report_error ("name %s is already defined\n", name); } static gboolean symbol_pass1 (ast_t *ast) { GList *list; symbol_table_t *table = (void *)find_outer_table (ast->common.parent); if (ast_is (ast, AST_DEFINITION, AST_FUNCTION_DEFINITION)) { ast_function_definition_t *function = &ast->definition.function; if (!symbol_table_insert (table, function->name, (ast_definition_t *)function)) { return already_defined (function->name); } function->symbol_table = symbol_table_new (table); } else if (ast_is (ast, AST_DEFINITION, AST_CLASS_DEFINITION)) { ast_class_definition_t *class = &ast->definition.class; if (!symbol_table_insert (table, class->name, (ast_definition_t *)class)) { return already_defined (class->name); } class->symbol_table = symbol_table_new (table); } else if (ast_is (ast, AST_DEFINITION, AST_TYPE_DEFINITION)) { ast_type_definition_t *type = &ast->definition.type; if (!symbol_table_insert (table, type->name, (ast_definition_t *)type)) { return already_defined (type->name); } } else if (ast_is (ast, AST_STATEMENT, AST_LABEL_STATEMENT)) { ast_label_statement_t *label = &ast->statement.label; ast_t *a; /* Labels have function scope or, if they are not in a function, * program scope, so we need a special-cased lookup here. */ a = ast->common.parent; for (;;) { if (a->common.type == AST_PROGRAM) { table = a->program.symbol_table; break; } else if (ast_is (a, AST_DEFINITION, AST_FUNCTION_DEFINITION)) { table = a->definition.function.symbol_table; break; } a = a->common.parent; } if (!symbol_table_insert (table, label->label, label->definition)) return report_error ("label %s already defined\n", label->label); } else if (ast_is (ast, AST_STATEMENT, AST_BLOCK_STATEMENT)) { ast_block_statement_t *block = &ast->statement.block; block->symbol_table = symbol_table_new (table); } else if (ast_is (ast, AST_EXPRESSION, AST_BLOCK_EXPRESSION)) { ast_block_expression_t *block = &ast->expression.block; block->symbol_table = symbol_table_new (table); } for (list = ast->common.children->head; list; list = list->next) { if (!symbol_pass1 (list->data)) return FALSE; } return TRUE; } /* * Second pass to check that variables are defined */ static gboolean symbol_pass2 (ast_t *ast) { symbol_table_t *table = find_outer_table (ast); GList *list; if (ast_is (ast, AST_DEFINITION, AST_VARIABLE_DEFINITION)) { ast_definition_t *definition = &ast->definition; if (!symbol_table_insert (table, definition->variable.name, definition)) { return report_error ("%s already defined\n", definition->variable.name); } } else if (ast_is (ast, AST_EXPRESSION, AST_IDENTIFIER_EXPRESSION)) { ast_identifier_expression_t *id = &ast->expression.identifier; ast_definition_t *definition = symbol_table_lookup (table, id->name); if (!definition) return report_error ("Undefined variable %s\n", id->name); id->definition = definition; } else if (ast_is (ast, AST_TYPE_SPEC, AST_IDENTIFIER_TYPE)) { ast_identifier_type_spec_t *id = &ast->type_spec.identifier; ast_definition_t *definition = symbol_table_lookup (table, id->name); if (!definition) return report_error ("Undefined type name %s\n", id->name); if (definition->common.type != AST_CLASS_DEFINITION && definition->common.type != AST_TYPE_DEFINITION) { return report_error ("%s is not a type name\n", id->name); } id->definition = definition; } for (list = ast->common.children->head; list; list = list->next) { if (!symbol_pass2 (list->data)) return FALSE; } return TRUE; } gboolean symbol (ast_t *ast) { ast->program.symbol_table = symbol_table_new (NULL); if (!symbol_pass1 (ast)) return FALSE; if (!symbol_pass2 (ast)) return FALSE; return TRUE; }