diff --git a/docs/language.md b/docs/language.md index 20a0f95b..ceefd4e3 100644 --- a/docs/language.md +++ b/docs/language.md @@ -703,6 +703,23 @@ There is actually a more convenient `polar(theta)` function that does the same thing as `zip(cos(theta), sin(theta))`. Arguably, it would be even neater to implement a clock face using `!transform rotate=` instead. +Some syntactic sugar is provided to make directly nested loops more readable. +For example: + +```flitter +for x in ..N + for y in ..N + for z in ..N + !sphere position=x;y;z size=0.5 +``` + +may be written as the more concise: + +```flitter +for x in ..N, y in ..N, z in ..N + !sphere position=x;y;z size=0.5 +``` + Again, loops may also be used in-line in non-sequence expressions with syntax borrowed from Python: @@ -716,6 +733,23 @@ This will evaluate to: !line points=0;0;1;5;2;10;3;15;4;20 ``` +There is no special syntax for nested in-line loops as these can already be +simply written as: + +```flitter +let grid = (x;y) for x in ..N for y in ..N +``` + +It is worth noting that – following the reversed notation – these loops operate +in the *reverse* order to nested non-inline loops, in that this example is +equivalent to: + +```flitter +let grid = ((x;y) for x in ..N) for y in ..N +``` + +and therefore the *last* loop is outermost. + ## Function calling Functions are called in the normal way, with the name of the function followed diff --git a/examples/bounce.fl b/examples/bounce.fl index e87e76cc..733a2108 100644 --- a/examples/bounce.fl +++ b/examples/bounce.fl @@ -65,6 +65,5 @@ let SIZE=1080;1080 !light position=0 falloff=1;0;1;0 color=color radius=RADII[i] -- These additional directional lights are just here to create some -- reflections on the glass box that lend it more presence - for x in (-1;1) - for y in (-1;1) - !light color=0.01 direction=x;y;0.5+0.1*y-0.05*x + for x in (-1;1), y in (-1;1) + !light color=0.01 direction=x;y;0.5+0.1*y-0.05*x diff --git a/src/flitter/language/grammar.lark b/src/flitter/language/grammar.lark index 2e69c05f..0ea07458 100644 --- a/src/flitter/language/grammar.lark +++ b/src/flitter/language/grammar.lark @@ -33,10 +33,12 @@ let_expression : "let" multiline_bindings sequence -> let ?expression : node _NL | node _NL _INDENT sequence _DEDENT -> append | "@" atom [attribute_bindings] _NL [_INDENT sequence _DEDENT] -> template_call - | "for" name_list "in" conditional _NL _INDENT sequence _DEDENT -> loop + | "for" iterators _NL _INDENT sequence _DEDENT -> loop | "if" conditions ["else" _NL _INDENT sequence _DEDENT] -> if_else | _EOF -> export +iterators : name_list "in" conditional ("," name_list "in" conditional)* -> tuple + function : "func" NAME _LPAREN parameters _RPAREN _NL _INDENT sequence _DEDENT parameters : (parameter ("," parameter)*)? -> tuple diff --git a/src/flitter/language/parser.py b/src/flitter/language/parser.py index 3788fb47..738472c1 100644 --- a/src/flitter/language/parser.py +++ b/src/flitter/language/parser.py @@ -77,6 +77,12 @@ def inline_if_else(self, then, condition, else_): def inline_loop(self, body, names, source): return tree.For(names, source, body) + def loop(self, iterators, expr): + while iterators: + expr = tree.For(iterators[-2], iterators[-1], expr) + iterators = iterators[:-2] + return expr + def call(self, function, args): args = list(args) bindings = [] @@ -120,7 +126,6 @@ def anonymous_function(self, parameters, body): logical_or = tree.Or logical_xor = tree.Xor lookup = tree.Lookup - loop = tree.For lt = tree.LessThan modulo = tree.Modulo multiply = tree.Multiply