Compare commits

...

2 Commits

Author SHA1 Message Date
0a0db012a3 parser enhancement to get rid of unneeded statements
All checks were successful
continuous-integration/drone/push Build is passing
2026-04-15 21:23:52 +09:00
21b22e28c4 implemented primitive dead code eliminator 2026-04-15 16:45:54 +09:00

View File

@@ -994,6 +994,223 @@ oops:
return -1;
}
static int is_nde_bool_literal_atom (hawk_nde_t* nde)
{
switch (nde->type)
{
case HAWK_NDE_XFALSE:
case HAWK_NDE_XTRUE:
case HAWK_NDE_XNIL:
case HAWK_NDE_INT:
case HAWK_NDE_FLT:
case HAWK_NDE_STR:
case HAWK_NDE_MBS:
return 1;
default:
return 0;
}
}
/* return the effective literal node for boolean decision.
* if nde is a grouped node, every grouped element must be literal.
* the returned node is the last literal element in the group.
*
* examples:
* (1, 0) -> INT(0)
* ((1, 0)) -> INT(0)
* ("x", @false) -> XFALSE
* (abc(), 0) -> HAWK_NULL
*/
static hawk_nde_t* get_nde_bool_literal_tail (hawk_nde_t* nde, int depth)
{
if (depth >= 256) return HAWK_NULL; /* i don't wawnt to support to deep recursion. TODO: remove hard-coded value or remove recursion? */
if (nde->type == HAWK_NDE_GRP)
{
hawk_nde_t* cur, * last;
cur = ((hawk_nde_grp_t*)nde)->body;
HAWK_ASSERT(cur != HAWK_NULL);
last = HAWK_NULL;
do
{
last = get_nde_bool_literal_tail(cur, depth + 1);
if (!last) return HAWK_NULL;
cur = cur->next;
}
while (cur);
return last;
}
return is_nde_bool_literal_atom(nde)? nde: HAWK_NULL;
}
static nde_hard_bool_t classify_nde_hard_bool (hawk_nde_t* nde)
{
nde = get_nde_bool_literal_tail(nde, 0);
if (!nde) return NDE_HARD_BOOL_UNKNOWN;
switch (nde->type)
{
case HAWK_NDE_XFALSE:
case HAWK_NDE_XNIL:
return NDE_HARD_BOOL_FALSE;
case HAWK_NDE_XTRUE:
return NDE_HARD_BOOL_TRUE;
case HAWK_NDE_INT:
return (((hawk_nde_int_t*)nde)->val == 0)?
NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE;
case HAWK_NDE_FLT:
return (((hawk_nde_flt_t*)nde)->val == 0.0)?
NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE;
case HAWK_NDE_STR:
return (((hawk_nde_str_t*)nde)->len == 0)?
NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE;
case HAWK_NDE_MBS:
return (((hawk_nde_mbs_t*)nde)->len == 0)?
NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE;
default:
return NDE_HARD_BOOL_UNKNOWN;
}
}
static HAWK_INLINE int is_nde_hard_false (hawk_nde_t* nde)
{
return classify_nde_hard_bool(nde) == NDE_HARD_BOOL_FALSE;
}
static HAWK_INLINE int is_nde_hard_true (hawk_nde_t* nde)
{
return classify_nde_hard_bool(nde) == NDE_HARD_BOOL_TRUE;
}
static int is_nde_unconditional_terminator (hawk_nde_t* nde)
{
switch (nde->type)
{
case HAWK_NDE_EXIT: /* exit or @abort */
case HAWK_NDE_RETURN: /* return */
case HAWK_NDE_NEXT:
case HAWK_NDE_NEXTFILE:
return 1;
/* TODO: recursion depth check? */
case HAWK_NDE_BLK:
{
hawk_nde_t* p;
for (p = ((hawk_nde_blk_t*)nde)->body; p; p = p->next)
{
if (is_nde_unconditional_terminator(p)) return 1;
}
return 0;
}
case HAWK_NDE_IF:
{
hawk_nde_if_t* p = (hawk_nde_if_t*)nde;
return p->else_part &&
is_nde_unconditional_terminator(p->else_part) &&
is_nde_unconditional_terminator(p->then_part);
}
default:
return 0;
}
}
static int does_nde_have_side_effect (hawk_nde_t* nde)
{
HAWK_ASSERT(nde != HAWK_NULL);
switch (nde->type)
{
case HAWK_NDE_NULL:
case HAWK_NDE_CHAR:
case HAWK_NDE_BCHR:
case HAWK_NDE_INT:
case HAWK_NDE_FLT:
case HAWK_NDE_STR:
case HAWK_NDE_MBS:
case HAWK_NDE_REX:
case HAWK_NDE_XNIL:
case HAWK_NDE_XTRUE:
case HAWK_NDE_XFALSE:
case HAWK_NDE_XARGC:
case HAWK_NDE_XARGV:
case HAWK_NDE_FUN:
case HAWK_NDE_MODSYM:
case HAWK_NDE_NAMED:
case HAWK_NDE_GBL:
case HAWK_NDE_LCL:
case HAWK_NDE_ARG:
return 0;
case HAWK_NDE_NAMEDIDX:
case HAWK_NDE_GBLIDX:
case HAWK_NDE_LCLIDX:
case HAWK_NDE_ARGIDX:
return does_nde_have_side_effect(((hawk_nde_var_t*)nde)->idx);
case HAWK_NDE_XARGVIDX:
return does_nde_have_side_effect(((hawk_nde_xargvidx_t*)nde)->pos);
case HAWK_NDE_POS:
return does_nde_have_side_effect(((hawk_nde_pos_t*)nde)->val);
case HAWK_NDE_GRP:
{
hawk_nde_t* p;
for (p = ((hawk_nde_grp_t*)nde)->body; p; p = p->next)
{
if (does_nde_have_side_effect(p)) return 1;
}
return 0;
}
case HAWK_NDE_EXP_BIN:
return does_nde_have_side_effect(((hawk_nde_exp_t*)nde)->left) ||
does_nde_have_side_effect(((hawk_nde_exp_t*)nde)->right);
case HAWK_NDE_EXP_UNR:
return does_nde_have_side_effect(((hawk_nde_exp_t*)nde)->left);
case HAWK_NDE_BLK:
{
hawk_nde_t* p;
for (p = ((hawk_nde_blk_t*)nde)->body; p; p = p->next)
{
if (does_nde_have_side_effect(p)) return 1;
}
return 0;
}
case HAWK_NDE_CND:
return does_nde_have_side_effect(((hawk_nde_cnd_t*)nde)->test) ||
does_nde_have_side_effect(((hawk_nde_cnd_t*)nde)->left) ||
does_nde_have_side_effect(((hawk_nde_cnd_t*)nde)->right);
case HAWK_NDE_IF:
{
hawk_nde_if_t* p = (hawk_nde_if_t*)nde;
return does_nde_have_side_effect(p->test) ||
does_nde_have_side_effect(p->then_part) ||
(p->else_part && does_nde_have_side_effect(p->else_part));
}
/* all other nodes */
default:
return 1;
}
}
static int parse_progunit (hawk_t* hawk)
{
/*
@@ -2024,6 +2241,7 @@ static hawk_nde_t* parse_block (hawk_t* hawk, const hawk_loc_t* xloc, int flags)
hawk_nde_blk_t* block;
hawk_oow_t nlcls_outer, nlcls_max, tmp;
nde_chain_t local_inits = { HAWK_NULL, HAWK_NULL };
int dead_code;
nlcls_outer = HAWK_ARR_SIZE(hawk->parse.lcls);
nlcls_max = hawk->parse.nlcls_max;
@@ -2034,6 +2252,7 @@ static hawk_nde_t* parse_block (hawk_t* hawk, const hawk_loc_t* xloc, int flags)
/* block body */
head = local_inits.head;
curr = local_inits.tail;
dead_code = 0;
while (1)
{
@@ -2100,9 +2319,17 @@ static hawk_nde_t* parse_block (hawk_t* hawk, const hawk_loc_t* xloc, int flags)
continue;
}
if (dead_code)
{
hawk_clrpt(hawk, nde);
continue;
}
if (curr == HAWK_NULL) head = nde;
else curr->next = nde;
curr = nde;
if (is_nde_unconditional_terminator(nde)) dead_code = 1;
}
}
@@ -3025,102 +3252,22 @@ oops:
return HAWK_NULL;
}
static int is_nde_bool_literal_atom (hawk_nde_t* nde)
static hawk_nde_t* make_null_nde (hawk_t* hawk, const hawk_loc_t* xloc)
{
switch (nde->type)
hawk_nde_t* null_nde;
null_nde = (hawk_nde_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*null_nde));
if (HAWK_UNLIKELY(!null_nde))
{
case HAWK_NDE_XFALSE:
case HAWK_NDE_XTRUE:
case HAWK_NDE_XNIL:
case HAWK_NDE_INT:
case HAWK_NDE_FLT:
case HAWK_NDE_STR:
case HAWK_NDE_MBS:
return 1;
default:
return 0;
}
}
/* return the effective literal node for boolean decision.
* if nde is a grouped node, every grouped element must be literal.
* the returned node is the last literal element in the group.
*
* examples:
* (1, 0) -> INT(0)
* ((1, 0)) -> INT(0)
* ("x", @false) -> XFALSE
* (abc(), 0) -> HAWK_NULL
*/
static hawk_nde_t* get_nde_bool_literal_tail (hawk_nde_t* nde, int depth)
{
if (depth >= 256) return HAWK_NULL; /* i don't wawnt to support to deep recursion. TODO: remove hard-coded value or remove recursion? */
if (nde->type == HAWK_NDE_GRP)
{
hawk_nde_t* cur, * last;
cur = ((hawk_nde_grp_t*)nde)->body;
HAWK_ASSERT(cur != HAWK_NULL);
last = HAWK_NULL;
do
{
last = get_nde_bool_literal_tail(cur, depth + 1);
if (!last) return HAWK_NULL;
cur = cur->next;
}
while (cur);
return last;
ADJERR_LOC(hawk, xloc);
return HAWK_NULL;
}
return is_nde_bool_literal_atom(nde)? nde: HAWK_NULL;
}
null_nde->type = HAWK_NDE_NULL;
if (xloc) null_nde->loc = *xloc;
null_nde->next = HAWK_NULL;
static nde_hard_bool_t classify_nde_hard_bool (hawk_nde_t* nde)
{
nde = get_nde_bool_literal_tail(nde, 0);
if (!nde) return NDE_HARD_BOOL_UNKNOWN;
switch (nde->type)
{
case HAWK_NDE_XFALSE:
case HAWK_NDE_XNIL:
return NDE_HARD_BOOL_FALSE;
case HAWK_NDE_XTRUE:
return NDE_HARD_BOOL_TRUE;
case HAWK_NDE_INT:
return (((hawk_nde_int_t*)nde)->val == 0)?
NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE;
case HAWK_NDE_FLT:
return (((hawk_nde_flt_t*)nde)->val == 0.0)?
NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE;
case HAWK_NDE_STR:
return (((hawk_nde_str_t*)nde)->len == 0)?
NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE;
case HAWK_NDE_MBS:
return (((hawk_nde_mbs_t*)nde)->len == 0)?
NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE;
default:
return NDE_HARD_BOOL_UNKNOWN;
}
}
static HAWK_INLINE int is_nde_hard_false (hawk_nde_t* nde)
{
return classify_nde_hard_bool(nde) == NDE_HARD_BOOL_FALSE;
}
static HAWK_INLINE int is_nde_hard_true (hawk_nde_t* nde)
{
return classify_nde_hard_bool(nde) == NDE_HARD_BOOL_TRUE;
return null_nde;
}
static hawk_nde_t* parse_if (hawk_t* hawk, const hawk_loc_t* xloc)
@@ -3148,10 +3295,6 @@ static hawk_nde_t* parse_if (hawk_t* hawk, const hawk_loc_t* xloc)
goto oops;
}
/* TODO: optimization. if you know 'test' evaluates to true or false,
* you can drop the 'if' statement and take either the 'then_part'
* or 'else_part'. */
if (get_token(hawk) <= -1) goto oops;
tloc = hawk->tok.loc;
@@ -3189,12 +3332,8 @@ static hawk_nde_t* parse_if (hawk_t* hawk, const hawk_loc_t* xloc)
{
hawk_nde_t* null_nde;
null_nde = (hawk_nde_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*null_nde));
if (HAWK_UNLIKELY(!null_nde))
{
ADJERR_LOC(hawk, xloc);
goto oops;
}
null_nde = make_null_nde(hawk, xloc);
if (HAWK_UNLIKELY(!null_nde)) goto oops;
/* [BE CAREFUL]
* Don't forget reset test and then_part to HAWK_NULL if the control
@@ -3202,8 +3341,6 @@ static hawk_nde_t* parse_if (hawk_t* hawk, const hawk_loc_t* xloc)
hawk_clrpt(hawk, test);
hawk_clrpt(hawk, then_part);
null_nde->type = HAWK_NDE_NULL;
null_nde->loc = *xloc;
return null_nde;
}
}
@@ -3480,18 +3617,12 @@ static hawk_nde_t* parse_while (hawk_t* hawk, const hawk_loc_t* xloc)
/* fold while(0) and while(@false) into a null statement. */
hawk_nde_t* null_nde;
null_nde = (hawk_nde_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*null_nde));
if (HAWK_UNLIKELY(!null_nde))
{
ADJERR_LOC(hawk, xloc);
goto oops;
}
null_nde = make_null_nde(hawk, xloc);
if (HAWK_UNLIKELY(!null_nde)) goto oops;
hawk_clrpt(hawk, body); body = HAWK_NULL;
hawk_clrpt(hawk, test); test = HAWK_NULL;
null_nde->type = HAWK_NDE_NULL;
null_nde->loc = *xloc;
return null_nde;
}
@@ -4342,20 +4473,12 @@ static hawk_nde_t* parse_statement (hawk_t* hawk, const hawk_loc_t* xloc)
if (MATCH(hawk,TOK_SEMICOLON))
{
/* null statement */
nde = (hawk_nde_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde));
if (HAWK_UNLIKELY(!nde))
{
ADJERR_LOC(hawk, xloc);
return HAWK_NULL;
}
nde->type = HAWK_NDE_NULL;
nde->loc = *xloc;
nde->next = HAWK_NULL;
nde = make_null_nde(hawk, xloc);
if (HAWK_UNLIKELY(!nde)) return HAWK_NULL;
if (get_token(hawk) <= -1)
{
hawk_freemem(hawk, nde);
hawk_clrpt(hawk, nde);
return HAWK_NULL;
}
}
@@ -4392,6 +4515,15 @@ static hawk_nde_t* parse_statement (hawk_t* hawk, const hawk_loc_t* xloc)
hawk->parse.id.stmt = old_id;
}
if (nde && !does_nde_have_side_effect(nde))
{
hawk_nde_t* tmp;
tmp = make_null_nde(hawk, &nde->loc);
hawk_clrpt(hawk, nde);
if (HAWK_UNLIKELY(!tmp)) return HAWK_NULL;
nde = tmp;
}
return nde;
}
@@ -4430,7 +4562,7 @@ static hawk_nde_t* parse_expr_basic (hawk_t* hawk, const hawk_loc_t* xloc)
hawk_nde_t* nde, * n1, * n2;
nde = parse_logical_or(hawk, xloc);
if (nde == HAWK_NULL) return HAWK_NULL;
if (!nde) return HAWK_NULL;
if (MATCH(hawk,TOK_QUEST))
{
@@ -4445,7 +4577,7 @@ static hawk_nde_t* parse_expr_basic (hawk_t* hawk, const hawk_loc_t* xloc)
eloc = hawk->tok.loc;
n1 = parse_expr_withdc(hawk, &eloc);
if (n1 == HAWK_NULL)
if (!n1)
{
hawk_clrpt(hawk, nde);
return HAWK_NULL;
@@ -4467,7 +4599,7 @@ static hawk_nde_t* parse_expr_basic (hawk_t* hawk, const hawk_loc_t* xloc)
eloc = hawk->tok.loc;
n2 = parse_expr_withdc(hawk, &eloc);
if (n2 == HAWK_NULL)
if (!n2)
{
hawk_clrpt(hawk, nde);
hawk_clrpt(hawk, n1);
@@ -4475,7 +4607,7 @@ static hawk_nde_t* parse_expr_basic (hawk_t* hawk, const hawk_loc_t* xloc)
}
cnd = (hawk_nde_cnd_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*cnd));
if (cnd == HAWK_NULL)
if (HAWK_UNLIKELY(!cnd))
{
hawk_clrpt(hawk, nde);
hawk_clrpt(hawk, n1);
@@ -7403,18 +7535,14 @@ static hawk_nde_t* parse_idx_chain (hawk_t* hawk, const hawk_loc_t* xloc)
/* additional index - a[10][20] or a.b.c ...
* use the NULL node as a splitter */
splitter = (hawk_nde_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*splitter));
if (HAWK_UNLIKELY(!splitter))
{
ADJERR_LOC(hawk, xloc);
goto oops;
}
splitter->type = HAWK_NDE_NULL;
splitter = make_null_nde(hawk, xloc);
if (HAWK_UNLIKELY(!splitter)) goto oops;
HAWK_ASSERT(idx.tail != HAWK_NULL);
idx.tail->next = splitter;
idx.tail = splitter;
/* splitter is already chained. no need seperate clearing upon failure */
if (parse_single_idx(hawk, &idx) <= -1) goto oops;
}