aboutsummaryrefslogtreecommitdiff
path: root/src/check_expr.cpp
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2018-10-27 18:44:28 +0100
committergingerBill <bill@gingerbill.org>2018-10-27 18:44:28 +0100
commit2ddb27869bd704d28e4704648c26b17d00ba2ff5 (patch)
tree2059d031bcfb8d4c448448f90dada2bc85358ce2 /src/check_expr.cpp
parent5c608b01ba8b445511690fe499b811bb2ea15dbe (diff)
Built-in procedure `#defined`
Diffstat (limited to 'src/check_expr.cpp')
-rw-r--r--src/check_expr.cpp57
1 files changed, 56 insertions, 1 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 0e4833ef1..6b2a07a86 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -2885,6 +2885,36 @@ bool is_type_normal_pointer(Type *ptr, Type **elem) {
return false;
}
+bool check_identifier_exists(Scope *s, Ast *node, bool nested = false, Scope **out_scope = nullptr) {
+ switch (node->kind) {
+ case_ast_node(i, Ident, node);
+ String name = i->token.string;
+ if (nested) {
+ Entity *e = scope_lookup_current(s, name);
+ if (e != nullptr) {
+ if (out_scope) *out_scope = e->scope;
+ return true;
+ }
+ } else {
+ Entity *e = scope_lookup(s, name);
+ if (e != nullptr) {
+ if (out_scope) *out_scope = e->scope;
+ return true;
+ }
+ }
+ case_end;
+ case_ast_node(se, SelectorExpr, node);
+ Ast *lhs = se->expr;
+ Ast *rhs = se->selector;
+ Scope *lhs_scope = nullptr;
+ if (check_identifier_exists(s, lhs, nested, &lhs_scope)) {
+ return check_identifier_exists(lhs_scope, rhs, true);
+ }
+ case_end;
+ }
+ return false;
+}
+
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id) {
ast_node(ce, CallExpr, call);
@@ -2920,6 +2950,15 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_len:
// NOTE(bill): The first arg may be a Type, this will be checked case by case
break;
+
+ case BuiltinProc_DIRECTIVE: {
+ ast_node(bd, BasicDirective, ce->proc);
+ String name = bd->name;
+ if (name == "defined") {
+ break;
+ }
+ /*fallthrough*/
+ }
default:
if (ce->args.count > 0) {
check_multi_expr(c, operand, ce->args[0]);
@@ -2984,6 +3023,22 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->type = t_untyped_bool;
operand->mode = Addressing_Constant;
+ } else if (name == "defined") {
+ if (ce->args.count != 1) {
+ error(call, "'#defined' expects 1 argument, got %td", ce->args.count);
+ return false;
+ }
+ Ast *arg = unparen_expr(ce->args[0]);
+ if (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr) {
+ error(call, "'#defined' expects an identifier or selector expression, got %s", LIT(ast_strings[arg->kind]));
+ return false;
+ }
+
+ bool is_defined = check_identifier_exists(c->scope, arg);
+ operand->type = t_untyped_bool;
+ operand->mode = Addressing_Constant;
+ operand->value = exact_value_bool(is_defined);
+
} else {
GB_PANIC("Unhandled #%.*s", LIT(name));
}
@@ -4957,7 +5012,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) {
ce->proc->kind == Ast_BasicDirective) {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name;
- if (name == "location" || name == "assert") {
+ if (name == "location" || name == "assert" || name == "defined") {
operand->mode = Addressing_Builtin;
operand->builtin_id = BuiltinProc_DIRECTIVE;
operand->expr = ce->proc;