lang: Add a forkv loop statement for iterating over a map

This adds a forkv statement which is used to iterate over a map with a
body of statements. This is an important data transformation tool which
should be used sparingly, but is important to have.

An import statement inside of a forkv loop is not currently supported.
We have a simple hack to detect the obvious cases, but more deeply
nested scenarios probably won't be caught, and you'll get an obscure
error message if you try to do this.

This was incredibly challenging to get right, and it's all thanks to Sam
for his brilliance.

Note, I couldn't think of a better keyword that "forkv" but suggestions
are welcome if you think you have a better idea. Other ideas were formap
and foreach, but neither got me very excited.
This commit is contained in:
James Shubin
2025-03-05 13:18:35 -05:00
parent cf7e73bbf6
commit 2899bc234a
51 changed files with 1885 additions and 2 deletions

View File

@@ -159,6 +159,11 @@
lval.str = yylex.Text()
return FOR
}
/forkv/ {
yylex.pos(lval) // our pos
lval.str = yylex.Text()
return FORKV
}
/\->/ {
yylex.pos(lval) // our pos
lval.str = yylex.Text()

View File

@@ -92,7 +92,7 @@ func init() {
%token OPEN_CURLY CLOSE_CURLY
%token OPEN_PAREN CLOSE_PAREN
%token OPEN_BRACK CLOSE_BRACK
%token IF ELSE FOR
%token IF ELSE FOR FORKV
%token BOOL STRING INTEGER FLOAT
%token EQUALS DOLLAR
%token COMMA COLON SEMICOLON
@@ -228,6 +228,18 @@ stmt:
}
locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt)
}
// iterate over maps
// `forkv $key, $val in $map { <body> }`
| FORKV var_identifier COMMA var_identifier IN expr OPEN_CURLY prog CLOSE_CURLY
{
$$.stmt = &ast.StmtForKV{
Key: $2.str, // no $ prefix
Val: $4.str, // no $ prefix
Expr: $6.expr, // XXX: name this Map ?
Body: $8.stmt,
}
locate(yylex, $1, yyDollar[len(yyDollar)-1], $$.stmt)
}
// this is the named version, iow, a user-defined function (statement)
// `func name() { <expr> }`
// `func name(<arg>) { <expr> }`