[qfcc] Add xvalue expressions and symbols

xvalue symbols refer to two expressions: an lvalue and an rvalue. They
are meant to be used with xvalue expressions.

xvalue expressions are useful when a distinction must be made between
the behavior of something (eg, a pascal function symbol) must change
depending on whether it's an lvalue (assignment of the function's return
value) or an rvalue (a call to the function, especially when the
function takes no parameters).
This commit is contained in:
Bill Currie 2024-12-08 18:59:40 +09:00
parent ed984e6eae
commit 1e19335b44
9 changed files with 83 additions and 0 deletions

View file

@ -397,6 +397,11 @@ typedef struct {
const expr_t *end_value; ///< for case value ranges (otherwise null)
} ex_caselabel_t;
typedef struct {
const expr_t *expr;
bool lvalue; ///< rvalue if false
} ex_xvalue_t;
typedef struct expr_s {
expr_t *next;
rua_loc_t loc; ///< source location of expression
@ -449,6 +454,7 @@ typedef struct expr_s {
ex_intrinsic_t intrinsic; ///< intrinsic intruction expression
ex_switch_t switchblock; ///< switch block expression
ex_caselabel_t caselabel; ///< case label expression
ex_xvalue_t xvalue; ///< lvalue/rvalue specific expression
};
} expr_t;
@ -1015,6 +1021,7 @@ expr_t *new_intrinsic_expr (const expr_t *expr_list);
expr_t *new_switch_expr (const expr_t *test, const expr_t *body,
const expr_t *break_label);
expr_t *new_caselabel_expr (const expr_t *value, const expr_t *end_value);
expr_t *new_xvalue_expr (const expr_t *expr, bool lvalue);
/** Create an expression of the correct type that references the specified
parameter slot.

View file

@ -80,6 +80,7 @@ EX_EXPR(select) ///< select construct expression (::ex_select_t)
EX_EXPR(intrinsic) ///< intrinsic instruction expression (::ex_intrinsic_t)
EX_EXPR(switch) ///< switch expression (::ex_switch_t)
EX_EXPR(caselabel) ///< case expression (::ex_caselabel_t)
EX_EXPR(xvalue) ///< xvalue expression (::ex_xvalue_t)
#undef EX_EXPR

View file

@ -48,6 +48,7 @@ SY_TYPE(convert) ///< symbol refers to a conversion function
SY_TYPE(macro) ///< symbol refers to a macro definition
SY_TYPE(namespace) ///< symbol refers to a namespace definition
SY_TYPE(list)
SY_TYPE(xvalue) ///< symbol has different lvalue and rvalue
#undef SY_TYPE

View file

@ -59,6 +59,11 @@ typedef struct symconv_s {
void *data;
} symconv_t;
typedef struct {
const expr_t *lvalue;
const expr_t *rvalue;
} sy_xvalue_t;
typedef struct var_s {
enum storage_class_e storage;
} var_t;
@ -87,6 +92,7 @@ typedef struct symbol_s {
struct rua_macro_s *macro; ///< sy_macro
struct symtab_s *namespace; ///< sy_namespace
ex_list_t list; ///< sy_list
sy_xvalue_t xvalue; ///< sy_xvalue
};
} symbol_t;

View file

@ -472,6 +472,20 @@ print_intrinsic (dstring_t *dstr, const expr_t *e, int level, int id,
"@intrinsic", e->loc.line);
}
static void
print_xvalue (dstring_t *dstr, const expr_t *e, int level, int id,
const expr_t *next)
{
int indent = level * 2 + 2;
_print_expr (dstr, e->xvalue.expr, level, id, next);
dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, e->xvalue.expr);
const char *b = e->xvalue.lvalue ? "Q" : "";
const char *a = e->xvalue.lvalue ? "" : "Q";
dasprintf (dstr, "%*se_%p [label=\"%s%c%s\\n%d\"];\n", indent, "", e,
b, '=', a, e->loc.line);
}
static void
print_subexpr (dstring_t *dstr, const expr_t *e, int level, int id, const expr_t *next)
{
@ -932,6 +946,7 @@ _print_expr (dstring_t *dstr, const expr_t *e, int level, int id,
[ex_loop] = nullptr,
[ex_select] = print_select,
[ex_intrinsic] = print_intrinsic,
[ex_xvalue] = print_xvalue,
};
int indent = level * 2 + 2;

View file

@ -81,6 +81,9 @@ get_type (const expr_t *e)
}
type = get_type (e->inout.out);
break;
case ex_xvalue:
bug (e, "should xvalue happen here?");
return get_type (e->xvalue.expr);
case ex_branch:
type = e->branch.ret_type;
break;
@ -1991,6 +1994,9 @@ has_function_call (const expr_t *e)
|| has_function_call (e->array.index));
case ex_switch:
return has_function_call (e->switchblock.test);
case ex_xvalue:
bug (e, "should xvalue happen here?");
return has_function_call (e->xvalue.expr);
case ex_count:
break;
}
@ -2304,6 +2310,18 @@ new_caselabel_expr (const expr_t *value, const expr_t *end_value)
return cl;
}
expr_t *
new_xvalue_expr (const expr_t *expr, bool lvalue)
{
auto xv = new_expr ();
xv->type = ex_xvalue;
xv->xvalue = (ex_xvalue_t) {
.expr = expr,
.lvalue = lvalue,
};
return xv;
}
expr_t *
new_decl (symbol_t *sym, const expr_t *init)
{

View file

@ -148,6 +148,12 @@ is_lvalue (const expr_t *expr)
return true;
case ex_array:
return true;
case ex_xvalue:
bug (expr, "should xvalue happen here?");
if (expr->xvalue.lvalue) {
return is_lvalue (expr->xvalue.expr);
}
return false;
case ex_count:
internal_error (expr, "invalid expression");
}

View file

@ -210,6 +210,16 @@ edag_add_expr (const expr_t *expr)
return e;
}
break;
case ex_xvalue:
bug (expr, "should xvalue happen here?");
if (e->xvalue.expr == expr->xvalue.expr
&& e->xvalue.lvalue == expr->xvalue.lvalue) {
// never dag an lvalue
if (!e->xvalue.lvalue) {
return e;
}
}
break;
}
}
DARRAY_APPEND (&expr_dag, expr);

View file

@ -644,6 +644,24 @@ proc_caselabel (const expr_t *expr, rua_ctx_t *ctx)
return current_target.proc_caselabel (expr, ctx);
}
static const expr_t *
proc_xvalue (const expr_t *expr, rua_ctx_t *ctx)
{
auto xvalue = expr->xvalue.expr;
if (xvalue->type == ex_symbol && xvalue->symbol->sy_type == sy_xvalue) {
if (expr->xvalue.lvalue) {
auto xv = xvalue->symbol->xvalue.lvalue;
if (!xv || !is_lvalue (xv)) {
return error (xv, "invalid lvalue");
}
xvalue = xv;
} else {
xvalue = xvalue->symbol->xvalue.rvalue;
}
}
return expr_process (xvalue, ctx);
}
const expr_t *
expr_process (const expr_t *expr, rua_ctx_t *ctx)
{
@ -677,6 +695,7 @@ expr_process (const expr_t *expr, rua_ctx_t *ctx)
[ex_intrinsic] = proc_intrinsic,
[ex_switch] = proc_switch,
[ex_caselabel] = proc_caselabel,
[ex_xvalue] = proc_xvalue,
};
if (expr->type >= ex_count) {