diff options
-rw-r--r-- | CHANGELOG.md | 51 | ||||
-rwxr-xr-x | doc/docs/doc/README.md | 89 | ||||
-rw-r--r-- | src/yuescript/yue_compiler.cpp | 9 | ||||
-rw-r--r-- | 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 @@ | |||
2 | 2 | ||
3 | 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. | 3 | 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. |
4 | 4 | ||
5 | ## v0.15.12 | ||
6 | |||
7 | ### Added Features | ||
8 | |||
9 | * yue.to_ast(): Reserve comment nodes followed by a statement in AST structures. | ||
10 | * Added `while` clause line decorator. | ||
11 | ```moonscript | ||
12 | reader\parse_line! until reader\eof! | ||
13 | ``` | ||
14 | compiles to: | ||
15 | ```lua | ||
16 | while not reader:eof() do | ||
17 | reader:parse_line() | ||
18 | end | ||
19 | ``` | ||
20 | * Supported underscores in number literal. `1_000_000`, `0xFF_EF_06` | ||
21 | * Refactored some error messages with details instead of just "syntax error". | ||
22 | * Added chaining assignment. `a = b = c = 0` | ||
23 | |||
24 | ### Fixed Issues | ||
25 | |||
26 | * Change metable accessing operator to `<>` instead of postfix `#`. | ||
27 | ```moonscript | ||
28 | <>: mt = tb | ||
29 | mt = tb.<> | ||
30 | a = <>: mt, value: 1 | ||
31 | b = :<add>, value: 2 | ||
32 | close _ = <close>: -> print "out of scope" | ||
33 | ``` | ||
34 | compiles to: | ||
35 | ```lua | ||
36 | local mt = getmetatable(tb) | ||
37 | mt = getmetatable(tb) | ||
38 | local a = setmetatable({ | ||
39 | value = 1 | ||
40 | }, mt) | ||
41 | local b = setmetatable({ | ||
42 | value = 2 | ||
43 | }, { | ||
44 | __add = add | ||
45 | }) | ||
46 | local _ <close> = setmetatable({ }, { | ||
47 | __close = function() | ||
48 | return print("out of scope") | ||
49 | end | ||
50 | }) | ||
51 | ``` | ||
52 | * Refactor `continue` keyword implementation with goto statement when targeting Lua version 5.2 and higher. | ||
53 | * Skip utf-8 bom in parser. | ||
54 | * Fixed classes don't inherits metamethods properly. | ||
55 | |||
5 | ## v0.14.5 | 56 | ## v0.14.5 |
6 | 57 | ||
7 | ### Added Features | 58 | ### 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 = | |||
671 | 671 | ||
672 | ### Import | 672 | ### Import |
673 | 673 | ||
674 | The import statement is a syntax sugar for requiring a module or help extracting items from an imported module. | 674 | 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. |
675 | 675 | ||
676 | ```moonscript | 676 | ```moonscript |
677 | -- used as table destructure | 677 | -- used as table destructuring |
678 | do | 678 | do |
679 | import C, Ct, Cmt from require "lpeg" | ||
680 | import insert, concat from table | 679 | import insert, concat from table |
680 | -- report error when assigning to insert, concat | ||
681 | import C, Ct, Cmt from require "lpeg" | ||
681 | 682 | ||
682 | -- shortcut for requring a module | 683 | -- shortcut for requring a module |
683 | do | 684 | do |
@@ -686,7 +687,7 @@ do | |||
686 | import "d-a-s-h-e-s" | 687 | import "d-a-s-h-e-s" |
687 | import "module.part" | 688 | import "module.part" |
688 | 689 | ||
689 | -- requring module with aliasing or table destruction | 690 | -- requring module with aliasing or table destructuring |
690 | do | 691 | do |
691 | import "player" as PlayerModule | 692 | import "player" as PlayerModule |
692 | import "lpeg" as :C, :Ct, :Cmt | 693 | import "lpeg" as :C, :Ct, :Cmt |
@@ -694,10 +695,11 @@ do | |||
694 | ``` | 695 | ``` |
695 | <YueDisplay> | 696 | <YueDisplay> |
696 | <pre> | 697 | <pre> |
697 | -- used as table destruction | 698 | -- used as table destructuring |
698 | do | 699 | do |
699 | import C, Ct, Cmt from require "lpeg" | ||
700 | import insert, concat from table | 700 | import insert, concat from table |
701 | -- report error when assigning to insert, concat | ||
702 | import C, Ct, Cmt from require "lpeg" | ||
701 | 703 | ||
702 | -- shortcut for requring a module | 704 | -- shortcut for requring a module |
703 | do | 705 | do |
@@ -706,7 +708,7 @@ do | |||
706 | import "d-a-s-h-e-s" | 708 | import "d-a-s-h-e-s" |
707 | import "module.part" | 709 | import "module.part" |
708 | 710 | ||
709 | -- requring module with aliasing or table destruction | 711 | -- requring module with aliasing or table destructuring |
710 | do | 712 | do |
711 | import "player" as PlayerModule | 713 | import "player" as PlayerModule |
712 | import "lpeg" as :C, :Ct, :Cmt | 714 | import "lpeg" as :C, :Ct, :Cmt |
@@ -843,6 +845,20 @@ arg or= "default value" | |||
843 | </pre> | 845 | </pre> |
844 | </YueDisplay> | 846 | </YueDisplay> |
845 | 847 | ||
848 | ### Chaining Assignment | ||
849 | |||
850 | You can do chaining assignment to assign multiple items to hold the same value. | ||
851 | ```moonscript | ||
852 | a = b = c = d = e = 0 | ||
853 | x = y = z = f! | ||
854 | ``` | ||
855 | <YueDisplay> | ||
856 | <pre> | ||
857 | a = b = c = d = e = 0 | ||
858 | x = y = z = f! | ||
859 | </pre> | ||
860 | </YueDisplay> | ||
861 | |||
846 | ### Explicit Locals | 862 | ### Explicit Locals |
847 | ```moonscript | 863 | ```moonscript |
848 | do | 864 | do |
@@ -1114,7 +1130,7 @@ else | |||
1114 | </pre> | 1130 | </pre> |
1115 | </YueDisplay> | 1131 | </YueDisplay> |
1116 | 1132 | ||
1117 | If assignment with extra return values. | 1133 | If assignment with multiple return values. Only the first value is getting checked, other values are scoped. |
1118 | ```moonscript | 1134 | ```moonscript |
1119 | if success, result = pcall -> "get result without problems" | 1135 | if success, result = pcall -> "get result without problems" |
1120 | print result -- variable result is scoped | 1136 | print result -- variable result is scoped |
@@ -1246,7 +1262,7 @@ catch err | |||
1246 | 1262 | ||
1247 | ## Attributes | 1263 | ## Attributes |
1248 | 1264 | ||
1249 | The syntax support for Lua 5.4 attributes. | 1265 | 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. |
1250 | 1266 | ||
1251 | ```moonscript | 1267 | ```moonscript |
1252 | const a = 123 | 1268 | const a = 123 |
@@ -1284,6 +1300,22 @@ print "I am #{math.random! * 100}% sure." | |||
1284 | </pre> | 1300 | </pre> |
1285 | </YueDisplay> | 1301 | </YueDisplay> |
1286 | 1302 | ||
1303 | ### Number Literals | ||
1304 | |||
1305 | You can use underscores in a number literal to increase readability. | ||
1306 | |||
1307 | ```moonscript | ||
1308 | integer = 1_000_000 | ||
1309 | hex = 0xEF_BB_BF | ||
1310 | ``` | ||
1311 | <YueDisplay> | ||
1312 | |||
1313 | <pre> | ||
1314 | integer = 1_000_000 | ||
1315 | hex = 0xEF_BB_BF | ||
1316 | </pre> | ||
1317 | </YueDisplay> | ||
1318 | |||
1287 | ## Function Literals | 1319 | ## Function Literals |
1288 | 1320 | ||
1289 | All functions are created using a function expression. A simple function is denoted using the arrow: **->**. | 1321 | 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 | |||
2302 | </pre> | 2334 | </pre> |
2303 | </YueDisplay> | 2335 | </YueDisplay> |
2304 | 2336 | ||
2337 | And with while loops: | ||
2338 | |||
2339 | ```moonscript | ||
2340 | game\update! while game\isRunning! | ||
2341 | |||
2342 | reader\parse_line! until reader\eof! | ||
2343 | ``` | ||
2344 | <YueDisplay> | ||
2345 | <pre> | ||
2346 | game\update! while game\isRunning! | ||
2347 | |||
2348 | reader\parse_line! until reader\eof! | ||
2349 | </pre> | ||
2350 | </YueDisplay> | ||
2351 | |||
2305 | ## Switch | 2352 | ## Switch |
2306 | 2353 | ||
2307 | 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. | 2354 | 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" | |||
2975 | </pre> | 3022 | </pre> |
2976 | </YueDisplay> | 3023 | </YueDisplay> |
2977 | 3024 | ||
3025 | Accessing special keys with `[]` in a `with` statement. | ||
3026 | |||
3027 | ```moonscript | ||
3028 | with tb | ||
3029 | [1] = 1 | ||
3030 | print [2] | ||
3031 | with [abc] | ||
3032 | [3] = [2]\func! | ||
3033 | ["key-name"] = value | ||
3034 | [] = "abc" -- appending to "tb" | ||
3035 | ``` | ||
3036 | <YueDisplay> | ||
3037 | <pre> | ||
3038 | with tb | ||
3039 | [1] = 1 | ||
3040 | print [2] | ||
3041 | with [abc] | ||
3042 | [3] = [2]\func! | ||
3043 | ["key-name"] = value | ||
3044 | [] = "abc" -- appending to "tb" | ||
3045 | </pre> | ||
3046 | </YueDisplay> | ||
3047 | |||
3048 | |||
2978 | ## Do | 3049 | ## Do |
2979 | 3050 | ||
2980 | When used as a statement, do works just like it does in Lua. | 3051 | 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 { | |||
59 | 59 | ||
60 | typedef std::list<std::string> str_list; | 60 | typedef std::list<std::string> str_list; |
61 | 61 | ||
62 | const std::string_view version = "0.15.11"sv; | 62 | const std::string_view version = "0.15.12"sv; |
63 | const std::string_view extension = "yue"sv; | 63 | const std::string_view extension = "yue"sv; |
64 | 64 | ||
65 | class YueCompilerImpl { | 65 | class YueCompilerImpl { |
@@ -3769,7 +3769,7 @@ private: | |||
3769 | if (!_enableReturn.top()) { | 3769 | if (!_enableReturn.top()) { |
3770 | ast_node* target = returnNode->valueList.get(); | 3770 | ast_node* target = returnNode->valueList.get(); |
3771 | if (!target) target = returnNode; | 3771 | if (!target) target = returnNode; |
3772 | throw std::logic_error(_info.errorMessage("illegal return statement here"sv, target)); | 3772 | throw std::logic_error(_info.errorMessage("can not mix use of return and export statements in module scope"sv, target)); |
3773 | } | 3773 | } |
3774 | if (auto valueList = returnNode->valueList.as<ExpListLow_t>()) { | 3774 | if (auto valueList = returnNode->valueList.as<ExpListLow_t>()) { |
3775 | if (valueList->exprs.size() == 1) { | 3775 | if (valueList->exprs.size() == 1) { |
@@ -8008,8 +8008,11 @@ private: | |||
8008 | 8008 | ||
8009 | void transformChainAssign(ChainAssign_t* chainAssign, str_list& out) { | 8009 | void transformChainAssign(ChainAssign_t* chainAssign, str_list& out) { |
8010 | auto x = chainAssign; | 8010 | auto x = chainAssign; |
8011 | str_list temp; | ||
8012 | auto value = chainAssign->assign->values.front(); | 8011 | auto value = chainAssign->assign->values.front(); |
8012 | if (chainAssign->assign->values.size() != 1) { | ||
8013 | throw std::logic_error(_info.errorMessage("only one right value expected"sv, value)); | ||
8014 | } | ||
8015 | str_list temp; | ||
8013 | bool constVal = false; | 8016 | bool constVal = false; |
8014 | if (auto simpleVal = simpleSingleValueFrom(value)) { | 8017 | if (auto simpleVal = simpleSingleValueFrom(value)) { |
8015 | constVal = ast_is<const_value_t, Num_t>(simpleVal->value); | 8018 | 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() { | |||
532 | return true; | 532 | return true; |
533 | }) >> (pl::user(Space >> export_default >> Exp, [](const item_t& item) { | 533 | }) >> (pl::user(Space >> export_default >> Exp, [](const item_t& item) { |
534 | State* st = reinterpret_cast<State*>(item.user_data); | 534 | State* st = reinterpret_cast<State*>(item.user_data); |
535 | bool isValid = !st->exportDefault && st->exportCount == 1; | 535 | if (st->exportDefault) { |
536 | throw ParserError("export default has already been declared", *item.begin, *item.end); | ||
537 | } | ||
538 | if (st->exportCount > 1) { | ||
539 | throw ParserError("there are items already been exported", *item.begin, *item.end); | ||
540 | } | ||
536 | st->exportDefault = true; | 541 | st->exportDefault = true; |
537 | return isValid; | 542 | return true; |
538 | }) | 543 | }) |
539 | | (not_(Space >> export_default) >> pl::user(true_(), [](const item_t& item) { | 544 | | (not_(Space >> export_default) >> pl::user(true_(), [](const item_t& item) { |
540 | State* st = reinterpret_cast<State*>(item.user_data); | 545 | State* st = reinterpret_cast<State*>(item.user_data); |
541 | if (st->exportDefault && st->exportCount > 1) { | 546 | if (st->exportDefault && st->exportCount > 1) { |
542 | return false; | 547 | throw ParserError("can not export more items when export default has been declared", *item.begin, *item.end); |
543 | } else { | ||
544 | return true; | ||
545 | } | 548 | } |
549 | return true; | ||
546 | }) >> ExpList >> -Assign) | 550 | }) >> ExpList >> -Assign) |
547 | | Space >> pl::user(Macro, [](const item_t& item) { | 551 | | Space >> pl::user(Macro, [](const item_t& item) { |
548 | State* st = reinterpret_cast<State*>(item.user_data); | 552 | State* st = reinterpret_cast<State*>(item.user_data); |
@@ -631,7 +635,7 @@ YueParser::YueParser() { | |||
631 | ) | arg_table_block; | 635 | ) | arg_table_block; |
632 | 636 | ||
633 | leading_spaces_error = pl::user(+space_one >> expr('(') >> Exp >> +(sym(',') >> Exp) >> sym(')'), [](const item_t& item) { | 637 | leading_spaces_error = pl::user(+space_one >> expr('(') >> Exp >> +(sym(',') >> Exp) >> sym(')'), [](const item_t& item) { |
634 | throw ParserError("write invoke arguments in parentheses without leading spaces or leading spaces without parentheses", *item.begin, *item.end); | 638 | throw ParserError("write invoke arguments in parentheses without leading spaces or just leading spaces without parentheses", *item.begin, *item.end); |
635 | return false; | 639 | return false; |
636 | }); | 640 | }); |
637 | 641 | ||
@@ -707,7 +711,7 @@ ParseInfo YueParser::parse(std::string_view codes, rule& r) { | |||
707 | res.codes = std::make_unique<input>(); | 711 | res.codes = std::make_unique<input>(); |
708 | *(res.codes) = _converter.from_bytes(&codes.front(), &codes.back() + 1); | 712 | *(res.codes) = _converter.from_bytes(&codes.front(), &codes.back() + 1); |
709 | } catch (const std::range_error&) { | 713 | } catch (const std::range_error&) { |
710 | res.error = "Invalid text encoding."sv; | 714 | res.error = "invalid text encoding"sv; |
711 | return res; | 715 | return res; |
712 | } | 716 | } |
713 | error_list errors; | 717 | error_list errors; |