commit 917f24dae32ad42b6d86848c0b2804cd26f150e0
parent 8db8f5319ee0408303e2082effca13f0f8683c46
Author: Drew DeVault <sir@cmpwn.com>
Date: Fri, 3 Sep 2021 09:18:00 +0200
cmd/harec: implement object access expressions
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
9 files changed, 243 insertions(+), 34 deletions(-)
diff --git a/cmd/harec/context.ha b/cmd/harec/context.ha
@@ -18,3 +18,13 @@ type binding = struct {
object: *unit::object,
name: temporary,
};
+
+fn binding_lookup(ctx: *context, object: *unit::object) *binding = {
+ // XXX: We could use a hash map here
+ for (let i = 0z; i < len(ctx.bindings); i += 1) {
+ if (ctx.bindings[i].object == object) {
+ return &ctx.bindings[i];
+ };
+ };
+ abort();
+};
diff --git a/cmd/harec/gen.ha b/cmd/harec/gen.ha
@@ -6,6 +6,7 @@ use hare::module;
use hare::types;
use hare::types::{builtin};
use hare::unit;
+use hare::unit::{object_kind};
use io;
use os;
use strings;
@@ -95,12 +96,34 @@ fn gen_store(ctx: *context, object: value, value: value) void = {
emit(ctx.out, void, instr, value, object);
};
+fn gen_load(ctx: *context, object: value) value = {
+ match (types::dealias(object._type).repr) {
+ bi: types::builtin => switch (bi) {
+ builtin::STR => abort(), // TODO
+ * => void,
+ },
+ types::_struct => abort(), // TODO
+ (types::array | types::slice | types::tagged | types::tuple) =>
+ abort(), // TODO
+ * => void,
+ };
+ const instr = qinstr_load(ctx, object._type);
+ const temp = mktemp(ctx);
+ const ty = qtype_lookup(ctx, object._type, false);
+ emit(ctx.out, (ty, temp), instr, object);
+ return value {
+ value = temp,
+ _type = object._type,
+ };
+};
+
fn gen_expr(ctx: *context, expr: *unit::expr) value = {
return match (expr.expr) {
- unit::binding_expr => gen_binding(ctx, expr),
- unit::compound_expr => gen_compound(ctx, expr),
- unit::constant_expr => gen_const(ctx, expr),
- unit::return_expr => gen_return(ctx, expr),
+ unit::access => gen_access(ctx, expr),
+ unit::bindings => gen_binding(ctx, expr),
+ unit::compound => gen_compound(ctx, expr),
+ unit::constant => gen_const(ctx, expr),
+ unit::_return => gen_return(ctx, expr),
};
};
@@ -113,8 +136,37 @@ fn gen_expr_at(ctx: *context, expr: *unit::expr, at: value) void = {
gen_store(ctx, at, value);
};
+fn gen_access_object(ctx: *context, object: *unit::object) value = {
+ return switch (object.kind) {
+ object_kind::CONST, object_kind::TYPE => abort(),
+ object_kind::BIND => {
+ const binding = binding_lookup(ctx, object);
+ yield value {
+ value = binding.name,
+ _type = object._type,
+ };
+ },
+ object_kind::DECL => abort(), // TODO
+ };
+};
+
+fn gen_access_addr(ctx: *context, expr: *unit::expr) value = {
+ const access = expr.expr as unit::access;
+ return match (access) {
+ ao: unit::access_object => gen_access_object(ctx, ao),
+ ai: unit::access_index => abort(), // TODO
+ af: unit::access_field => abort(), // TODO
+ at: unit::access_tuple => abort(), // TODO
+ };
+};
+
+fn gen_access(ctx: *context, expr: *unit::expr) value = {
+ const addr = gen_access_addr(ctx, expr);
+ return gen_load(ctx, addr);
+};
+
fn gen_binding(ctx: *context, expr: *unit::expr) value = {
- const bindings = expr.expr as unit::binding_expr;
+ const bindings = expr.expr as unit::bindings;
for (let i = 0z; i < len(bindings); i += 1) {
const item = bindings[i];
const temp = mktemp(ctx);
@@ -137,7 +189,7 @@ fn gen_binding(ctx: *context, expr: *unit::expr) value = {
};
fn gen_compound(ctx: *context, expr: *unit::expr) value = {
- const compound = expr.expr as unit::compound_expr;
+ const compound = expr.expr as unit::compound;
for (let i = 0z; i < len(compound); i += 1) {
gen_expr(ctx, compound[i]);
};
@@ -145,7 +197,7 @@ fn gen_compound(ctx: *context, expr: *unit::expr) value = {
};
fn gen_const(ctx: *context, expr: *unit::expr) value = {
- const constexpr = expr.expr as unit::constant_expr;
+ const constexpr = expr.expr as unit::constant;
const val: qval = match (constexpr) {
void => return vvoid,
b: bool => (if (b) 1u32 else 0u32): constant,
@@ -163,7 +215,7 @@ fn gen_const(ctx: *context, expr: *unit::expr) value = {
};
fn gen_return(ctx: *context, expr: *unit::expr) value = {
- const rexpr = expr.expr as unit::return_expr;
+ const rexpr = expr.expr as unit::_return;
match (rexpr) {
null => emit(ctx.out, void, qinstr::RET),
expr: *unit::expr => {
diff --git a/cmd/harec/qbe.ha b/cmd/harec/qbe.ha
@@ -185,6 +185,44 @@ fn qinstr_store(
* => abort(),
};
+fn qinstr_load(
+ ctx: *context,
+ ty: *types::_type,
+) qinstr = match (ty.repr) {
+ al: types::alias => qinstr_load(ctx, al.secondary: *types::_type),
+ bi: types::builtin => switch (bi) {
+ builtin::STR, builtin::VOID => abort(),
+ builtin::F32 => qinstr::LOADS,
+ builtin::F64 => qinstr::LOADD,
+ * => switch (ty.sz) {
+ 1 => if (types::is_signed(ty)) {
+ yield qinstr::LOADSB;
+ } else {
+ yield qinstr::LOADUB;
+ },
+ 2 => if (types::is_signed(ty)) {
+ yield qinstr::LOADSH;
+ } else {
+ yield qinstr::LOADUH;
+ },
+ 4 => if (types::is_signed(ty)) {
+ yield qinstr::LOADSW;
+ } else {
+ yield qinstr::LOADUW;
+ },
+ 8 => qinstr::LOADL,
+ * => abort(),
+ },
+ },
+ types::pointer => if (ctx.arch.ptr == &qlong) {
+ yield qinstr::LOADL;
+ } else if (ctx.arch.ptr == &qword) {
+ yield qinstr::LOADUW;
+ } else abort(),
+ en: types::_enum => abort(), // TODO
+ * => abort(),
+};
+
type qinstr = enum {
ADD,
ALLOC16,
diff --git a/hare/types/class.ha b/hare/types/class.ha
@@ -0,0 +1,43 @@
+// Returns true if the given type is a signed type.
+export fn is_signed(ty: const *_type) bool = {
+ return match (ty.repr) {
+ bi: builtin => switch (bi) {
+ builtin::F32, builtin::F64,
+ builtin::I16, builtin::I32, builtin::I64, builtin::I8,
+ builtin::INT => true,
+ * => false,
+ },
+ e: _enum => switch (e.storage) {
+ builtin::I16, builtin::I32, builtin::I64, builtin::I8,
+ builtin::INT => true,
+ * => false,
+ },
+ * => false,
+ };
+};
+
+// Returns true if the given type is a floating-point type.
+export fn is_float(ty: const *_type) bool = {
+ return match (ty.repr) {
+ bi: builtin => switch (bi) {
+ builtin::F32, builtin::F64 => true,
+ * => false,
+ },
+ * => false,
+ };
+};
+
+// Returns true if the given type is an integer type.
+export fn is_integer(ty: const *_type) bool = {
+ return match (ty.repr) {
+ bi: builtin => switch (bi) {
+ builtin::INT, builtin::UINT,
+ builtin::I16, builtin::I32, builtin::I64, builtin::I8,
+ builtin::U16, builtin::U32, builtin::U64, builtin::U8,
+ builtin::CHAR, builtin::SIZE, builtin::UINTPTR => true,
+ * => false,
+ },
+ _enum => true,
+ * => false,
+ };
+};
diff --git a/hare/unit/expr.ha b/hare/unit/expr.ha
@@ -8,7 +8,26 @@ export type expr = struct {
end: lex::location,
result: const *types::_type,
terminates: bool,
- expr: (binding_expr | compound_expr | constant_expr | return_expr),
+ expr: (access | bindings | compound | constant | _return),
+};
+
+export type access = (access_object | access_field | access_index | access_tuple);
+
+export type access_object = *object;
+
+export type access_field = struct {
+ object: *expr,
+ field: const *types::struct_field,
+};
+
+export type access_index = struct {
+ object: *expr,
+ index: *expr,
+};
+
+export type access_tuple = struct {
+ object: *expr,
+ value: const *types::tuple_value,
};
// A single variable biding.
@@ -20,13 +39,13 @@ export type binding = struct {
};
// A list of variable bindings.
-export type binding_expr = []binding;
+export type bindings = []binding;
// A compound expression, i.e. { ... }
-export type compound_expr = []*expr;
+export type compound = []*expr;
// The value of a constant expression.
-export type constant_expr = ast::value; // TODO: composite types
+export type constant = ast::value; // TODO: composite types
// A return expression, i.e. return <value>
-export type return_expr = nullable *expr;
+export type _return = nullable *expr;
diff --git a/hare/unit/process.ha b/hare/unit/process.ha
@@ -73,7 +73,7 @@ fn process_expr(
ctx: *context,
expr: *ast::expr,
) (*expr | error) = match (expr.expr) {
- ast::access_expr => abort(), // TODO
+ ast::access_expr => process_access(ctx, expr),
ast::alloc_expr => abort(), // TODO
ast::append_expr => abort(), // TODO
ast::assert_expr => abort(), // TODO
@@ -102,11 +102,33 @@ fn process_expr(
ast::unarithm_expr => abort(), // TODO
};
+fn process_access(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
+ const access = aexpr.expr as ast::access_expr;
+ const op: (const *types::_type, access) = match (access) {
+ ai: ast::access_identifier => {
+ const object = match (ctx_lookup(ctx, ai)) {
+ null => abort(), // TODO: Error
+ obj: *object => obj,
+ };
+ yield (object._type, object);
+ },
+ ai: ast::access_index => abort(), // TODO
+ af: ast::access_field => abort(), // TODO
+ at: ast::access_tuple => abort(), // TODO
+ };
+ return alloc(expr {
+ start = aexpr.start,
+ end = aexpr.end,
+ result = op.0,
+ expr = op.1,
+ });
+};
+
fn process_binding(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
const bind = aexpr.expr as ast::binding_expr;
assert(!bind.is_static && !bind.is_const); // TODO
- let bindings: binding_expr = [];
+ let bindings: bindings = [];
for (let i = 0z; i < len(bind.bindings); i += 1) {
const item = bind.bindings[i];
const init = process_expr(ctx, item.init)?;
@@ -139,7 +161,7 @@ fn process_compound(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
const compound = aexpr.expr as ast::compound_expr;
const scope = scope_push(ctx, scope_class::COMPOUND);
- let exprs: compound_expr = alloc([], len(compound.exprs));
+ let exprs: compound = alloc([], len(compound.exprs));
let i = 0z;
for (i < len(compound.exprs); i += 1) {
append(exprs, process_expr(ctx, compound.exprs[i])?);
@@ -163,7 +185,7 @@ fn process_compound(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
defer ast::expr_free(aexpr);
const expr = process_compound(&ctx, aexpr)!;
assert(expr.result.repr as types::builtin == types::builtin::VOID);
- const compound = expr.expr as compound_expr;
+ const compound = expr.expr as compound;
assert(len(compound) == 3);
const aexpr = parse_expr("{ return; }");
@@ -177,7 +199,7 @@ fn process_compound(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
fn process_constant(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
const constexpr = aexpr.expr as ast::constant_expr;
// TODO: Tuple unpacking
- const er: (const *types::_type, constant_expr) = match (constexpr) {
+ const er: (const *types::_type, constant) = match (constexpr) {
v: ast::value => (
// TODO: iconst/fconst lowering
types::lookup_builtin(ctx.store, match (v) {
@@ -212,28 +234,28 @@ fn process_constant(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
defer ast::expr_free(aexpr);
const expr = process_constant(&ctx, aexpr)!;
assert(expr.result.repr as types::builtin == types::builtin::VOID);
- const constexpr = expr.expr as constant_expr;
+ const constexpr = expr.expr as constant;
assert(constexpr is void);
const aexpr = parse_expr("true");
defer ast::expr_free(aexpr);
const expr = process_constant(&ctx, aexpr)!;
assert(expr.result.repr as types::builtin == types::builtin::BOOL);
- const constexpr = expr.expr as constant_expr;
+ const constexpr = expr.expr as constant;
assert(constexpr as bool == true);
const aexpr = parse_expr("false");
defer ast::expr_free(aexpr);
const expr = process_constant(&ctx, aexpr)!;
assert(expr.result.repr as types::builtin == types::builtin::BOOL);
- const constexpr = expr.expr as constant_expr;
+ const constexpr = expr.expr as constant;
assert(constexpr as bool == false);
const aexpr = parse_expr("null");
defer ast::expr_free(aexpr);
const expr = process_constant(&ctx, aexpr)!;
assert(expr.result.repr as types::builtin == types::builtin::NULL);
- assert(expr.expr is constant_expr);
+ assert(expr.expr is constant);
const cases: [_](str, types::builtin, ast::value) = [
("1234", types::builtin::INT, 1234),
@@ -248,7 +270,7 @@ fn process_constant(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
defer ast::expr_free(aexpr);
const expr = process_constant(&ctx, aexpr)!;
assert(expr.result.repr as types::builtin == case.1);
- const constexpr = expr.expr as constant_expr;
+ const constexpr = expr.expr as constant;
match (case.2) {
s: str => assert(constexpr as str == s),
r: rune => assert(constexpr as rune == r),
@@ -272,7 +294,7 @@ fn process_return(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
end = aexpr.end,
terminates = true,
result = &types::builtin_void,
- expr = rval: return_expr,
+ expr = rval: _return,
});
};
@@ -284,7 +306,7 @@ fn process_return(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
const expr = process_return(&ctx, aexpr)!;
assert(expr.terminates);
assert(expr.result.repr as types::builtin == types::builtin::VOID);
- const rval = expr.expr as return_expr;
+ const rval = expr.expr as _return;
assert(rval == null);
const aexpr = parse_expr("return 10;");
@@ -292,7 +314,7 @@ fn process_return(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
const expr = process_return(&ctx, aexpr)!;
assert(expr.terminates);
assert(expr.result.repr as types::builtin == types::builtin::VOID);
- const rval = expr.expr as return_expr;
+ const rval = expr.expr as _return;
assert(rval != null);
- assert((rval: *expr).expr is constant_expr);
+ assert((rval: *expr).expr is constant);
};
diff --git a/hare/unit/scope.ha b/hare/unit/scope.ha
@@ -59,19 +59,41 @@ fn scope_pop(ctx: *context) *scope = {
return scope;
};
-fn scope_insert(ctx: *context, obj: object) *object = {
- const scope = ctx.scope;
- append(scope.objects, obj);
- let obj = &scope.objects[len(scope.objects) - 1];
+fn ident_hash(ident: ast::ident) u64 = {
let hash = fnv::fnv64a();
defer hash::close(hash);
const zerobuf = [0u8];
- for (let i = len(obj.ident); i > 0; i -= 1) {
- hash::write(hash, strings::toutf8(obj.ident[i - 1]));
+ for (let i = len(ident); i > 0; i -= 1) {
+ hash::write(hash, strings::toutf8(ident[i - 1]));
hash::write(hash, zerobuf[..]);
};
- let hash = fnv::sum64(hash);
+ return fnv::sum64(hash);
+};
+
+fn scope_insert(ctx: *context, obj: object) *object = {
+ const scope = ctx.scope;
+ append(scope.objects, obj);
+ let obj = &scope.objects[len(scope.objects) - 1];
+ const hash = ident_hash(obj.ident);
obj.hash = hash;
append(scope.hashmap[hash: size % SCOPE_BUCKETS], obj);
return obj;
};
+
+fn ctx_lookup(ctx: *context, ident: ast::ident) nullable *object =
+ scope_lookup(ctx.scope, ident);
+
+fn scope_lookup(scope: *scope, ident: ast::ident) nullable *object = {
+ const hash = ident_hash(ident);
+ const bucket = scope.hashmap[hash: size % SCOPE_BUCKETS];
+ for (let i = 0z; i < len(bucket); i += 1) {
+ if (ast::ident_eq(bucket[i].name, ident)
+ || ast::ident_eq(bucket[i].ident, ident)) {
+ return bucket[i];
+ };
+ };
+ return match (scope.parent) {
+ null => null,
+ scope: *scope => scope_lookup(scope, ident),
+ };
+};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -363,6 +363,7 @@ gensrcs_hare_types() {
gen_srcs hare::types \
arch.ha \
builtins.ha \
+ class.ha \
hash.ha \
lookup.ha \
store.ha \
diff --git a/stdlib.mk b/stdlib.mk
@@ -684,6 +684,7 @@ $(HARECACHE)/hare/parse/hare_parse.ssa: $(stdlib_hare_parse_srcs) $(stdlib_rt) $
stdlib_hare_types_srcs= \
$(STDLIB)/hare/types/arch.ha \
$(STDLIB)/hare/types/builtins.ha \
+ $(STDLIB)/hare/types/class.ha \
$(STDLIB)/hare/types/hash.ha \
$(STDLIB)/hare/types/lookup.ha \
$(STDLIB)/hare/types/store.ha \
@@ -1912,6 +1913,7 @@ $(TESTCACHE)/hare/parse/hare_parse.ssa: $(testlib_hare_parse_srcs) $(testlib_rt)
testlib_hare_types_srcs= \
$(STDLIB)/hare/types/arch.ha \
$(STDLIB)/hare/types/builtins.ha \
+ $(STDLIB)/hare/types/class.ha \
$(STDLIB)/hare/types/hash.ha \
$(STDLIB)/hare/types/lookup.ha \
$(STDLIB)/hare/types/store.ha \