Bring back table[key]
syntax
This commit is contained in:
parent
f3fc7558bb
commit
40c33987fa
21
compile.c
21
compile.c
@ -356,6 +356,15 @@ static CORD compile_lvalue(env_t *env, ast_t *ast)
|
||||
", ", heap_strf("%ld", ast->start - ast->file->text),
|
||||
", ", heap_strf("%ld", ast->end - ast->file->text), ")");
|
||||
}
|
||||
} else if (container_t->tag == TableType) {
|
||||
type_t *key_type = Match(container_t, TableType)->key_type;
|
||||
type_t *value_type = Match(container_t, TableType)->value_type;
|
||||
if (index->unchecked)
|
||||
code_err(ast, "Table indexes cannot be unchecked");
|
||||
return CORD_all("*(", compile_type(Type(PointerType, value_type)), ")Table$reserve(",
|
||||
compile_to_pointer_depth(env, index->indexed, 1, false), ", ",
|
||||
compile_to_type(env, index->index, Type(PointerType, key_type, .is_view=true)), ", NULL,",
|
||||
compile_type_info(env, container_t), ")");
|
||||
} else {
|
||||
code_err(ast, "I don't know how to assign to this target");
|
||||
}
|
||||
@ -669,6 +678,8 @@ CORD compile_statement(env_t *env, ast_t *ast)
|
||||
int64_t i = 1;
|
||||
for (ast_list_t *value = assign->values, *target = assign->targets; value && target; value = value->next, target = target->next) {
|
||||
type_t *lhs_t = get_type(env, target->ast);
|
||||
if (target->ast->tag == Index && get_type(env, Match(target->ast, Index)->indexed)->tag == TableType)
|
||||
lhs_t = Match(lhs_t, OptionalType)->type;
|
||||
env_t *val_env = with_enum_scope(env, lhs_t);
|
||||
CORD val = compile_to_type(val_env, value->ast, lhs_t);
|
||||
CORD_appendf(&code, "%r $%ld = %r;\n", compile_type(lhs_t), i++, val);
|
||||
@ -3566,6 +3577,16 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
CORD_asprintf("%ld", (int64_t)(indexing->index->start - f->text)), ", ",
|
||||
CORD_asprintf("%ld", (int64_t)(indexing->index->end - f->text)),
|
||||
")");
|
||||
} else if (container_t->tag == TableType) {
|
||||
type_t *key_type = Match(container_t, TableType)->key_type;
|
||||
type_t *value_type = Match(container_t, TableType)->value_type;
|
||||
if (indexing->unchecked)
|
||||
code_err(ast, "Table indexes cannot be unchecked");
|
||||
return CORD_all("({ ", compile_declaration(Type(PointerType, value_type, .is_view=true), "value"),
|
||||
" = Table$get(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ",
|
||||
compile_to_type(env, indexing->index, Type(PointerType, key_type, .is_view=true)), ", ",
|
||||
compile_type_info(env, container_t), "); \n"
|
||||
"value ? ", promote_to_optional(value_type, "*value"), " : ", compile_null(value_type), "; })");
|
||||
} else {
|
||||
code_err(ast, "Indexing is not supported for type: %T", container_t);
|
||||
}
|
||||
|
@ -86,10 +86,10 @@ add_cache := @{:add_args:Int}
|
||||
|
||||
func add(x, y:Int -> Int):
|
||||
args := add_args(x, y)
|
||||
if add_cache:has(args):
|
||||
return add_cache:get(args)
|
||||
if cached := add_cache[args]:
|
||||
return cached
|
||||
ret := _add(x, y)
|
||||
add_cache:set(args, ret)
|
||||
add_cache[args] = ret
|
||||
return ret
|
||||
```
|
||||
|
||||
|
@ -5,11 +5,6 @@ Map. Tables are efficiently implemented as a hash table that preserves
|
||||
insertion order and has fast access to keys and values as array slices. Tables
|
||||
support *all* types as both keys and values.
|
||||
|
||||
Tables do not support square bracket indexing (`t[key]`), but instead rely on
|
||||
the methods `:get(key)` and `:set(key, value)`. This is explicit to avoid
|
||||
hiding the fact that table lookups and table insertion are performing function
|
||||
calls and have edge conditions like a failure to find an entry.
|
||||
|
||||
## Syntax
|
||||
|
||||
Tables are written using `{}` curly braces with `:` colons associating key
|
||||
@ -38,21 +33,29 @@ t := {i: 10*i for i in 10 if i mod 2 == 0}
|
||||
t := {-1:-10, i: 10*i for i in 10}
|
||||
```
|
||||
|
||||
### Getting Values
|
||||
## Accessing Values
|
||||
|
||||
To get a value from a table, use `:get(key)`, which returns an _optional_
|
||||
value, depending on whether it was present in the table or not. For convenience,
|
||||
you can use the `!` postifx operator to perform a check to ensure that the value
|
||||
was found or error if it wasn't:
|
||||
Table values can be accessed with square bracket indexing. The result is an
|
||||
optional value:
|
||||
|
||||
```tomo
|
||||
>> t := {"x":1, "y":2}
|
||||
>> t:get("x")
|
||||
table := {"A": 1, "B": 2}
|
||||
>> table["A"]
|
||||
= 1 : Int?
|
||||
>> t:get("????")
|
||||
>> table["missing"]
|
||||
= NONE : Int?
|
||||
>> t:get("x")!
|
||||
= 1
|
||||
```
|
||||
|
||||
As with all optional values, you can use the `!` postfix operator to assert
|
||||
that the value is non-NONE (and create a runtime error if it is), or you can
|
||||
use the `or` operator to provide a fallback value in the case that it's NONE:
|
||||
|
||||
```tomo
|
||||
>> table["A"]!
|
||||
= 1 : Int
|
||||
|
||||
>> table["missing"] or -1
|
||||
= -1 : Int
|
||||
```
|
||||
|
||||
### Fallback Tables
|
||||
@ -63,12 +66,32 @@ is not found in the table itself:
|
||||
```tomo
|
||||
t := {"A": 10}
|
||||
t2 := {"B": 20; fallback=t}
|
||||
>> t2:get("A")
|
||||
= 10
|
||||
>> t2["A"]
|
||||
= 10 : Int?
|
||||
```
|
||||
|
||||
The fallback is available by the `.fallback` field, which returns an optional
|
||||
table value.
|
||||
table value:
|
||||
|
||||
```tomo
|
||||
>> t2.fallback
|
||||
= {"A":10} : {Text:Int}?
|
||||
>> t.fallback
|
||||
= NONE : {Text:Int}?
|
||||
```
|
||||
|
||||
## Setting Values
|
||||
|
||||
You can assign a new key/value mapping or overwrite an existing one using
|
||||
`:set(key, value)` or an `=` assignment statement:
|
||||
|
||||
```tomo
|
||||
t := {"A": 1, "B": 2}
|
||||
t["B"] = 222
|
||||
t["C"] = 333
|
||||
>> t
|
||||
= {"A":1, "B":222, "C":333}
|
||||
```
|
||||
|
||||
## Length
|
||||
|
||||
|
@ -20,11 +20,11 @@ func parse_ini(path:Path -> {Text:{Text:Text}}):
|
||||
if line:matches($/[?]/):
|
||||
section_name := line:replace($/[?]/, "\1"):trim():lower()
|
||||
current_section = @{:Text:Text}
|
||||
sections:set(section_name, current_section)
|
||||
sections[section_name] = current_section
|
||||
else if line:matches($/{..}={..}/):
|
||||
key := line:replace($/{..}={..}/, "\1"):trim():lower()
|
||||
value := line:replace($/{..}={..}/, "\2"):trim()
|
||||
current_section:set(key, value)
|
||||
current_section[key] = value
|
||||
|
||||
return {k:v[] for k,v in sections[]}
|
||||
|
||||
@ -42,7 +42,7 @@ func main(path:Path, key:Text?):
|
||||
return
|
||||
|
||||
section := keys[1]:lower()
|
||||
section_data := data:get(section) or exit("
|
||||
section_data := data[section] or exit("
|
||||
Invalid section name: $\[31;1]$section$\[]
|
||||
Valid names: $\[1]$(", ":join([k:quoted() for k in data.keys]))$\[]
|
||||
")
|
||||
@ -51,7 +51,7 @@ func main(path:Path, key:Text?):
|
||||
return
|
||||
|
||||
section_key := keys[2]:lower()
|
||||
value := section_data:get(section_key) or exit("
|
||||
value := section_data[section_key] or exit("
|
||||
Invalid key: $\[31;1]$section_key$\[]
|
||||
Valid keys: $\[1]$(", ":join([s:quoted() for s in section_data.keys]))$\[]
|
||||
")
|
||||
|
@ -105,21 +105,21 @@ func main():
|
||||
|
||||
# Tables are efficient hash maps
|
||||
table := {"one": 1, "two": 2}
|
||||
>> table:get("two")
|
||||
>> table["two"]
|
||||
= 2 : Int?
|
||||
|
||||
# The value returned is optional because NONE will be returned if the key
|
||||
# is not in the table:
|
||||
>> table:get("xxx")!
|
||||
>> table["xxx"]!
|
||||
= NONE : Int?
|
||||
|
||||
# Optional values can be converted to regular values using `!` (which will
|
||||
# create a runtime error if the value is null):
|
||||
>> table:get("two")!
|
||||
>> table["two"]!
|
||||
= 2 : Int
|
||||
|
||||
# You can also use `or` to provide a fallback value to replace NONE:
|
||||
>> table:get("xxx") or 0
|
||||
>> table["xxx"] or 0
|
||||
= 0 : Int
|
||||
|
||||
# Empty tables require specifying the key and value types:
|
||||
@ -142,9 +142,9 @@ func main():
|
||||
# Tables can have a fallback table that's used as a fallback when the key
|
||||
# isn't found in the table itself:
|
||||
table2 := {"three": 3; fallback=table}
|
||||
>> table2:get("two")!
|
||||
>> table2["two"]!
|
||||
= 2
|
||||
>> table2:get("three")!
|
||||
>> table2["three"]!
|
||||
= 3
|
||||
|
||||
# Tables can also be created with comprehension loops:
|
||||
@ -157,7 +157,7 @@ func main():
|
||||
# Any types can be used in tables, for example, a table mapping arrays to
|
||||
# strings:
|
||||
table3 := {[10, 20]: "one", [30, 40, 50]: "two"}
|
||||
>> table3:get([10, 20])!
|
||||
>> table3[[10, 20]]!
|
||||
= "one"
|
||||
|
||||
# Sets are similar to tables, but they represent an unordered collection of
|
||||
@ -294,7 +294,7 @@ func demo_structs():
|
||||
= yes
|
||||
|
||||
table := {alice: "first", bob: "second"}
|
||||
>> table:get(alice)!
|
||||
>> table[alice]!
|
||||
= "first"
|
||||
|
||||
|
||||
|
@ -28,7 +28,7 @@ func _get_file_dependencies(file:Path -> {Dependency}):
|
||||
func _build_dependency_graph(dep:Dependency, dependencies:@{Dependency:{Dependency}}):
|
||||
return if dependencies:has(dep)
|
||||
|
||||
dependencies:set(dep, {:Dependency}) # Placeholder
|
||||
dependencies[dep] = {:Dependency} # Placeholder
|
||||
|
||||
dep_deps := when dep is File(path):
|
||||
_get_file_dependencies(path)
|
||||
@ -50,7 +50,7 @@ func _build_dependency_graph(dep:Dependency, dependencies:@{Dependency:{Dependen
|
||||
module_deps:add(file_dep)
|
||||
module_deps[]
|
||||
|
||||
dependencies:set(dep, dep_deps)
|
||||
dependencies[dep] = dep_deps
|
||||
|
||||
for dep2 in dep_deps:
|
||||
_build_dependency_graph(dep2, dependencies)
|
||||
@ -80,7 +80,7 @@ func _draw_tree(dep:Dependency, dependencies:{Dependency:{Dependency}}, already_
|
||||
|
||||
child_prefix := prefix ++ (if is_last: " " else: "│ ")
|
||||
|
||||
children := dependencies:get(dep) or {:Dependency}
|
||||
children := dependencies[dep] or {:Dependency}
|
||||
for i,child in children.items:
|
||||
is_child_last := (i == children.length)
|
||||
_draw_tree(child, dependencies, already_printed, child_prefix, is_child_last)
|
||||
@ -89,7 +89,7 @@ func draw_tree(dep:Dependency, dependencies:{Dependency:{Dependency}}):
|
||||
printed := @{:Dependency}
|
||||
say(_printable_name(dep))
|
||||
printed:add(dep)
|
||||
deps := dependencies:get(dep) or {:Dependency}
|
||||
deps := dependencies[dep] or {:Dependency}
|
||||
for i,child in deps.items:
|
||||
is_child_last := (i == deps.length)
|
||||
_draw_tree(child, dependencies, already_printed=printed, is_last=is_child_last)
|
||||
|
@ -74,7 +74,7 @@ func main():
|
||||
= Password(...)
|
||||
>> users_by_password := {my_pass:"User1", Password("xxx"):"User2"}
|
||||
= {Password(...):"User1", Password(...):"User2"}
|
||||
>> users_by_password:get(my_pass)!
|
||||
>> users_by_password[my_pass]!
|
||||
= "User1"
|
||||
|
||||
>> CorecursiveA(@CorecursiveB())
|
||||
|
@ -2,15 +2,15 @@ func main():
|
||||
>> t := {"one":1, "two":2}
|
||||
= {"one":1, "two":2}
|
||||
|
||||
>> t:get("one")
|
||||
>> t["one"]
|
||||
= 1 : Int?
|
||||
>> t:get("two")
|
||||
>> t["two"]
|
||||
= 2 : Int?
|
||||
>> t:get("???")
|
||||
>> t["???"]
|
||||
= NONE : Int?
|
||||
>> t:get("one")!
|
||||
>> t["one"]!
|
||||
= 1
|
||||
>> t:get("???") or -1
|
||||
>> t["???"] or -1
|
||||
= -1
|
||||
|
||||
t_str := ""
|
||||
@ -32,11 +32,11 @@ func main():
|
||||
>> t2 := {"three":3; fallback=t}
|
||||
= {"three":3; fallback={"one":1, "two":2}}
|
||||
|
||||
>> t2:get("one")
|
||||
>> t2["one"]
|
||||
= 1 : Int?
|
||||
>> t2:get("three")
|
||||
>> t2["three"]
|
||||
= 3 : Int?
|
||||
>> t2:get("???")
|
||||
>> t2["???"]
|
||||
= NONE : Int?
|
||||
|
||||
>> t2.length
|
||||
@ -64,11 +64,11 @@ func main():
|
||||
|
||||
do:
|
||||
>> plain := {1:10, 2:20, 3:30}
|
||||
>> plain:get(2)!
|
||||
>> plain[2]!
|
||||
= 20
|
||||
>> plain:get(2)!
|
||||
>> plain[2]!
|
||||
= 20
|
||||
>> plain:get(456) or -999
|
||||
>> plain[456] or -999
|
||||
= -999
|
||||
>> plain:has(2)
|
||||
= yes
|
||||
@ -78,6 +78,13 @@ func main():
|
||||
>> fallback := {4:40; fallback=plain}
|
||||
>> fallback:has(1)
|
||||
= yes
|
||||
>> fallback:get(1) or -999
|
||||
>> fallback[1] or -999
|
||||
= 10
|
||||
|
||||
do:
|
||||
>> t4 := {"one": 1}
|
||||
>> t4["one"] = 999
|
||||
>> t4["two"] = 222
|
||||
>> t4
|
||||
= {"one":999, "two":222}
|
||||
|
||||
|
@ -718,7 +718,7 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
return Match(value_t, ArrayType)->item_type;
|
||||
code_err(indexing->index, "I only know how to index lists using integers, not %T", index_t);
|
||||
} else if (value_t->tag == TableType) {
|
||||
code_err(ast, "Tables use the table:get(key) method, not square bracket indexing like table[key]");
|
||||
return Type(OptionalType, Match(value_t, TableType)->value_type);
|
||||
} else {
|
||||
code_err(ast, "I don't know how to index %T values", indexed_t);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user