From 6db82a69096a48c8b348217b0db6e06b297218ca Mon Sep 17 00:00:00 2001
From: Li Jin <dragon-fly@qq.com>
Date: Wed, 9 Nov 2022 11:30:17 +0800
Subject: refactor and update readme and changelog.

---
 CHANGELOG.md                   | 51 ++++++++++++++++++++++++
 doc/docs/doc/README.md         | 89 +++++++++++++++++++++++++++++++++++++-----
 src/yuescript/yue_compiler.cpp |  9 +++--
 src/yuescript/yue_parser.cpp   | 18 +++++----
 4 files changed, 148 insertions(+), 19 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 33c038b..dedd8e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,57 @@
 
 The implementation for the original Moonscript language 0.5.0 can be found in the `0.5.0` branch of Yuescript. The Moonscript with fixes and new features is in the main branch of Yuescript. Here are the changelogs for each Yuescript version.
 
+## v0.15.12
+
+### Added Features
+
+* yue.to_ast(): Reserve comment nodes followed by a statement in AST structures.
+* Added `while` clause line decorator.
+   ```moonscript
+   reader\parse_line! until reader\eof!
+   ```
+  compiles to:
+  ```lua
+   while not reader:eof() do
+     reader:parse_line()
+   end
+  ```
+* Supported underscores in number literal. `1_000_000`, `0xFF_EF_06`
+* Refactored some error messages with details instead of just "syntax error".
+* Added chaining assignment. `a = b = c = 0`
+
+### Fixed Issues
+
+* Change metable accessing operator to `<>` instead of postfix `#`.
+  ```moonscript
+  <>: mt = tb
+  mt = tb.<>
+  a = <>: mt, value: 1
+  b = :<add>, value: 2
+  close _ = <close>: -> print "out of scope"
+  ```
+  compiles to:
+  ```lua
+  local mt = getmetatable(tb)
+  mt = getmetatable(tb)
+  local a = setmetatable({
+    value = 1
+  }, mt)
+  local b = setmetatable({
+    value = 2
+  }, {
+    __add = add
+  })
+  local _ <close> = setmetatable({ }, {
+    __close = function()
+      return print("out of scope")
+    end
+  })
+  ```
+* Refactor `continue` keyword implementation with goto statement when targeting Lua version 5.2 and higher.
+* Skip utf-8 bom in parser.
+* Fixed classes don't inherits metamethods properly.
+
 ## v0.14.5
 
 ### Added Features
diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md
index f23a407..df9002d 100755
--- a/doc/docs/doc/README.md
+++ b/doc/docs/doc/README.md
@@ -671,13 +671,14 @@ tb =
 
 ### Import
 
-The import statement is a syntax sugar for requiring a module or help extracting items from an imported module.
+The import statement is a syntax sugar for requiring a module or help extracting items from an imported module. The imported items are const by default.
 
 ```moonscript
--- used as table destructure
+-- used as table destructuring
 do
-  import C, Ct, Cmt from require "lpeg"
   import insert, concat from table
+  -- report error when assigning to insert, concat
+  import C, Ct, Cmt from require "lpeg"
 
 -- shortcut for requring a module
 do
@@ -686,7 +687,7 @@ do
   import "d-a-s-h-e-s"
   import "module.part"
 
--- requring module with aliasing or table destruction
+-- requring module with aliasing or table destructuring
 do
   import "player" as PlayerModule
   import "lpeg" as :C, :Ct, :Cmt
@@ -694,10 +695,11 @@ do
 ```
 <YueDisplay>
 <pre>
--- used as table destruction
+-- used as table destructuring
 do
-  import C, Ct, Cmt from require "lpeg"
   import insert, concat from table
+  -- report error when assigning to insert, concat
+  import C, Ct, Cmt from require "lpeg"
 
 -- shortcut for requring a module
 do
@@ -706,7 +708,7 @@ do
   import "d-a-s-h-e-s"
   import "module.part"
 
--- requring module with aliasing or table destruction
+-- requring module with aliasing or table destructuring
 do
   import "player" as PlayerModule
   import "lpeg" as :C, :Ct, :Cmt
@@ -843,6 +845,20 @@ arg or= "default value"
 </pre>
 </YueDisplay>
 
+### Chaining Assignment
+
+You can do chaining assignment to assign multiple items to hold the same value.
+```moonscript
+a = b = c = d = e = 0
+x = y = z = f!
+```
+<YueDisplay>
+<pre>
+a = b = c = d = e = 0
+x = y = z = f!
+</pre>
+</YueDisplay>
+
 ### Explicit Locals
 ```moonscript
 do
@@ -1114,7 +1130,7 @@ else
 </pre>
 </YueDisplay>
 
-If assignment with extra return values.
+If assignment with multiple return values. Only the first value is getting checked, other values are scoped.
 ```moonscript
 if success, result = pcall -> "get result without problems"
   print result -- variable result is scoped
@@ -1246,7 +1262,7 @@ catch err
 
 ## Attributes
 
