Skip to content

Commit

Permalink
Map keys consistent with == (#256)
Browse files Browse the repository at this point in the history
  • Loading branch information
vtereshkov committed Jan 7, 2023
1 parent 48bda72 commit dac090e
Show file tree
Hide file tree
Showing 13 changed files with 65 additions and 56 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BSD 2-Clause License

Copyright (c) 2020-2022, Vasiliy Tereshkov
Copyright (c) 2020-2023, Vasiliy Tereshkov
All rights reserved.

Redistribution and use in source and binary forms, with or without
Expand Down
2 changes: 1 addition & 1 deletion playground/umka.js

Large diffs are not rendered by default.

50 changes: 26 additions & 24 deletions spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ String assignment copies the contents of the string.

#### Map types

A map is a collection of items of a single type indexed by unique values, called *keys*, of another type. The key type cannot be an interface type.
A map is a collection of items of a single type indexed by unique values, called *keys*, of another type. The key type must be comparable, i.e., the `==` and `!=` operators must be applicable to operands of this type.

Syntax:

Expand Down Expand Up @@ -945,40 +945,42 @@ p != null && p[i] > 0
#### Unary operators

```
+ Unary plus Integers, reals
- Unary minus Integers, reals
~ Bitwise "not" Integers
! Logical "not" Booleans
& Address Addressable designators
+ Unary plus Integers, reals
- Unary minus Integers, reals
~ Bitwise "not" Integers
! Logical "not" Booleans
& Address Addressable designators
```

#### Binary operators

```
+ Sum Integers, reals, strings
- Difference Integers, reals
* Product Integers, reals
/ Quotient Integers, reals
% Remainder Integers, reals
& Bitwise "and" Integers
| Bitwise "or" Integers
~ Bitwise "xor" Integers
<< Left shift Integers
>> Right shift Integers
&& Logical "and" Booleans
|| Logical "or" Booleans
== "Equal" Ordinals, reals, pointers, strings
!= "Not equal" Ordinals, reals, pointers, strings
> "Greater" Ordinals, reals, strings
< "Less" Ordinals, reals, strings
>= "Greater or equal" Ordinals, reals, strings
<= "Less or equal" Ordinals, reals, strings
+ Sum Integers, reals, strings
- Difference Integers, reals
* Product Integers, reals
/ Quotient Integers, reals
% Remainder Integers, reals
& Bitwise "and" Integers
| Bitwise "or" Integers
~ Bitwise "xor" Integers
<< Left shift Integers
>> Right shift Integers
&& Logical "and" Booleans
|| Logical "or" Booleans
== "Equal" Ordinals, reals, pointers, strings, arrays, structures, functions
!= "Not equal" Ordinals, reals, pointers, strings, arrays, structures, functions
> "Greater" Ordinals, reals, strings
< "Less" Ordinals, reals, strings
>= "Greater or equal" Ordinals, reals, strings
<= "Less or equal" Ordinals, reals, strings
```

The `/` operator performs an integer division (with the remainder discarded) if both operands are of integer types, otherwise it performs a real division.

The `&&` and `||` operators don't evaluate the second operand if the first operand is sufficient to evaluate the result.

The `==` and `!=` operators, when applied to arrays or structures, perform bitwise comparison.

##### Operand type conversions

Operand types are implicitly converted in two steps:
Expand Down
2 changes: 1 addition & 1 deletion src/umka.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ enum
void help(void)
{
printf("%s\n", umkaGetVersion());
printf("(C) Vasiliy Tereshkov, 2020-2022\n");
printf("(C) Vasiliy Tereshkov, 2020-2023\n");
printf("Usage: umka [<parameters>] <file.um> [<script-parameters>]\n");
printf("Parameters:\n");
printf(" -stack <stack-size> - Set stack size\n");
Expand Down
4 changes: 2 additions & 2 deletions src/umka_decl.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,8 @@ static Type *parseMapType(Compiler *comp)
Type *type = typeAdd(&comp->types, &comp->blocks, TYPE_MAP);

Type *keyType = parseType(comp, NULL);
if (keyType->kind == TYPE_INTERFACE)
comp->error.handler(comp->error.context, "Map key type cannot be an interface");
if (!typeValidOperator(keyType, TOK_EQEQ))
comp->error.handler(comp->error.context, "Map key type is not comparable");

Type *ptrKeyType = typeAddPtrTo(&comp->types, &comp->blocks, keyType);

Expand Down
2 changes: 1 addition & 1 deletion src/umka_expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ void doApplyOperator(Compiler *comp, Type **type, Type **rightType, Const *const
if (constant)
constBinary(&comp->consts, constant, rightConstant, op, (*type)->kind);
else
genBinary(&comp->gen, op, (*type)->kind, 0);
genBinary(&comp->gen, op, (*type)->kind, typeSize(&comp->types, *type));
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/umka_gen.c
Original file line number Diff line number Diff line change
Expand Up @@ -526,11 +526,11 @@ void genUnary(CodeGen *gen, TokenKind tokKind, TypeKind typeKind)
}


void genBinary(CodeGen *gen, TokenKind tokKind, TypeKind typeKind, int bufOffset)
void genBinary(CodeGen *gen, TokenKind tokKind, TypeKind typeKind, int structSize)
{
if (!optimizeBinary(gen, tokKind, typeKind))
{
const Instruction instr = {.opcode = OP_BINARY, .tokKind = tokKind, .typeKind = typeKind, .operand.intVal = bufOffset};
const Instruction instr = {.opcode = OP_BINARY, .tokKind = tokKind, .typeKind = typeKind, .operand.intVal = structSize};
genAddInstr(gen, &instr);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/umka_gen.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void genChangeRefCntAssign (CodeGen *gen, Type *type);
void genSwapChangeRefCntAssign (CodeGen *gen, Type *type);

void genUnary (CodeGen *gen, TokenKind tokKind, TypeKind typeKind);
void genBinary(CodeGen *gen, TokenKind tokKind, TypeKind typeKind, int bufOffset /*bytes*/);
void genBinary(CodeGen *gen, TokenKind tokKind, TypeKind typeKind, int structSize);

void genGetArrayPtr (CodeGen *gen, int itemSize, int len);
void genGetDynArrayPtr(CodeGen *gen);
Expand Down
4 changes: 2 additions & 2 deletions src/umka_types.c
Original file line number Diff line number Diff line change
Expand Up @@ -539,12 +539,12 @@ bool typeValidOperator(Type *type, TokenKind op)
case TOK_OROR: return type->kind == TYPE_BOOL;
case TOK_PLUSPLUS:
case TOK_MINUSMINUS:return typeInteger(type);
case TOK_EQEQ: return typeOrdinal(type) || typeReal(type) || type->kind == TYPE_PTR || type->kind == TYPE_WEAKPTR || type->kind == TYPE_STR;
case TOK_EQEQ: return typeOrdinal(type) || typeReal(type) || type->kind == TYPE_PTR || type->kind == TYPE_WEAKPTR || type->kind == TYPE_STR || type->kind == TYPE_ARRAY || type->kind == TYPE_STRUCT || type->kind == TYPE_FN;
case TOK_LESS:
case TOK_GREATER: return typeOrdinal(type) || typeReal(type) || type->kind == TYPE_STR;
case TOK_EQ: return true;
case TOK_NOT: return type->kind == TYPE_BOOL;
case TOK_NOTEQ: return typeOrdinal(type) || typeReal(type) || type->kind == TYPE_PTR || type->kind == TYPE_WEAKPTR || type->kind == TYPE_STR;
case TOK_NOTEQ: return typeOrdinal(type) || typeReal(type) || type->kind == TYPE_PTR || type->kind == TYPE_WEAKPTR || type->kind == TYPE_STR || type->kind == TYPE_ARRAY || type->kind == TYPE_STRUCT || type->kind == TYPE_FN;
case TOK_LESSEQ:
case TOK_GREATEREQ: return typeOrdinal(type) || typeReal(type) || type->kind == TYPE_STR;
default: return false;
Expand Down
33 changes: 21 additions & 12 deletions src/umka_vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,6 @@ static void doGetMapKeyBytes(Slot key, Type *keyType, Error *error, char **keyBy
case TYPE_REAL:
case TYPE_PTR:
case TYPE_WEAKPTR:
case TYPE_FIBER:
case TYPE_FN:
{
// keyBytes must point to a pre-allocated 8-byte buffer
Expand All @@ -850,20 +849,12 @@ static void doGetMapKeyBytes(Slot key, Type *keyType, Error *error, char **keyBy
break;
}
case TYPE_ARRAY:
case TYPE_MAP:
case TYPE_STRUCT:
{
*keyBytes = (char *)key.ptrVal;
*keySize = typeSizeNoCheck(keyType);
break;
}
case TYPE_DYNARRAY:
{
DynArray *array = (DynArray *)key.ptrVal;
*keyBytes = (char *)array->data;
*keySize = array->data ? (getDims(array)->len * array->itemSize) : 0;
break;
}
default:
{
*keyBytes = NULL;
Expand Down Expand Up @@ -1151,8 +1142,8 @@ static int doFillReprBuf(Slot *slot, Type *type, char *buf, int maxLen, int maxD
break;
}

case TYPE_FIBER: len = snprintf(buf, maxLen, "fiber "); break;
case TYPE_FN: len = snprintf(buf, maxLen, "fn "); break;
case TYPE_FIBER: len = snprintf(buf, maxLen, "fiber @ %p ", slot->ptrVal); break;
case TYPE_FN: len = snprintf(buf, maxLen, "fn @ %lld ", (long long int)slot->intVal); break;
default: break;
}

Expand Down Expand Up @@ -2395,7 +2386,20 @@ static FORCE_INLINE void doBinary(Fiber *fiber, HeapPages *pages, Error *error)
default: error->runtimeHandler(error->context, "Illegal instruction"); return;
}
}
else if (fiber->code[fiber->ip].typeKind == TYPE_REAL || fiber->code[fiber->ip].typeKind == TYPE_REAL32)
else if (fiber->code[fiber->ip].typeKind == TYPE_ARRAY || fiber->code[fiber->ip].typeKind == TYPE_STRUCT)
{
const int structSize = fiber->code[fiber->ip].operand.intVal;

switch (fiber->code[fiber->ip].tokKind)
{
case TOK_EQEQ: fiber->top->intVal = memcmp(fiber->top->ptrVal, rhs.ptrVal, structSize) == 0; break;
case TOK_NOTEQ: fiber->top->intVal = memcmp(fiber->top->ptrVal, rhs.ptrVal, structSize) != 0; break;

default: error->runtimeHandler(error->context, "Illegal instruction"); return;
}
}
else if (typeKindReal(fiber->code[fiber->ip].typeKind))
{
switch (fiber->code[fiber->ip].tokKind)
{
case TOK_PLUS: fiber->top->realVal += rhs.realVal; break;
Expand Down Expand Up @@ -2425,7 +2429,9 @@ static FORCE_INLINE void doBinary(Fiber *fiber, HeapPages *pages, Error *error)

default: error->runtimeHandler(error->context, "Illegal instruction"); return;
}
}
else if (fiber->code[fiber->ip].typeKind == TYPE_UINT)
{
switch (fiber->code[fiber->ip].tokKind)
{
case TOK_PLUS: fiber->top->uintVal += rhs.uintVal; break;
Expand Down Expand Up @@ -2461,7 +2467,9 @@ static FORCE_INLINE void doBinary(Fiber *fiber, HeapPages *pages, Error *error)

default: error->runtimeHandler(error->context, "Illegal instruction"); return;
}
}
else // All ordinal types except TYPE_UINT
{
switch (fiber->code[fiber->ip].tokKind)
{
case TOK_PLUS: fiber->top->intVal += rhs.intVal; break;
Expand Down Expand Up @@ -2497,6 +2505,7 @@ static FORCE_INLINE void doBinary(Fiber *fiber, HeapPages *pages, Error *error)

default: error->runtimeHandler(error->context, "Illegal instruction"); return;
}
}

fiber->ip++;
}
Expand Down
1 change: 0 additions & 1 deletion tests/expected.log
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,6 @@ repr():
{ q: { 1.000000 0.000000 0.000000 0.000000 } normalized: true }
4.680000
'a'
fn
{ 0.500000 0.700000 0.900000 }


Expand Down
14 changes: 7 additions & 7 deletions tests/maps2.um
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ fn test3() {
}


fn foo(): ^map[[]int]str {
return &map[[]int]str{
[]int{13, 15}: "So-so",
[]int{57, 89}: "Nice"}
fn foo(): ^map[[2]int]str {
return &map[[2]int]str{
[2]int{13, 15}: "So-so",
[2]int{57, 89}: "Nice"}
}


Expand All @@ -75,10 +75,10 @@ fn test4() {

m4 := foo()

m4[[]int{2, 5}] = "OK"
m4[[]int{7, 9}] = "Also OK"
m4[[2]int{2, 5}] = "OK"
m4[[2]int{7, 9}] = "Also OK"

printf(repr(m4[[]int{2, 5}]) + '\n')
printf(repr(m4[[2]int{2, 5}]) + '\n')
printf(repr(m4^) + '\n')

for key, item in m4 {
Expand Down
1 change: 0 additions & 1 deletion tests/tour.um
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ fn test*() {
printf("%s\n", repr(q))
printf("%s\n", repr(sum))
printf("%s\n", repr('a'))
printf("%s\n", repr(parentFunc))
printf("%s\n", repr(printable))

}
Expand Down

1 comment on commit dac090e

@RobLoach
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Congrats on hitting 0.10! It looks great 👍

Please sign in to comment.