From 652a8839f885b73fff57942a9db8b26e9cb5233b Mon Sep 17 00:00:00 2001
From: Li Jin <dragon-fly@qq.com>
Date: Sun, 22 Oct 2023 00:55:02 +0800
Subject: fixing issues from #152.

---
 doc/docs/doc/README.md                 | 180 ++++++------
 doc/docs/zh/doc/README.md              | 182 ++++++------
 spec/inputs/cond.yue                   |  11 +
 spec/inputs/in_expression.yue          |  25 +-
 spec/inputs/lists.yue                  |  24 +-
 spec/inputs/tables.yue                 |  20 ++
 spec/inputs/unicode/cond.yue           |  11 +
 spec/inputs/unicode/in_expression.yue  |  21 +-
 spec/outputs/cond.lua                  |  17 ++
 spec/outputs/in_expression.lua         |  31 +-
 spec/outputs/lists.lua                 |  65 ++++-
 spec/outputs/tables.lua                |  83 ++++++
 spec/outputs/unicode/cond.lua          |  17 ++
 spec/outputs/unicode/in_expression.lua |  31 +-
 src/yuescript/yue_ast.cpp              |  27 +-
 src/yuescript/yue_ast.h                |  34 +--
 src/yuescript/yue_compiler.cpp         | 497 ++++++++++++++++++++++++---------
 src/yuescript/yue_parser.cpp           |  61 +++-
 src/yuescript/yue_parser.h             |  10 +-
 19 files changed, 913 insertions(+), 434 deletions(-)

diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md
index c60330a..6662752 100755
--- a/doc/docs/doc/README.md
+++ b/doc/docs/doc/README.md
@@ -389,6 +389,80 @@ tb::func! if tb != nil
 </pre>
 </YueDisplay>
 
+### Chaining Comparisons
+
+Comparisons can be arbitrarily chained:
+
+```moonscript
+print 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
+-- output: true
+
+a = 5
+print 1 <= a <= 10
+-- output: true
+```
+<YueDisplay>
+<pre>
+print 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
+-- output: true
+
+a = 5
+print 1 <= a <= 10
+-- output: true
+</pre>
+</YueDisplay>
+
+Note the evaluation behavior of chained comparisons:
+
+```moonscript
+v = (x)->
+   print x
+   x
+
+print v(1) < v(2) <= v(3)
+--[[
+   output:
+   2
+   1
+   3
+   true
+]]
+
+print v(1) > v(2) <= v(3)
+--[[
+   output:
+   2
+   1
+   false
+]]
+```
+<YueDisplay>
+<pre>
+v = (x)->
+   print x
+   x
+
+print v(1) < v(2) <= v(3)
+--[[
+   output:
+   2
+   1
+   3
+true
+]]
+
+print v(1) > v(2) <= v(3)
+--[[
+   output:
+   2
+   1
+   false
+]]
+</pre>
+</YueDisplay>
+
+The middle expression is only evaluated once, rather than twice as it would be if the expression were written as `v(1) < v(2) and v(2) <= v(3)`. However, the order of evaluations in a chained comparison is undefined. It is strongly recommended not to use expressions with side effects (such as printing) in chained comparisons. If side effects are required, the short-circuit `and` operator should be used explicitly.
+
 ### Table Appending
 The **[] =** operator is used to append values to tables.
 
@@ -984,17 +1058,17 @@ Typically when you see a table literal, {1,2,3}, it is on the right hand side of
 This is best explained with examples. Here is how you would unpack the first two values from a table:
 
 ```moonscript
-thing = {1, 2}
+thing = [1, 2]
 
-{a, b} = thing
+[a, b] = thing
 print a, b
 ```
 <YueDisplay>
 
 <pre>
-thing = {1, 2}
+thing = [1, 2]
 
-{a, b} = thing
+[a, b] = thing
 print a, b
 </pre>
 </YueDisplay>