-The syntax support for Lua 5.4 attributes.
+Syntax support for Lua 5.4 attributes. But you can use still use `const` declaration and get constant check functioning when targeting Lua versions below 5.4.
 
 ```moonscript
 const a = 123
@@ -1284,6 +1300,22 @@ print "I am #{math.random! * 100}% sure."
 </pre>
 </YueDisplay>
 
+### Number Literals
+
+You can use underscores in a number literal to increase readability.
+
+```moonscript
+integer = 1_000_000
+hex = 0xEF_BB_BF
+```
+<YueDisplay>
+
+<pre>
+integer = 1_000_000
+hex = 0xEF_BB_BF
+</pre>
+</YueDisplay>
+
 ## Function Literals
 
 All functions are created using a function expression. A simple function is denoted using the arrow: **->**.
@@ -2302,6 +2334,21 @@ print "item: ", item for item in *items
 </pre>
 </YueDisplay>
 
+And with while loops:
+
+```moonscript
+game\update! while game\isRunning!
+
+reader\parse_line! until reader\eof!
+```
+<YueDisplay>
+<pre>
+game\update! while game\isRunning!
+
+reader\parse_line! until reader\eof!
+</pre>
+</YueDisplay>
+
 ## Switch
 
 The switch statement is shorthand for writing a series of if statements that check against the same value. Note that the value is only evaluated once. Like if statements, switches can have an else block to handle no matches. Comparison is done with the == operator.
@@ -2975,6 +3022,30 @@ with str = "Hello"
 </pre>
 </YueDisplay>
 
+Accessing special keys with `[]` in a `with` statement.
+
+```moonscript
+with tb
+  [1] = 1
+  print [2]
+  with [abc]
+    [3] = [2]\func!
+    ["key-name"] = value
+  [] = "abc" -- appending to "tb"
+```
+<YueDisplay>
+<pre>
+with tb
+  [1] = 1
+  print [2]
+  with [abc]
+    [3] = [2]\func!
+    ["key-name"] = value
+  [] = "abc" -- appending to "tb"
+</pre>
+</YueDisplay>
+
+
 ## Do
 
 When used as a statement, do works just like it does in Lua.
diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp
index 4072974..f5436d1 100644
--- a/src/yuescript/yue_compiler.cpp
+++ b/src/yuescript/yue_compiler.cpp
@@ -59,7 +59,7 @@ namespace yue {
 
 typedef std::list<std::string> str_list;
 
-const std::string_view version = "0.15.11"sv;
+const std::string_view version = "0.15.12"sv;
 const std::string_view extension = "yue"sv;
 
 class YueCompilerImpl {
@@ -3769,7 +3769,7 @@ private:
 		if (!_enableReturn.top()) {
 			ast_node* target = returnNode->valueList.get();
 			if (!target) target = returnNode;
-			throw std::logic_error(_info.errorMessage("illegal return statement here"sv, target));
+			throw std::logic_error(_info.errorMessage("can not mix use of return and export statements in module scope"sv, target));
 		}
 		if (auto valueList = returnNode->valueList.as<ExpListLow_t>()) {
 			if (valueList->exprs.size() == 1) {
@@ -8008,8 +8008,11 @@ private:
 
 	void transformChainAssign(ChainAssign_t* chainAssign, str_list& out) {
 		auto x = chainAssign;
-		str_list temp;
 		auto value = chainAssign->assign->values.front();
+		if (chainAssign->assign->values.size() != 1) {
+			throw std::logic_error(_info.errorMessage("only one right value expected"sv, value));
+		}
+		str_list temp;
 		bool constVal = false;
 		if (auto simpleVal = simpleSingleValueFrom(value)) {
 			constVal = ast_is<const_value_t, Num_t>(simpleVal->value);
diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp
index b1f29a9..c8b974f 100644
--- a/src/yuescript/yue_parser.cpp
+++ b/src/yuescript/yue_parser.cpp
@@ -532,17 +532,21 @@ YueParser::YueParser() {
 		return true;
 	}) >> (pl::user(Space >> export_default >> Exp, [](const item_t& item) {
 		State* st = reinterpret_cast<State*>(item.user_data);
-		bool isValid = !st->exportDefault && st->exportCount == 1;
+		if (st->exportDefault) {
+			throw ParserError("export default has already been declared", *item.begin, *item.end);
+		}
+		if (st->exportCount > 1) {
+			throw ParserError("there are items already been exported", *item.begin, *item.end);
+		}
 		st->exportDefault = true;
-		return isValid;
+		return true;
 	})
 	| (not_(Space >> export_default) >> pl::user(true_(), [](const item_t& item) {
 		State* st = reinterpret_cast<State*>(item.user_data);
 		if (st->exportDefault && st->exportCount > 1) {
-			return false;
-		} else {
-			return true;
+			throw ParserError("can not export more items when export default has been declared", *item.begin, *item.end);
 		}
+		return true;
 	}) >> ExpList >> -Assign)
 	| Space >> pl::user(Macro, [](const item_t& item) {
 		State* st = reinterpret_cast<State*>(item.user_data);
@@ -631,7 +635,7 @@ YueParser::YueParser() {
 		) | arg_table_block;
 
 	leading_spaces_error = pl::user(+space_one >> expr('(') >> Exp >> +(sym(',') >> Exp) >> sym(')'), [](const item_t& item) {
-		throw ParserError("write invoke arguments in parentheses without leading spaces or leading spaces without parentheses", *item.begin, *item.end);
+		throw ParserError("write invoke arguments in parentheses without leading spaces or just leading spaces without parentheses", *item.begin, *item.end);
 		return false;
 	});
 
@@ -707,7 +711,7 @@ ParseInfo YueParser::parse(std::string_view codes, rule& r) {
 		res.codes = std::make_unique<input>();
 		*(res.codes) = _converter.from_bytes(&codes.front(), &codes.back() + 1);
 	} catch (const std::range_error&) {
-		res.error = "Invalid text encoding."sv;
+		res.error = "invalid text encoding"sv;
 		return res;
 	}
 	error_list errors;
-- 
cgit v1.2.3-55-g6feb