Compare commits
4 Commits
60b45869fb
...
27c5f7bf56
| Author | SHA1 | Date | |
|---|---|---|---|
| 27c5f7bf56 | |||
| 5418dfa577 | |||
| 60fe691731 | |||
| 634c5a2f93 |
26
README.md
26
README.md
@@ -136,6 +136,19 @@ Individual elements can be accessed by index using square brackets. Indexing is
|
|||||||
> I
|
> I
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Arrays are concatenated with `@`:
|
||||||
|
|
||||||
|
```
|
||||||
|
DESIGNA x VT [I, II, III] @ [IV, V]
|
||||||
|
DIC x
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
> [I, II, III, IV, V]
|
||||||
|
```
|
||||||
|
|
||||||
|
Both operands must be arrays — using `@` on non-arrays raises an error.
|
||||||
|
|
||||||
A sub-array can be extracted with `VSQVE` inside the index brackets. Both bounds are inclusive and 1-based:
|
A sub-array can be extracted with `VSQVE` inside the index brackets. Both bounds are inclusive and 1-based:
|
||||||
|
|
||||||

|

|
||||||
@@ -240,6 +253,19 @@ condition. Exit the loop with `ERVMPE` (or `REDI` from inside a function).
|
|||||||
> V
|
> V
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Variables can be unpacked in `PER` loops, similar to `DESIGNA` destructuring:
|
||||||
|
|
||||||
|
```
|
||||||
|
PER a, b IN [[I, II], [III, IV]] FAC {
|
||||||
|
DIC(a + b)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
> III
|
||||||
|
> VII
|
||||||
|
```
|
||||||
|
|
||||||
## Error handling
|
## Error handling
|
||||||
|
|
||||||
Errors can be caught using `TEMPTA` (temptare = to try) and `CAPE` (capere = to catch). The `CAPE` block binds the error message to a variable as a string.
|
Errors can be caught using `TEMPTA` (temptare = to try) and `CAPE` (capere = to catch). The `CAPE` block binds the error message to a variable as a string.
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ def rep_join(l):
|
|||||||
OP_STR = {
|
OP_STR = {
|
||||||
"SYMBOL_PLUS": "+", "SYMBOL_MINUS": "-",
|
"SYMBOL_PLUS": "+", "SYMBOL_MINUS": "-",
|
||||||
"SYMBOL_TIMES": "*", "SYMBOL_DIVIDE": "/",
|
"SYMBOL_TIMES": "*", "SYMBOL_DIVIDE": "/",
|
||||||
"SYMBOL_AMPERSAND": "&",
|
"SYMBOL_AMPERSAND": "&", "SYMBOL_AT": "@",
|
||||||
"KEYWORD_RELIQVVM": "RELIQVVM",
|
"KEYWORD_RELIQVVM": "RELIQVVM",
|
||||||
"KEYWORD_EST": "EST", "KEYWORD_DISPAR": "DISPAR",
|
"KEYWORD_EST": "EST", "KEYWORD_DISPAR": "DISPAR",
|
||||||
"KEYWORD_MINVS": "MINVS",
|
"KEYWORD_MINVS": "MINVS",
|
||||||
@@ -598,42 +598,97 @@ class Designa(Node):
|
|||||||
return vtable, ValNul()
|
return vtable, ValNul()
|
||||||
|
|
||||||
|
|
||||||
|
def _index_get(container, index):
|
||||||
|
if isinstance(container, ValDict):
|
||||||
|
if not isinstance(index, (ValStr, ValInt)):
|
||||||
|
raise CentvrionError("Dict key must be a string or integer")
|
||||||
|
d = container.value()
|
||||||
|
k = index.value()
|
||||||
|
if k not in d:
|
||||||
|
raise CentvrionError("Key not found in dict")
|
||||||
|
return d[k]
|
||||||
|
if isinstance(container, ValList):
|
||||||
|
i = index.value()
|
||||||
|
lst = container.value()
|
||||||
|
if i < 1 or i > len(lst):
|
||||||
|
raise CentvrionError(f"Index {i} out of range for array of length {len(lst)}")
|
||||||
|
return lst[i - 1]
|
||||||
|
if isinstance(container, ValStr):
|
||||||
|
if isinstance(index, ValInt):
|
||||||
|
i = index.value()
|
||||||
|
elif isinstance(index, ValFrac) and index.value().denominator == 1:
|
||||||
|
i = index.value().numerator
|
||||||
|
else:
|
||||||
|
raise CentvrionError("String index must be a number")
|
||||||
|
s = container.value()
|
||||||
|
if i < 1 or i > len(s):
|
||||||
|
raise CentvrionError(f"Index {i} out of range for string of length {len(s)}")
|
||||||
|
return ValStr(s[i - 1])
|
||||||
|
raise CentvrionError("Cannot index into a non-array, non-dict value")
|
||||||
|
|
||||||
|
|
||||||
|
def _index_set(container, index, value):
|
||||||
|
if isinstance(container, ValDict):
|
||||||
|
if not isinstance(index, (ValStr, ValInt)):
|
||||||
|
raise CentvrionError("Dict key must be a string or integer")
|
||||||
|
d = dict(container.value())
|
||||||
|
d[index.value()] = value
|
||||||
|
return ValDict(d)
|
||||||
|
if isinstance(container, ValList):
|
||||||
|
i = index.value()
|
||||||
|
lst = list(container.value())
|
||||||
|
if i < 1 or i > len(lst):
|
||||||
|
raise CentvrionError(f"Index {i} out of range for array of length {len(lst)}")
|
||||||
|
lst[i - 1] = value
|
||||||
|
return ValList(lst)
|
||||||
|
if isinstance(container, ValStr):
|
||||||
|
if isinstance(index, ValInt):
|
||||||
|
i = index.value()
|
||||||
|
elif isinstance(index, ValFrac) and index.value().denominator == 1:
|
||||||
|
i = index.value().numerator
|
||||||
|
else:
|
||||||
|
raise CentvrionError("String index must be a number")
|
||||||
|
if not isinstance(value, ValStr) or len(value.value()) != 1:
|
||||||
|
raise CentvrionError("String index assignment requires a single character")
|
||||||
|
s = container.value()
|
||||||
|
if i < 1 or i > len(s):
|
||||||
|
raise CentvrionError(f"Index {i} out of range for string of length {len(s)}")
|
||||||
|
return ValStr(s[:i - 1] + value.value() + s[i:])
|
||||||
|
raise CentvrionError("Cannot assign to index of a non-array, non-dict value")
|
||||||
|
|
||||||
|
|
||||||
class DesignaIndex(Node):
|
class DesignaIndex(Node):
|
||||||
def __init__(self, variable: ID, index, value) -> None:
|
def __init__(self, variable: ID, indices, value) -> None:
|
||||||
self.id = variable
|
self.id = variable
|
||||||
self.index = index
|
self.indices = indices if isinstance(indices, list) else [indices]
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return type(self) == type(other) and self.id == other.id and self.index == other.index and self.value == other.value
|
return type(self) == type(other) and self.id == other.id and self.indices == other.indices and self.value == other.value
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"DesignaIndex({self.id!r}, {self.index!r}, {self.value!r})"
|
return f"DesignaIndex({self.id!r}, {self.indices!r}, {self.value!r})"
|
||||||
|
|
||||||
def print(self):
|
def print(self):
|
||||||
return f"DESIGNA {self.id.print()}[{self.index.print()}] VT {self.value.print()}"
|
idx_str = ''.join(f'[{idx.print()}]' for idx in self.indices)
|
||||||
|
return f"DESIGNA {self.id.print()}{idx_str} VT {self.value.print()}"
|
||||||
|
|
||||||
def _eval(self, vtable):
|
def _eval(self, vtable):
|
||||||
vtable, index = self.index.eval(vtable)
|
evaluated_indices = []
|
||||||
|
for idx_expr in self.indices:
|
||||||
|
vtable, idx_val = idx_expr.eval(vtable)
|
||||||
|
evaluated_indices.append(idx_val)
|
||||||
vtable, val = self.value.eval(vtable)
|
vtable, val = self.value.eval(vtable)
|
||||||
if self.id.name not in vtable:
|
if self.id.name not in vtable:
|
||||||
raise CentvrionError(f"Undefined variable: {self.id.name}")
|
raise CentvrionError(f"Undefined variable: {self.id.name}")
|
||||||
target = vtable[self.id.name]
|
root = vtable[self.id.name]
|
||||||
if isinstance(target, ValDict):
|
containers = [root]
|
||||||
if not isinstance(index, (ValStr, ValInt)):
|
for idx in evaluated_indices[:-1]:
|
||||||
raise CentvrionError("Dict key must be a string or integer")
|
containers.append(_index_get(containers[-1], idx))
|
||||||
d = dict(target.value())
|
new_val = _index_set(containers[-1], evaluated_indices[-1], val)
|
||||||
d[index.value()] = val
|
for i in range(len(containers) - 2, -1, -1):
|
||||||
vtable[self.id.name] = ValDict(d)
|
new_val = _index_set(containers[i], evaluated_indices[i], new_val)
|
||||||
return vtable, ValNul()
|
vtable[self.id.name] = new_val
|
||||||
if not isinstance(target, ValList):
|
|
||||||
raise CentvrionError(f"{self.id.name} is not an array or dict")
|
|
||||||
i = index.value()
|
|
||||||
lst = list(target.value())
|
|
||||||
if i < 1 or i > len(lst):
|
|
||||||
raise CentvrionError(f"Index {i} out of range for array of length {len(lst)}")
|
|
||||||
lst[i - 1] = val
|
|
||||||
vtable[self.id.name] = ValList(lst)
|
|
||||||
return vtable, ValNul()
|
return vtable, ValNul()
|
||||||
|
|
||||||
|
|
||||||
@@ -833,6 +888,10 @@ class BinOp(Node):
|
|||||||
return vtable, ValNul()
|
return vtable, ValNul()
|
||||||
result = (lv or 0) + (rv or 0)
|
result = (lv or 0) + (rv or 0)
|
||||||
return vtable, ValFrac(result) if isinstance(result, Fraction) else ValInt(result)
|
return vtable, ValFrac(result) if isinstance(result, Fraction) else ValInt(result)
|
||||||
|
case "SYMBOL_AT":
|
||||||
|
if not isinstance(left, ValList) or not isinstance(right, ValList):
|
||||||
|
raise CentvrionError("@ requires two arrays")
|
||||||
|
return vtable, ValList(list(lv) + list(rv))
|
||||||
case "SYMBOL_AMPERSAND":
|
case "SYMBOL_AMPERSAND":
|
||||||
magnvm = "MAGNVM" in vtable["#modules"]
|
magnvm = "MAGNVM" in vtable["#modules"]
|
||||||
svbnvlla = "SVBNVLLA" in vtable["#modules"]
|
svbnvlla = "SVBNVLLA" in vtable["#modules"]
|
||||||
@@ -1136,6 +1195,10 @@ class PerStatement(Node):
|
|||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return type(self) == type(other) and self.data_list == other.data_list and self.variable_name == other.variable_name and self.statements == other.statements
|
return type(self) == type(other) and self.data_list == other.data_list and self.variable_name == other.variable_name and self.statements == other.statements
|
||||||
|
|
||||||
|
@property
|
||||||
|
def destructure(self):
|
||||||
|
return isinstance(self.variable_name, list)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
test = repr(self.data_list)
|
test = repr(self.data_list)
|
||||||
variable_name = repr(self.variable_name)
|
variable_name = repr(self.variable_name)
|
||||||
@@ -1145,7 +1208,23 @@ class PerStatement(Node):
|
|||||||
|
|
||||||
def print(self):
|
def print(self):
|
||||||
body = "\n".join(s.print() for s in self.statements)
|
body = "\n".join(s.print() for s in self.statements)
|
||||||
return f"PER {self.variable_name.print()} IN {self.data_list.print()} FAC {{\n{body}\n}}"
|
if self.destructure:
|
||||||
|
var_str = ", ".join(v.print() for v in self.variable_name)
|
||||||
|
else:
|
||||||
|
var_str = self.variable_name.print()
|
||||||
|
return f"PER {var_str} IN {self.data_list.print()} FAC {{\n{body}\n}}"
|
||||||
|
|
||||||
|
def _assign_loop_var(self, vtable, item):
|
||||||
|
if self.destructure:
|
||||||
|
if not isinstance(item, ValList):
|
||||||
|
raise CentvrionError("Cannot destructure non-array value in PER loop")
|
||||||
|
if len(item.value()) != len(self.variable_name):
|
||||||
|
raise CentvrionError(
|
||||||
|
f"Destructuring mismatch: {len(self.variable_name)} targets, {len(item.value())} values")
|
||||||
|
for id_node, val in zip(self.variable_name, item.value()):
|
||||||
|
vtable[id_node.name] = val
|
||||||
|
else:
|
||||||
|
vtable[self.variable_name.name] = item
|
||||||
|
|
||||||
def _eval(self, vtable):
|
def _eval(self, vtable):
|
||||||
vtable, array = self.data_list.eval(vtable)
|
vtable, array = self.data_list.eval(vtable)
|
||||||
@@ -1154,10 +1233,9 @@ class PerStatement(Node):
|
|||||||
array = ValList(keys)
|
array = ValList(keys)
|
||||||
if not isinstance(array, ValList):
|
if not isinstance(array, ValList):
|
||||||
raise CentvrionError("PER requires an array or dict")
|
raise CentvrionError("PER requires an array or dict")
|
||||||
variable_name = self.variable_name.name
|
|
||||||
last_val = ValNul()
|
last_val = ValNul()
|
||||||
for item in array:
|
for item in array:
|
||||||
vtable[variable_name] = item
|
self._assign_loop_var(vtable, item)
|
||||||
for statement in self.statements:
|
for statement in self.statements:
|
||||||
vtable, val = statement.eval(vtable)
|
vtable, val = statement.eval(vtable)
|
||||||
if vtable["#break"] or vtable["#continue"] or vtable["#return"] is not None:
|
if vtable["#break"] or vtable["#continue"] or vtable["#return"] is not None:
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ _BINOP_FN = {
|
|||||||
"SYMBOL_TIMES": "cent_mul",
|
"SYMBOL_TIMES": "cent_mul",
|
||||||
"SYMBOL_DIVIDE": "cent_div",
|
"SYMBOL_DIVIDE": "cent_div",
|
||||||
"SYMBOL_AMPERSAND": "cent_concat",
|
"SYMBOL_AMPERSAND": "cent_concat",
|
||||||
|
"SYMBOL_AT": "cent_array_concat",
|
||||||
"KEYWORD_RELIQVVM": "cent_mod",
|
"KEYWORD_RELIQVVM": "cent_mod",
|
||||||
"KEYWORD_EST": "cent_eq",
|
"KEYWORD_EST": "cent_eq",
|
||||||
"KEYWORD_DISPAR": "cent_neq",
|
"KEYWORD_DISPAR": "cent_neq",
|
||||||
|
|||||||
@@ -16,16 +16,32 @@ def emit_stmt(node, ctx):
|
|||||||
return val_lines + [f'cent_scope_set(&_scope, "{node.id.name}", {val_var});']
|
return val_lines + [f'cent_scope_set(&_scope, "{node.id.name}", {val_var});']
|
||||||
|
|
||||||
if isinstance(node, DesignaIndex):
|
if isinstance(node, DesignaIndex):
|
||||||
idx_lines, idx_var = emit_expr(node.index, ctx)
|
lines = []
|
||||||
|
idx_vars = []
|
||||||
|
for idx_expr in node.indices:
|
||||||
|
idx_lines, idx_var = emit_expr(idx_expr, ctx)
|
||||||
|
lines += idx_lines
|
||||||
|
idx_vars.append(idx_var)
|
||||||
val_lines, val_var = emit_expr(node.value, ctx)
|
val_lines, val_var = emit_expr(node.value, ctx)
|
||||||
arr_tmp = ctx.fresh_tmp()
|
lines += val_lines
|
||||||
return (
|
root_tmp = ctx.fresh_tmp()
|
||||||
idx_lines + val_lines + [
|
lines.append(f'CentValue {root_tmp} = cent_scope_get(&_scope, "{node.id.name}");')
|
||||||
f'CentValue {arr_tmp} = cent_scope_get(&_scope, "{node.id.name}");',
|
if len(idx_vars) == 1:
|
||||||
f"cent_list_index_set(&{arr_tmp}, {idx_var}, {val_var});",
|
lines.append(f"cent_list_index_set(&{root_tmp}, {idx_vars[0]}, {val_var});")
|
||||||
f'cent_scope_set(&_scope, "{node.id.name}", {arr_tmp});',
|
else:
|
||||||
]
|
# Walk down to collect intermediate containers
|
||||||
)
|
container_tmps = [root_tmp]
|
||||||
|
for idx_var in idx_vars[:-1]:
|
||||||
|
tmp = ctx.fresh_tmp()
|
||||||
|
lines.append(f"CentValue {tmp} = cent_list_index({container_tmps[-1]}, {idx_var});")
|
||||||
|
container_tmps.append(tmp)
|
||||||
|
# Set at deepest level
|
||||||
|
lines.append(f"cent_list_index_set(&{container_tmps[-1]}, {idx_vars[-1]}, {val_var});")
|
||||||
|
# Rebuild up the chain
|
||||||
|
for i in range(len(container_tmps) - 2, -1, -1):
|
||||||
|
lines.append(f"cent_list_index_set(&{container_tmps[i]}, {idx_vars[i]}, {container_tmps[i + 1]});")
|
||||||
|
lines.append(f'cent_scope_set(&_scope, "{node.id.name}", {root_tmp});')
|
||||||
|
return lines
|
||||||
|
|
||||||
if isinstance(node, DesignaDestructure):
|
if isinstance(node, DesignaDestructure):
|
||||||
n = len(node.ids)
|
n = len(node.ids)
|
||||||
@@ -65,23 +81,44 @@ def emit_stmt(node, ctx):
|
|||||||
if isinstance(node, PerStatement):
|
if isinstance(node, PerStatement):
|
||||||
arr_lines, arr_var = emit_expr(node.data_list, ctx)
|
arr_lines, arr_var = emit_expr(node.data_list, ctx)
|
||||||
i_var = ctx.fresh_tmp()
|
i_var = ctx.fresh_tmp()
|
||||||
var_name = node.variable_name.name
|
|
||||||
body_lines = _emit_body(node.statements, ctx)
|
body_lines = _emit_body(node.statements, ctx)
|
||||||
lines = arr_lines + [
|
|
||||||
f"if ({arr_var}.type == CENT_DICT) {{",
|
if node.destructure:
|
||||||
f" for (int {i_var} = 0; {i_var} < {arr_var}.dval.len; {i_var}++) {{",
|
# Destructuring PER — each element must be a list
|
||||||
f' cent_scope_set(&_scope, "{var_name}", {arr_var}.dval.keys[{i_var}]);',
|
elem_var = ctx.fresh_tmp()
|
||||||
]
|
assign_lines = [
|
||||||
lines += [f" {l}" for l in body_lines]
|
f"CentValue {elem_var} = {arr_var}.lval.items[{i_var}];",
|
||||||
lines += [
|
f'if ({elem_var}.type != CENT_LIST) cent_type_error("Cannot destructure non-array value in PER loop");',
|
||||||
" }",
|
f'if ({elem_var}.lval.len != {len(node.variable_name)}) cent_runtime_error("Destructuring mismatch");',
|
||||||
"} else {",
|
]
|
||||||
f' if ({arr_var}.type != CENT_LIST) cent_type_error("PER requires an array or dict");',
|
for j, id_node in enumerate(node.variable_name):
|
||||||
f" for (int {i_var} = 0; {i_var} < {arr_var}.lval.len; {i_var}++) {{",
|
tmp = ctx.fresh_tmp()
|
||||||
f' cent_scope_set(&_scope, "{var_name}", {arr_var}.lval.items[{i_var}]);',
|
assign_lines.append(f"CentValue {tmp} = cent_list_index({elem_var}, cent_int({j + 1}));")
|
||||||
]
|
assign_lines.append(f'cent_scope_set(&_scope, "{id_node.name}", {tmp});')
|
||||||
lines += [f" {l}" for l in body_lines]
|
lines = arr_lines + [
|
||||||
lines += [" }", "}"]
|
f'if ({arr_var}.type != CENT_LIST) cent_type_error("PER requires an array");',
|
||||||
|
f"for (int {i_var} = 0; {i_var} < {arr_var}.lval.len; {i_var}++) {{",
|
||||||
|
]
|
||||||
|
lines += [f" {l}" for l in assign_lines]
|
||||||
|
lines += [f" {l}" for l in body_lines]
|
||||||
|
lines += ["}"]
|
||||||
|
else:
|
||||||
|
var_name = node.variable_name.name
|
||||||
|
lines = arr_lines + [
|
||||||
|
f"if ({arr_var}.type == CENT_DICT) {{",
|
||||||
|
f" for (int {i_var} = 0; {i_var} < {arr_var}.dval.len; {i_var}++) {{",
|
||||||
|
f' cent_scope_set(&_scope, "{var_name}", {arr_var}.dval.keys[{i_var}]);',
|
||||||
|
]
|
||||||
|
lines += [f" {l}" for l in body_lines]
|
||||||
|
lines += [
|
||||||
|
" }",
|
||||||
|
"} else {",
|
||||||
|
f' if ({arr_var}.type != CENT_LIST) cent_type_error("PER requires an array or dict");',
|
||||||
|
f" for (int {i_var} = 0; {i_var} < {arr_var}.lval.len; {i_var}++) {{",
|
||||||
|
f' cent_scope_set(&_scope, "{var_name}", {arr_var}.lval.items[{i_var}]);',
|
||||||
|
]
|
||||||
|
lines += [f" {l}" for l in body_lines]
|
||||||
|
lines += [" }", "}"]
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
if isinstance(node, Defini):
|
if isinstance(node, Defini):
|
||||||
|
|||||||
@@ -409,6 +409,18 @@ CentValue cent_add(CentValue a, CentValue b) {
|
|||||||
return cent_null();
|
return cent_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CentValue cent_array_concat(CentValue a, CentValue b) {
|
||||||
|
if (a.type != CENT_LIST || b.type != CENT_LIST)
|
||||||
|
cent_type_error("'@' requires two arrays");
|
||||||
|
int total = a.lval.len + b.lval.len;
|
||||||
|
CentValue result = cent_list_new(total);
|
||||||
|
for (int i = 0; i < a.lval.len; i++)
|
||||||
|
cent_list_push(&result, a.lval.items[i]);
|
||||||
|
for (int i = 0; i < b.lval.len; i++)
|
||||||
|
cent_list_push(&result, b.lval.items[i]);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
CentValue cent_concat(CentValue a, CentValue b) {
|
CentValue cent_concat(CentValue a, CentValue b) {
|
||||||
const char *sa = (a.type == CENT_NULL) ? "" : cent_make_string(a);
|
const char *sa = (a.type == CENT_NULL) ? "" : cent_make_string(a);
|
||||||
const char *sb = (b.type == CENT_NULL) ? "" : cent_make_string(b);
|
const char *sb = (b.type == CENT_NULL) ? "" : cent_make_string(b);
|
||||||
@@ -852,8 +864,23 @@ void cent_list_index_set(CentValue *lst, CentValue idx, CentValue v) {
|
|||||||
cent_dict_set(lst, idx, v);
|
cent_dict_set(lst, idx, v);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (lst->type == CENT_STR) {
|
||||||
|
if (idx.type != CENT_INT)
|
||||||
|
cent_type_error("string index must be an integer");
|
||||||
|
if (v.type != CENT_STR || strlen(v.sval) != 1)
|
||||||
|
cent_type_error("string index assignment requires a single character");
|
||||||
|
long slen = (long)strlen(lst->sval);
|
||||||
|
long i = idx.ival;
|
||||||
|
if (i < 1 || i > slen)
|
||||||
|
cent_runtime_error("string index out of range");
|
||||||
|
char *buf = cent_arena_alloc(cent_arena, slen + 1);
|
||||||
|
memcpy(buf, lst->sval, slen + 1);
|
||||||
|
buf[i - 1] = v.sval[0];
|
||||||
|
lst->sval = buf;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (lst->type != CENT_LIST)
|
if (lst->type != CENT_LIST)
|
||||||
cent_type_error("index-assign requires a list or dict");
|
cent_type_error("index-assign requires a list, dict, or string");
|
||||||
if (idx.type != CENT_INT)
|
if (idx.type != CENT_INT)
|
||||||
cent_type_error("list index must be an integer");
|
cent_type_error("list index must be an integer");
|
||||||
long i = idx.ival;
|
long i = idx.ival;
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ char *cent_make_string(CentValue v);
|
|||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
CentValue cent_add(CentValue a, CentValue b); /* INT+INT or FRAC+FRAC/INT */
|
CentValue cent_add(CentValue a, CentValue b); /* INT+INT or FRAC+FRAC/INT */
|
||||||
|
CentValue cent_array_concat(CentValue a, CentValue b); /* @ operator: concatenate two arrays */
|
||||||
CentValue cent_concat(CentValue a, CentValue b); /* & operator: coerce all types to str */
|
CentValue cent_concat(CentValue a, CentValue b); /* & operator: coerce all types to str */
|
||||||
CentValue cent_sub(CentValue a, CentValue b); /* INT-INT or FRAC-FRAC/INT */
|
CentValue cent_sub(CentValue a, CentValue b); /* INT-INT or FRAC-FRAC/INT */
|
||||||
CentValue cent_mul(CentValue a, CentValue b); /* INT*INT or FRAC*FRAC/INT */
|
CentValue cent_mul(CentValue a, CentValue b); /* INT*INT or FRAC*FRAC/INT */
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ symbol_tokens = [
|
|||||||
("SYMBOL_TIMES", r"\*"),
|
("SYMBOL_TIMES", r"\*"),
|
||||||
("SYMBOL_DIVIDE", r"\/"),
|
("SYMBOL_DIVIDE", r"\/"),
|
||||||
("SYMBOL_AMPERSAND", r"&"),
|
("SYMBOL_AMPERSAND", r"&"),
|
||||||
|
("SYMBOL_AT", r"@"),
|
||||||
("SYMBOL_COMMA", r",")
|
("SYMBOL_COMMA", r",")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class Parser():
|
|||||||
('left', ["KEYWORD_AVT"]),
|
('left', ["KEYWORD_AVT"]),
|
||||||
('left', ["KEYWORD_ET"]),
|
('left', ["KEYWORD_ET"]),
|
||||||
('left', ["KEYWORD_PLVS", "KEYWORD_MINVS", "KEYWORD_EST", "KEYWORD_DISPAR"]),
|
('left', ["KEYWORD_PLVS", "KEYWORD_MINVS", "KEYWORD_EST", "KEYWORD_DISPAR"]),
|
||||||
('left', ["SYMBOL_AMPERSAND", "SYMBOL_PLUS", "SYMBOL_MINUS"]),
|
('left', ["SYMBOL_AMPERSAND", "SYMBOL_AT", "SYMBOL_PLUS", "SYMBOL_MINUS"]),
|
||||||
('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE", "KEYWORD_RELIQVVM"]),
|
('left', ["SYMBOL_TIMES", "SYMBOL_DIVIDE", "KEYWORD_RELIQVVM"]),
|
||||||
('right', ["UMINUS", "UNOT"]),
|
('right', ["UMINUS", "UNOT"]),
|
||||||
('left', ["SYMBOL_LBRACKET", "INDEX"]),
|
('left', ["SYMBOL_LBRACKET", "INDEX"]),
|
||||||
@@ -173,9 +173,17 @@ class Parser():
|
|||||||
def statement_designa(tokens):
|
def statement_designa(tokens):
|
||||||
return ast_nodes.Designa(tokens[1], tokens[3])
|
return ast_nodes.Designa(tokens[1], tokens[3])
|
||||||
|
|
||||||
@self.pg.production('statement : KEYWORD_DESIGNA id SYMBOL_LBRACKET expression SYMBOL_RBRACKET KEYWORD_VT expression')
|
@self.pg.production('index_chain : SYMBOL_LBRACKET expression SYMBOL_RBRACKET')
|
||||||
|
def index_chain_single(tokens):
|
||||||
|
return [tokens[1]]
|
||||||
|
|
||||||
|
@self.pg.production('index_chain : SYMBOL_LBRACKET expression SYMBOL_RBRACKET index_chain')
|
||||||
|
def index_chain_multi(tokens):
|
||||||
|
return [tokens[1]] + tokens[3]
|
||||||
|
|
||||||
|
@self.pg.production('statement : KEYWORD_DESIGNA id index_chain KEYWORD_VT expression')
|
||||||
def statement_designa_index(tokens):
|
def statement_designa_index(tokens):
|
||||||
return ast_nodes.DesignaIndex(tokens[1], tokens[3], tokens[6])
|
return ast_nodes.DesignaIndex(tokens[1], tokens[2], tokens[4])
|
||||||
|
|
||||||
@self.pg.production('statement : KEYWORD_DESIGNA id SYMBOL_COMMA id_list_rest KEYWORD_VT expression')
|
@self.pg.production('statement : KEYWORD_DESIGNA id SYMBOL_COMMA id_list_rest KEYWORD_VT expression')
|
||||||
def statement_designa_destructure(tokens):
|
def statement_designa_destructure(tokens):
|
||||||
@@ -242,6 +250,10 @@ class Parser():
|
|||||||
def aeternvm(tokens):
|
def aeternvm(tokens):
|
||||||
return ast_nodes.DumStatement(ast_nodes.Bool(False), tokens[3])
|
return ast_nodes.DumStatement(ast_nodes.Bool(False), tokens[3])
|
||||||
|
|
||||||
|
@self.pg.production('per_statement : KEYWORD_PER id SYMBOL_COMMA id_list_rest KEYWORD_IN expression KEYWORD_FAC SYMBOL_LCURL statements SYMBOL_RCURL')
|
||||||
|
def per_destructure(tokens):
|
||||||
|
return ast_nodes.PerStatement(tokens[5], [tokens[1]] + tokens[3], tokens[8])
|
||||||
|
|
||||||
@self.pg.production('per_statement : KEYWORD_PER id KEYWORD_IN expression KEYWORD_FAC SYMBOL_LCURL statements SYMBOL_RCURL')
|
@self.pg.production('per_statement : KEYWORD_PER id KEYWORD_IN expression KEYWORD_FAC SYMBOL_LCURL statements SYMBOL_RCURL')
|
||||||
def per(tokens):
|
def per(tokens):
|
||||||
return ast_nodes.PerStatement(tokens[3], tokens[1], tokens[6])
|
return ast_nodes.PerStatement(tokens[3], tokens[1], tokens[6])
|
||||||
@@ -273,14 +285,14 @@ class Parser():
|
|||||||
|
|
||||||
@self.pg.production('array_items : ')
|
@self.pg.production('array_items : ')
|
||||||
@self.pg.production('array_items : expression')
|
@self.pg.production('array_items : expression')
|
||||||
@self.pg.production('array_items : expression SYMBOL_COMMA array_items')
|
@self.pg.production('array_items : expression SYMBOL_COMMA opt_newline array_items')
|
||||||
def array_items(calls):
|
def array_items(calls):
|
||||||
if len(calls) == 0:
|
if len(calls) == 0:
|
||||||
return []
|
return []
|
||||||
elif len(calls) == 1:
|
elif len(calls) == 1:
|
||||||
return [calls[0]]
|
return [calls[0]]
|
||||||
else:
|
else:
|
||||||
return [calls[0]] + calls[2]
|
return [calls[0]] + calls[3]
|
||||||
|
|
||||||
@self.pg.production('expression : id')
|
@self.pg.production('expression : id')
|
||||||
def expression_id(tokens):
|
def expression_id(tokens):
|
||||||
@@ -311,6 +323,7 @@ class Parser():
|
|||||||
def expression_nullus(_):
|
def expression_nullus(_):
|
||||||
return ast_nodes.Nullus()
|
return ast_nodes.Nullus()
|
||||||
|
|
||||||
|
@self.pg.production('expression : expression SYMBOL_AT expression')
|
||||||
@self.pg.production('expression : expression SYMBOL_AMPERSAND expression')
|
@self.pg.production('expression : expression SYMBOL_AMPERSAND expression')
|
||||||
@self.pg.production('expression : expression SYMBOL_MINUS expression')
|
@self.pg.production('expression : expression SYMBOL_MINUS expression')
|
||||||
@self.pg.production('expression : expression SYMBOL_PLUS expression')
|
@self.pg.production('expression : expression SYMBOL_PLUS expression')
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
\languageline{statement}{\texttt{DVM} \textit{expression} \texttt{FAC} \textit{scope}} \\
|
\languageline{statement}{\texttt{DVM} \textit{expression} \texttt{FAC} \textit{scope}} \\
|
||||||
\languageline{statement}{\texttt{AETERNVM} \texttt{FAC} \textit{scope}} \\
|
\languageline{statement}{\texttt{AETERNVM} \texttt{FAC} \textit{scope}} \\
|
||||||
\languageline{statement}{\texttt{PER} \textbf{id} \texttt{IN} \textit{expression} \texttt{FAC} \textit{scope}} \\
|
\languageline{statement}{\texttt{PER} \textbf{id} \texttt{IN} \textit{expression} \texttt{FAC} \textit{scope}} \\
|
||||||
|
\languageline{statement}{\texttt{PER} \textbf{id}\texttt{,} \textbf{id-list} \texttt{IN} \textit{expression} \texttt{FAC} \textit{scope}} \\
|
||||||
\languageline{statement}{\texttt{DONICVM} \textbf{id} \texttt{VT} \textit{expression} \texttt{VSQVE} \textit{expression} \texttt{FAC} \textit{scope}} \\
|
\languageline{statement}{\texttt{DONICVM} \textbf{id} \texttt{VT} \textit{expression} \texttt{VSQVE} \textit{expression} \texttt{FAC} \textit{scope}} \\
|
||||||
\languageline{statement}{\texttt{REDI(} \textit{optional-expressions} \texttt{)}} \\
|
\languageline{statement}{\texttt{REDI(} \textit{optional-expressions} \texttt{)}} \\
|
||||||
\languageline{statement}{\texttt{ERVMPE}} \\
|
\languageline{statement}{\texttt{ERVMPE}} \\
|
||||||
@@ -105,7 +106,7 @@
|
|||||||
\item \textbf{interpolated-string}: \\ A double-quoted string containing \texttt{\{}\textit{expression}\texttt{\}} segments. Each expression is evaluated and coerced to a string. Use \texttt{\{\{} and \texttt{\}\}} for literal braces.
|
\item \textbf{interpolated-string}: \\ A double-quoted string containing \texttt{\{}\textit{expression}\texttt{\}} segments. Each expression is evaluated and coerced to a string. Use \texttt{\{\{} and \texttt{\}\}} for literal braces.
|
||||||
\item \textbf{numeral}: \\ Roman numerals consisting of the uppercase characters I, V, X, L, C, D, and M. Can also include underscore if the module MAGNVM.
|
\item \textbf{numeral}: \\ Roman numerals consisting of the uppercase characters I, V, X, L, C, D, and M. Can also include underscore if the module MAGNVM.
|
||||||
\item \textbf{bool}: \\ VERITAS or FALSITAS.
|
\item \textbf{bool}: \\ VERITAS or FALSITAS.
|
||||||
\item \textbf{binop}: \\ Binary operators: \texttt{+}, \texttt{-}, \texttt{*}, \texttt{/}, \texttt{RELIQVVM} (modulo), \texttt{EST} (equality), \texttt{DISPAR} (not-equal), \texttt{MINVS} (<), \texttt{PLVS} (>), \texttt{ET} (and), \texttt{AVT} (or), \texttt{\&} (string concatenation).
|
\item \textbf{binop}: \\ Binary operators: \texttt{+}, \texttt{-}, \texttt{*}, \texttt{/}, \texttt{RELIQVVM} (modulo), \texttt{EST} (equality), \texttt{DISPAR} (not-equal), \texttt{MINVS} (<), \texttt{PLVS} (>), \texttt{ET} (and), \texttt{AVT} (or), \texttt{\&} (string concatenation), \texttt{@} (array concatenation).
|
||||||
\item \textbf{unop}: \\ Unary operators: \texttt{-} (negation), \texttt{NON} (boolean not).
|
\item \textbf{unop}: \\ Unary operators: \texttt{-} (negation), \texttt{NON} (boolean not).
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ contexts:
|
|||||||
scope: keyword.control.centvrion
|
scope: keyword.control.centvrion
|
||||||
|
|
||||||
operators:
|
operators:
|
||||||
- match: '[+\-*/&]'
|
- match: '[+\-*/&@]'
|
||||||
scope: keyword.operator.centvrion
|
scope: keyword.operator.centvrion
|
||||||
|
|
||||||
identifiers:
|
identifiers:
|
||||||
|
|||||||
227
tests.py
227
tests.py
@@ -473,6 +473,20 @@ control_tests = [
|
|||||||
("DONICVM i VT I VSQVE V FAC { DIC(i) }",
|
("DONICVM i VT I VSQVE V FAC { DIC(i) }",
|
||||||
Program([], [PerStatement(DataRangeArray(Numeral("I"), Numeral("V")), ID("i"), [ExpressionStatement(BuiltIn("DIC", [ID("i")]))])]),
|
Program([], [PerStatement(DataRangeArray(Numeral("I"), Numeral("V")), ID("i"), [ExpressionStatement(BuiltIn("DIC", [ID("i")]))])]),
|
||||||
ValStr("V"), "I\nII\nIII\nIV\nV\n"),
|
ValStr("V"), "I\nII\nIII\nIV\nV\n"),
|
||||||
|
# PER destructuring
|
||||||
|
("PER a, b IN [[I, II], [III, IV]] FAC { DIC(a + b) }",
|
||||||
|
Program([], [PerStatement(
|
||||||
|
DataArray([DataArray([Numeral("I"), Numeral("II")]), DataArray([Numeral("III"), Numeral("IV")])]),
|
||||||
|
[ID("a"), ID("b")],
|
||||||
|
[ExpressionStatement(BuiltIn("DIC", [BinOp(ID("a"), ID("b"), "SYMBOL_PLUS")]))])]),
|
||||||
|
ValStr("VII"), "III\nVII\n"),
|
||||||
|
# PER destructuring: three variables
|
||||||
|
("PER a, b, c IN [[I, II, III]] FAC { DIC(a + b + c) }",
|
||||||
|
Program([], [PerStatement(
|
||||||
|
DataArray([DataArray([Numeral("I"), Numeral("II"), Numeral("III")])]),
|
||||||
|
[ID("a"), ID("b"), ID("c")],
|
||||||
|
[ExpressionStatement(BuiltIn("DIC", [BinOp(BinOp(ID("a"), ID("b"), "SYMBOL_PLUS"), ID("c"), "SYMBOL_PLUS")]))])]),
|
||||||
|
ValStr("VI"), "VI\n"),
|
||||||
]
|
]
|
||||||
|
|
||||||
class TestControl(unittest.TestCase):
|
class TestControl(unittest.TestCase):
|
||||||
@@ -701,6 +715,9 @@ error_tests = [
|
|||||||
("FALSITAS AVT NVLLVS", CentvrionError), # no short-circuit: right side evaluated, NVLLVS not boolean
|
("FALSITAS AVT NVLLVS", CentvrionError), # no short-circuit: right side evaluated, NVLLVS not boolean
|
||||||
("VERITAS ET NVLLVS", CentvrionError), # no short-circuit: right side evaluated, NVLLVS not boolean
|
("VERITAS ET NVLLVS", CentvrionError), # no short-circuit: right side evaluated, NVLLVS not boolean
|
||||||
("NVLLVS ET VERITAS", CentvrionError), # NVLLVS cannot be used as boolean in ET
|
("NVLLVS ET VERITAS", CentvrionError), # NVLLVS cannot be used as boolean in ET
|
||||||
|
('I @ [II]', CentvrionError), # @ requires two arrays (int @ array)
|
||||||
|
('[I] @ "hello"', CentvrionError), # @ requires two arrays (array @ string)
|
||||||
|
('"a" @ "b"', CentvrionError), # @ requires two arrays (string @ string)
|
||||||
('"hello" + " world"', CentvrionError), # use & for string concatenation, not +
|
('"hello" + " world"', CentvrionError), # use & for string concatenation, not +
|
||||||
("[I, II][III]", CentvrionError), # index too high
|
("[I, II][III]", CentvrionError), # index too high
|
||||||
("CVM SVBNVLLA\n[I, II][-I]", CentvrionError), # negative index
|
("CVM SVBNVLLA\n[I, II][-I]", CentvrionError), # negative index
|
||||||
@@ -751,6 +768,8 @@ error_tests = [
|
|||||||
("DESIGNA a, b VT III", CentvrionError), # destructure non-array
|
("DESIGNA a, b VT III", CentvrionError), # destructure non-array
|
||||||
("DESIGNA a, b VT [I]", CentvrionError), # destructure length mismatch: too many targets
|
("DESIGNA a, b VT [I]", CentvrionError), # destructure length mismatch: too many targets
|
||||||
("DESIGNA a, b VT [I, II, III]", CentvrionError), # destructure length mismatch: too few targets
|
("DESIGNA a, b VT [I, II, III]", CentvrionError), # destructure length mismatch: too few targets
|
||||||
|
("PER a, b IN [I, II, III] FAC { DIC(a) }", CentvrionError), # PER destructure: element is not an array
|
||||||
|
("PER a, b IN [[I], [II]] FAC { DIC(a) }", CentvrionError), # PER destructure: wrong number of elements
|
||||||
("[I, II, III][II VSQVE IV]", CentvrionError), # slice upper bound out of range
|
("[I, II, III][II VSQVE IV]", CentvrionError), # slice upper bound out of range
|
||||||
("[I, II, III][NVLLVS VSQVE II]", CentvrionError), # slice with non-integer bound
|
("[I, II, III][NVLLVS VSQVE II]", CentvrionError), # slice with non-integer bound
|
||||||
("I[I VSQVE II]", CentvrionError), # slice on non-array
|
("I[I VSQVE II]", CentvrionError), # slice on non-array
|
||||||
@@ -1031,6 +1050,40 @@ class TestArithmeticEdge(unittest.TestCase):
|
|||||||
run_test(self, source, nodes, value)
|
run_test(self, source, nodes, value)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Array concatenation ---
|
||||||
|
|
||||||
|
array_concat_tests = [
|
||||||
|
("[I, II] @ [III, IV]",
|
||||||
|
Program([], [ExpressionStatement(BinOp(DataArray([Numeral("I"), Numeral("II")]), DataArray([Numeral("III"), Numeral("IV")]), "SYMBOL_AT"))]),
|
||||||
|
ValList([ValInt(1), ValInt(2), ValInt(3), ValInt(4)])),
|
||||||
|
("[] @ [I]",
|
||||||
|
Program([], [ExpressionStatement(BinOp(DataArray([]), DataArray([Numeral("I")]), "SYMBOL_AT"))]),
|
||||||
|
ValList([ValInt(1)])),
|
||||||
|
("[I] @ []",
|
||||||
|
Program([], [ExpressionStatement(BinOp(DataArray([Numeral("I")]), DataArray([]), "SYMBOL_AT"))]),
|
||||||
|
ValList([ValInt(1)])),
|
||||||
|
("[] @ []",
|
||||||
|
Program([], [ExpressionStatement(BinOp(DataArray([]), DataArray([]), "SYMBOL_AT"))]),
|
||||||
|
ValList([])),
|
||||||
|
('["a"] @ [I]',
|
||||||
|
Program([], [ExpressionStatement(BinOp(DataArray([String("a")]), DataArray([Numeral("I")]), "SYMBOL_AT"))]),
|
||||||
|
ValList([ValStr("a"), ValInt(1)])),
|
||||||
|
# left-associative chaining
|
||||||
|
("[I] @ [II] @ [III]",
|
||||||
|
Program([], [ExpressionStatement(BinOp(BinOp(DataArray([Numeral("I")]), DataArray([Numeral("II")]), "SYMBOL_AT"), DataArray([Numeral("III")]), "SYMBOL_AT"))]),
|
||||||
|
ValList([ValInt(1), ValInt(2), ValInt(3)])),
|
||||||
|
# concat with variable
|
||||||
|
("DESIGNA a VT [I, II]\nDESIGNA b VT [III]\na @ b",
|
||||||
|
Program([], [Designa(ID("a"), DataArray([Numeral("I"), Numeral("II")])), Designa(ID("b"), DataArray([Numeral("III")])), ExpressionStatement(BinOp(ID("a"), ID("b"), "SYMBOL_AT"))]),
|
||||||
|
ValList([ValInt(1), ValInt(2), ValInt(3)])),
|
||||||
|
]
|
||||||
|
|
||||||
|
class TestArrayConcat(unittest.TestCase):
|
||||||
|
@parameterized.expand(array_concat_tests)
|
||||||
|
def test_array_concat(self, source, nodes, value):
|
||||||
|
run_test(self, source, nodes, value)
|
||||||
|
|
||||||
|
|
||||||
# --- String concatenation ---
|
# --- String concatenation ---
|
||||||
|
|
||||||
string_concat_tests = [
|
string_concat_tests = [
|
||||||
@@ -1512,6 +1565,31 @@ loop_edge_tests = [
|
|||||||
[Designa(ID("x"), BinOp(ID("x"), ID("i"), "SYMBOL_PLUS"))]),
|
[Designa(ID("x"), BinOp(ID("x"), ID("i"), "SYMBOL_PLUS"))]),
|
||||||
ExpressionStatement(ID("x"))]),
|
ExpressionStatement(ID("x"))]),
|
||||||
ValNul(), ""),
|
ValNul(), ""),
|
||||||
|
# PER destructuring with ERVMPE
|
||||||
|
("DESIGNA r VT I\nPER a, b IN [[I, II], [III, IV], [V, VI]] FAC {\nSI a EST III TVNC { ERVMPE }\nDESIGNA r VT r + a + b\n}\nr",
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("r"), Numeral("I")),
|
||||||
|
PerStatement(
|
||||||
|
DataArray([DataArray([Numeral("I"), Numeral("II")]), DataArray([Numeral("III"), Numeral("IV")]), DataArray([Numeral("V"), Numeral("VI")])]),
|
||||||
|
[ID("a"), ID("b")],
|
||||||
|
[SiStatement(BinOp(ID("a"), Numeral("III"), "KEYWORD_EST"), [Erumpe()], None),
|
||||||
|
Designa(ID("r"), BinOp(BinOp(ID("r"), ID("a"), "SYMBOL_PLUS"), ID("b"), "SYMBOL_PLUS"))],
|
||||||
|
),
|
||||||
|
ExpressionStatement(ID("r")),
|
||||||
|
]),
|
||||||
|
ValInt(4)), # 1 + 1 + 2 = 4, breaks before [III, IV]
|
||||||
|
# PER destructuring with REDI
|
||||||
|
("DEFINI f () VT {\nPER a, b IN [[I, II], [III, IV]] FAC {\nSI a EST III TVNC { REDI (b) }\n}\n}\nINVOCA f ()",
|
||||||
|
Program([], [
|
||||||
|
Defini(ID("f"), [],
|
||||||
|
[PerStatement(
|
||||||
|
DataArray([DataArray([Numeral("I"), Numeral("II")]), DataArray([Numeral("III"), Numeral("IV")])]),
|
||||||
|
[ID("a"), ID("b")],
|
||||||
|
[SiStatement(BinOp(ID("a"), Numeral("III"), "KEYWORD_EST"), [Redi([ID("b")])], None)],
|
||||||
|
)]),
|
||||||
|
ExpressionStatement(Invoca(ID("f"), [])),
|
||||||
|
]),
|
||||||
|
ValInt(4)), # returns b=IV when a=III
|
||||||
]
|
]
|
||||||
|
|
||||||
class TestLoopEdge(unittest.TestCase):
|
class TestLoopEdge(unittest.TestCase):
|
||||||
@@ -1696,7 +1774,7 @@ array_index_assign_tests = [
|
|||||||
("DESIGNA a VT [I, II, III]\nDESIGNA a[II] VT X\na[II]",
|
("DESIGNA a VT [I, II, III]\nDESIGNA a[II] VT X\na[II]",
|
||||||
Program([], [
|
Program([], [
|
||||||
Designa(ID("a"), DataArray([Numeral("I"), Numeral("II"), Numeral("III")])),
|
Designa(ID("a"), DataArray([Numeral("I"), Numeral("II"), Numeral("III")])),
|
||||||
DesignaIndex(ID("a"), Numeral("II"), Numeral("X")),
|
DesignaIndex(ID("a"), [Numeral("II")], Numeral("X")),
|
||||||
ExpressionStatement(ArrayIndex(ID("a"), Numeral("II"))),
|
ExpressionStatement(ArrayIndex(ID("a"), Numeral("II"))),
|
||||||
]),
|
]),
|
||||||
ValInt(10)),
|
ValInt(10)),
|
||||||
@@ -1704,7 +1782,7 @@ array_index_assign_tests = [
|
|||||||
("DESIGNA a VT [I, II, III]\nDESIGNA a[I] VT V\na[I]",
|
("DESIGNA a VT [I, II, III]\nDESIGNA a[I] VT V\na[I]",
|
||||||
Program([], [
|
Program([], [
|
||||||
Designa(ID("a"), DataArray([Numeral("I"), Numeral("II"), Numeral("III")])),
|
Designa(ID("a"), DataArray([Numeral("I"), Numeral("II"), Numeral("III")])),
|
||||||
DesignaIndex(ID("a"), Numeral("I"), Numeral("V")),
|
DesignaIndex(ID("a"), [Numeral("I")], Numeral("V")),
|
||||||
ExpressionStatement(ArrayIndex(ID("a"), Numeral("I"))),
|
ExpressionStatement(ArrayIndex(ID("a"), Numeral("I"))),
|
||||||
]),
|
]),
|
||||||
ValInt(5)),
|
ValInt(5)),
|
||||||
@@ -1712,7 +1790,7 @@ array_index_assign_tests = [
|
|||||||
("DESIGNA a VT [I, II, III]\nDESIGNA a[III] VT L\na[III]",
|
("DESIGNA a VT [I, II, III]\nDESIGNA a[III] VT L\na[III]",
|
||||||
Program([], [
|
Program([], [
|
||||||
Designa(ID("a"), DataArray([Numeral("I"), Numeral("II"), Numeral("III")])),
|
Designa(ID("a"), DataArray([Numeral("I"), Numeral("II"), Numeral("III")])),
|
||||||
DesignaIndex(ID("a"), Numeral("III"), Numeral("L")),
|
DesignaIndex(ID("a"), [Numeral("III")], Numeral("L")),
|
||||||
ExpressionStatement(ArrayIndex(ID("a"), Numeral("III"))),
|
ExpressionStatement(ArrayIndex(ID("a"), Numeral("III"))),
|
||||||
]),
|
]),
|
||||||
ValInt(50)),
|
ValInt(50)),
|
||||||
@@ -1720,7 +1798,7 @@ array_index_assign_tests = [
|
|||||||
("DESIGNA a VT [I, II, III]\nDESIGNA a[II] VT X\na[I]",
|
("DESIGNA a VT [I, II, III]\nDESIGNA a[II] VT X\na[I]",
|
||||||
Program([], [
|
Program([], [
|
||||||
Designa(ID("a"), DataArray([Numeral("I"), Numeral("II"), Numeral("III")])),
|
Designa(ID("a"), DataArray([Numeral("I"), Numeral("II"), Numeral("III")])),
|
||||||
DesignaIndex(ID("a"), Numeral("II"), Numeral("X")),
|
DesignaIndex(ID("a"), [Numeral("II")], Numeral("X")),
|
||||||
ExpressionStatement(ArrayIndex(ID("a"), Numeral("I"))),
|
ExpressionStatement(ArrayIndex(ID("a"), Numeral("I"))),
|
||||||
]),
|
]),
|
||||||
ValInt(1)),
|
ValInt(1)),
|
||||||
@@ -1729,7 +1807,7 @@ array_index_assign_tests = [
|
|||||||
Program([], [
|
Program([], [
|
||||||
Designa(ID("a"), DataArray([Numeral("I"), Numeral("II"), Numeral("III")])),
|
Designa(ID("a"), DataArray([Numeral("I"), Numeral("II"), Numeral("III")])),
|
||||||
Designa(ID("i"), Numeral("II")),
|
Designa(ID("i"), Numeral("II")),
|
||||||
DesignaIndex(ID("a"), ID("i"), Numeral("X")),
|
DesignaIndex(ID("a"), [ID("i")], Numeral("X")),
|
||||||
ExpressionStatement(ArrayIndex(ID("a"), Numeral("II"))),
|
ExpressionStatement(ArrayIndex(ID("a"), Numeral("II"))),
|
||||||
]),
|
]),
|
||||||
ValInt(10)),
|
ValInt(10)),
|
||||||
@@ -1741,6 +1819,109 @@ class TestArrayIndexAssign(unittest.TestCase):
|
|||||||
run_test(self, source, nodes, value)
|
run_test(self, source, nodes, value)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Multi-dimensional array index assignment ---
|
||||||
|
|
||||||
|
multidim_assign_tests = [
|
||||||
|
# 2D array assignment
|
||||||
|
("DESIGNA a VT [[I, II], [III, IV]]\nDESIGNA a[I][II] VT X\na[I][II]",
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("a"), DataArray([DataArray([Numeral("I"), Numeral("II")]), DataArray([Numeral("III"), Numeral("IV")])])),
|
||||||
|
DesignaIndex(ID("a"), [Numeral("I"), Numeral("II")], Numeral("X")),
|
||||||
|
ExpressionStatement(ArrayIndex(ArrayIndex(ID("a"), Numeral("I")), Numeral("II"))),
|
||||||
|
]),
|
||||||
|
ValInt(10)),
|
||||||
|
# other elements unaffected
|
||||||
|
("DESIGNA a VT [[I, II], [III, IV]]\nDESIGNA a[I][II] VT X\na[II][I]",
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("a"), DataArray([DataArray([Numeral("I"), Numeral("II")]), DataArray([Numeral("III"), Numeral("IV")])])),
|
||||||
|
DesignaIndex(ID("a"), [Numeral("I"), Numeral("II")], Numeral("X")),
|
||||||
|
ExpressionStatement(ArrayIndex(ArrayIndex(ID("a"), Numeral("II")), Numeral("I"))),
|
||||||
|
]),
|
||||||
|
ValInt(3)),
|
||||||
|
# dict inside array
|
||||||
|
('DESIGNA a VT [TABVLA {"x" VT I}]\nDESIGNA a[I]["x"] VT X\na[I]["x"]',
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("a"), DataArray([DataDict([(String("x"), Numeral("I"))])])),
|
||||||
|
DesignaIndex(ID("a"), [Numeral("I"), String("x")], Numeral("X")),
|
||||||
|
ExpressionStatement(ArrayIndex(ArrayIndex(ID("a"), Numeral("I")), String("x"))),
|
||||||
|
]),
|
||||||
|
ValInt(10)),
|
||||||
|
# array inside dict
|
||||||
|
('DESIGNA d VT TABVLA {"a" VT [I, II]}\nDESIGNA d["a"][I] VT X\nd["a"][I]',
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("d"), DataDict([(String("a"), DataArray([Numeral("I"), Numeral("II")]))])),
|
||||||
|
DesignaIndex(ID("d"), [String("a"), Numeral("I")], Numeral("X")),
|
||||||
|
ExpressionStatement(ArrayIndex(ArrayIndex(ID("d"), String("a")), Numeral("I"))),
|
||||||
|
]),
|
||||||
|
ValInt(10)),
|
||||||
|
# 3 levels deep
|
||||||
|
("DESIGNA a VT [[[I]]]\nDESIGNA a[I][I][I] VT X\na[I][I][I]",
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("a"), DataArray([DataArray([DataArray([Numeral("I")])])])),
|
||||||
|
DesignaIndex(ID("a"), [Numeral("I"), Numeral("I"), Numeral("I")], Numeral("X")),
|
||||||
|
ExpressionStatement(ArrayIndex(ArrayIndex(ArrayIndex(ID("a"), Numeral("I")), Numeral("I")), Numeral("I"))),
|
||||||
|
]),
|
||||||
|
ValInt(10)),
|
||||||
|
]
|
||||||
|
|
||||||
|
class TestMultidimAssign(unittest.TestCase):
|
||||||
|
@parameterized.expand(multidim_assign_tests)
|
||||||
|
def test_multidim_assign(self, source, nodes, value):
|
||||||
|
run_test(self, source, nodes, value)
|
||||||
|
|
||||||
|
|
||||||
|
# --- String index assignment ---
|
||||||
|
|
||||||
|
string_index_assign_tests = [
|
||||||
|
# assign to middle character
|
||||||
|
('DESIGNA s VT "ABCDE"\nDESIGNA s[III] VT "X"\ns',
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("s"), String("ABCDE")),
|
||||||
|
DesignaIndex(ID("s"), [Numeral("III")], String("X")),
|
||||||
|
ExpressionStatement(ID("s")),
|
||||||
|
]),
|
||||||
|
ValStr("ABXDE")),
|
||||||
|
# assign to first character
|
||||||
|
('DESIGNA s VT "ABCDE"\nDESIGNA s[I] VT "Z"\ns',
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("s"), String("ABCDE")),
|
||||||
|
DesignaIndex(ID("s"), [Numeral("I")], String("Z")),
|
||||||
|
ExpressionStatement(ID("s")),
|
||||||
|
]),
|
||||||
|
ValStr("ZBCDE")),
|
||||||
|
# assign to last character
|
||||||
|
('DESIGNA s VT "ABCDE"\nDESIGNA s[V] VT "Z"\ns',
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("s"), String("ABCDE")),
|
||||||
|
DesignaIndex(ID("s"), [Numeral("V")], String("Z")),
|
||||||
|
ExpressionStatement(ID("s")),
|
||||||
|
]),
|
||||||
|
ValStr("ABCDZ")),
|
||||||
|
# variable as index
|
||||||
|
('DESIGNA s VT "ABCDE"\nDESIGNA i VT II\nDESIGNA s[i] VT "X"\ns',
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("s"), String("ABCDE")),
|
||||||
|
Designa(ID("i"), Numeral("II")),
|
||||||
|
DesignaIndex(ID("s"), [ID("i")], String("X")),
|
||||||
|
ExpressionStatement(ID("s")),
|
||||||
|
]),
|
||||||
|
ValStr("AXCDE")),
|
||||||
|
# string inside array
|
||||||
|
('DESIGNA a VT ["ABC", "DEF"]\nDESIGNA a[I][II] VT "X"\na[I]',
|
||||||
|
Program([], [
|
||||||
|
Designa(ID("a"), DataArray([String("ABC"), String("DEF")])),
|
||||||
|
DesignaIndex(ID("a"), [Numeral("I"), Numeral("II")], String("X")),
|
||||||
|
ExpressionStatement(ArrayIndex(ID("a"), Numeral("I"))),
|
||||||
|
]),
|
||||||
|
ValStr("AXC")),
|
||||||
|
]
|
||||||
|
|
||||||
|
class TestStringIndexAssign(unittest.TestCase):
|
||||||
|
@parameterized.expand(string_index_assign_tests)
|
||||||
|
def test_string_index_assign(self, source, nodes, value):
|
||||||
|
run_test(self, source, nodes, value)
|
||||||
|
|
||||||
|
|
||||||
# --- Array slicing ---
|
# --- Array slicing ---
|
||||||
|
|
||||||
array_slice_tests = [
|
array_slice_tests = [
|
||||||
@@ -1798,6 +1979,36 @@ class TestArraySlice(unittest.TestCase):
|
|||||||
run_test(self, source, nodes, value)
|
run_test(self, source, nodes, value)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Multiline arrays ---
|
||||||
|
|
||||||
|
multiline_array_tests = [
|
||||||
|
# newlines after commas
|
||||||
|
("[I,\nII,\nIII]",
|
||||||
|
Program([], [ExpressionStatement(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]))]),
|
||||||
|
ValList([ValInt(1), ValInt(2), ValInt(3)])),
|
||||||
|
# single newline after comma
|
||||||
|
("[I, II,\nIII]",
|
||||||
|
Program([], [ExpressionStatement(DataArray([Numeral("I"), Numeral("II"), Numeral("III")]))]),
|
||||||
|
ValList([ValInt(1), ValInt(2), ValInt(3)])),
|
||||||
|
# empty array still works
|
||||||
|
("[]",
|
||||||
|
Program([], [ExpressionStatement(DataArray([]))]),
|
||||||
|
ValList([])),
|
||||||
|
# nested arrays with newlines
|
||||||
|
("[I,\n[II, III],\nIV]",
|
||||||
|
Program([], [ExpressionStatement(DataArray([
|
||||||
|
Numeral("I"),
|
||||||
|
DataArray([Numeral("II"), Numeral("III")]),
|
||||||
|
Numeral("IV")]))]),
|
||||||
|
ValList([ValInt(1), ValList([ValInt(2), ValInt(3)]), ValInt(4)])),
|
||||||
|
]
|
||||||
|
|
||||||
|
class TestMultilineArray(unittest.TestCase):
|
||||||
|
@parameterized.expand(multiline_array_tests)
|
||||||
|
def test_multiline_array(self, source, nodes, value):
|
||||||
|
run_test(self, source, nodes, value)
|
||||||
|
|
||||||
|
|
||||||
# --- String indexing ---
|
# --- String indexing ---
|
||||||
|
|
||||||
string_index_tests = [
|
string_index_tests = [
|
||||||
@@ -2387,7 +2598,7 @@ dict_assign_tests = [
|
|||||||
('DESIGNA d VT TABVLA {"a" VT I}\nDESIGNA d["a"] VT X\nd["a"]',
|
('DESIGNA d VT TABVLA {"a" VT I}\nDESIGNA d["a"] VT X\nd["a"]',
|
||||||
Program([], [
|
Program([], [
|
||||||
Designa(ID("d"), DataDict([(String("a"), Numeral("I"))])),
|
Designa(ID("d"), DataDict([(String("a"), Numeral("I"))])),
|
||||||
DesignaIndex(ID("d"), String("a"), Numeral("X")),
|
DesignaIndex(ID("d"), [String("a")], Numeral("X")),
|
||||||
ExpressionStatement(ArrayIndex(ID("d"), String("a"))),
|
ExpressionStatement(ArrayIndex(ID("d"), String("a"))),
|
||||||
]),
|
]),
|
||||||
ValInt(10)),
|
ValInt(10)),
|
||||||
@@ -2395,7 +2606,7 @@ dict_assign_tests = [
|
|||||||
('DESIGNA d VT TABVLA {"a" VT I}\nDESIGNA d["b"] VT II\nd["b"]',
|
('DESIGNA d VT TABVLA {"a" VT I}\nDESIGNA d["b"] VT II\nd["b"]',
|
||||||
Program([], [
|
Program([], [
|
||||||
Designa(ID("d"), DataDict([(String("a"), Numeral("I"))])),
|
Designa(ID("d"), DataDict([(String("a"), Numeral("I"))])),
|
||||||
DesignaIndex(ID("d"), String("b"), Numeral("II")),
|
DesignaIndex(ID("d"), [String("b")], Numeral("II")),
|
||||||
ExpressionStatement(ArrayIndex(ID("d"), String("b"))),
|
ExpressionStatement(ArrayIndex(ID("d"), String("b"))),
|
||||||
]),
|
]),
|
||||||
ValInt(2)),
|
ValInt(2)),
|
||||||
@@ -2403,7 +2614,7 @@ dict_assign_tests = [
|
|||||||
('DESIGNA d VT TABVLA {"a" VT I}\nDESIGNA d["b"] VT II\nd["a"]',
|
('DESIGNA d VT TABVLA {"a" VT I}\nDESIGNA d["b"] VT II\nd["a"]',
|
||||||
Program([], [
|
Program([], [
|
||||||
Designa(ID("d"), DataDict([(String("a"), Numeral("I"))])),
|
Designa(ID("d"), DataDict([(String("a"), Numeral("I"))])),
|
||||||
DesignaIndex(ID("d"), String("b"), Numeral("II")),
|
DesignaIndex(ID("d"), [String("b")], Numeral("II")),
|
||||||
ExpressionStatement(ArrayIndex(ID("d"), String("a"))),
|
ExpressionStatement(ArrayIndex(ID("d"), String("a"))),
|
||||||
]),
|
]),
|
||||||
ValInt(1)),
|
ValInt(1)),
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "keyword.operator.arithmetic.cent",
|
"name": "keyword.operator.arithmetic.cent",
|
||||||
"match": "(\\*|\\+|-|/|&)"
|
"match": "(\\*|\\+|-|/|&|@)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user