@@ -1921,6 +1995,19 @@ t = {
 </pre>
 </YueDisplay>
 
+Lua tables have both an array part and a hash part, but sometimes you want to make a semantic distinction between array and hash usage when writing Lua tables. Then you can write Lua table with **[ ]** instead of **{ }** to represent an array table and writing any key value pair in a list table won't be allowed.
+
+```moonscript
+some_values = [ 1, 2, 3, 4 ]
+list_with_one_element = [ 1, ]
+```
+<YueDisplay>
+<pre>
+some_values = [ 1, 2, 3, 4 ]
+list_with_one_element = [ 1, ]
+</pre>
+</YueDisplay>
+
 ## Comprehensions
 
 Comprehensions provide a convenient syntax for constructing a new table by iterating over some existing object and applying an expression to its values. There are two kinds of comprehensions: list comprehensions and table comprehensions. They both produce Lua tables; list comprehensions accumulate values into an array-like table, and table comprehensions let you set both the key and the value on each iteration.
@@ -1930,12 +2017,12 @@ Comprehensions provide a convenient syntax for constructing a new table by itera
 The following creates a copy of the items table but with all the values doubled.
 
 ```moonscript
-items = { 1, 2, 3, 4 }
+items = [ 1, 2, 3, 4 ]
 doubled = [item * 2 for i, item in ipairs items]
 ```
 <YueDisplay>
 <pre>
-items = { 1, 2, 3, 4 }
+items = [ 1, 2, 3, 4 ]
 doubled = [item * 2 for i, item in ipairs items]
 </pre>
 </YueDisplay>
@@ -1969,18 +2056,18 @@ The for and when clauses can be chained as much as desired. The only requirement
 Using multiple for clauses is the same as using nested loops:
 
 ```moonscript
-x_coords = {4, 5, 6, 7}
-y_coords = {9, 2, 3}
+x_coords = [4, 5, 6, 7]
+y_coords = [9, 2, 3]
 
-points = [{x, y} for x in *x_coords \
+points = [ [x, y] for x in *x_coords \
 for y in *y_coords]
 ```
 <YueDisplay>
 <pre>
-x_coords = {4, 5, 6, 7}
-y_coords = {9, 2, 3}
+x_coords = [4, 5, 6, 7]
+y_coords = [9, 2, 3]
 
-points = [{x, y} for x in *x_coords \
+points = [ [x, y] for x in *x_coords \
 for y in *y_coords]
 </pre>
 </YueDisplay>
@@ -2035,12 +2122,12 @@ no_color = {k, v for k, v in pairs thing when k != "color"}
 The **\*** operator is also supported. Here we create a square root look up table for a few numbers.
 
 ```moonscript
-numbers = {1, 2, 3, 4}
+numbers = [1, 2, 3, 4]
 sqrts = {i, math.sqrt i for i in *numbers}
 ```
 <YueDisplay>
 <pre>
-numbers = {1, 2, 3, 4}
+numbers = [1, 2, 3, 4]
 sqrts = {i, math.sqrt i for i in *numbers}
 </pre>
 </YueDisplay>
@@ -2405,15 +2492,6 @@ You can write range checking code with an `in-expression`.
 ```moonscript
 a = 5
 
-if a in [1, 10]
-  print "a is in range from 1 to 10"
-
-if a not in [1, 10]
-  print "a is not in range from 1 to 10"
-
-if a in (0, 11)
-  print "a is between 0 and 11 with open intervals"
-
 if a in {1, 3, 5, 7}
   print "checking equality with discrete values"
 
@@ -2424,15 +2502,6 @@ if a in list
 <pre>
 a = 5
 
-if a in [1, 10]
-  print "a is in range from 1 to 10"
-
-if a not in [1, 10]
-  print "a is not in range from 1 to 10"
-
-if a in (0, 11)
-  print "a is between 0 and 11 with open intervals"
-
 if a in {1, 3, 5, 7}
   print "checking equality with discrete values"
 
@@ -2649,53 +2718,6 @@ switch item
 </pre>
 </YueDisplay>
 
-### Range Matching
-
-You can do range matching in a switch when clause using `In` expressions.
-
-```moonscript
-value = 5
-
-switch item
-  -- range checking with closed interval
-  when in [1, 3]
-    print "1 <= value <= 3"
-
-  -- range checking with open and closed interval
-  when in (6, 8]
-    print "6 < value <= 8"
-
-  -- not in range checking
-  when not in [1, 10)
-    print "not (1 <= value < 10)"
-
-  -- checking discrete values
-  when in {11, 21, 99}
-    print "value is 11, 21 or 99"
-```
-<YueDisplay>
-<pre>
-value = 5
-
-switch item
-  -- range checking with closed interval
-  when in [1, 3]
-    print "1 <= value <= 3"
-
-  -- range checking with open and closed interval
-  when in (6, 8]
-    print "6 < value <= 8"
-
-  -- not in range checking
-  when not in [1, 10)
-    print "not (1 <= value < 10)"
-
-  -- checking discrete values
-  when in {11, 21, 99}
-    print "value is 11, 21 or 99"
-</pre>
-</YueDisplay>
-
 ## Object Oriented Programming
 
 In these examples, the generated Lua code may appear overwhelming. It is best to focus on the meaning of the Yuescript code at first, then look into the Lua code if you wish to know the implementation details.
diff --git a/doc/docs/zh/doc/README.md b/doc/docs/zh/doc/README.md
index 1d234a0..8a236c1 100755
--- a/doc/docs/zh/doc/README.md
+++ b/doc/docs/zh/doc/README.md
@@ -386,6 +386,80 @@ tb::func! if tb != nil
 </pre>
 </YueDisplay>
 
+### 链式比较
+
+您可以在月之脚本中进行比较表达式的链式书写:
+
+```moonscript
+print 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
+-- 输出:true
+
+a = 5
+print 1 <= a <= 10
+-- 输出:true
+```
+<YueDisplay>
+<pre>
+print 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
+-- 输出:true
+
+a = 5
+print 1 <= a <= 10
+-- 输出:true
+</pre>
+</YueDisplay>
+
+可以注意一下链式比较表达式的求值行为:
+
+```moonscript
+v = (x)->
+   print x
+   x
+
+print v(1) < v(2) <= v(3)
+--[[
+   输出:
+   2
+   1
+   3
+   true
+]]
+
+print v(1) > v(2) <= v(3)
+--[[
+   输出:
+   2
+   1
+   false
+]]
+```
+<YueDisplay>
+<pre>
+v = (x)->
+   print x
+   x
+
+print v(1) < v(2) <= v(3)
+--[[
+   输出:
+   2
+   1
+   3
+   true
+]]
+
+print v(1) > v(2) <= v(3)
+--[[
+   输出:
+   2
+   1
+   false
+]]
+</pre>
+</YueDisplay>
+
+在上面的例子里,中间的表达式`v(2)`仅被计算一次,如果把表达式写成`v(1) < v(2) and v(2) <= v(3)`的方式,中间的`v(2)`才会被计算两次。在链式比较中,求值的顺序往往是未定义的。所以强烈建议不要在链式比较中使用具有副作用(比如做打印操作)的表达式。如果需要使用有副作用的函数,应明确使用短路 `and` 运算符来做连接。
+
 ### 表追加
 
 **[] =** 操作符用于向Lua表的最后插入值。
@@ -982,17 +1056,17 @@ do
 最好是通过示例来解释。以下是如何从表格中解包前两个值的方法:
 
 ```moonscript
-thing = {1, 2}
+thing = [1, 2]
 
-{a, b} = thing
+[a, b] = thing
 print a, b
 ```
 <YueDisplay>
 
 <pre>
-thing = {1, 2}
+thing = [1, 2]
 
-{a, b} = thing
+[a, b] = thing
 print a, b
 </pre>
 </YueDisplay>
@@ -1881,6 +1955,19 @@ t = {
 </pre>
 </YueDisplay>
 
+Lua的表同时具有数组部分和哈希部分,但有时候你会希望在书写Lua表时,对Lua表做数组和哈希不同用法的语义区分。然后你可以用 **[ ]** 而不是 **{ }** 来编写表示数组的 Lua 表,并且不允许在数组 Lua 表中写入任何键值对。
+
+```moonscript
+some_values = [ 1, 2, 3, 4 ]
+list_with_one_element = [ 1, ]
+```
+<YueDisplay>
+<pre>
+some_values = [ 1, 2, 3, 4 ]
+list_with_one_element = [ 1, ]
+</pre>
+</YueDisplay>
+
 ## 推导式
 
 推导式为我们提供了一种便捷的语法,通过遍历现有对象并对其值应用表达式来构造出新的表格。月之脚本有两种推导式:列表推导式和表格推导式。它们最终都是产生Lua表格;列表推导式将值累积到类似数组的表格中,而表格推导式允许您在每次遍历时设置新表格的键和值。
@@ -1890,12 +1977,12 @@ t = {
 以下操作创建了一个items表的副本,但所有包含的值都翻倍了。
 
 ```moonscript
-items = { 1, 2, 3, 4 }
+items = [ 1, 2, 3, 4 ]
 doubled = [item * 2 for i, item in ipairs items]
 ```
 <YueDisplay>
 <pre>
-items = { 1, 2, 3, 4 }
+items = [ 1, 2, 3, 4 ]
 doubled = [item * 2 for i, item in ipairs items]
 </pre>
 </YueDisplay>
@@ -1929,18 +2016,18 @@ for和when子句可以根据需要进行链式操作。唯一的要求是推导
 使用多个for子句与使用多重循环的效果相同:
 
 ```moonscript
-x_coords = {4, 5, 6, 7}
-y_coords = {9, 2, 3}
+x_coords = [4, 5, 6, 7]
+y_coords = [9, 2, 3]
 
-points = [{x, y} for x in *x_coords \
+points = [ [x, y] for x in *x_coords \
 for y in *y_coords]
 ```
 <YueDisplay>
 <pre>
-x_coords = {4, 5, 6, 7}
-y_coords = {9, 2, 3}
+x_coords = [4, 5, 6, 7]
+y_coords = [9, 2, 3]
 
-points = [{x, y} for x in *x_coords \
+points = [ [x, y] for x in *x_coords \
 for y in *y_coords]
 </pre>
 </YueDisplay>
@@ -1995,12 +2082,12 @@ no_color = {k, v for k, v in pairs thing when k != "color"}
 **\***操作符在表格推导式中能使用。在下面的例子里,我们为几个数字创建了一个平方根查找表。
 
 ```moonscript
-numbers = {1, 2, 3, 4}
+numbers = [1, 2, 3, 4]
 sqrts = {i, math.sqrt i for i in *numbers}
 ```
 <YueDisplay>
 <pre>
-numbers = {1, 2, 3, 4}
+numbers = [1, 2, 3, 4]
 sqrts = {i, math.sqrt i for i in *numbers}
 </pre>
 </YueDisplay>
@@ -2361,20 +2448,11 @@ print "你真幸运!" unless math.random! > 0.1
 
 ### 范围表达式
 
-您可以使用范围表达式来编写进行边界范围检查的代码。
+您可以使用范围表达式来编写进行范围检查的代码。
 
 ```moonscript
 a = 5
 
-if a in [1, 10]
-  print "a在1到10的范围内"
-
-if a not in [1, 10]
-  print "a不在1到10的范围内"
-
-if a in (0, 11)
-  print "a在0到11的开放区间内"
-
 if a in {1, 3, 5, 7}
   print "检查离散值的相等性"
 
@@ -2385,15 +2463,6 @@ if a in list
 <pre>
 a = 5
 
-if a in [1, 10]
-  print "a在1到10的范围内"
-
-if a not in [1, 10]
-  print "a不在1到10的范围内"
-
-if a in (0, 11)
-  print "a在0到11的开放区间内"
-
 if a in {1, 3, 5, 7}
   print "检查离散值的相等性"
 
@@ -2610,53 +2679,6 @@ switch item
 </pre>
 </YueDisplay>
 
-### 范围匹配
-
-使用`in`范围匹配表达式,你可以在switch的when子句中进行范围匹配的检查处理。
-
-```moonscript
-value = 5
-
-switch item
-  -- 使用闭区间进行范围检查
-  when in [1, 3]
-    print "1 <= value <= 3"
-
-  -- 使用开闭区间进行范围检查
-  when in (6, 8]
-    print "6 < value <= 8"
-
-  -- 检查不在范围内
-  when not in [1, 10)
-    print "不是 (1 <= value < 10)"
-
-  -- 检查离散值
-  when in {11, 21, 99}
-    print "值是 11, 21 或 99"
-```
-<YueDisplay>
-<pre>
-value = 5
-
-switch item
-  -- 使用闭区间进行范围检查
-  when in [1, 3]
-    print "1 <= value <= 3"
-
-  -- 使用开闭区间进行范围检查
-  when in (6, 8]
-    print "6 < value <= 8"
-
-  -- 检查不在范围内
-  when not in [1, 10)
-    print "不是 (1 <= value < 10)"
-
-  -- 检查离散值
-  when in {11, 21, 99}
-    print "值是 11, 21 或 99"
-</pre>
-</YueDisplay>
-
 ## 面向对象编程
 
 在以下的示例中,月之脚本生成的Lua代码可能看起来会很复杂。所以最好主要关注月之脚本代码层面的意义,然后如果您想知道关于面向对象功能的实现细节,再查看Lua代码。
diff --git a/spec/inputs/cond.yue b/spec/inputs/cond.yue
index 638b5c3..df7d78e 100644
--- a/spec/inputs/cond.yue
+++ b/spec/inputs/cond.yue
@@ -226,6 +226,17 @@ do
 	else
 		2
 
+do
+	condChain = 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
+
+	v = (x)->
+		print x
+		x
+
+	evaluation = v(1) < v(2) <= v(3)
+
+	evaluation = v(1) > v(2) <= v(3)
+
 nil
 
 
diff --git a/spec/inputs/in_expression.yue b/spec/inputs/in_expression.yue
index 6e923e1..6faff4e 100644
--- a/spec/inputs/in_expression.yue
+++ b/spec/inputs/in_expression.yue
@@ -1,28 +1,13 @@
 -a^2 in {1, 2, 3} |> f
-
-a, b = x(...) not in [1, 3], 2
-
-d = (tb.x.y ...) not in [1, 3]
+-a^2 in [1, 2, 3] |> f
 
 has = "foo" in { "bar", "foo" }
 
-if a in {1} and b in {2, 3, 4} or c in [1, 10]
+if a in {1} and b in {2, 3, 4}
 	print a, b, c
 
-switch val
-	when 1, 2, 3
-		print "1, 2, 3"
-
-	when not in (0, 100]
-		print "not (0 < val <= 100)"
-
-	when in [200, 300)
-		print "200 <= val < 300)"
-
-	when not in {333, 444, 555}
-		print "not 333, 444 or 555"
-
-do return y not in (a, b)
+if a in [1,] and b in [2, 3, 4]
+	print a, b, c
 
 do
 	exist = item in list
@@ -36,9 +21,11 @@ do
 do
 	item = get!
 	list = {1, 2, 3}
+	list = [1, 2, 3]
 	not_exist = item not in list
 	check item in list
 	check item in {1, 2, 3}
+	check item in [1, 2, 3]
 	check item(...) in {[1]: 1, [2]: 2, [3]: 3}
 
 do
diff --git a/spec/inputs/lists.yue b/spec/inputs/lists.yue
index 15eb9ab..921cae0 100644
--- a/spec/inputs/lists.yue
+++ b/spec/inputs/lists.yue
@@ -67,6 +67,24 @@ normal = (hello) ->
 test = x 1,2,3,4,5
 print thing for thing in *test
 
--> a = b for row in *rows
-
-
+_ = -> a = b for row in *rows
+
+with tb
+	f [a] -- indexing
+	f [a,] -- list with one element
+	print v for v in *f[a,] -- table slicing in for-loop
+	f [] -- empty list
+	f[] = x -- table appending to f
+	[a] = x -- assign to tb[a]
+	[a,] = x -- list destructuring for x
+	[] = x -- table appending to tb
+	c = a in [1] -- check if a in tb[1]
+	c = a in [1,] -- check if a == 1
+	c = a in {1} -- check if a == 1
+	c = a in {1,} -- check if a == 1
+
+do
+	[a, b] = hello
+	[name = "nameless", job = "jobless"] = person
+
+nil
diff --git a/spec/inputs/tables.yue b/spec/inputs/tables.yue
index 0b5af46..53a53ae 100644
--- a/spec/inputs/tables.yue
+++ b/spec/inputs/tables.yue
@@ -40,6 +40,8 @@ ya = { 1,2,3, key: 100, 343, "hello", umm: 232 }
 x = { 1,2,
 	4343, 343 ,343 }
 
+x = [ 1,2,
+	4343, 343 ,343 ]
 
 g, p = {
 	1,2, nowy: "yes", 3,4,
@@ -52,6 +54,12 @@ annother = {
 	6,7,8
 }
 
+annother = [
+	1,2,3
+	3,4,5
+	6,7,8
+]
+
 yeah = {
 	[232]: 3434, "helo"
 	ice: "cake"
@@ -255,6 +263,11 @@ tbMixA = {
 	11
 }
 
+tbMixA = [
+	...[i for i = 1, 10]
+	11
+]
+
 tbMixB = {
 	... ... -- only the first item in vararg been accessed here
 	... {...}
@@ -262,6 +275,13 @@ tbMixB = {
 	1, 2, 3
 }
 
+tbMixB = [
+	... ... -- only the first item in vararg been accessed here
+	... {...}
+	... {......}
+	1, 2, 3
+]
+
 const template = {
 	foo: "Hello"
 	bar: "World"
diff --git a/spec/inputs/unicode/cond.yue b/spec/inputs/unicode/cond.yue
index 362408c..fca6d60 100644
--- a/spec/inputs/unicode/cond.yue
+++ b/spec/inputs/unicode/cond.yue
@@ -226,6 +226,17 @@ do
 	else
 		2
 
+do
+	链式比较 = 1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
+
+	值 = (输入)->
+		打印 输入
+		输入
+
+	求值 = 值(1) < 值(2) <= 值(3)
+
+	求值 = 值(1) > 值(2) <= 值(3)
+
 nil
 
 
diff --git a/spec/inputs/unicode/in_expression.yue b/spec/inputs/unicode/in_expression.yue
index efaca47..e068cbf 100644
--- a/spec/inputs/unicode/in_expression.yue
+++ b/spec/inputs/unicode/in_expression.yue
@@ -1,29 +1,10 @@
 -变量a^2 in {1, 2, 3} |> 函数
 
-变量a, 变量b = 函数x(...) not in [1, 3], 2
-
-变量d = (对象.字段x.字段y ...) not in [1, 3]
-
 在的 = "东" in { "东", "西" }
 
-if 变量a in {1} and 变量b in {2, 3, 4} or 变量c in [1, 10]
+if 变量a in {1} and 变量b in {2, 3, 4}
 	打印 变量a, 变量b, 变量c
 
-switch 值
-	when 1, 2, 3
-		打印 "1, 2, 3"
-
-	when not in (0, 100]
-		打印 "非 (0 < 值 <= 100)"
-
-	when in [200, 300)
-		打印 "200 <= 值 < 300)"
-
-	when not in {333, 444, 555}
-		打印 "非 333, 444 或 555"
-
-do return 变量y not in (开始, 结束)
-
 do
 	存在 = 元素 in 表
 	检查 元素 in 表
diff --git a/spec/outputs/cond.lua b/spec/outputs/cond.lua
index 1f6aa63..651c14a 100644
--- a/spec/outputs/cond.lua
+++ b/spec/outputs/cond.lua
@@ -345,4 +345,21 @@ do
 		v = 2
 	end
 end
+do
+	local condChain = 1 < 2 and 2 <= 2 and 2 < 3 and 3 == 3 and 3 > 2 and 2 >= 1 and 1 == 1 and 1 < 3 and 3 ~= 5
+	local v
+	v = function(x)
+		print(x)
+		return x
+	end
+	local evaluation
+	do
+		local _cond_0 = v(2)
+		evaluation = v(1) < _cond_0 and _cond_0 <= v(3)
+	end
+	do
+		local _cond_0 = v(2)
+		evaluation = v(1) > _cond_0 and _cond_0 <= v(3)
+	end
+end
 return nil
diff --git a/spec/outputs/in_expression.lua b/spec/outputs/in_expression.lua
index ddba69a..60802c9 100644
--- a/spec/outputs/in_expression.lua
+++ b/spec/outputs/in_expression.lua
@@ -2,42 +2,17 @@ f((function()
 	local _val_0 = -a ^ 2
 	return 1 == _val_0 or 2 == _val_0 or 3 == _val_0
 end)())
-local a, b = (function(...)
-	local _val_0 = x(...)
-	return not (1 <= _val_0 and _val_0 <= 3)
-end)(...), 2
-local d
-do
-	local _val_0 = (tb.x.y(...))
-	d = not (1 <= _val_0 and _val_0 <= 3)
-end
 local has
 do
 	local _val_0 = "foo"
 	has = "bar" == _val_0 or "foo" == _val_0
 end
-if (1 == a) and (2 == b or 3 == b or 4 == b) or (function()
-	local _val_0 = c
-	return 1 <= _val_0 and _val_0 <= 10
+if (1 == a) and (function()
+	local _val_0 = b
+	return 2 == _val_0 or 3 == _val_0 or 4 == _val_0
 end)() then
 	print(a, b, c)
 end
-do
-	local _exp_0 = val
-	if 1 == _exp_0 or 2 == _exp_0 or 3 == _exp_0 then
-		print("1, 2, 3")
-	elseif not (0 < _exp_0 and _exp_0 <= 100) then
-		print("not (0 < val <= 100)")
-	elseif (200 <= _exp_0 and _exp_0 < 300) then
-		print("200 <= val < 300)")
-	elseif not (333 == _exp_0 or 444 == _exp_0 or 555 == _exp_0) then
-		print("not 333, 444 or 555")
-	end
-end
-do
-	local _val_0 = y
-	return not (a < _val_0 and _val_0 < b)
-end
 do
 	local exist
 	do
diff --git a/spec/outputs/lists.lua b/spec/outputs/lists.lua
index 581cc23..e6f306d 100644
--- a/spec/outputs/lists.lua
+++ b/spec/outputs/lists.lua
@@ -273,10 +273,73 @@ for _index_0 = 1, #test do
 	local thing = test[_index_0]
 	print(thing)
 end
-return function()
+_ = function()
 	local _list_0 = rows
 	for _index_0 = 1, #_list_0 do
 		local row = _list_0[_index_0]
 		a = b
 	end
 end
+do
+	local _with_0 = tb
+	f(_with_0[a])
+	f({
+		a
+	})
+	local _list_0 = f
+	for _index_0 = a, #_list_0 do
+		local v = _list_0[_index_0]
+		print(v)
+	end
+	f({ })
+	do
+		local _obj_0 = f
+		_obj_0[#_obj_0 + 1] = x
+	end
+	_with_0[a] = x
+	a = x[1]
+	_with_0[#_with_0 + 1] = x
+	do
+		local _check_0 = _with_0[1]
+		local _find_0 = false
+		for _index_0 = 1, #_check_0 do
+			local _item_0 = _check_0[_index_0]
+			if _item_0 == a then
+				_find_0 = true
+				break
+			end
+		end
+		c = _find_0
+	end
+	c = (1 == a)
+	c = (1 == a)
+	do
+		local _check_0 = {
+			1
+		}
+		local _find_0 = false
+		for _index_0 = 1, #_check_0 do
+			local _item_0 = _check_0[_index_0]
+			if _item_0 == a then
+				_find_0 = true
+				break
+			end
+		end
+		c = _find_0
+	end
+end
+do
+	a, b = hello[1], hello[2]
+	local name, job
+	do
+		local _obj_0 = person
+		name, job = _obj_0[1], _obj_0[2]
+		if name == nil then
+			name = "nameless"
+		end
+		if job == nil then
+			job = "jobless"
+		end
+	end
+end
+return nil
diff --git a/spec/outputs/tables.lua b/spec/outputs/tables.lua
index e9df1c4..f836d58 100644
--- a/spec/outputs/tables.lua
+++ b/spec/outputs/tables.lua
@@ -52,6 +52,13 @@ local x = {
 	343,
 	343
 }
+x = {
+	1,
+	2,
+	4343,
+	343,
+	343
+}
 local g, p = {
 	1,
 	2,
@@ -72,6 +79,17 @@ local annother = {
 	7,
 	8
 }
+annother = {
+	1,
+	2,
+	3,
+	3,
+	4,
+	5,
+	6,
+	7,
+	8
+}
 local yeah = {
 	[232] = 3434,
 	"helo",
@@ -435,6 +453,27 @@ do
 	_tab_0[#_tab_0 + 1] = 11
 	tbMixA = _tab_0
 end
+do
+	local _tab_0 = { }
+	local _obj_0
+	do
+		local _accum_0 = { }
+		local _len_0 = 1
+		for i = 1, 10 do
+			_accum_0[_len_0] = i
+			_len_0 = _len_0 + 1
+		end
+		_obj_0 = _accum_0
+	end
+	local _idx_0 = #_tab_0 + 1
+	for _index_0 = 1, #_obj_0 do
+		local _value_0 = _obj_0[_index_0]
+		_tab_0[_idx_0] = _value_0
+		_idx_0 = _idx_0 + 1
+	end
+	_tab_0[#_tab_0 + 1] = 11
+	tbMixA = _tab_0
+end
 local tbMixB
 do
 	local _tab_0 = { }
@@ -489,6 +528,50 @@ do
 	_tab_0[#_tab_0 + 1] = 3
 	tbMixB = _tab_0
 end
+do
+	local _tab_0 = { }
+	local _obj_0 = ...
+	local _idx_0 = #_tab_0 + 1
+	for _index_0 = 1, #_obj_0 do
+		local _value_0 = _obj_0[_index_0]
+		_tab_0[_idx_0] = _value_0
+		_idx_0 = _idx_0 + 1
+	end
+	local _obj_1 = {
+		...
+	}
+	local _idx_1 = #_tab_0 + 1
+	for _index_0 = 1, #_obj_1 do
+		local _value_0 = _obj_1[_index_0]
+		_tab_0[_idx_1] = _value_0
+		_idx_1 = _idx_1 + 1
+	end
+	local _obj_2
+	do
+		local _tab_1 = { }
+		local _obj_3 = ...
+		local _idx_2 = 1
+		for _key_0, _value_0 in pairs(_obj_3) do
+			if _idx_2 == _key_0 then
+				_tab_1[#_tab_1 + 1] = _value_0
+				_idx_2 = _idx_2 + 1
+			else
+				_tab_1[_key_0] = _value_0
+			end
+		end
+		_obj_2 = _tab_1
+	end
+	local _idx_2 = #_tab_0 + 1
+	for _index_0 = 1, #_obj_2 do
+		local _value_0 = _obj_2[_index_0]
+		_tab_0[_idx_2] = _value_0
+		_idx_2 = _idx_2 + 1
+	end
+	_tab_0[#_tab_0 + 1] = 1
+	_tab_0[#_tab_0 + 1] = 2
+	_tab_0[#_tab_0 + 1] = 3
+	tbMixB = _tab_0
+end
 local template <const> = {
 	foo = "Hello",
 	bar = "World",
diff --git a/spec/outputs/unicode/cond.lua b/spec/outputs/unicode/cond.lua
index f972dea..9a4ccb9 100644
--- a/spec/outputs/unicode/cond.lua
+++ b/spec/outputs/unicode/cond.lua
@@ -351,4 +351,21 @@ do
 		_u53d8_u91cfv = 2
 	end
 end
+do
+	local _u94fe_u5f0f_u6bd4_u8f83 = 1 < 2 and 2 <= 2 and 2 < 3 and 3 == 3 and 3 > 2 and 2 >= 1 and 1 == 1 and 1 < 3 and 3 ~= 5
+	local _u503c
+	_u503c = function(_u8f93_u5165)
+		_u6253_u5370(_u8f93_u5165)
+		return _u8f93_u5165
+	end
+	local _u6c42_u503c
+	do
+		local _cond_0 = _u503c(2)
+		_u6c42_u503c = _u503c(1) < _cond_0 and _cond_0 <= _u503c(3)
+	end
+	do
+		local _cond_0 = _u503c(2)
+		_u6c42_u503c = _u503c(1) > _cond_0 and _cond_0 <= _u503c(3)
+	end
+end
 return nil
diff --git a/spec/outputs/unicode/in_expression.lua b/spec/outputs/unicode/in_expression.lua
index 62aad05..7c584f3 100644
--- a/spec/outputs/unicode/in_expression.lua
+++ b/spec/outputs/unicode/in_expression.lua
@@ -2,42 +2,17 @@ _u51fd_u6570((function()
 	local _val_0 = -_u53d8_u91cfa ^ 2
 	return 1 == _val_0 or 2 == _val_0 or 3 == _val_0
 end)())
-local _u53d8_u91cfa, _u53d8_u91cfb = (function(...)
-	local _val_0 = _u51fd_u6570x(...)
-	return not (1 <= _val_0 and _val_0 <= 3)
-end)(...), 2
-local _u53d8_u91cfd
-do
-	local _val_0 = (_u5bf9_u8c61["字段x"]["字段y"](...))
-	_u53d8_u91cfd = not (1 <= _val_0 and _val_0 <= 3)
-end
 local _u5728_u7684
 do
 	local _val_0 = "东"
 	_u5728_u7684 = "东" == _val_0 or "西" == _val_0
 end
-if (1 == _u53d8_u91cfa) and (2 == _u53d8_u91cfb or 3 == _u53d8_u91cfb or 4 == _u53d8_u91cfb) or (function()
-	local _val_0 = _u53d8_u91cfc
-	return 1 <= _val_0 and _val_0 <= 10
+if (1 == _u53d8_u91cfa) and (function()
+	local _val_0 = _u53d8_u91cfb
+	return 2 == _val_0 or 3 == _val_0 or 4 == _val_0
 end)() then
 	_u6253_u5370(_u53d8_u91cfa, _u53d8_u91cfb, _u53d8_u91cfc)
 end
-do
-	local _exp_0 = _u503c
-	if 1 == _exp_0 or 2 == _exp_0 or 3 == _exp_0 then
-		_u6253_u5370("1, 2, 3")
-	elseif not (0 < _exp_0 and _exp_0 <= 100) then
-		_u6253_u5370("非 (0 < 值 <= 100)")
-	elseif (200 <= _exp_0 and _exp_0 < 300) then
-		_u6253_u5370("200 <= 值 < 300)")
-	elseif not (333 == _exp_0 or 444 == _exp_0 or 555 == _exp_0) then
-		_u6253_u5370("非 333, 444 或 555")
-	end
-end
-do
-	local _val_0 = _u53d8_u91cfy
-	return not (_u5f00_u59cb < _val_0 and _val_0 < _u7ed3_u675f)
-end
 do
 	local _u5b58_u5728
 	do
diff --git a/src/yuescript/yue_ast.cpp b/src/yuescript/yue_ast.cpp
index ed34274..6126f71 100644
--- a/src/yuescript/yue_ast.cpp
+++ b/src/yuescript/yue_ast.cpp
@@ -182,12 +182,6 @@ std::string ConstValue_t::to_string(void* ud) const {
 	auto info = reinterpret_cast<YueFormat*>(ud);
 	return info->convert(this);
 }
-std::string InRangeOpen_t::to_string(void*) const {
-	return {};
-}
-std::string InRangeClose_t::to_string(void*) const {
-	return {};
-}
 std::string NotIn_t::to_string(void*) const {
 	return {};
 }
@@ -569,8 +563,18 @@ std::string Try_t::to_string(void* ud) const {
 	return join(temp, "\n"sv);
 }
 std::string Comprehension_t::to_string(void* ud) const {
-	auto valueStr = value->to_string(ud);
-	return '[' + (valueStr[0] == '[' ? " "s : ""s) + valueStr + ' ' + forLoop->to_string(ud) + ']';
+	str_list temp;
+	for (const auto& item : items.objects()) {
+		temp.push_back(item->to_string(ud));
+	}
+	if (temp.size() > 0) {
+		temp.front().insert(0, temp.front()[0] == '[' ? " "s : ""s);
+	}
+	if (items.size() != 2 || !ast_is<CompInner_t>(items.back())) {
+		return '[' + join(temp, ", "sv) + ']';
+	} else {
+		return '[' + join(temp, " "sv) + ']';
+	}
 }
 std::string CompValue_t::to_string(void* ud) const {
 	return value->to_string(ud);
@@ -830,6 +834,9 @@ std::string Invoke_t::to_string(void* ud) const {
 std::string SpreadExp_t::to_string(void* ud) const {
 	return "..."s + exp->to_string(ud);
 }
+std::string SpreadListExp_t::to_string(void* ud) const {
+	return "..."s + exp->to_string(ud);
+}
 std::string TableLit_t::to_string(void* ud) const {
 	auto info = reinterpret_cast<YueFormat*>(ud);
 	if (values.empty()) {
@@ -1168,10 +1175,6 @@ std::string UnaryExp_t::to_string(void* ud) const {
 	}
 	return line;
 }
-std::string InRange_t::to_string(void* ud) const {
-	auto valueStr = openValue->to_string(ud);
-	return (open.is<InRangeOpen_t>() ? "("s : "["s + (valueStr[0] == '[' ? " "s : ""s)) + valueStr + ", "s + closeValue->to_string(ud) + (close.is<InRangeOpen_t>() ? ')' : ']');
-}
 std::string InDiscrete_t::to_string(void* ud) const {
 	str_list temp;
 	for (auto value : values.objects()) {
diff --git a/src/yuescript/yue_ast.h b/src/yuescript/yue_ast.h
index a02f548..2136849 100644
--- a/src/yuescript/yue_ast.h
+++ b/src/yuescript/yue_ast.h
@@ -78,6 +78,8 @@ class InvokeArgs_t;
 class TableBlockIndent_t;
 class Macro_t;
 class In_t;
+class NormalDef_t;
+class SpreadListExp_t;
 } // namespace yue
 
 AST_LEAF(Num)
@@ -286,7 +288,7 @@ AST_NODE(SwitchList)
 AST_END(SwitchList, "switch_list"sv)
 
 AST_NODE(SwitchCase)
-	ast_sel<true, SwitchList_t, In_t> condition;
+	ast_ptr<true, SwitchList_t> condition;
 	ast_sel<true, Block_t, Statement_t> body;
 	AST_MEMBER(SwitchCase, &condition, &body)
 AST_END(SwitchCase, "switch_case"sv)
@@ -374,9 +376,10 @@ AST_NODE(Try)
 AST_END(Try, "try"sv)
 
 AST_NODE(Comprehension)
-	ast_sel<true, Exp_t, /*non-syntax-rule*/ Statement_t> value;
-	ast_ptr<true, CompInner_t> forLoop;
-	AST_MEMBER(Comprehension, &value, &forLoop)
+	ast_ptr<true, Seperator_t> sep;
+	ast_sel_list<false, NormalDef_t, SpreadListExp_t, CompInner_t,
+		/*non-syntax-rule*/ Statement_t> items;
+	AST_MEMBER(Comprehension, &sep, &items)
 AST_END(Comprehension, "comp"sv)
 
 AST_NODE(CompValue)
@@ -437,23 +440,9 @@ AST_END(BinaryOperator, "binary_op"sv)
 AST_LEAF(UnaryOperator)
 AST_END(UnaryOperator, "unary_op"sv)
 
-AST_LEAF(InRangeOpen)
-AST_END(InRangeOpen, "in_range_open"sv)
-
-AST_LEAF(InRangeClose)
-AST_END(InRangeClose, "in_range_close"sv)
-
 AST_LEAF(NotIn)
 AST_END(NotIn, "not_in"sv)
 
-AST_NODE(InRange)
-	ast_sel<true, InRangeOpen_t, InRangeClose_t> open;
-	ast_ptr<true, Exp_t> openValue;
-	ast_ptr<true, Exp_t> closeValue;
-	ast_sel<true, InRangeOpen_t, InRangeClose_t> close;
-	AST_MEMBER(InRange, &open, &openValue, &closeValue, &close)
-AST_END(InRange, "in_range"sv)
-
 AST_NODE(InDiscrete)
 	ast_ptr<true, Seperator_t> sep;
 	ast_list<true, Exp_t> values;
@@ -462,7 +451,7 @@ AST_END(InDiscrete, "in_discrete"sv)
 
 AST_NODE(In)
 	ast_ptr<false, NotIn_t> not_;
-	ast_sel<true, InRange_t, InDiscrete_t, Exp_t> item;
+	ast_sel<true, InDiscrete_t, Exp_t> item;
 	AST_MEMBER(In, &not_, &item)
 AST_END(In, "in"sv)
 
@@ -666,6 +655,11 @@ AST_NODE(SpreadExp)
 	AST_MEMBER(SpreadExp, &exp)
 AST_END(SpreadExp, "spread_exp"sv)
 
+AST_NODE(SpreadListExp)
+	ast_ptr<true, Exp_t> exp;
+	AST_MEMBER(SpreadListExp, &exp)
+AST_END(SpreadListExp, "spread_list_exp"sv)
+
 AST_NODE(TableLit)
 	ast_ptr<true, Seperator_t> sep;
 	ast_sel_list<false,
@@ -673,7 +667,7 @@ AST_NODE(TableLit)
 		MetaVariablePairDef_t, MetaNormalPairDef_t,
 		VariablePair_t, NormalPair_t, Exp_t,
 		MetaVariablePair_t, MetaNormalPair_t,
-		/*non-syntax-rule*/ TableBlockIndent_t> values;
+		/*non-syntax-rule*/ TableBlockIndent_t, SpreadListExp_t> values;
 	AST_MEMBER(TableLit, &sep, &values)
 AST_END(TableLit, "table_lit"sv)
 
diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp
index 5b9770d..d32d9c1 100644
--- a/src/yuescript/yue_compiler.cpp
+++ b/src/yuescript/yue_compiler.cpp
@@ -15,6 +15,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
+#include <variant>
 
 #include "yuescript/yue_compiler.h"
 #include "yuescript/yue_parser.h"
@@ -74,7 +75,7 @@ static std::unordered_set<std::string> Metamethods = {
 	"close"s // Lua 5.4
 };
 
-const std::string_view version = "0.19.6"sv;
+const std::string_view version = "0.20.0"sv;
 const std::string_view extension = "yue"sv;
 
 class CompileError : public std::logic_error {
@@ -1148,6 +1149,10 @@ private:
 					auto simpleValue = static_cast<SimpleValue_t*>(item);
 					if (simpleValue->value.is<TableLit_t>()) {
 						return true;
+					} else if (auto comp = simpleValue->value.as<Comprehension_t>()) {
+						if (comp->items.size() != 2 || !ast_is<CompInner_t>(comp->items.back())) {
+							return true;
+						}
 					}
 					return false;
 				}
@@ -1195,6 +1200,28 @@ private:
 		return false;
 	}
 
+	bool isConditionChainingOperator(const std::string& op) {
+		return op == "=="sv || op == "~="sv || op == "!="sv ||
+			op == "<"sv || op == "<="sv || op == ">"sv || op == ">="sv;
+	}
+
+	bool isConditionChaining(Exp_t* exp) {
+		int conditionChaining = 0;
+		for (const auto& opValue_ : exp->opValues.objects()) {
+			auto opValue = static_cast<ExpOpValue_t*>(opValue_);
+			auto op = _parser.toString(opValue->op);
+			if (isConditionChainingOperator(op)) {
+				conditionChaining++;
+				if (conditionChaining > 1) {
+					return true;
+				}
+			} else {
+				conditionChaining = 0;
+			}
+		}
+		return false;
+	}
+
 	UnaryExp_t* unaryGeneratingAnonFunc(Exp_t* exp) {
 		if (!exp) return nullptr;
 		BLOCK_START
@@ -1440,10 +1467,10 @@ private:
 				case id<CompInner_t>(): {
 					auto compInner = appendix->item.to<CompInner_t>();
 					auto comp = x->new_ptr<Comprehension_t>();
-					comp->forLoop.set(compInner);
 					auto stmt = x->new_ptr<Statement_t>();
 					stmt->content.set(statement->content);
-					comp->value.set(stmt);
+					comp->items.push_back(stmt);
+					comp->items.push_back(compInner);
 					auto simpleValue = x->new_ptr<SimpleValue_t>();
 					simpleValue->value.set(comp);
 					auto exp = newExp(simpleValue, x);
@@ -2032,6 +2059,10 @@ private:
 			transformUnaryExp(unary, out, ExpUsage::Assignment, expList);
 			out.back().insert(0, preDefine);
 			return;
+		} else if (isConditionChaining(exp)) {
+			auto expList = assignment->expList.get();
+			transformExp(exp, out, ExpUsage::Assignment, expList);
+			return;
 		}
 		auto singleVal = singleValueFrom(exp);
 		BREAK_IF(!singleVal);
@@ -2320,12 +2351,15 @@ private:
 			case id<Exp_t>(): {
 				auto item = singleValueFrom(node)->item.get();
 				if (!item) throw CompileError("invalid destructure value"sv, node);
-				auto tbA = item->get_by_path<TableLit_t>();
-				if (tbA) {
+				if (auto tbA = item->get_by_path<TableLit_t>()) {
 					tableItems = &tbA->values.objects();
-				} else {
-					auto tbB = ast_cast<SimpleTable_t>(item);
-					if (tbB) tableItems = &tbB->pairs.objects();
+				} else if (auto tbB = item->get_by_path<Comprehension_t>()) {
+					if (tbB->items.size() == 2 && ast_is<CompInner_t>(tbB->items.back())) {
+						throw CompileError("invalid destructure value"sv, tbB);
+					}
+					tableItems = &tbB->items.objects();
+				} else if (auto tbC = ast_cast<SimpleTable_t>(item)) {
+					tableItems = &tbC->pairs.objects();
 				}
 				break;
 			}
@@ -2340,15 +2374,23 @@ private:
 				break;
 			}
 			case id<TableLit_t>(): {
-				auto table = ast_cast<TableLit_t>(node);
+				auto table = static_cast<TableLit_t*>(node);
 				tableItems = &table->values.objects();
 				break;
 			}
 			case id<SimpleTable_t>(): {
-				auto table = ast_cast<SimpleTable_t>(node);
+				auto table = static_cast<SimpleTable_t*>(node);
 				tableItems = &table->pairs.objects();
 				break;
 			}
+			case id<Comprehension_t>(): {
+				auto table = static_cast<Comprehension_t*>(node);
+				if (table->items.size() == 2 && ast_is<CompInner_t>(table->items.back())) {
+					throw CompileError("invalid destructure value"sv, table);
+				}
+				tableItems = &table->items.objects();
+				break;
+			}
 			default: YUEE("AST node mismatch", node); break;
 		}
 		if (!tableItems) throw CompileError("invalid destructure value"sv, node);
@@ -2370,8 +2412,9 @@ private:
 					}
 					auto value = singleValueFrom(pair);
 					auto item = value->item.get();
-					if (ast_is<SimpleTable_t>(item) || item->get_by_path<TableLit_t>()) {
-						auto subPairs = destructFromExp(pair, varDefOnly, optional);
+					ast_node* subExp = ast_cast<SimpleTable_t>(item);
+					if (subExp || (subExp = item->get_by_path<TableLit_t>()) || (subExp = item->get_by_path<Comprehension_t>())) {
+						auto subPairs = destructFromExp(subExp, varDefOnly, optional);
 						if (!subPairs.empty()) {
 							if (defVal) {
 								throw CompileError("default value is not supported here"sv, defVal);
@@ -2448,8 +2491,9 @@ private:
 					if (auto exp = np->value.as<Exp_t>()) {
 						if (!varDefOnly && !isAssignable(exp)) throw CompileError("can't do destructure value"sv, exp);
 						auto item = singleValueFrom(exp)->item.get();
-						if (ast_is<SimpleTable_t>(item) || item->get_by_path<TableLit_t>()) {
-							auto subPairs = destructFromExp(exp, varDefOnly, optional);
+						ast_node* subExp = ast_cast<SimpleTable_t>(item);
+						if (subExp || (subExp = item->get_by_path<TableLit_t>()) || (subExp = item->get_by_path<Comprehension_t>())) {
+							auto subPairs = destructFromExp(subExp, varDefOnly, optional);
 							if (!subPairs.empty()) {
 								if (defVal) {
 									throw CompileError("default value is not supported here"sv, defVal);
@@ -2602,8 +2646,19 @@ private:
 			if (!value) {
 				throw CompileError("invalid destructure"sv, expr);
 			}
-			ast_node* destructNode = value->get_by_path<SimpleValue_t, TableLit_t>();
-			if (destructNode || (destructNode = value->item.as<SimpleTable_t>())) {
+			ast_node* destructNode = value->item.as<SimpleTable_t>();
+			if (!destructNode) {
+				if (auto sVal = value->item.as<SimpleValue_t>()) {
+					if (auto tab = sVal->value.as<TableLit_t>()) {
+						destructNode = tab;
+					} else if (auto comp = sVal->value.as<Comprehension_t>()) {
+						if (comp->items.size() != 2 || !ast_is<CompInner_t>(comp->items.back())) {
+							destructNode = comp;
+						}
+					}
+				}
+			}
+			if (destructNode) {
 				if (*j != nil) {
 					if (auto ssVal = simpleSingleValueFrom(*j)) {
 						switch (ssVal->value->get_id()) {
@@ -2630,6 +2685,9 @@ private:
 					case id<SimpleTable_t>():
 						dlist = &static_cast<SimpleTable_t*>(destructNode)->pairs.objects();
 						break;
+					case id<Comprehension_t>():
+						dlist = &static_cast<Comprehension_t*>(destructNode)->items.objects();
+						break;
 					default: YUEE("AST node mismatch", destructNode); break;
 				}
 				if (dlist->empty()) throw CompileError("expect items to be destructured"sv, destructNode);
@@ -3300,27 +3358,173 @@ private:
 			transform_pipe_exp(exp->pipeExprs.objects(), out, usage, assignList);
 			return;
 		}
-		if (usage != ExpUsage::Closure) {
-			YUEE("invalid expression usage", exp);
-		}
 		if (exp->nilCoalesed) {
+			if (usage != ExpUsage::Closure) {
+				YUEE("invalid expression usage", exp);
+			}
 			transformNilCoalesedExp(exp, out, ExpUsage::Closure);
 			return;
 		}
 		str_list temp;
-		transform_pipe_exp(exp->pipeExprs.objects(), temp, ExpUsage::Closure);
+		std::list<std::pair<std::string, ast_list<true, UnaryExp_t>*>> chains;
+		chains.emplace_back(std::string(), &exp->pipeExprs);
+		int conditionChainCount = 0;
+		str_list evalLines;
+		auto checkChains = [&]() {
+			std::optional<str_list> result;
+			if (conditionChainCount > 1) {
+				bool needWrapping = false;
+				str_list conds;
+				str_list preDefines;
+				std::list<std::variant<std::string, ast_ptr<false, Exp_t>>> stack;
+				for (const auto& item : chains) {
+					if (!item.first.empty()) {
+						stack.push_back(item.first);
+					}
+					auto node = item.second->front();
+					bool checkEvalOnce = item != chains.front() && item != chains.back();
+					if (checkEvalOnce) {
+						std::string varName;
+						if (item.second->size() == 1) {
+							if (auto unary = singleUnaryExpFrom(node)) {
+								if (auto value = singleValueFrom(unary)) {
+									varName = singleVariableFrom(value, true);
+								}
+								if (varName.empty()) {
+									if (auto sval = static_cast<Value_t*>(unary->expos.front())->item.as<SimpleValue_t>()) {
+										if (ast_is<ConstValue_t, Num_t>(sval->value)) {
+											auto condExp = unary->new_ptr<Exp_t>();
+											condExp->pipeExprs.dup(*item.second);
+											stack.push_back(condExp);
+											goto reduce;
+										}
+									}
+								}
+							}
+						}
+						if (varName.empty() || !isLocal(varName)) {
+							varName = getUnusedName("_cond_"sv);
+							auto condExp = node->new_ptr<Exp_t>();
+							condExp->pipeExprs.dup(*item.second);
+							auto varExp = toAst<Exp_t>(varName, node);
+							auto assignment = assignmentFrom(varExp, condExp, node);
+							if (!needWrapping) {
+								needWrapping = true;
+								if (usage == ExpUsage::Closure) {
+									pushFunctionScope();
+									pushAnonVarArg();
+									pushScope();
+								} else if (usage == ExpUsage::Assignment) {
+									pushScope();
+								}
+							}
+							transformAssignment(assignment, preDefines);
+							stack.push_back(varExp);
+							goto reduce;
+						}
+					}
+					{
+						auto condExp = node->new_ptr<Exp_t>();
+						condExp->pipeExprs.dup(*item.second);
+						stack.push_back(condExp);
+					}
+					reduce:
+						if (stack.size() == 3) {
+							str_list tmp;
+							auto one = std::get<ast_ptr<false, Exp_t>>(stack.front()).get();
+							transformExp(one, tmp, ExpUsage::Closure);
+							stack.pop_front();
+							auto two = std::get<std::string>(stack.front());
+							tmp.push_back(two);
+							stack.pop_front();
+							auto three = std::get<ast_ptr<false, Exp_t>>(stack.front()).get();
+							transformExp(three, tmp, ExpUsage::Closure);
+							conds.push_back(join(tmp, " "sv));
+						}
+				}
+				auto condStr = join(conds, " and "sv);
+				if (needWrapping && usage == ExpUsage::Closure) {
+					str_list closureLines{anonFuncStart() + nll(exp)};
+					closureLines.insert(closureLines.end(), preDefines.begin(), preDefines.end());
+					closureLines.push_back(indent() + "return "s + condStr + nll(exp));
+					popScope();
+					closureLines.push_back(indent() + anonFuncEnd());
+					temp.push_back(join(closureLines));
+					popAnonVarArg();
+					popFunctionScope();
+				} else {
+					temp.push_back(condStr);
+					if (!preDefines.empty()) {
+						evalLines.insert(evalLines.end(), preDefines.begin(), preDefines.end());
+						if (usage == ExpUsage::Assignment) {
+							popScope();
+						}
+					}
+				}
+			} else {
+				for (const auto& item : chains) {
+					if (!item.first.empty()) {
+						temp.push_back(item.first);
+					}
+					transform_pipe_exp(item.second->objects(), temp, ExpUsage::Closure);
+				}
+			}
+			conditionChainCount = 0;
+			chains.clear();
+		};
+		str_list preDefines;
 		for (auto _opValue : exp->opValues.objects()) {
 			auto opValue = static_cast<ExpOpValue_t*>(_opValue);
 			transformBinaryOperator(opValue->op, temp);
-			transform_pipe_exp(opValue->pipeExprs.objects(), temp, ExpUsage::Closure);
+			auto op = temp.back();
+			temp.pop_back();
+			if (isConditionChainingOperator(op)) {
+				conditionChainCount++;
+				chains.emplace_back(op, &opValue->pipeExprs);
+			} else {
+				checkChains();
+				temp.push_back(op);
+				transform_pipe_exp(opValue->pipeExprs.objects(), temp, ExpUsage::Closure);
+			}
+		}
+		checkChains();
+		auto condStr = join(temp, " "sv);
+		switch (usage) {
+			case ExpUsage::Closure: {
+				out.push_back(condStr);
+				break;
+			}
+			case ExpUsage::Assignment: {
+				auto assignment = exp->new_ptr<ExpListAssign_t>();
+				assignment->expList.set(assignList);
+				auto assign = exp->new_ptr<Assign_t>();
+				assign->values.push_back(toAst<Exp_t>(condStr, exp));
+				assignment->action.set(assign);
+				if (evalLines.empty()) {
+					transformAssignment(assignment, out);
+				} else {
+					evalLines.push_front(indent() + "do"s + nll(exp));
+					evalLines.push_front(getPreDefineLine(assignment));
+					pushScope();
+					transformAssignment(assignment, evalLines);
+					popScope();
+					evalLines.push_back(indent() + "end"s + nlr(exp));
+					out.push_back(join(evalLines));
+				}
+				break;
+			}
+			case ExpUsage::Return: {
+				evalLines.push_back(indent() + "return "s + condStr + nll(exp));
+				out.push_back(join(evalLines));
+				break;
+			}
+			default: YUEE("invalid expression usage", exp); break;
 		}
-		out.push_back(join(temp, " "sv));
 	}
 
 	void transformNilCoalesedExp(Exp_t* exp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr, bool nilBranchOnly = false) {
 		auto x = exp;
 		str_list temp;
-		std::string prefix;
 		auto left = exp->new_ptr<Exp_t>();
 		if (exp->opValues.empty()) {
 			left->pipeExprs.dup(exp->pipeExprs);
@@ -3328,20 +3532,16 @@ private:
 			if (usage != ExpUsage::Closure) {
 				YUEE("invalid expression usage", exp);
 			}
-			transform_pipe_exp(exp->pipeExprs.objects(), temp, ExpUsage::Closure);
-			auto last = exp->opValues.objects().back();
-			for (auto _opValue : exp->opValues.objects()) {
-				auto opValue = static_cast<ExpOpValue_t*>(_opValue);
-				transformBinaryOperator(opValue->op, temp);
-				if (opValue == last) {
-					left->pipeExprs.dup(opValue->pipeExprs);
-				} else {
-					transform_pipe_exp(opValue->pipeExprs.objects(), temp, ExpUsage::Closure);
-				}
-			}
-			prefix = join(temp, " "sv) + ' ';
-			temp.clear();
-			temp.push_back(prefix);
+			auto last = static_cast<ExpOpValue_t*>(exp->opValues.back());
+			left->pipeExprs.dup(last->pipeExprs);
+
+			auto startExp = x->new_ptr<Exp_t>();
+			startExp->pipeExprs.dup(exp->pipeExprs);
+			startExp->opValues.dup(exp->opValues);
+			startExp->opValues.pop_back();
+			transformExp(startExp, temp, ExpUsage::Closure);
+			transformBinaryOperator(last->op, temp);
+			temp.back() = " "s + temp.back() + " "s;
 		}
 		std::string* funcStart = nullptr;
 		if (usage == ExpUsage::Closure) {
@@ -4303,6 +4503,9 @@ private:
 				} else if (auto unary = unaryGeneratingAnonFunc(exp)) {
 					transformUnaryExp(unary, out, ExpUsage::Return);
 					return;
+				} else if (isConditionChaining(exp)) {
+					transformExp(exp, out, ExpUsage::Return);
+					return;
 				}
 			}
 			if (auto singleValue = singleValueFrom(valueList)) {
@@ -5791,6 +5994,24 @@ private:
 					return;
 				}
 			}
+			auto discrete = unary_exp->inExp->item.to<InDiscrete_t>();
+			if (usage == ExpUsage::Closure && discrete->values.size() == 1) {
+				str_list tmp;
+				transformExp(static_cast<Exp_t*>(discrete->values.front()), tmp, ExpUsage::Closure);
+				tmp.push_back(" == "s);
+				auto newUnaryExp = x->new_ptr<UnaryExp_t>();
+				newUnaryExp->ops.dup(unary_exp->ops);
+				newUnaryExp->expos.dup(unary_exp->expos);
+				transformUnaryExp(newUnaryExp, tmp, ExpUsage::Closure);
+				tmp.push_back(")"s);
+				if (unary_exp->inExp->not_) {
+					tmp.push_front("not ("s);
+				} else {
+					tmp.push_front("("s);
+				}
+				out.push_back(join(tmp));
+				return;
+			}
 			if (varName.empty()) {
 				str_list temp;
 				if (usage == ExpUsage::Closure) {
@@ -5809,54 +6030,32 @@ private:
 				auto assignExp = toAst<Exp_t>(newVar, x);
 				auto assignment = assignmentFrom(assignExp, exp, x);
 				transformAssignment(assignment, temp);
-				if (auto range = unary_exp->inExp->item.as<InRange_t>()) {
-					str_list tmp;
-					transformExp(range->openValue, tmp, ExpUsage::Closure);
-					transformExp(range->closeValue, tmp, ExpUsage::Closure);
-					if (usage == ExpUsage::Assignment) {
-						str_list tmpList;
-						transformExp(static_cast<Exp_t*>(assignList->exprs.front()), tmpList, ExpUsage::Closure);
-						_buf << indent() << tmpList.back() << " = "sv;
-					} else {
-						_buf << indent() << "return "sv;
-					}
-					if (unary_exp->inExp->not_) {
-						_buf << "not ("sv;
-					}
-					_buf << tmp.front() << (range->open.is<InRangeOpen_t>() ? " < "sv : " <= "sv) << newVar << " and "sv << newVar << (range->close.is<InRangeOpen_t>() ? " < "sv : " <= "sv) << tmp.back();
-					if (unary_exp->inExp->not_) {
-						_buf << ")"sv;
-					}
-					_buf << nll(x);
-					temp.push_back(clearBuf());
+
+				str_list tmp;
+				for (auto exp : discrete->values.objects()) {
+					transformExp(static_cast<Exp_t*>(exp), tmp, ExpUsage::Closure);
+				}
+				if (usage == ExpUsage::Assignment) {
+					str_list tmpList;
+					transformExp(static_cast<Exp_t*>(assignList->exprs.front()), tmpList, ExpUsage::Closure);
+					_buf << indent() << tmpList.back() << " = "sv;
 				} else {
-					auto discrete = unary_exp->inExp->item.to<InDiscrete_t>();
-					str_list tmp;
-					for (auto exp : discrete->values.objects()) {
-						transformExp(static_cast<Exp_t*>(exp), tmp, ExpUsage::Closure);
-					}
-					if (usage == ExpUsage::Assignment) {
-						str_list tmpList;
-						transformExp(static_cast<Exp_t*>(assignList->exprs.front()), tmpList, ExpUsage::Closure);
-						_buf << indent() << tmpList.back() << " = "sv;
-					} else {
-						_buf << indent() << "return "sv;
-					}
-					if (unary_exp->inExp->not_) {
-						_buf << "not ("sv;
-					}
-					for (const auto& exp : tmp) {
-						_buf << exp << " == "sv << newVar;
-						if (exp != tmp.back()) {
-							_buf << " or "sv;
-						}
-					}
-					if (unary_exp->inExp->not_) {
-						_buf << ")"sv;
+					_buf << indent() << "return "sv;
+				}
+				if (unary_exp->inExp->not_) {
+					_buf << "not ("sv;
+				}
+				for (const auto& exp : tmp) {
+					_buf << exp << " == "sv << newVar;
+					if (exp != tmp.back()) {
+						_buf << " or "sv;
 					}
-					_buf << nll(x);
-					temp.push_back(clearBuf());
 				}
+				if (unary_exp->inExp->not_) {
+					_buf << ")"sv;
+				}
+				_buf << nll(x);
+				temp.push_back(clearBuf());
 				if (usage == ExpUsage::Closure) {
 					temp.push_front(anonFuncStart() + nll(x));
 					popScope();
@@ -5872,35 +6071,22 @@ private:
 					out.push_back(join(temp));
 				}
 			} else {
-				if (auto range = unary_exp->inExp->item.as<InRange_t>()) {
-					str_list tmp;
-					transformExp(range->openValue, tmp, ExpUsage::Closure);
-					transformExp(range->closeValue, tmp, ExpUsage::Closure);
-					if (unary_exp->inExp->not_) {
-						_buf << "not "sv;
-					}
-					_buf << '(' << tmp.front() << (range->open.is<InRangeOpen_t>() ? " < "sv : " <= "sv) << varName << " and "sv << varName << (range->close.is<InRangeOpen_t>() ? " < "sv : " <= "sv) << tmp.back();
-					_buf << ')';
-					out.push_back(clearBuf());
-				} else {
-					auto discrete = unary_exp->inExp->item.to<InDiscrete_t>();
-					str_list tmp;
-					for (auto exp : discrete->values.objects()) {
-						transformExp(static_cast<Exp_t*>(exp), tmp, ExpUsage::Closure);
-					}
-					if (unary_exp->inExp->not_) {
-						_buf << "not "sv;
-					}
-					_buf << '(';
-					for (const auto& exp : tmp) {
-						_buf << exp << " == "sv << varName;
-						if (exp != tmp.back()) {
-							_buf << " or "sv;
-						}
+				str_list tmp;
+				for (auto exp : discrete->values.objects()) {
+					transformExp(static_cast<Exp_t*>(exp), tmp, ExpUsage::Closure);
+				}
+				if (unary_exp->inExp->not_) {
+					_buf << "not "sv;
+				}
+				_buf << '(';
+				for (const auto& exp : tmp) {
+					_buf << exp << " == "sv << varName;
+					if (exp != tmp.back()) {
+						_buf << " or "sv;
 					}
-					_buf << ')';
-					out.push_back(clearBuf());
 				}
+				_buf << ')';
+				out.push_back(clearBuf());
 			}
 			return;
 		}
@@ -5945,7 +6131,7 @@ private:
 
 	bool hasSpreadExp(const node_container& items) {
 		for (auto item : items) {
-			if (ast_is<SpreadExp_t>(item)) return true;
+			if (ast_is<SpreadExp_t, SpreadListExp_t>(item)) return true;
 		}
 		return false;
 	}
@@ -5968,11 +6154,11 @@ private:
 		std::string tableVar = getUnusedName("_tab_"sv);
 		forceAddToScope(tableVar);
 		auto it = values.begin();
-		if (ast_is<SpreadExp_t>(*it)) {
+		if (ast_is<SpreadExp_t, SpreadListExp_t>(*it)) {
 			temp.push_back(indent() + "local "s + tableVar + " = { }"s + nll(x));
 		} else {
 			auto initialTab = x->new_ptr<TableLit_t>();
-			while (it != values.end() && !ast_is<SpreadExp_t>(*it)) {
+			while (it != values.end() && !ast_is<SpreadExp_t, SpreadListExp_t>(*it)) {
 				initialTab->values.push_back(*it);
 				++it;
 			}
@@ -6007,6 +6193,28 @@ private:
 					transformForEach(forEach, temp);
 					break;
 				}
+				case id<SpreadListExp_t>(): {
+					auto spread = static_cast<SpreadListExp_t*>(item);
+					std::string indexVar = getUnusedName("_idx_"sv);
+					std::string valueVar = getUnusedName("_value_"sv);
+					auto objVar = singleVariableFrom(spread->exp, true);
+					if (objVar.empty()) {
+						objVar = getUnusedName("_obj_");
+						auto assignment = toAst<ExpListAssign_t>(objVar + "=nil"s, item);
+						auto assign = assignment->action.to<Assign_t>();
+						assign->values.clear();
+						assign->values.push_back(spread->exp);
+						transformAssignment(assignment, temp);
+					}
+					forceAddToScope(indexVar);
+					temp.push_back(indent() + "local "s + indexVar + " = #"s + tableVar + " + 1"s + nll(item));
+					_buf << "for "sv << valueVar << " in *"sv << objVar
+						 << "\n\t"sv << tableVar << '[' << indexVar << "]="sv << valueVar
+						 << "\n\t"sv << indexVar << "+=1"sv;
+					auto forEach = toAst<ForEach_t>(clearBuf(), item);
+					transformForEach(forEach, temp);
+					break;
+				}
 				case id<VariablePair_t>():
 				case id<VariablePairDef_t>(): {
 					if (auto pair = ast_cast<VariablePairDef_t>(item)) {
@@ -6374,7 +6582,7 @@ private:
 	void transformCompCommon(Comprehension_t* comp, str_list& out) {
 		str_list temp;
 		auto x = comp;
-		auto compInner = comp->forLoop.get();
+		auto compInner = static_cast<CompInner_t*>(comp->items.back());
 		for (auto item : compInner->items.objects()) {
 			switch (item->get_id()) {
 				case id<CompForEach_t>():
@@ -6391,9 +6599,9 @@ private:
 				default: YUEE("AST node mismatch", item); break;
 			}
 		}
-		if (auto stmt = comp->value.as<Statement_t>()) {
+		if (auto stmt = ast_cast<Statement_t>(comp->items.front())) {
 			transformStatement(stmt, temp);
-		} else if (auto exp = comp->value.as<Exp_t>()) {
+		} else if (auto exp = ast_cast<Exp_t>(comp->items.front())) {
 			auto expList = x->new_ptr<ExpList_t>();
 			expList->exprs.push_back(exp);
 			auto expListAssign = x->new_ptr<ExpListAssign_t>();
@@ -6414,6 +6622,48 @@ private:
 
 	void transformComprehension(Comprehension_t* comp, str_list& out, ExpUsage usage, ExpList_t* assignList = nullptr) {
 		auto x = comp;
+		if (comp->items.size() != 2 || !ast_is<CompInner_t>(comp->items.back())) {
+			auto tableLit = x->new_ptr<TableLit_t>();
+			tableLit->values.dup(comp->items);
+			switch (usage) {
+				case ExpUsage::Assignment: {
+					auto simpleValue = x->new_ptr<SimpleValue_t>();
+					simpleValue->value.set(tableLit);
+					auto exp = newExp(simpleValue, x);
+					auto assignment = x->new_ptr<ExpListAssign_t>();
+					assignment->expList.set(assignList);
+					auto assign = x->new_ptr<Assign_t>();
+					assign->values.push_back(exp);
+					assignment->action.set(assign);
+					transformAssignment(assignment, out);
+					break;
+				}
+				case ExpUsage::Return: {
+					auto simpleValue = x->new_ptr<SimpleValue_t>();
+					simpleValue->value.set(tableLit);
+					auto exp = newExp(simpleValue, x);
+					auto returnNode = x->new_ptr<Return_t>();
+					auto expList = x->new_ptr<ExpListLow_t>();
+					expList->exprs.push_back(exp);
+					returnNode->valueList.set(expList);
+					transformReturn(returnNode, out);
+					break;
+				}
+				case ExpUsage::Closure:
+					transformTableLit(tableLit, out);
+					break;
+				default:
+					YUEE("invalid comprehension usage", comp);
+					break;
+			}
+			return;
+		}
+		auto def = ast_cast<NormalDef_t>(comp->items.front());
+		if (!def || def->defVal) {
+			throw CompileError("invalid comprehension expression", comp->items.front());
+		}
+		auto value = def->item.get();
+		auto compInner = static_cast<CompInner_t*>(comp->items.back());
 		switch (usage) {
 			case ExpUsage::Closure:
 				pushFunctionScope();
@@ -6431,7 +6681,6 @@ private:
 		std::string lenVar = getUnusedName("_len_"sv);
 		addToScope(accumVar);
 		addToScope(lenVar);
-		auto compInner = comp->forLoop.get();
 		for (auto item : compInner->items.objects()) {
 			switch (item->get_id()) {
 				case id<CompForEach_t>():
@@ -6451,7 +6700,7 @@ private:
 		{
 			auto assignLeft = toAst<ExpList_t>(accumVar + '[' + lenVar + ']', x);
 			auto assign = x->new_ptr<Assign_t>();
-			assign->values.push_back(comp->value);
+			assign->values.push_back(value);
 			auto assignment = x->new_ptr<ExpListAssign_t>();
 			assignment->expList.set(assignLeft);
 			assignment->action.set(assign);
@@ -8830,19 +9079,7 @@ private:
 		std::string tabCheckVar;
 		for (auto branch_ : branches) {
 			auto branch = static_cast<SwitchCase_t*>(branch_);
-			if (auto inExp = branch->condition.as<In_t>()) {
-				auto unary = branch->new_ptr<UnaryExp_t>();
-				unary->expos.push_back(toAst<Value_t>(objVar, branch));
-				unary->inExp.set(inExp);
-				transformUnaryExp(unary, temp, ExpUsage::Closure);
-				temp.back() = indent() + (firstBranch ? "if "s : "elseif "s) + temp.back() + " then"s + nll(branch);
-				pushScope();
-				transform_plain_body(branch->body, temp, usage, assignList);
-				popScope();
-				firstBranch = false;
-				continue;
-			}
-			auto valueList = branch->condition.to<SwitchList_t>();
+			auto valueList = static_cast<SwitchList_t*>(branch->condition.get());
 			if (auto value = singleValueFrom(valueList);
 				value && (value->item.is<SimpleTable_t>() || value->get_by_path<SimpleValue_t, TableLit_t>())) {
 				if (!firstBranch) {
diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp
index c818098..8ceee9a 100644
--- a/src/yuescript/yue_parser.cpp
+++ b/src/yuescript/yue_parser.cpp
@@ -109,6 +109,11 @@ YueParser::YueParser() {
 		return false;
 	});
 
+	table_key_pair_error = pl::user(true_(), [](const item_t& item) {
+		throw ParserError("can not put hash pair in a list"sv, item.begin);
+		return false;
+	});
+
 	#define ensure(patt, finally) ((patt) >> (finally) | (finally) >> cut)
 
 	#define key(str) (expr(str) >> not_alpha_num)
@@ -342,7 +347,7 @@ YueParser::YueParser() {
 	with_exp = ExpList >> -(space >> Assign);
 
 	With = key("with") >> -ExistentialOp >> space >> disable_do_chain_arg_table_block_rule(with_exp) >> space >> body_with("do");
-	SwitchCase = key("when") >> space >> (disable_chain_rule(disable_arg_table_block_rule(SwitchList)) | In) >> space >> body_with("then");
+	SwitchCase = key("when") >> space >> disable_chain_rule(disable_arg_table_block_rule(SwitchList)) >> space >> body_with("then");
 	switch_else = key("else") >> space >> body;
 
 	switch_block =
@@ -449,7 +454,37 @@ YueParser::YueParser() {
 	CatchBlock = line_break >> *space_break >> check_indent_match >> space >> key("catch") >> space >> Variable >> space >> in_block;
 	Try = key("try") >> space >> (in_block | Exp) >> -CatchBlock;
 
-	Comprehension = '[' >> not_('[') >> space >> disable_for_rule(Exp) >> space >> CompInner >> space >> ']';
+	list_value =
+		and_(
+			VariablePairDef |
+			NormalPairDef |
+			MetaVariablePairDef |
+			MetaNormalPairDef
+		) >> table_key_pair_error |
+		SpreadListExp |
+		NormalDef;
+
+	list_value_list = +(space >> ',' >> space >> list_value);
+
+	list_lit_line = (
+		push_indent_match >> (space >> list_value >> -list_value_list >> pop_indent | pop_indent)
+	) | (
+		space
+	);
+
+	list_lit_lines = space_break >> list_lit_line >> *(-(space >> ',') >> space_break >> list_lit_line) >> -(space >> ',');
+
+	Comprehension = '[' >> not_('[') >>
+		Seperator >> space >> (
+			disable_for_rule(list_value) >> space >> (
+				CompInner >> space >> ']' |
+				(list_value_list >> -(space >> ',') | space >> ',')  >> -list_lit_lines >> white >> ']'
+			) |
+			list_lit_lines >> white >> ']' |
+			white >> ']' >> not_(space >> '=')
+		);
+
+	(space >> disable_for_rule(Exp) >> space >> CompInner >> space >> ']');
 	CompValue = ',' >> space >> Exp;
 	TblComprehension = and_('{') >> ('{' >> space >> disable_for_rule(Exp >> space >> -(CompValue >> space)) >> CompInner >> space >> '}' | braces_expression_error);
 
@@ -479,12 +514,11 @@ YueParser::YueParser() {
 	expo_value = exponential_operator >> *space_break >> space >> Value;
 	expo_exp = Value >> *(space >> expo_value);
 
-	InRangeOpen = true_();
-	InRangeClose = true_();
 	NotIn = true_();
-	InRange = ('(' >> InRangeOpen | '[' >> InRangeClose) >> space >> Exp >> space >> ',' >> space >> Exp >> space >> (')' >> InRangeOpen | ']' >> InRangeClose);
-	InDiscrete = '{' >> Seperator >> space >> exp_not_tab >> *(space >> ',' >> space >> exp_not_tab) >> space >> '}';
-	In = -(key("not") >> NotIn >> space) >> key("in") >> space >> (InRange | InDiscrete | and_(key("not")) >> confusing_unary_not_error | Exp);
+	InDiscrete =
+		'[' >> Seperator >> space >> exp_not_tab >> (+(space >> ',' >> space >> exp_not_tab) | space >> ',') >> space >> ']' |
+		'{' >> Seperator >> space >> exp_not_tab >> *(space >> ',' >> space >> exp_not_tab) >> space >> '}';
+	In = -(key("not") >> NotIn >> space) >> key("in") >> space >> (InDiscrete | and_(key("not")) >> confusing_unary_not_error | Exp);
 
 	UnaryOperator =
 		'-' >> not_(set(">=") | space_one) |
@@ -635,7 +669,7 @@ YueParser::YueParser() {
 		space >> ',' >>
 		space >> (Exp | DefaultValue) >>
 		space >> (',' >> space >> Exp | DefaultValue) >>
-		space >> ']';
+		space >> (']' | slice_expression_error);
 
 	Invoke = Seperator >> (
 		fn_args |
@@ -646,6 +680,7 @@ YueParser::YueParser() {
 	);
 
 	SpreadExp = "..." >> space >> Exp;
+	SpreadListExp = "..." >> space >> Exp;
 
 	table_value =
 		VariablePairDef |
@@ -667,8 +702,7 @@ YueParser::YueParser() {
 
 	TableLit =
 		space >> '{' >> Seperator >>
-		-(space >> table_value_list) >>
-		-(space >> ',') >>
+		-(space >> table_value_list >> -(space >> ',')) >>
 		-table_lit_lines >>
 		white >> '}';
 
@@ -834,7 +868,7 @@ YueParser::YueParser() {
 	});
 
 	InvokeArgs =
-		not_(set("-~")) >> space >> Seperator >> (
+		not_(set("-~") | "[]") >> space >> Seperator >> (
 			Exp >> *(space >> ',' >> space >> Exp) >> -(space >> invoke_args_with_table) |
 			arg_table_block |
 			leading_spaces_error
@@ -852,6 +886,11 @@ YueParser::YueParser() {
 		return false;
 	});
 
+	slice_expression_error = pl::user(true_(), [](const item_t& item) {
+		throw ParserError("syntax error in slice expression"sv, item.begin);
+		return false;
+	});
+
 	SimpleValue =
 		TableLit | ConstValue | If | Switch | Try | With |
 		ClassDecl | ForEach | For | While | Do |
diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h
index 7864300..8156ae2 100644
--- a/src/yuescript/yue_parser.h
+++ b/src/yuescript/yue_parser.h
@@ -136,9 +136,11 @@ private:
 	NONE_AST_RULE(indentation_error);
 	NONE_AST_RULE(braces_expression_error);
 	NONE_AST_RULE(brackets_expression_error);
+	NONE_AST_RULE(slice_expression_error);
 	NONE_AST_RULE(export_expression_error);
 	NONE_AST_RULE(invalid_interpolation_error);
 	NONE_AST_RULE(confusing_unary_not_error);
+	NONE_AST_RULE(table_key_pair_error);
 
 	NONE_AST_RULE(inc_exp_level);
 	NONE_AST_RULE(dec_exp_level);
@@ -202,6 +204,10 @@ private:
 	NONE_AST_RULE(for_key);
 	NONE_AST_RULE(for_args);
 	NONE_AST_RULE(for_in);
+	NONE_AST_RULE(list_value);
+	NONE_AST_RULE(list_value_list);
+	NONE_AST_RULE(list_lit_line);
+	NONE_AST_RULE(list_lit_lines);
 	NONE_AST_RULE(comp_clause);
 	NONE_AST_RULE(chain);
 	NONE_AST_RULE(chain_list);
@@ -352,6 +358,7 @@ private:
 	AST_RULE(ExistentialOp);
 	AST_RULE(TableAppendingOp);
 	AST_RULE(SpreadExp);
+	AST_RULE(SpreadListExp);
 	AST_RULE(TableLit);
 	AST_RULE(TableBlock);
 	AST_RULE(TableBlockIndent);
@@ -388,10 +395,7 @@ private:
 	AST_RULE(ConstValue);
 	AST_RULE(UnaryValue);
 	AST_RULE(UnaryExp);
-	AST_RULE(InRangeOpen);
-	AST_RULE(InRangeClose);
 	AST_RULE(NotIn);
-	AST_RULE(InRange);
 	AST_RULE(InDiscrete);
 	AST_RULE(In);
 	AST_RULE(ExpListAssign);
-- 
cgit v1.2.3-55-g6feb