wip: working on analyzer
- Feat: implement return analyzer - Fix: add block return check if inside function
This commit is contained in:
@@ -31,6 +31,11 @@ typedef struct fzl_an_ctx
|
||||
struct fzl_an_ctx *parent;
|
||||
|
||||
fzl_an_ctx_type_t type;
|
||||
|
||||
union
|
||||
{
|
||||
fzl_node_t *fn;
|
||||
};
|
||||
} fzl_an_ctx_t;
|
||||
|
||||
// Analyzer descriptor
|
||||
@@ -73,6 +78,7 @@ fzl_an_res_t analyze_identifier(fzl_analyzer_t *analyzer, fzl_node_t *id);
|
||||
fzl_an_res_t analyze_conditional(fzl_analyzer_t *analyzer, fzl_node_t *cond);
|
||||
fzl_an_res_t analyze_function(fzl_analyzer_t *analyzer, fzl_node_t *fn_node);
|
||||
fzl_an_res_t analyze_block(fzl_analyzer_t *analyzer, fzl_node_t *block_node);
|
||||
fzl_an_res_t analyze_return(fzl_analyzer_t *analyzer, fzl_node_t *ret_node);
|
||||
|
||||
fzl_an_ctx_t *an_nest_ctx(fzl_analyzer_t *analyzer, fzl_an_ctx_type_t type);
|
||||
fzl_an_ctx_t *an_fin_ctx(fzl_analyzer_t *analyzer);
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
#include <common/fzl_types.h>
|
||||
#include <compiler/fzl_analyzer.h>
|
||||
#include <compiler/fzl_parser.h>
|
||||
#include <compiler/fzl_token.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
char block_problem[256];
|
||||
|
||||
// defined in `compiler/analyzer/utility.c`
|
||||
extern fzl_type_t an_toktotype(fzl_token_t *tok);
|
||||
extern char *an_typetostr(fzl_type_t type, char *classname);
|
||||
|
||||
// Analyze block
|
||||
fzl_an_res_t analyze_block(fzl_analyzer_t *analyzer, fzl_node_t *block_node)
|
||||
{
|
||||
@@ -38,6 +43,48 @@ fzl_an_res_t analyze_block(fzl_analyzer_t *analyzer, fzl_node_t *block_node)
|
||||
has_return = true;
|
||||
}
|
||||
|
||||
fzl_an_ctx_t *fnctx = analyzer->ctx;
|
||||
while (fnctx != NULL)
|
||||
{
|
||||
if (fnctx->type == FZL_AN_CTX_TYPE_FUNCTION)
|
||||
break;
|
||||
|
||||
fnctx = fnctx->parent;
|
||||
}
|
||||
|
||||
if (fnctx && fnctx->type == FZL_AN_CTX_TYPE_FUNCTION)
|
||||
{
|
||||
fzl_type_t rettype = an_toktotype(fnctx->fn->fn.type);
|
||||
char *classname = NULL;
|
||||
char *rettype_str = an_typetostr(rettype, classname);
|
||||
|
||||
if (rettype == FZL_TYPE_OBJECT)
|
||||
classname = (char *)fnctx->fn->fn.type->value;
|
||||
|
||||
if (rettype != FZL_TYPE_NULL)
|
||||
{
|
||||
if (!has_return || block_node->list.entry_count < 1)
|
||||
{
|
||||
snprintf(block_problem, sizeof(block_problem),
|
||||
"expected 'return <%s>' in function because return type is %s",
|
||||
rettype_str, rettype_str);
|
||||
an_push_error(analyzer, block_node->token, block_problem);
|
||||
return (fzl_an_res_t){.error = true};
|
||||
}
|
||||
|
||||
fzl_node_t *last_node =
|
||||
block_node->list.entries[block_node->list.entry_count - 1];
|
||||
if (last_node->type != FZL_NODETYPE_RETURN)
|
||||
{
|
||||
snprintf(block_problem, sizeof(block_problem),
|
||||
"expected last statement in block to be 'return <%s>'",
|
||||
rettype_str);
|
||||
an_push_error(analyzer, last_node->token, block_problem);
|
||||
return (fzl_an_res_t){.error = true};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
an_fin_ctx(analyzer);
|
||||
|
||||
return (fzl_an_res_t){.error = error, .type = FZL_TYPE_NULL};
|
||||
|
||||
@@ -11,6 +11,10 @@ fzl_an_res_t analyze_common(fzl_analyzer_t *analyzer, fzl_node_t *node)
|
||||
fzl_an_res_t res = {.error = false, .type = FZL_TYPE_NULL, .classname = NULL};
|
||||
switch (node->type)
|
||||
{
|
||||
case FZL_NODETYPE_RETURN:
|
||||
res = analyze_return(analyzer, node);
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_FUNCTION:
|
||||
res = analyze_function(analyzer, node);
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "common/fzl_types.h"
|
||||
#include <compiler/fzl_analyzer.h>
|
||||
#include <compiler/fzl_parser.h>
|
||||
#include <compiler/fzl_token.h>
|
||||
#include <stdio.h>
|
||||
|
||||
char ret_problem[256];
|
||||
|
||||
// defined in `compiler/analyzer/utility.c`
|
||||
extern bool an_is_compatible(fzl_type_t a, fzl_type_t b, char *ac, char *bc);
|
||||
extern fzl_type_t an_toktotype(fzl_token_t *tok);
|
||||
extern char *an_typetostr(fzl_type_t type, char *classname);
|
||||
|
||||
// Analyze return statement
|
||||
fzl_an_res_t analyze_return(fzl_analyzer_t *analyzer, fzl_node_t *ret_node)
|
||||
{
|
||||
if (!analyzer)
|
||||
return (fzl_an_res_t){.error = true};
|
||||
|
||||
if (!ret_node || ret_node->type != FZL_NODETYPE_RETURN)
|
||||
{
|
||||
an_push_error(analyzer, NULL, "node is not typeof RETURN");
|
||||
return (fzl_an_res_t){.error = false};
|
||||
}
|
||||
|
||||
fzl_an_ctx_t *fnctx = analyzer->ctx;
|
||||
while (!fnctx)
|
||||
{
|
||||
if (fnctx->type == FZL_AN_CTX_TYPE_FUNCTION)
|
||||
break;
|
||||
|
||||
fnctx = fnctx->parent;
|
||||
}
|
||||
|
||||
if (!fnctx)
|
||||
{
|
||||
an_push_error(analyzer, ret_node->token,
|
||||
"cannot use 'return' outside function");
|
||||
return (fzl_an_res_t){.error = true};
|
||||
}
|
||||
|
||||
fzl_type_t rettype = an_toktotype(fnctx->fn->fn.type);
|
||||
char *classname = NULL;
|
||||
if (rettype == FZL_TYPE_OBJECT)
|
||||
classname = (char *)fnctx->fn->fn.type->value;
|
||||
|
||||
char *rettype_str = an_typetostr(rettype, classname);
|
||||
if (rettype != FZL_TYPE_NULL)
|
||||
{
|
||||
if (ret_node->ret.value == NULL)
|
||||
{
|
||||
an_push_error(analyzer, ret_node->token,
|
||||
"return must be used with value in this function");
|
||||
return (fzl_an_res_t){.error = true};
|
||||
}
|
||||
|
||||
fzl_an_res_t ret_res = analyze_common(analyzer, ret_node->ret.value);
|
||||
if (!an_is_compatible(ret_res.type, rettype, ret_res.classname, classname))
|
||||
{
|
||||
snprintf(ret_problem, sizeof(ret_problem),
|
||||
"expected 'return <%s>' instead of 'return <%s>'", rettype_str,
|
||||
an_typetostr(ret_res.type, ret_res.classname));
|
||||
an_push_error(analyzer, ret_node->token, ret_problem);
|
||||
return (fzl_an_res_t){.error = true};
|
||||
}
|
||||
}
|
||||
|
||||
return (fzl_an_res_t){
|
||||
.error = false, .type = rettype, .classname = classname};
|
||||
}
|
||||
Reference in New Issue
Block a user