diff --git a/docs/SPECIFICATION.html b/docs/SPECIFICATION.html
index 7fb2c2b..948629b 100644
--- a/docs/SPECIFICATION.html
+++ b/docs/SPECIFICATION.html
@@ -54,7 +54,7 @@
Keywords and built-in names MUST be matched case-sensitively and MUST be written in their canonical uppercase forms. If a reserved word is written in any other case, it MUST be tokenized as an identifier instead.
-The character `-` MUST be interpreted only as the leading sign of a numeric literal or as the dash used by slice syntax `lo-hi`. Any unsupported use of `-` MUST raise a syntax error.
+The character `-` MUST be interpreted only as the leading sign of a numeric literal. Any unsupported use of `-` MUST raise a syntax error.
The character `~` MUST be reserved for coerced function parameters and MUST NOT appear inside identifiers.
@@ -104,7 +104,7 @@
Indexed assignment to a map MUST use the same form as ordinary map access, such as `map = expression`. Intermediate nested maps MUST be created on demand when assigning to deeper keys. Reassigning an existing key to a value of a different static type MUST raise a runtime error.
-If a tensor target uses slice indices such as `lo-hi` or `*`, the right-hand side MUST evaluate to a `TNS` whose shape exactly matches the selected slice, and every written element MUST satisfy the target's static type constraints.
+If a tensor target uses slice indices such as `lo:hi` or `*`, the right-hand side MUST evaluate to a `TNS` whose shape exactly matches the selected slice, and every written element MUST satisfy the target's static type constraints.
---
@@ -328,7 +328,7 @@
`TNS` indexing MUST use square brackets (`[` and `]`) with a comma-separated list of indices. Indexing MUST be one-based. Negative indices MUST count backwards from the end of the dimension.
-Slice indexing MUST be supported in any index position using a range of the form `lo-hi`. The selected slice MUST be inclusive of both endpoints. The symbol `*` MAY be used in an index position to denote a full-dimension slice, selecting every element along that axis.
+Slice indexing MUST be supported in any index position using a range of the form `lo:hi`. The selected slice MUST be inclusive of both endpoints. The symbol `*` MAY be used in an index position to denote a full-dimension slice, selecting every element along that axis.
---
diff --git a/lib/std/image/init.pre b/lib/std/image/init.pre
index 82e96de..c5d3898 100644
--- a/lib/std/image/init.pre
+++ b/lib/std/image/init.pre
@@ -120,7 +120,7 @@ FUNC TNS CROP(TNS img, TNS corners){
IF(NEQ(SHAPE(corners), [0d4, 0d2])){
THROW("CROP corners must be a 0d4 by 0d2 tensor: [[tl_x, tl_y], [tr_x, tr_y], [bl_x, bl_y], [br_x, br_y]]")
}
- RETURN(img[MIN(corners[*, 0d1])-MAX(corners[*, 0d1]), MIN(corners[*, 0d2])-MAX(corners[*, 0d2]), *])
+ RETURN(img[MIN(corners[*, 0d1]):MAX(corners[*, 0d1]), MIN(corners[*, 0d2]):MAX(corners[*, 0d2]), *])
}
FUNC BOOL SHOW(TNS img){
diff --git a/src/parser.c b/src/parser.c
index 1306bd8..fd4f39a 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -718,16 +718,9 @@ static Expr* parse_call(Parser* parser) {
// parse an expression for index or possibly a range
Expr* start = parse_expression(parser);
if (!start) return NULL;
- // Accept either an explicit DASH token or a negative-number token as the range separator
- bool is_range = false;
- if (parser->current_token.type == TOKEN_DASH) {
- is_range = true;
- } else if (parser->current_token.type == TOKEN_NUMBER && parser->current_token.literal && parser->current_token.literal[0] == '-') {
- is_range = true;
- }
- if (is_range) {
- // If it's a DASH token, consume it; otherwise leave the negative-number token for parse_expression
- if (parser->current_token.type == TOKEN_DASH) advance(parser);
+ // Range separator is ':' inside index expressions
+ if (parser->current_token.type == TOKEN_COLON) {
+ advance(parser); // consume ':'
Expr* end = parse_expression(parser);
if (!end) return NULL;
Expr* range = expr_range(start, end, start->line, start->column);
@@ -969,20 +962,17 @@ static Stmt* parse_statement(Parser* parser) {
Expr* wc = expr_wildcard(parser->previous_token.line, parser->previous_token.column);
expr_list_add(&idx->as.index.indices, wc);
} else {
- Expr* start = parse_expression(parser);
- if (!start) return NULL;
- bool is_range = false;
- if (parser->current_token.type == TOKEN_DASH) is_range = true;
- else if (parser->current_token.type == TOKEN_NUMBER && parser->current_token.literal && parser->current_token.literal[0] == '-') is_range = true;
- if (is_range) {
- if (parser->current_token.type == TOKEN_DASH) advance(parser);
- Expr* end = parse_expression(parser);
- if (!end) return NULL;
- Expr* range = expr_range(start, end, start->line, start->column);
- expr_list_add(&idx->as.index.indices, range);
- } else {
- expr_list_add(&idx->as.index.indices, start);
- }
+ Expr* start = parse_expression(parser);
+ if (!start) return NULL;
+ if (parser->current_token.type == TOKEN_COLON) {
+ advance(parser); // consume ':'
+ Expr* end = parse_expression(parser);
+ if (!end) return NULL;
+ Expr* range = expr_range(start, end, start->line, start->column);
+ expr_list_add(&idx->as.index.indices, range);
+ } else {
+ expr_list_add(&idx->as.index.indices, start);
+ }
}
if (parser->current_token.type == TOKEN_COMMA) { advance(parser); continue; }
diff --git a/src/token.h b/src/token.h
index 9852383..1339112 100644
--- a/src/token.h
+++ b/src/token.h
@@ -31,7 +31,7 @@ typedef enum {
TOKEN_TILDE, // ~
TOKEN_STAR, // *
TOKEN_DOT, // .
- TOKEN_DASH, // - (when used as slice range separator)
+ TOKEN_DASH, // - (reserved; negative numeric literals are part of NUMBER/FLOAT; standalone '-' is a syntax error)
// Keywords
TOKEN_TRY,
diff --git a/tests/cases/passing/tns-indexing.pre b/tests/cases/passing/tns-indexing.pre
index 8aefd82..990acb2 100644
--- a/tests/cases/passing/tns-indexing.pre
+++ b/tests/cases/passing/tns-indexing.pre
@@ -15,16 +15,16 @@ ASSERT(EQ(cube[0d2, 0d2, 0d1], 0d7))
ASSERT(EQ(cube[0d1], [[0d1, 0d2], [0d3, 0d4]]))
ASSERT(EQ(cube[0d1, *], [[0d1, 0d2], [0d3, 0d4]]))
-ASSERT(EQ(vec[0d1-0d2], [0d1, 0d2]))
-ASSERT(EQ(vec[0d2-0d2], 0d2))
-ASSERT(EQ(vec[0d1--0d1], [0d1, 0d2, 0d3]))
-ASSERT(EQ(vec[0d0-0d4], [0d1, 0d2, 0d3]))
+ASSERT(EQ(vec[0d1:0d2], [0d1, 0d2]))
+ASSERT(EQ(vec[0d2:0d2], 0d2))
+ASSERT(EQ(vec[0d1:-0d1], [0d1, 0d2, 0d3]))
+ASSERT(EQ(vec[0d0:0d4], [0d1, 0d2, 0d3]))
ASSERT(EQ(matrix[*, *], matrix))
ASSERT(EQ(matrix[*, 0d1], [0d1, 0d3]))
ASSERT(EQ(matrix[0d1, *], [0d1, 0d2]))
-ASSERT(EQ(matrix[0d1-0d2, 0d1-0d2], matrix))
-ASSERT(EQ(matrix[0d1--0d1, 0d1], [0d1, 0d3]))
+ASSERT(EQ(matrix[0d1:0d2, 0d1:0d2], matrix))
+ASSERT(EQ(matrix[0d1:-0d1, 0d1], [0d1, 0d3]))
ASSERT(EQ(cube[*, 0d2, 0d1], [0d3, 0d7]))
INT errors_caught = 0d0
@@ -44,7 +44,7 @@ TRY {
}
TRY {
- vec[TRUE-0d1]
+ vec[TRUE:0d1]
} CATCH(err) {
ASSERT(EQ(err, 'Range bounds must be INT'))
ADD(@errors_caught, 0d1)
diff --git a/tests/cases/passing/tns-slice-assign-1d.pre b/tests/cases/passing/tns-slice-assign-1d.pre
index 08cb2b4..48c811b 100644
--- a/tests/cases/passing/tns-slice-assign-1d.pre
+++ b/tests/cases/passing/tns-slice-assign-1d.pre
@@ -1,4 +1,4 @@
TNS vec = [0d1, 0d2, 0d3]
-ASSERT(EQ(ASSIGN(vec[0d1-0d2], [0d9, 0d10]), [0d9, 0d10]))
+ASSERT(EQ(ASSIGN(vec[0d1:0d2], [0d9, 0d10]), [0d9, 0d10]))
ASSERT(EQ(vec, [0d9, 0d10, 0d3]))