aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Jin <dragon-fly@qq.com>2025-05-21 11:44:54 +0800
committerLi Jin <dragon-fly@qq.com>2025-05-21 11:44:54 +0800
commit0603800a4114ed8b4c9572a7d7852995c9b9f334 (patch)
tree456524685562bcd0d874530e3ddc2a0fc0731525
parentff137ac73d999a5849f02706cfd52f4659b025ef (diff)
downloadyuescript-0603800a4114ed8b4c9572a7d7852995c9b9f334.tar.gz
yuescript-0603800a4114ed8b4c9572a7d7852995c9b9f334.tar.bz2
yuescript-0603800a4114ed8b4c9572a7d7852995c9b9f334.zip
Added break with value syntax.
-rwxr-xr-xdoc/docs/doc/README.md27
-rwxr-xr-xdoc/docs/zh/doc/README.md27
-rw-r--r--spec/inputs/loops.yue39
-rw-r--r--spec/outputs/5.1/loops.lua104
-rw-r--r--spec/outputs/codes_from_doc.lua60
-rw-r--r--spec/outputs/codes_from_doc_zh.lua60
-rw-r--r--spec/outputs/loops.lua104
-rw-r--r--spec/outputs/macro.lua2
-rw-r--r--spec/outputs/unicode/loops.lua4
-rw-r--r--spec/outputs/unicode/macro.lua2
-rw-r--r--src/yuescript/yue_ast.cpp12
-rw-r--r--src/yuescript/yue_ast.h14
-rw-r--r--src/yuescript/yue_compiler.cpp183
-rw-r--r--src/yuescript/yue_parser.cpp4
-rw-r--r--src/yuescript/yue_parser.h2
15 files changed, 561 insertions, 83 deletions
diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md
index c275c52..dac96ee 100755
--- a/doc/docs/doc/README.md
+++ b/doc/docs/doc/README.md
@@ -29,6 +29,16 @@ inventory =
29 * name: "bread" 29 * name: "bread"
30 count: 3 30 count: 3
31 31
32-- list comprehension
33map = (arr, action) ->
34 [action item for item in *arr]
35
36filter = (arr, cond) ->
37 [item for item in *arr when cond item]
38
39reduce = (arr, init, action): init ->
40 init = action init, item for item in *arr
41
32-- pipe operator 42-- pipe operator
33[1, 2, 3] 43[1, 2, 3]
34 |> map (x) -> x * 2 44 |> map (x) -> x * 2
@@ -2324,6 +2334,23 @@ doubled_evens = for i = 1, 20
2324</pre> 2334</pre>
2325</YueDisplay> 2335</YueDisplay>
2326 2336
2337In addition, for loops support break with a return value, allowing the loop itself to be used as an expression that exits early with a meaningful result.
2338
2339For example, to find the first number greater than 10:
2340
2341```moonscript
2342first_large = for n in *numbers
2343 break n if n > 10
2344```
2345<YueDisplay>
2346<pre>
2347first_large = for n in *numbers
2348 break n if n > 10
2349</pre>
2350</YueDisplay>
2351
2352This break-with-value syntax enables concise and expressive search or early-exit patterns directly within loop expressions.
2353
2327You can also filter values by combining the for loop expression with the continue statement. 2354You can also filter values by combining the for loop expression with the continue statement.
2328 2355
2329For loops at the end of a function body are not accumulated into a table for a return value (Instead the function will return nil). Either an explicit return statement can be used, or the loop can be converted into a list comprehension. 2356For loops at the end of a function body are not accumulated into a table for a return value (Instead the function will return nil). Either an explicit return statement can be used, or the loop can be converted into a list comprehension.
diff --git a/doc/docs/zh/doc/README.md b/doc/docs/zh/doc/README.md
index 11dc108..850afed 100755
--- a/doc/docs/zh/doc/README.md
+++ b/doc/docs/zh/doc/README.md
@@ -29,6 +29,16 @@ inventory =
29 * name: "bread" 29 * name: "bread"
30 count: 3 30 count: 3
31 31
32-- 列表推导
33map = (arr, action) ->
34 [action item for item in *arr]
35
36filter = (arr, cond) ->
37 [item for item in *arr when cond item]
38
39reduce = (arr, init, action): init ->
40 init = action init, item for item in *arr
41
32-- 管道操作符 42-- 管道操作符
33[1, 2, 3] 43[1, 2, 3]
34 |> map (x) -> x * 2 44 |> map (x) -> x * 2
@@ -2286,9 +2296,24 @@ doubled_evens = for i = 1, 20
2286</pre> 2296</pre>
2287</YueDisplay> 2297</YueDisplay>
2288 2298
2299此外,for循环还支持带返回值的break语句,这样循环本身就可以作为一个表达式,在满足条件时提前退出并返回有意义的结果。
2300
2301例如,查找第一个大于10的数字:
2302
2303```moonscript
2304first_large = for n in *numbers
2305 break n if n > 10
2306```
2307<YueDisplay>
2308<pre>
2309first_large = for n in *numbers
2310 break n if n > 10
2311</pre>
2312</YueDisplay>
2313
2289你还可以结合for循环表达式与continue语句来过滤值。 2314你还可以结合for循环表达式与continue语句来过滤值。
2290 2315
2291注意出现在函数体末尾的for循环,不会被当作是一个表达式,并将循环结果累积到一个列表中作为返回值(相反,函数将返回nil)。如果要函数末尾的循环转换为列表表达式,可以使用返回语句加for循环表达式。 2316注意出现在函数体末尾的for循环,不会被当作是一个表达式并将循环结果累积到一个列表中作为返回值(相反,函数将返回nil)。如果要函数末尾的循环转换为列表表达式,可以显式地使用返回语句加for循环表达式。
2292 2317
2293```moonscript 2318```moonscript
2294func_a = -> for i = 1, 10 do print i 2319func_a = -> for i = 1, 10 do print i
diff --git a/spec/inputs/loops.yue b/spec/inputs/loops.yue
index c5b28b3..9a91b42 100644
--- a/spec/inputs/loops.yue
+++ b/spec/inputs/loops.yue
@@ -213,3 +213,42 @@ do
213do 213do
214 until x := func 'a', b do 214 until x := func 'a', b do
215 print "false expected" 215 print "false expected"
216
217do
218 index = for i = 1, #tb
219 break i if tb[i]
220
221 f for i = 1, #tb
222 break i if tb[i]
223
224 f for i = 1, #tb
225 i if tb[i]
226
227 i = 1
228 ids = while tb[i]
229 i += 1
230 i - 1
231
232 i = 1
233 idx = while tb[i]
234 i += 1
235 break i - 1
236
237 f1 = ->
238 i = 1
239 f while tb[i]
240 i += 1
241 i - 1
242
243 i = 1
244 f while tb[i]
245 i += 1
246 break i - 1
247
248 list = for item in *items
249 switch item
250 when type: "A", :value
251 if value > 5
252 item
253
254
diff --git a/spec/outputs/5.1/loops.lua b/spec/outputs/5.1/loops.lua
index 57b19be..bc720f6 100644
--- a/spec/outputs/5.1/loops.lua
+++ b/spec/outputs/5.1/loops.lua
@@ -60,8 +60,8 @@ do
60 local y = hello[_index_0] 60 local y = hello[_index_0]
61 if y % 2 == 0 then 61 if y % 2 == 0 then
62 _accum_0[_len_0] = y 62 _accum_0[_len_0] = y
63 _len_0 = _len_0 + 1
63 end 64 end
64 _len_0 = _len_0 + 1
65 end 65 end
66 x = _accum_0 66 x = _accum_0
67end 67end
@@ -132,13 +132,11 @@ do
132end 132end
133do 133do
134 local _accum_0 = { } 134 local _accum_0 = { }
135 local _len_0 = 1
136 local _list_2 = 3 135 local _list_2 = 3
137 for _index_0 = 1, #_list_2 do 136 for _index_0 = 1, #_list_2 do
138 local thing = _list_2[_index_0] 137 local thing = _list_2[_index_0]
139 y = "hello" 138 y = "hello"
140 break 139 break
141 _len_0 = _len_0 + 1
142 end 140 end
143 x = _accum_0 141 x = _accum_0
144end 142end
@@ -489,3 +487,103 @@ do
489 end 487 end
490 until false 488 until false
491end 489end
490local _anon_func_0 = function(i, tb)
491 local _accum_0 = { }
492 local _len_0 = 1
493 while tb[i] do
494 i = i + 1
495 _accum_0[_len_0] = i - 1
496 _len_0 = _len_0 + 1
497 end
498 return _accum_0
499end
500do
501 local index
502 do
503 local _accum_0
504 for i = 1, #tb do
505 if tb[i] then
506 _accum_0 = i
507 break
508 end
509 end
510 index = _accum_0
511 end
512 f((function()
513 local _accum_0
514 for i = 1, #tb do
515 if tb[i] then
516 _accum_0 = i
517 break
518 end
519 end
520 return _accum_0
521 end)())
522 f((function()
523 local _accum_0 = { }
524 local _len_0 = 1
525 for i = 1, #tb do
526 if tb[i] then
527 _accum_0[_len_0] = i
528 _len_0 = _len_0 + 1
529 end
530 end
531 return _accum_0
532 end)())
533 i = 1
534 local ids
535 do
536 local _accum_0 = { }
537 local _len_0 = 1
538 while tb[i] do
539 i = i + 1
540 _accum_0[_len_0] = i - 1
541 _len_0 = _len_0 + 1
542 end
543 ids = _accum_0
544 end
545 i = 1
546 local idx
547 do
548 local _accum_0
549 while tb[i] do
550 i = i + 1
551 _accum_0 = i - 1
552 break
553 end
554 idx = _accum_0
555 end
556 local f1
557 f1 = function()
558 i = 1
559 return f(_anon_func_0(i, tb))
560 end
561 i = 1
562 f((function()
563 local _accum_0
564 while tb[i] do
565 i = i + 1
566 _accum_0 = i - 1
567 break
568 end
569 return _accum_0
570 end)())
571 local _accum_0 = { }
572 local _len_0 = 1
573 local _list_3 = items
574 for _index_0 = 1, #_list_3 do
575 local item = _list_3[_index_0]
576 local _type_0 = type(item)
577 local _tab_0 = "table" == _type_0 or "userdata" == _type_0
578 if _tab_0 then
579 local value = item.value
580 if "A" == item.type and value ~= nil then
581 if value > 5 then
582 _accum_0[_len_0] = item
583 _len_0 = _len_0 + 1
584 end
585 end
586 end
587 end
588 list = _accum_0
589end
diff --git a/spec/outputs/codes_from_doc.lua b/spec/outputs/codes_from_doc.lua
index a5412ab..644b7c3 100644
--- a/spec/outputs/codes_from_doc.lua
+++ b/spec/outputs/codes_from_doc.lua
@@ -20,6 +20,38 @@ local inventory = {
20 } 20 }
21 } 21 }
22} 22}
23local map
24map = function(arr, action)
25 local _accum_0 = { }
26 local _len_0 = 1
27 for _index_0 = 1, #arr do
28 local item = arr[_index_0]
29 _accum_0[_len_0] = action(item)
30 _len_0 = _len_0 + 1
31 end
32 return _accum_0
33end
34local filter
35filter = function(arr, cond)
36 local _accum_0 = { }
37 local _len_0 = 1
38 for _index_0 = 1, #arr do
39 local item = arr[_index_0]
40 if cond(item) then
41 _accum_0[_len_0] = item
42 _len_0 = _len_0 + 1
43 end
44 end
45 return _accum_0
46end
47local reduce
48reduce = function(arr, init, action)
49 for _index_0 = 1, #arr do
50 local item = arr[_index_0]
51 init = action(init, item)
52 end
53 return init
54end
23print(reduce(filter(map({ 55print(reduce(filter(map({
24 1, 56 1,
25 2, 57 2,
@@ -1026,12 +1058,24 @@ local _len_0 = 1
1026for i = 1, 20 do 1058for i = 1, 20 do
1027 if i % 2 == 0 then 1059 if i % 2 == 0 then
1028 _accum_0[_len_0] = i * 2 1060 _accum_0[_len_0] = i * 2
1061 _len_0 = _len_0 + 1
1029 else 1062 else
1030 _accum_0[_len_0] = i 1063 _accum_0[_len_0] = i
1064 _len_0 = _len_0 + 1
1031 end 1065 end
1032 _len_0 = _len_0 + 1
1033end 1066end
1034doubled_evens = _accum_0 1067doubled_evens = _accum_0
1068local first_large
1069local _accum_0
1070local _list_0 = numbers
1071for _index_0 = 1, #_list_0 do
1072 local n = _list_0[_index_0]
1073 if n > 10 then
1074 _accum_0 = n
1075 break
1076 end
1077end
1078first_large = _accum_0
1035local func_a 1079local func_a
1036func_a = function() 1080func_a = function()
1037 for i = 1, 10 do 1081 for i = 1, 10 do
@@ -3189,12 +3233,24 @@ local _len_0 = 1
3189for i = 1, 20 do 3233for i = 1, 20 do
3190 if i % 2 == 0 then 3234 if i % 2 == 0 then
3191 _accum_0[_len_0] = i * 2 3235 _accum_0[_len_0] = i * 2
3236 _len_0 = _len_0 + 1
3192 else 3237 else
3193 _accum_0[_len_0] = i 3238 _accum_0[_len_0] = i
3239 _len_0 = _len_0 + 1
3194 end 3240 end
3195 _len_0 = _len_0 + 1
3196end 3241end
3197doubled_evens = _accum_0 3242doubled_evens = _accum_0
3243local first_large
3244local _accum_0
3245local _list_0 = numbers
3246for _index_0 = 1, #_list_0 do
3247 local n = _list_0[_index_0]
3248 if n > 10 then
3249 _accum_0 = n
3250 break
3251 end
3252end
3253first_large = _accum_0
3198local func_a 3254local func_a
3199func_a = function() 3255func_a = function()
3200 for i = 1, 10 do 3256 for i = 1, 10 do
diff --git a/spec/outputs/codes_from_doc_zh.lua b/spec/outputs/codes_from_doc_zh.lua
index d9cc4dc..2457c52 100644
--- a/spec/outputs/codes_from_doc_zh.lua
+++ b/spec/outputs/codes_from_doc_zh.lua
@@ -20,6 +20,38 @@ local inventory = {
20 } 20 }
21 } 21 }
22} 22}
23local map
24map = function(arr, action)
25 local _accum_0 = { }
26 local _len_0 = 1
27 for _index_0 = 1, #arr do
28 local item = arr[_index_0]
29 _accum_0[_len_0] = action(item)
30 _len_0 = _len_0 + 1
31 end
32 return _accum_0
33end
34local filter
35filter = function(arr, cond)
36 local _accum_0 = { }
37 local _len_0 = 1
38 for _index_0 = 1, #arr do
39 local item = arr[_index_0]
40 if cond(item) then
41 _accum_0[_len_0] = item
42 _len_0 = _len_0 + 1
43 end
44 end
45 return _accum_0
46end
47local reduce
48reduce = function(arr, init, action)
49 for _index_0 = 1, #arr do
50 local item = arr[_index_0]
51 init = action(init, item)
52 end
53 return init
54end
23print(reduce(filter(map({ 55print(reduce(filter(map({
24 1, 56 1,
25 2, 57 2,
@@ -1020,12 +1052,24 @@ local _len_0 = 1
1020for i = 1, 20 do 1052for i = 1, 20 do
1021 if i % 2 == 0 then 1053 if i % 2 == 0 then
1022 _accum_0[_len_0] = i * 2 1054 _accum_0[_len_0] = i * 2
1055 _len_0 = _len_0 + 1
1023 else 1056 else
1024 _accum_0[_len_0] = i 1057 _accum_0[_len_0] = i
1058 _len_0 = _len_0 + 1
1025 end 1059 end
1026 _len_0 = _len_0 + 1
1027end 1060end
1028doubled_evens = _accum_0 1061doubled_evens = _accum_0
1062local first_large
1063local _accum_0
1064local _list_0 = numbers
1065for _index_0 = 1, #_list_0 do
1066 local n = _list_0[_index_0]
1067 if n > 10 then
1068 _accum_0 = n
1069 break
1070 end
1071end
1072first_large = _accum_0
1029local func_a 1073local func_a
1030func_a = function() 1074func_a = function()
1031 for i = 1, 10 do 1075 for i = 1, 10 do
@@ -3177,12 +3221,24 @@ local _len_0 = 1
3177for i = 1, 20 do 3221for i = 1, 20 do
3178 if i % 2 == 0 then 3222 if i % 2 == 0 then
3179 _accum_0[_len_0] = i * 2 3223 _accum_0[_len_0] = i * 2
3224 _len_0 = _len_0 + 1
3180 else 3225 else
3181 _accum_0[_len_0] = i 3226 _accum_0[_len_0] = i
3227 _len_0 = _len_0 + 1
3182 end 3228 end
3183 _len_0 = _len_0 + 1
3184end 3229end
3185doubled_evens = _accum_0 3230doubled_evens = _accum_0
3231local first_large
3232local _accum_0
3233local _list_0 = numbers
3234for _index_0 = 1, #_list_0 do
3235 local n = _list_0[_index_0]
3236 if n > 10 then
3237 _accum_0 = n
3238 break
3239 end
3240end
3241first_large = _accum_0
3186local func_a 3242local func_a
3187func_a = function() 3243func_a = function()
3188 for i = 1, 10 do 3244 for i = 1, 10 do
diff --git a/spec/outputs/loops.lua b/spec/outputs/loops.lua
index 8624d49..9a47579 100644
--- a/spec/outputs/loops.lua
+++ b/spec/outputs/loops.lua
@@ -60,8 +60,8 @@ do
60 local y = hello[_index_0] 60 local y = hello[_index_0]
61 if y % 2 == 0 then 61 if y % 2 == 0 then
62 _accum_0[_len_0] = y 62 _accum_0[_len_0] = y
63 _len_0 = _len_0 + 1
63 end 64 end
64 _len_0 = _len_0 + 1
65 end 65 end
66 x = _accum_0 66 x = _accum_0
67end 67end
@@ -132,13 +132,11 @@ do
132end 132end
133do 133do
134 local _accum_0 = { } 134 local _accum_0 = { }
135 local _len_0 = 1
136 local _list_2 = 3 135 local _list_2 = 3
137 for _index_0 = 1, #_list_2 do 136 for _index_0 = 1, #_list_2 do
138 local thing = _list_2[_index_0] 137 local thing = _list_2[_index_0]
139 y = "hello" 138 y = "hello"
140 break 139 break
141 _len_0 = _len_0 + 1
142 end 140 end
143 x = _accum_0 141 x = _accum_0
144end 142end
@@ -370,3 +368,103 @@ do
370 end 368 end
371 until false 369 until false
372end 370end
371local _anon_func_0 = function(i, tb)
372 local _accum_0 = { }
373 local _len_0 = 1
374 while tb[i] do
375 i = i + 1
376 _accum_0[_len_0] = i - 1
377 _len_0 = _len_0 + 1
378 end
379 return _accum_0
380end
381do
382 local index
383 do
384 local _accum_0
385 for i = 1, #tb do
386 if tb[i] then
387 _accum_0 = i
388 break
389 end
390 end
391 index = _accum_0
392 end
393 f((function()
394 local _accum_0
395 for i = 1, #tb do
396 if tb[i] then
397 _accum_0 = i
398 break
399 end
400 end
401 return _accum_0
402 end)())
403 f((function()
404 local _accum_0 = { }
405 local _len_0 = 1
406 for i = 1, #tb do
407 if tb[i] then
408 _accum_0[_len_0] = i
409 _len_0 = _len_0 + 1
410 end
411 end
412 return _accum_0
413 end)())
414 i = 1
415 local ids
416 do
417 local _accum_0 = { }
418 local _len_0 = 1
419 while tb[i] do
420 i = i + 1
421 _accum_0[_len_0] = i - 1
422 _len_0 = _len_0 + 1
423 end
424 ids = _accum_0
425 end
426 i = 1
427 local idx
428 do
429 local _accum_0
430 while tb[i] do
431 i = i + 1
432 _accum_0 = i - 1
433 break
434 end
435 idx = _accum_0
436 end
437 local f1
438 f1 = function()
439 i = 1
440 return f(_anon_func_0(i, tb))
441 end
442 i = 1
443 f((function()
444 local _accum_0
445 while tb[i] do
446 i = i + 1
447 _accum_0 = i - 1
448 break
449 end
450 return _accum_0
451 end)())
452 local _accum_0 = { }
453 local _len_0 = 1
454 local _list_3 = items
455 for _index_0 = 1, #_list_3 do
456 local item = _list_3[_index_0]
457 local _type_0 = type(item)
458 local _tab_0 = "table" == _type_0 or "userdata" == _type_0
459 if _tab_0 then
460 local value = item.value
461 if "A" == item.type and value ~= nil then
462 if value > 5 then
463 _accum_0[_len_0] = item
464 _len_0 = _len_0 + 1
465 end
466 end
467 end
468 end
469 list = _accum_0
470end
diff --git a/spec/outputs/macro.lua b/spec/outputs/macro.lua
index 4d31574..9f5507c 100644
--- a/spec/outputs/macro.lua
+++ b/spec/outputs/macro.lua
@@ -330,10 +330,8 @@ local _1
330_1 = function() 330_1 = function()
331 print(1) 331 print(1)
332 local _accum_0 = { } 332 local _accum_0 = { }
333 local _len_0 = 1
334 while false do 333 while false do
335 break 334 break
336 _len_0 = _len_0 + 1
337 end 335 end
338 return _accum_0 336 return _accum_0
339end 337end
diff --git a/spec/outputs/unicode/loops.lua b/spec/outputs/unicode/loops.lua
index 8379993..27bbe2e 100644
--- a/spec/outputs/unicode/loops.lua
+++ b/spec/outputs/unicode/loops.lua
@@ -60,8 +60,8 @@ do
60 local _u53d8_u91cfy = _u4f60_u597d[_index_0] 60 local _u53d8_u91cfy = _u4f60_u597d[_index_0]
61 if _u53d8_u91cfy % 2 == 0 then 61 if _u53d8_u91cfy % 2 == 0 then
62 _accum_0[_len_0] = _u53d8_u91cfy 62 _accum_0[_len_0] = _u53d8_u91cfy
63 _len_0 = _len_0 + 1
63 end 64 end
64 _len_0 = _len_0 + 1
65 end 65 end
66 _u53d8_u91cfx = _accum_0 66 _u53d8_u91cfx = _accum_0
67end 67end
@@ -132,13 +132,11 @@ do
132end 132end
133do 133do
134 local _accum_0 = { } 134 local _accum_0 = { }
135 local _len_0 = 1
136 local _list_2 = 3 135 local _list_2 = 3
137 for _index_0 = 1, #_list_2 do 136 for _index_0 = 1, #_list_2 do
138 local _u4e1c_u897f = _list_2[_index_0] 137 local _u4e1c_u897f = _list_2[_index_0]
139 _u53d8_u91cfy = "你好" 138 _u53d8_u91cfy = "你好"
140 break 139 break
141 _len_0 = _len_0 + 1
142 end 140 end
143 _u53d8_u91cfx = _accum_0 141 _u53d8_u91cfx = _accum_0
144end 142end
diff --git a/spec/outputs/unicode/macro.lua b/spec/outputs/unicode/macro.lua
index b14f571..3b9327a 100644
--- a/spec/outputs/unicode/macro.lua
+++ b/spec/outputs/unicode/macro.lua
@@ -365,10 +365,8 @@ local _1
365_1 = function() 365_1 = function()
366 _u6253_u5370(1) 366 _u6253_u5370(1)
367 local _accum_0 = { } 367 local _accum_0 = { }
368 local _len_0 = 1
369 while false do 368 while false do
370 break 369 break
371 _len_0 = _len_0 + 1
372 end 370 end
373 return _accum_0 371 return _accum_0
374end 372end
diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp
index fe6e726..612bdcd 100644
--- a/src/yuescript/yue_ast.cpp
+++ b/src/yuescript/yue_ast.cpp
@@ -188,9 +188,17 @@ std::string ConstValue_t::to_string(void* ud) const {
188std::string NotIn_t::to_string(void*) const { 188std::string NotIn_t::to_string(void*) const {
189 return {}; 189 return {};
190} 190}
191std::string Break_t::to_string(void*) const {
192 return "break"s;
193}
194std::string Continue_t::to_string(void*) const {
195 return "continue"s;
196}
191std::string BreakLoop_t::to_string(void* ud) const { 197std::string BreakLoop_t::to_string(void* ud) const {
192 auto info = reinterpret_cast<YueFormat*>(ud); 198 if (value) {
193 return info->convert(this); 199 return type->to_string(ud) + ' ' + value->to_string(ud);
200 }
201 return type->to_string(ud);
194} 202}
195std::string YueLineComment_t::to_string(void* ud) const { 203std::string YueLineComment_t::to_string(void* ud) const {
196 auto info = reinterpret_cast<YueFormat*>(ud); 204 auto info = reinterpret_cast<YueFormat*>(ud);
diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h
index 5e70645..782db5a 100644
--- a/src/yuescript/yue_ast.h
+++ b/src/yuescript/yue_ast.h
@@ -273,6 +273,8 @@ AST_NODE(ExpList)
273 ast_ptr<true, Seperator_t> sep; 273 ast_ptr<true, Seperator_t> sep;
274 ast_list<true, Exp_t> exprs; 274 ast_list<true, Exp_t> exprs;
275 AST_MEMBER(ExpList, &sep, &exprs) 275 AST_MEMBER(ExpList, &sep, &exprs)
276 bool followStmtProcessed = false;
277 Statement_t* followStmt = nullptr;
276AST_END(ExpList) 278AST_END(ExpList)
277 279
278AST_NODE(Return) 280AST_NODE(Return)
@@ -856,7 +858,17 @@ AST_NODE(WhileLine)
856 AST_MEMBER(WhileLine, &type, &condition) 858 AST_MEMBER(WhileLine, &type, &condition)
857AST_END(WhileLine) 859AST_END(WhileLine)
858 860
859AST_LEAF(BreakLoop) 861AST_LEAF(Break)
862AST_END(Break)
863
864AST_LEAF(Continue)
865AST_END(Continue)
866
867AST_NODE(BreakLoop)
868 ast_sel<true, Break_t, Continue_t> type;
869 ast_ptr<false, Exp_t> value;
870 AST_MEMBER(BreakLoop, &type, &value)
871 std::string varBWV;
860AST_END(BreakLoop) 872AST_END(BreakLoop)
861 873
862AST_NODE(PipeBody) 874AST_NODE(PipeBody)
diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp
index bc4574b..1a9387b 100644
--- a/src/yuescript/yue_compiler.cpp
+++ b/src/yuescript/yue_compiler.cpp
@@ -78,7 +78,7 @@ static std::unordered_set<std::string> Metamethods = {
78 "close"s // Lua 5.4 78 "close"s // Lua 5.4
79}; 79};
80 80
81const std::string_view version = "0.27.6"sv; 81const std::string_view version = "0.28.0"sv;
82const std::string_view extension = "yue"sv; 82const std::string_view extension = "yue"sv;
83 83
84class CompileError : public std::logic_error { 84class CompileError : public std::logic_error {
@@ -2464,6 +2464,10 @@ private:
2464 auto info = extractDestructureInfo(assignment, false, optionalDestruct); 2464 auto info = extractDestructureInfo(assignment, false, optionalDestruct);
2465 if (info.destructures.empty()) { 2465 if (info.destructures.empty()) {
2466 transformAssignmentCommon(assignment, out); 2466 transformAssignmentCommon(assignment, out);
2467 if (assignment->expList->followStmt) {
2468 transformStatement(assignment->expList->followStmt, out);
2469 assignment->expList->followStmtProcessed = true;
2470 }
2467 return true; 2471 return true;
2468 } else { 2472 } else {
2469 auto x = assignment; 2473 auto x = assignment;
@@ -2729,8 +2733,12 @@ private:
2729 temp.push_back(indent() + "end"s + nlr(x)); 2733 temp.push_back(indent() + "end"s + nlr(x));
2730 } 2734 }
2731 out.push_back(join(temp)); 2735 out.push_back(join(temp));
2736 if (assignment->expList->followStmt) {
2737 transformStatement(assignment->expList->followStmt, out);
2738 assignment->expList->followStmtProcessed = true;
2739 }
2740 return false;
2732 } 2741 }
2733 return false;
2734 } 2742 }
2735 2743
2736 void transformAssignItem(ast_node* value, str_list& out) { 2744 void transformAssignItem(ast_node* value, str_list& out) {
@@ -4329,7 +4337,9 @@ private:
4329 return false; 4337 return false;
4330 }; 4338 };
4331 switch (usage) { 4339 switch (usage) {
4332 case ExpUsage::Common: YUEE("AST node mismatch", x); return; 4340 case ExpUsage::Common:
4341 YUEE("AST node mismatch", x);
4342 return;
4333 case ExpUsage::Return: 4343 case ExpUsage::Return:
4334 case ExpUsage::Closure: { 4344 case ExpUsage::Closure: {
4335 prepareValue(); 4345 prepareValue();
@@ -7195,7 +7205,7 @@ private:
7195 try { 7205 try {
7196 unsigned long long value = std::stoull(binaryPart, nullptr, 2); 7206 unsigned long long value = std::stoull(binaryPart, nullptr, 2);
7197 numStr = std::to_string(value); 7207 numStr = std::to_string(value);
7198 } catch (const std::exception& e) { 7208 } catch (const std::exception&) {
7199 throw CompileError("invalid binary literal"sv, num); 7209 throw CompileError("invalid binary literal"sv, num);
7200 } 7210 }
7201 } else if (getLuaTarget(num) < 502) { 7211 } else if (getLuaTarget(num) < 502) {
@@ -8162,11 +8172,44 @@ private:
8162 } 8172 }
8163 } 8173 }
8164 8174
8165 bool hasContinueStatement(ast_node* body) { 8175 enum class BreakLoopType {
8166 return traversal::Stop == body->traverse([&](ast_node* node) { 8176 None = 0,
8177 Break = 1,
8178 BreakWithValue = 1 << 1,
8179 Continue = 1 << 2
8180 };
8181
8182 bool hasBreak(uint32_t breakLoopType) const {
8183 return (breakLoopType & int(BreakLoopType::Break)) != 0;
8184 }
8185
8186 bool hasBreakWithValue(uint32_t breakLoopType) const {
8187 return (breakLoopType & int(BreakLoopType::BreakWithValue)) != 0;
8188 }
8189
8190 bool hasContinue(uint32_t breakLoopType) const {
8191 return (breakLoopType & int(BreakLoopType::Continue)) != 0;
8192 }
8193
8194 uint32_t getBreakLoopType(ast_node* body, const std::string& varBWV) {
8195 uint32_t type = 0;
8196 body->traverse([&](ast_node* node) {
8167 if (auto stmt = ast_cast<Statement_t>(node)) { 8197 if (auto stmt = ast_cast<Statement_t>(node)) {
8168 if (stmt->content.is<BreakLoop_t>()) { 8198 if (auto breakLoop = stmt->content.as<BreakLoop_t>()) {
8169 return _parser.toString(stmt->content) == "continue"sv ? traversal::Stop : traversal::Return; 8199 if (breakLoop->type.is<Continue_t>()) {
8200 type |= int(BreakLoopType::Continue);
8201 return traversal::Return;
8202 } else {
8203 if (breakLoop->value) {
8204 if (varBWV.empty()) {
8205 throw CompileError("break with a value is not allowed here"sv, breakLoop->value);
8206 }
8207 type |= int(BreakLoopType::BreakWithValue);
8208 breakLoop->varBWV = varBWV;
8209 } else {
8210 type |= int(BreakLoopType::Break);
8211 }
8212 }
8170 } else if (auto expList = expListFrom(stmt)) { 8213 } else if (auto expList = expListFrom(stmt)) {
8171 BLOCK_START 8214 BLOCK_START
8172 auto value = singleValueFrom(expList); 8215 auto value = singleValueFrom(expList);
@@ -8177,40 +8220,30 @@ private:
8177 switch (sVal->get_id()) { 8220 switch (sVal->get_id()) {
8178 case id<With_t>(): { 8221 case id<With_t>(): {
8179 auto withNode = static_cast<With_t*>(sVal); 8222 auto withNode = static_cast<With_t*>(sVal);
8180 if (hasContinueStatement(withNode->body)) { 8223 type |= getBreakLoopType(withNode->body, varBWV);
8181 return traversal::Stop; 8224 return traversal::Return;
8182 }
8183 break;
8184 } 8225 }
8185 case id<Do_t>(): { 8226 case id<Do_t>(): {
8186 auto doNode = static_cast<Do_t*>(sVal); 8227 auto doNode = static_cast<Do_t*>(sVal);
8187 if (hasContinueStatement(doNode->body)) { 8228 type |= getBreakLoopType(doNode->body, varBWV);
8188 return traversal::Stop; 8229 return traversal::Return;
8189 }
8190 break;
8191 } 8230 }
8192 case id<If_t>(): { 8231 case id<If_t>(): {
8193 auto ifNode = static_cast<If_t*>(sVal); 8232 auto ifNode = static_cast<If_t*>(sVal);
8194 for (auto n : ifNode->nodes.objects()) { 8233 for (auto n : ifNode->nodes.objects()) {
8195 if (hasContinueStatement(n)) { 8234 type |= getBreakLoopType(n, varBWV);
8196 return traversal::Stop;
8197 }
8198 } 8235 }
8199 break; 8236 return traversal::Return;
8200 } 8237 }
8201 case id<Switch_t>(): { 8238 case id<Switch_t>(): {
8202 auto switchNode = static_cast<Switch_t*>(sVal); 8239 auto switchNode = static_cast<Switch_t*>(sVal);
8203 for (auto branch : switchNode->branches.objects()) { 8240 for (auto branch : switchNode->branches.objects()) {
8204 if (hasContinueStatement(static_cast<SwitchCase_t*>(branch)->body)) { 8241 type |= getBreakLoopType(static_cast<SwitchCase_t*>(branch)->body, varBWV);
8205 return traversal::Stop;
8206 }
8207 } 8242 }
8208 if (switchNode->lastBranch) { 8243 if (switchNode->lastBranch) {
8209 if (hasContinueStatement(switchNode->lastBranch)) { 8244 type |= getBreakLoopType(switchNode->lastBranch, varBWV);
8210 return traversal::Stop;
8211 }
8212 } 8245 }
8213 break; 8246 return traversal::Return;
8214 } 8247 }
8215 } 8248 }
8216 BLOCK_END 8249 BLOCK_END
@@ -8224,6 +8257,7 @@ private:
8224 } 8257 }
8225 return traversal::Return; 8258 return traversal::Return;
8226 }); 8259 });
8260 return type;
8227 } 8261 }
8228 8262
8229 void addDoToLastLineReturn(ast_node* body) { 8263 void addDoToLastLineReturn(ast_node* body) {
@@ -8247,10 +8281,10 @@ private:
8247 } 8281 }
8248 } 8282 }
8249 8283
8250 void transformLoopBody(ast_node* body, str_list& out, const std::string& appendContent, ExpUsage usage, ExpList_t* assignList = nullptr) { 8284 void transformLoopBody(ast_node* body, str_list& out, uint32_t breakLoopType, ExpUsage usage, ExpList_t* assignList = nullptr) {
8251 str_list temp; 8285 str_list temp;
8252 bool extraDo = false; 8286 bool extraDo = false;
8253 bool withContinue = hasContinueStatement(body); 8287 bool withContinue = hasContinue(breakLoopType);
8254 int target = getLuaTarget(body); 8288 int target = getLuaTarget(body);
8255 std::string extraLabel; 8289 std::string extraLabel;
8256 if (withContinue) { 8290 if (withContinue) {
@@ -8259,7 +8293,7 @@ private:
8259 if (!block->statements.empty()) { 8293 if (!block->statements.empty()) {
8260 auto stmt = static_cast<Statement_t*>(block->statements.back()); 8294 auto stmt = static_cast<Statement_t*>(block->statements.back());
8261 if (auto breakLoop = ast_cast<BreakLoop_t>(stmt->content)) { 8295 if (auto breakLoop = ast_cast<BreakLoop_t>(stmt->content)) {
8262 extraDo = _parser.toString(breakLoop) == "break"sv; 8296 extraDo = breakLoop->type.is<Break_t>();
8263 } 8297 }
8264 } 8298 }
8265 } 8299 }
@@ -8292,9 +8326,6 @@ private:
8292 popScope(); 8326 popScope();
8293 _buf << indent() << "end"sv << nll(body); 8327 _buf << indent() << "end"sv << nll(body);
8294 } 8328 }
8295 if (!appendContent.empty()) {
8296 _buf << indent() << appendContent;
8297 }
8298 _buf << indent() << _continueVars.top().var << " = true"sv << nll(body); 8329 _buf << indent() << _continueVars.top().var << " = true"sv << nll(body);
8299 popScope(); 8330 popScope();
8300 _buf << indent() << "until true"sv << nlr(body); 8331 _buf << indent() << "until true"sv << nlr(body);
@@ -8304,14 +8335,9 @@ private:
8304 temp.push_back(clearBuf()); 8335 temp.push_back(clearBuf());
8305 _continueVars.pop(); 8336 _continueVars.pop();
8306 } else { 8337 } else {
8307 if (!appendContent.empty()) {
8308 temp.push_back(indent() + appendContent);
8309 }
8310 temp.push_back(extraLabel); 8338 temp.push_back(extraLabel);
8311 _continueVars.pop(); 8339 _continueVars.pop();
8312 } 8340 }
8313 } else if (!appendContent.empty()) {
8314 temp.back().append(indent() + appendContent);
8315 } 8341 }
8316 out.push_back(join(temp)); 8342 out.push_back(join(temp));
8317 } 8343 }
@@ -8320,7 +8346,8 @@ private:
8320 str_list temp; 8346 str_list temp;
8321 bool extraDo = false; 8347 bool extraDo = false;
8322 auto body = repeatNode->body->content.get(); 8348 auto body = repeatNode->body->content.get();
8323 bool withContinue = hasContinueStatement(body); 8349 auto breakLoopType = getBreakLoopType(body, Empty);
8350 bool withContinue = hasContinue(breakLoopType);
8324 std::string conditionVar; 8351 std::string conditionVar;
8325 std::string extraLabel; 8352 std::string extraLabel;
8326 ast_ptr<false, ExpListAssign_t> condAssign; 8353 ast_ptr<false, ExpListAssign_t> condAssign;
@@ -8331,7 +8358,7 @@ private:
8331 if (!block->statements.empty()) { 8358 if (!block->statements.empty()) {
8332 auto stmt = static_cast<Statement_t*>(block->statements.back()); 8359 auto stmt = static_cast<Statement_t*>(block->statements.back());
8333 if (auto breakLoop = ast_cast<BreakLoop_t>(stmt->content)) { 8360 if (auto breakLoop = ast_cast<BreakLoop_t>(stmt->content)) {
8334 extraDo = _parser.toString(breakLoop) == "break"sv; 8361 extraDo = breakLoop->type.is<Break_t>();
8335 } 8362 }
8336 } 8363 }
8337 } 8364 }
@@ -8394,7 +8421,8 @@ private:
8394 void transformFor(For_t* forNode, str_list& out) { 8421 void transformFor(For_t* forNode, str_list& out) {
8395 str_list temp; 8422 str_list temp;
8396 transformForHead(forNode, temp); 8423 transformForHead(forNode, temp);
8397 transformLoopBody(forNode->body, temp, Empty, ExpUsage::Common); 8424 auto breakLoopType = getBreakLoopType(forNode->body, Empty);
8425 transformLoopBody(forNode->body, temp, breakLoopType, ExpUsage::Common);
8398 popScope(); 8426 popScope();
8399 out.push_back(join(temp) + indent() + "end"s + nlr(forNode)); 8427 out.push_back(join(temp) + indent() + "end"s + nlr(forNode));
8400 } 8428 }
@@ -8405,13 +8433,19 @@ private:
8405 addToScope(accum); 8433 addToScope(accum);
8406 std::string len = getUnusedName("_len_"sv); 8434 std::string len = getUnusedName("_len_"sv);
8407 addToScope(len); 8435 addToScope(len);
8408 _buf << indent() << "local "sv << accum << " = { }"sv << nll(forNode); 8436 auto breakLoopType = getBreakLoopType(forNode->body, accum);
8437 _buf << indent() << "local "sv << accum << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(forNode);
8438 out.emplace_back(clearBuf());
8409 _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode); 8439 _buf << indent() << "local "sv << len << " = 1"sv << nll(forNode);
8410 out.push_back(clearBuf()); 8440 auto& lenAssign = out.emplace_back(clearBuf());
8411 transformForHead(forNode, out); 8441 transformForHead(forNode, out);
8412 auto expList = toAst<ExpList_t>(accum + '[' + len + ']', x); 8442 auto expList = toAst<ExpList_t>(accum + '[' + len + ']', x);
8413 auto lenLine = len + " = "s + len + " + 1"s + nlr(forNode->body); 8443 auto followStmt = toAst<Statement_t>(len + "+=1"s, forNode->body);
8414 transformLoopBody(forNode->body, out, lenLine, ExpUsage::Assignment, expList); 8444 expList->followStmt = followStmt.get();
8445 transformLoopBody(forNode->body, out, breakLoopType, ExpUsage::Assignment, expList);
8446 if (!expList->followStmtProcessed) {
8447 lenAssign.clear();
8448 }
8415 popScope(); 8449 popScope();
8416 out.push_back(indent() + "end"s + nlr(forNode)); 8450 out.push_back(indent() + "end"s + nlr(forNode));
8417 return accum; 8451 return accum;
@@ -8490,7 +8524,8 @@ private:
8490 void transformForEach(ForEach_t* forEach, str_list& out) { 8524 void transformForEach(ForEach_t* forEach, str_list& out) {
8491 str_list temp; 8525 str_list temp;
8492 bool extraScoped = transformForEachHead(forEach->nameList, forEach->loopValue, temp, false); 8526 bool extraScoped = transformForEachHead(forEach->nameList, forEach->loopValue, temp, false);
8493 transformLoopBody(forEach->body, temp, Empty, ExpUsage::Common); 8527 auto breakLoopType = getBreakLoopType(forEach->body, Empty);
8528 transformLoopBody(forEach->body, temp, breakLoopType, ExpUsage::Common);
8494 popScope(); 8529 popScope();
8495 out.push_back(temp.front() + temp.back() + indent() + "end"s + nlr(forEach)); 8530 out.push_back(temp.front() + temp.back() + indent() + "end"s + nlr(forEach));
8496 if (extraScoped) { 8531 if (extraScoped) {
@@ -8505,13 +8540,19 @@ private:
8505 addToScope(accum); 8540 addToScope(accum);
8506 std::string len = getUnusedName("_len_"sv); 8541 std::string len = getUnusedName("_len_"sv);
8507 addToScope(len); 8542 addToScope(len);
8508 _buf << indent() << "local "sv << accum << " = { }"sv << nll(forEach); 8543 auto breakLoopType = getBreakLoopType(forEach->body, accum);
8544 _buf << indent() << "local "sv << accum << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(forEach);
8545 out.emplace_back(clearBuf());
8509 _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach); 8546 _buf << indent() << "local "sv << len << " = 1"sv << nll(forEach);
8510 out.push_back(clearBuf()); 8547 auto& lenAssign = out.emplace_back(clearBuf());
8511 transformForEachHead(forEach->nameList, forEach->loopValue, out, true); 8548 transformForEachHead(forEach->nameList, forEach->loopValue, out, true);
8512 auto expList = toAst<ExpList_t>(accum + '[' + len + ']', x); 8549 auto expList = toAst<ExpList_t>(accum + '[' + len + ']', x);
8513 auto lenLine = len + " = "s + len + " + 1"s + nlr(forEach->body); 8550 auto followStmt = toAst<Statement_t>(len + "+=1"s, forEach->body);
8514 transformLoopBody(forEach->body, out, lenLine, ExpUsage::Assignment, expList); 8551 expList->followStmt = followStmt.get();
8552 transformLoopBody(forEach->body, out, breakLoopType, ExpUsage::Assignment, expList);
8553 if (!expList->followStmtProcessed) {
8554 lenAssign.clear();
8555 }
8515 popScope(); 8556 popScope();
8516 out.push_back(indent() + "end"s + nlr(forEach)); 8557 out.push_back(indent() + "end"s + nlr(forEach));
8517 return accum; 8558 return accum;
@@ -10440,15 +10481,22 @@ private:
10440 addToScope(accumVar); 10481 addToScope(accumVar);
10441 auto lenVar = getUnusedName("_len_"sv); 10482 auto lenVar = getUnusedName("_len_"sv);
10442 addToScope(lenVar); 10483 addToScope(lenVar);
10443 temp.push_back(indent() + "local "s + accumVar + " = { }"s + nll(whileNode)); 10484 auto breakLoopType = getBreakLoopType(whileNode->body, accumVar);
10444 temp.push_back(indent() + "local "s + lenVar + " = 1"s + nll(whileNode)); 10485 _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(whileNode);
10486 temp.emplace_back(clearBuf());
10487 _buf << indent() << "local "s << lenVar << " = 1"s << nll(whileNode);
10488 auto& lenAssign = temp.emplace_back(clearBuf());
10445 bool isUntil = _parser.toString(whileNode->type) == "until"sv; 10489 bool isUntil = _parser.toString(whileNode->type) == "until"sv;
10446 auto condStr = transformCondExp(whileNode->condition, isUntil); 10490 auto condStr = transformCondExp(whileNode->condition, isUntil);
10447 temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode)); 10491 temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode));
10448 pushScope(); 10492 pushScope();
10449 auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x); 10493 auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x);
10450 auto lenLine = lenVar + " = "s + lenVar + " + 1"s + nlr(whileNode); 10494 auto followStmt = toAst<Statement_t>(lenVar + "+=1"s, whileNode);
10451 transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); 10495 assignLeft->followStmt = followStmt.get();
10496 transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Assignment, assignLeft);
10497 if (!assignLeft->followStmtProcessed) {
10498 lenAssign.clear();
10499 }
10452 popScope(); 10500 popScope();
10453 temp.push_back(indent() + "end"s + nlr(whileNode)); 10501 temp.push_back(indent() + "end"s + nlr(whileNode));
10454 if (expList) { 10502 if (expList) {
@@ -10484,15 +10532,21 @@ private:
10484 addToScope(accumVar); 10532 addToScope(accumVar);
10485 auto lenVar = getUnusedName("_len_"sv); 10533 auto lenVar = getUnusedName("_len_"sv);
10486 addToScope(lenVar); 10534 addToScope(lenVar);
10487 temp.push_back(indent() + "local "s + accumVar + " = { }"s + nll(whileNode)); 10535 auto breakLoopType = getBreakLoopType(whileNode->body, accumVar);
10488 temp.push_back(indent() + "local "s + lenVar + " = 1"s + nll(whileNode)); 10536 _buf << indent() << "local "sv << accumVar << (hasBreakWithValue(breakLoopType) ? ""sv : " = { }"sv) << nll(whileNode);
10537 temp.emplace_back(clearBuf());
10538 auto& lenAssign = temp.emplace_back(indent() + "local "s + lenVar + " = 1"s + nll(whileNode));
10489 bool isUntil = _parser.toString(whileNode->type) == "until"sv; 10539 bool isUntil = _parser.toString(whileNode->type) == "until"sv;
10490 auto condStr = transformCondExp(whileNode->condition, isUntil); 10540 auto condStr = transformCondExp(whileNode->condition, isUntil);
10491 temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode)); 10541 temp.push_back(indent() + "while "s + condStr + " do"s + nll(whileNode));
10492 pushScope(); 10542 pushScope();
10493 auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x); 10543 auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x);
10494 auto lenLine = lenVar + " = "s + lenVar + " + 1"s + nlr(whileNode); 10544 auto followStmt = toAst<Statement_t>(lenVar + "+=1"s, whileNode);
10495 transformLoopBody(whileNode->body, temp, lenLine, ExpUsage::Assignment, assignLeft); 10545 assignLeft->followStmt = followStmt.get();
10546 transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Assignment, assignLeft);
10547 if (!assignLeft->followStmtProcessed) {
10548 lenAssign.clear();
10549 }
10496 popScope(); 10550 popScope();
10497 temp.push_back(indent() + "end"s + nlr(whileNode)); 10551 temp.push_back(indent() + "end"s + nlr(whileNode));
10498 temp.push_back(indent() + "return "s + accumVar + nlr(whileNode)); 10552 temp.push_back(indent() + "return "s + accumVar + nlr(whileNode));
@@ -10537,7 +10591,8 @@ private:
10537 pushScope(); 10591 pushScope();
10538 bool isUntil = _parser.toString(whileNode->type) == "until"sv; 10592 bool isUntil = _parser.toString(whileNode->type) == "until"sv;
10539 auto condStr = transformCondExp(whileNode->condition, isUntil); 10593 auto condStr = transformCondExp(whileNode->condition, isUntil);
10540 transformLoopBody(whileNode->body, temp, Empty, ExpUsage::Common); 10594 auto breakLoopType = getBreakLoopType(whileNode->body, Empty);
10595 transformLoopBody(whileNode->body, temp, breakLoopType, ExpUsage::Common);
10541 popScope(); 10596 popScope();
10542 _buf << indent() << "while "sv << condStr << " do"sv << nll(whileNode); 10597 _buf << indent() << "while "sv << condStr << " do"sv << nll(whileNode);
10543 _buf << temp.back(); 10598 _buf << temp.back();
@@ -11077,11 +11132,17 @@ private:
11077 } 11132 }
11078 11133
11079 void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) { 11134 void transformBreakLoop(BreakLoop_t* breakLoop, str_list& out) {
11080 auto keyword = _parser.toString(breakLoop); 11135 auto isBreak = breakLoop->type.is<Break_t>();
11136 auto keyword = isBreak ? "break"s : "continue"s;
11081 if (_enableBreakLoop.empty() || !_enableBreakLoop.top()) { 11137 if (_enableBreakLoop.empty() || !_enableBreakLoop.top()) {
11082 throw CompileError(keyword + " is not inside a loop"s, breakLoop); 11138 throw CompileError(keyword + " is not inside a loop"s, breakLoop);
11083 } 11139 }
11084 if (keyword == "break"sv) { 11140 if (isBreak) {
11141 if (breakLoop->value) {
11142 auto exp = toAst<Exp_t>(breakLoop->varBWV, breakLoop->value);
11143 auto assignment = assignmentFrom(exp, breakLoop->value, breakLoop);
11144 transformAssignment(assignment, out);
11145 }
11085 out.push_back(indent() + keyword + nll(breakLoop)); 11146 out.push_back(indent() + keyword + nll(breakLoop));
11086 return; 11147 return;
11087 } 11148 }
diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp
index 77c5901..eaabf0d 100644
--- a/src/yuescript/yue_parser.cpp
+++ b/src/yuescript/yue_parser.cpp
@@ -350,7 +350,9 @@ YueParser::YueParser() {
350 350
351 ShortTabAppending = "[]" >> space >> Assign; 351 ShortTabAppending = "[]" >> space >> Assign;
352 352
353 BreakLoop = (expr("break") | "continue") >> not_alpha_num; 353 Break = key("break");
354 Continue = key("continue");
355 BreakLoop = (Break >> -(space >> Exp) | Continue) >> not_alpha_num;
354 356
355 Return = key("return") >> -(space >> (TableBlock | ExpListLow)); 357 Return = key("return") >> -(space >> (TableBlock | ExpListLow));
356 358
diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h
index 7281ec3..63afcb9 100644
--- a/src/yuescript/yue_parser.h
+++ b/src/yuescript/yue_parser.h
@@ -427,6 +427,8 @@ private:
427 AST_RULE(ExpListAssign); 427 AST_RULE(ExpListAssign);
428 AST_RULE(IfLine); 428 AST_RULE(IfLine);
429 AST_RULE(WhileLine); 429 AST_RULE(WhileLine);
430 AST_RULE(Break);
431 AST_RULE(Continue);
430 AST_RULE(BreakLoop); 432 AST_RULE(BreakLoop);
431 AST_RULE(StatementAppendix); 433 AST_RULE(StatementAppendix);
432 AST_RULE(Statement); 434 AST_RULE(Statement);