diff --git a/CMakeLists.txt b/CMakeLists.txt index fecc083..e388003 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,12 +22,14 @@ add_executable(fzc src/compiler/checkers/fzl_loop_check.c src/compiler/analyzer/utility.c src/compiler/analyzer/context.c + src/compiler/analyzer/an_assign.c src/compiler/analyzer/an_binmath.c src/compiler/analyzer/an_block.c src/compiler/analyzer/an_break.c src/compiler/analyzer/an_call.c src/compiler/analyzer/an_class.c src/compiler/analyzer/an_common.c + src/compiler/analyzer/an_condition.c src/compiler/analyzer/an_conditional.c src/compiler/analyzer/an_construct.c src/compiler/analyzer/an_declaration.c @@ -36,6 +38,8 @@ add_executable(fzc src/compiler/analyzer/an_identifier.c src/compiler/analyzer/an_loop.c src/compiler/analyzer/an_return.c + src/compiler/analyzer/an_unarycond.c + src/compiler/analyzer/an_unarymath.c src/compiler/fzl_analyzer.c src/compiler/fzl_debug.c src/compiler/fzl_parser.c diff --git a/include/compiler/fzl_analyzer.h b/include/compiler/fzl_analyzer.h index 56a5794..76cced5 100644 --- a/include/compiler/fzl_analyzer.h +++ b/include/compiler/fzl_analyzer.h @@ -88,6 +88,10 @@ fzl_an_res_t analyze_construct(fzl_analyzer_t *analyzer, fzl_node_t *construct); fzl_an_res_t analyze_loop(fzl_analyzer_t *analyzer, fzl_node_t *loop_node); fzl_an_res_t analyze_break(fzl_analyzer_t *analyzer, fzl_node_t *break_node); fzl_an_res_t analyze_binmath(fzl_analyzer_t *analyzer, fzl_node_t *bin_node); +fzl_an_res_t analyze_unarymath(fzl_analyzer_t *analyzer, fzl_node_t *unary); +fzl_an_res_t analyze_condition(fzl_analyzer_t *analyzer, fzl_node_t *cond_node); +fzl_an_res_t analyze_unarycond(fzl_analyzer_t *analyzer, fzl_node_t *unary); +fzl_an_res_t analyze_assign(fzl_analyzer_t *analyzer, fzl_node_t *assign_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); diff --git a/src/compiler/analyzer/an_assign.c b/src/compiler/analyzer/an_assign.c new file mode 100644 index 0000000..927e997 --- /dev/null +++ b/src/compiler/analyzer/an_assign.c @@ -0,0 +1,54 @@ +#include "common/fzl_types.h" +#include "compiler/fzl_analyzer.h" +#include "compiler/fzl_parser.h" +#include + +char asn_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 char *an_typetostr(fzl_type_t type, char *classname); + +// Analyze assigns +fzl_an_res_t analyze_assign(fzl_analyzer_t *analyzer, fzl_node_t *assign_node) +{ + if (!analyzer) + return (fzl_an_res_t){.error = true}; + + if (!assign_node || assign_node->type != FZL_NODETYPE_ASSIGN) + { + an_push_error(analyzer, NULL, "node is not typeof ASSIGN"); + return (fzl_an_res_t){.error = true}; + } + + fzl_node_t *left = assign_node->binary_op.left; + fzl_an_res_t leftside = analyze_common(analyzer, left); + if (leftside.error) + return (fzl_an_res_t){.error = true}; + + fzl_an_res_t rightside = + analyze_common(analyzer, assign_node->binary_op.right); + if (rightside.error) + return (fzl_an_res_t){.error = true}; + + if (left->type != FZL_NODETYPE_DECLARATION && + left->type != FZL_NODETYPE_IDENTIFIER && left->type != FZL_NODETYPE_FIELD) + { + an_push_error(analyzer, left->token, + "this type of assignment left is not supported"); + return (fzl_an_res_t){.error = true}; + } + + if (!an_is_compatible(leftside.type, rightside.type, leftside.classname, + rightside.classname)) + { + snprintf(asn_problem, sizeof(asn_problem), "cannot assign %s to %s", + an_typetostr(rightside.type, rightside.classname), + an_typetostr(leftside.type, leftside.classname)); + an_push_error(analyzer, assign_node->binary_op.right->token, asn_problem); + return (fzl_an_res_t){.error = true}; + } + + return (fzl_an_res_t){ + .error = false, .type = FZL_TYPE_NULL, .classname = NULL}; +} diff --git a/src/compiler/analyzer/an_binmath.c b/src/compiler/analyzer/an_binmath.c index 3ceb96f..2e89aac 100644 --- a/src/compiler/analyzer/an_binmath.c +++ b/src/compiler/analyzer/an_binmath.c @@ -11,11 +11,12 @@ fzl_an_res_t analyze_binmath(fzl_analyzer_t *analyzer, fzl_node_t *bin_node) if (!analyzer) return (fzl_an_res_t){.error = true}; - if (!bin_node || bin_node->type != FZL_NODETYPE_ADD || - bin_node->type != FZL_NODETYPE_SUB || - bin_node->type != FZL_NODETYPE_MUL || - bin_node->type != FZL_NODETYPE_DIV || - bin_node->type != FZL_NODETYPE_POW || bin_node->type != FZL_NODETYPE_MOD) + if (!bin_node || (bin_node->type != FZL_NODETYPE_ADD && + bin_node->type != FZL_NODETYPE_SUB && + bin_node->type != FZL_NODETYPE_MUL && + bin_node->type != FZL_NODETYPE_DIV && + bin_node->type != FZL_NODETYPE_POW && + bin_node->type != FZL_NODETYPE_MOD)) { an_push_error(analyzer, NULL, "node is not typeof binary math"); return (fzl_an_res_t){.error = true}; diff --git a/src/compiler/analyzer/an_common.c b/src/compiler/analyzer/an_common.c index c893867..a8f43fb 100644 --- a/src/compiler/analyzer/an_common.c +++ b/src/compiler/analyzer/an_common.c @@ -11,6 +11,30 @@ 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_ASSIGN: + res = analyze_assign(analyzer, node); + break; + + case FZL_NODETYPE_NOT: + res = analyze_unarycond(analyzer, node); + break; + + case FZL_NODETYPE_OR: + case FZL_NODETYPE_AND: + case FZL_NODETYPE_LESS: + case FZL_NODETYPE_LESS_EQUAL: + case FZL_NODETYPE_GREATER: + case FZL_NODETYPE_GREATER_EQUAL: + case FZL_NODETYPE_EQUAL: + case FZL_NODETYPE_NEQUAL: + res = analyze_condition(analyzer, node); + break; + + case FZL_NODETYPE_UNARY_MINUS: + case FZL_NODETYPE_UNARY_PLUS: + res = analyze_unarymath(analyzer, node); + break; + case FZL_NODETYPE_ADD: case FZL_NODETYPE_SUB: case FZL_NODETYPE_MUL: diff --git a/src/compiler/analyzer/an_condition.c b/src/compiler/analyzer/an_condition.c new file mode 100644 index 0000000..0a0505b --- /dev/null +++ b/src/compiler/analyzer/an_condition.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include + +char bincond_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 char *an_typetostr(fzl_type_t type, char *classname); + +// Analyze binary conditional nodes +fzl_an_res_t analyze_condition(fzl_analyzer_t *analyzer, fzl_node_t *cond_node) +{ + if (!analyzer) + return (fzl_an_res_t){.error = true}; + + if (!cond_node || (cond_node->type != FZL_NODETYPE_OR && + cond_node->type != FZL_NODETYPE_AND && + cond_node->type != FZL_NODETYPE_LESS && + cond_node->type != FZL_NODETYPE_LESS_EQUAL && + cond_node->type != FZL_NODETYPE_GREATER && + cond_node->type != FZL_NODETYPE_GREATER_EQUAL && + cond_node->type != FZL_NODETYPE_EQUAL && + cond_node->type != FZL_NODETYPE_NEQUAL)) + { + an_push_error(analyzer, NULL, "node is not typeof binary condition"); + return (fzl_an_res_t){.error = true}; + } + + fzl_an_res_t left = analyze_common(analyzer, cond_node->binary_op.left); + if (left.error) + return (fzl_an_res_t){.error = true}; + + fzl_an_res_t right = analyze_common(analyzer, cond_node->binary_op.right); + if (right.error) + return (fzl_an_res_t){.error = true}; + + if (cond_node->type == FZL_NODETYPE_OR || cond_node->type == FZL_NODETYPE_AND) + { + if (left.type != FZL_TYPE_BOOL) + an_push_warning(analyzer, cond_node->binary_op.left->token, + "left operand is not resulting with boolean type"); + + if (right.type != FZL_TYPE_BOOL) + an_push_warning(analyzer, cond_node->binary_op.right->token, + "right operand is not resulting with boolean type"); + } + else if (cond_node->type == FZL_NODETYPE_LESS || + cond_node->type == FZL_NODETYPE_LESS_EQUAL || + cond_node->type == FZL_NODETYPE_GREATER || + cond_node->type == FZL_NODETYPE_GREATER_EQUAL) + { + if (left.type < FZL_TYPE_INT8) + { + an_push_error(analyzer, cond_node->binary_op.left->token, + "left operand must be number"); + return (fzl_an_res_t){.error = true}; + } + + if (left.type < FZL_TYPE_INT8) + { + an_push_error(analyzer, cond_node->binary_op.left->token, + "right operand must be number"); + return (fzl_an_res_t){.error = true}; + } + } + else if (left.type >= FZL_TYPE_INT8) + { + if (right.type < FZL_TYPE_INT8) + { + an_push_error(analyzer, cond_node->binary_op.right->token, + "right operand must be an integer"); + return (fzl_an_res_t){.error = true}; + } + + return (fzl_an_res_t){ + .error = false, .type = FZL_TYPE_BOOL, .classname = NULL}; + } + else if (left.type == FZL_TYPE_OBJECT) + { + if (!left.classname || !right.classname) + { + an_push_error(analyzer, cond_node->binary_op.left->token, + "unhandled error :("); + return (fzl_an_res_t){.error = true}; + } + + if (!an_is_compatible(left.type, right.type, left.classname, + right.classname)) + { + snprintf(bincond_problem, sizeof(bincond_problem), + "'%s' and '%s' are incompatible", + an_typetostr(left.type, left.classname), + an_typetostr(right.type, right.classname)); + an_push_error(analyzer, cond_node->binary_op.right->token, + bincond_problem); + return (fzl_an_res_t){.error = true}; + } + } + else if (left.type == FZL_TYPE_STRING) + { + if (right.type != FZL_TYPE_STRING) + { + an_push_error(analyzer, cond_node->binary_op.right->token, + "right operand must be a string"); + return (fzl_an_res_t){.error = true}; + } + } + else if (!an_is_compatible(left.type, right.type, left.classname, + right.classname)) + { + snprintf(bincond_problem, sizeof(bincond_problem), + "equality not supported between %s and %s", + an_typetostr(left.type, left.classname), + an_typetostr(right.type, right.classname)); + return (fzl_an_res_t){.error = true}; + } + + return (fzl_an_res_t){ + .error = false, .type = FZL_TYPE_BOOL, .classname = NULL}; +} diff --git a/src/compiler/analyzer/an_declaration.c b/src/compiler/analyzer/an_declaration.c index a6bc786..efc0300 100644 --- a/src/compiler/analyzer/an_declaration.c +++ b/src/compiler/analyzer/an_declaration.c @@ -67,5 +67,5 @@ fzl_an_res_t analyze_declaration(fzl_analyzer_t *analyzer, fzl_node_t *decl) classname); } - return (fzl_an_res_t){.error = false, .type = FZL_TYPE_NULL}; + return (fzl_an_res_t){.error = false, .type = type, .classname = classname}; } diff --git a/src/compiler/analyzer/an_unarycond.c b/src/compiler/analyzer/an_unarycond.c new file mode 100644 index 0000000..29123dc --- /dev/null +++ b/src/compiler/analyzer/an_unarycond.c @@ -0,0 +1,30 @@ +#include +#include +#include + +// Analyze unary condition +fzl_an_res_t analyze_unarycond(fzl_analyzer_t *analyzer, fzl_node_t *unary) +{ + if (!analyzer) + return (fzl_an_res_t){.error = true}; + + if (!unary || unary->type != FZL_NODETYPE_NOT) + { + an_push_error(analyzer, NULL, "node is not typeof unary condition"); + return (fzl_an_res_t){.error = true}; + } + + fzl_an_res_t operand_res = analyze_common(analyzer, unary->unary_op.operand); + if (operand_res.error) + return (fzl_an_res_t){.error = true}; + + if (operand_res.type != FZL_TYPE_BOOL && operand_res.type < FZL_TYPE_INT8) + { + an_push_error(analyzer, unary->unary_op.operand->token, + "unary condition only support bool and integer types"); + return (fzl_an_res_t){.error = true}; + } + + return (fzl_an_res_t){ + .error = false, .type = FZL_TYPE_BOOL, .classname = NULL}; +} diff --git a/src/compiler/analyzer/an_unarymath.c b/src/compiler/analyzer/an_unarymath.c new file mode 100644 index 0000000..6df52a5 --- /dev/null +++ b/src/compiler/analyzer/an_unarymath.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include + +// Analyze unary arithmetic +fzl_an_res_t analyze_unarymath(fzl_analyzer_t *analyzer, fzl_node_t *unary) +{ + if (!analyzer) + return (fzl_an_res_t){.error = true}; + + if (!unary || (unary->type != FZL_NODETYPE_UNARY_MINUS && + unary->type != FZL_NODETYPE_UNARY_PLUS)) + { + an_push_error(analyzer, NULL, "node is not typeof unary math"); + return (fzl_an_res_t){.error = true}; + } + + fzl_an_res_t operand_res = analyze_common(analyzer, unary->unary_op.operand); + if (operand_res.error) + return (fzl_an_res_t){.error = true}; + + if (operand_res.type < FZL_TYPE_INT8) + { + an_push_error(analyzer, unary->unary_op.operand->token, + "unary operator only support integer types"); + return (fzl_an_res_t){.error = true}; + } + + return (fzl_an_res_t){ + .error = false, .type = operand_res.type, .classname = NULL}; +}