diff options
| author | Li Jin <dragon-fly@qq.com> | 2025-07-18 11:51:39 +0800 |
|---|---|---|
| committer | Li Jin <dragon-fly@qq.com> | 2025-07-18 11:51:39 +0800 |
| commit | 9d3d8ef2be15dfbf279de71241ff747a568e2c49 (patch) | |
| tree | 53f88fa8a0e64dfa2262898312c9b4d988876fe8 | |
| parent | 51a74726e6f68851bafccba0fe69900274a81d59 (diff) | |
| download | yuescript-9d3d8ef2be15dfbf279de71241ff747a568e2c49.tar.gz yuescript-9d3d8ef2be15dfbf279de71241ff747a568e2c49.tar.bz2 yuescript-9d3d8ef2be15dfbf279de71241ff747a568e2c49.zip | |
Added specs, tests and docs.
| -rwxr-xr-x | doc/docs/doc/README.md | 107 | ||||
| -rwxr-xr-x | doc/docs/zh/doc/README.md | 107 | ||||
| -rw-r--r-- | spec/inputs/macro.yue | 54 | ||||
| -rw-r--r-- | spec/inputs/macro_export.yue | 31 | ||||
| -rw-r--r-- | spec/inputs/macro_teal.yue | 13 | ||||
| -rw-r--r-- | spec/inputs/macro_todo.yue | 7 | ||||
| -rw-r--r-- | spec/inputs/string.yue | 73 | ||||
| -rw-r--r-- | spec/outputs/macro.lua | 13 | ||||
| -rw-r--r-- | spec/outputs/string.lua | 41 | ||||
| -rw-r--r-- | src/yuescript/yue_compiler.cpp | 46 | ||||
| -rw-r--r-- | src/yuescript/yue_parser.cpp | 4 | ||||
| -rw-r--r-- | src/yuescript/yue_parser.h | 2 |
12 files changed, 435 insertions, 63 deletions
diff --git a/doc/docs/doc/README.md b/doc/docs/doc/README.md index 7135a26..f0d67a5 100755 --- a/doc/docs/doc/README.md +++ b/doc/docs/doc/README.md | |||
| @@ -444,6 +444,54 @@ print "Valid enum type:", $BodyType Static | |||
| 444 | </pre> | 444 | </pre> |
| 445 | </YueDisplay> | 445 | </YueDisplay> |
| 446 | 446 | ||
| 447 | ### Argument Validation | ||
| 448 | |||
| 449 | You can declare the expected AST node types in the argument list, and check whether the incoming macro arguments meet the expectations at compile time. | ||
| 450 | |||
| 451 | ```moonscript | ||
| 452 | macro printNumAndStr = (num `Num, str `String) -> | | ||
| 453 | print( | ||
| 454 | #{num} | ||
| 455 | #{str} | ||
| 456 | ) | ||
| 457 | |||
| 458 | $printNumAndStr 123, "hello" | ||
| 459 | ``` | ||
| 460 | <YueDisplay> | ||
| 461 | <pre> | ||
| 462 | macro printNumAndStr = (num `Num, str `String) -> | | ||
| 463 | print( | ||
| 464 | #{num} | ||
| 465 | #{str} | ||
| 466 | ) | ||
| 467 | |||
| 468 | $printNumAndStr 123, "hello" | ||
| 469 | </pre> | ||
| 470 | </YueDisplay> | ||
| 471 | |||
| 472 | If you need more flexible argument checking, you can use the built-in `$is_ast` macro function to manually check at the appropriate place. | ||
| 473 | |||
| 474 | ```moonscript | ||
| 475 | macro printNumAndStr = (num, str) -> | ||
| 476 | error "expected Num as first argument" unless $is_ast Num, num | ||
| 477 | error "expected String as second argument" unless $is_ast String, str | ||
| 478 | "print(#{num}, #{str})" | ||
| 479 | |||
| 480 | $printNumAndStr 123, "hello" | ||
| 481 | ``` | ||
| 482 | <YueDisplay> | ||
| 483 | <pre> | ||
| 484 | macro printNumAndStr = (num, str) -> | ||
| 485 | error "expected Num as first argument" unless $is_ast Num, num | ||
| 486 | error "expected String as second argument" unless $is_ast String, str | ||
| 487 | "print(#{num}, #{str})" | ||
| 488 | |||
| 489 | $printNumAndStr 123, "hello" | ||
| 490 | </pre> | ||
| 491 | </YueDisplay> | ||
| 492 | |||
| 493 | For more details about available AST nodes, please refer to the uppercased definitions in [yue_parser.cpp](https://github.com/IppClub/YueScript/blob/main/src/yuescript/yue_parser.cpp). | ||
| 494 | |||
| 447 | ## Operator | 495 | ## Operator |
| 448 | 496 | ||
| 449 | All of Lua's binary and unary operators are available. Additionally **!=** is as an alias for **~=**, and either **\\** or **::** can be used to write a chaining function call like `tb\func!` or `tb::func!`. And Yuescipt offers some other special operators to write more expressive codes. | 497 | All of Lua's binary and unary operators are available. Additionally **!=** is as an alias for **~=**, and either **\\** or **::** can be used to write a chaining function call like `tb\func!` or `tb::func!`. And Yuescipt offers some other special operators to write more expressive codes. |
| @@ -1702,6 +1750,65 @@ binary = 0B10011 | |||
| 1702 | </pre> | 1750 | </pre> |
| 1703 | </YueDisplay> | 1751 | </YueDisplay> |
| 1704 | 1752 | ||
| 1753 | ### YAML Multiline String | ||
| 1754 | |||
| 1755 | The `|` prefix introduces a YAML-style multiline string literal: | ||
| 1756 | |||
| 1757 | ```moonscript | ||
| 1758 | str = | | ||
| 1759 | key: value | ||
| 1760 | list: | ||
| 1761 | - item1 | ||
| 1762 | - #{expr} | ||
| 1763 | ``` | ||
| 1764 | <YueDisplay> | ||
| 1765 | <pre> | ||
| 1766 | str = | | ||
| 1767 | key: value | ||
| 1768 | list: | ||
| 1769 | - item1 | ||
| 1770 | - #{expr} | ||
| 1771 | </pre> | ||
| 1772 | </YueDisplay> | ||
| 1773 | |||
| 1774 | This allows writing structured multiline text conveniently. All line breaks and indentation are preserved relative to the first non-empty line, and expressions inside `#{...}` are interpolated automatically as `tostring(expr)`. | ||
| 1775 | |||
| 1776 | YAML Multiline String automatically detects the common leading whitespace prefix (minimum indentation across all non-empty lines) and removes it from all lines. This makes it easy to indent your code visually without affecting the resulting string content. | ||
| 1777 | |||
| 1778 | ```moonscript | ||
| 1779 | fn = -> | ||
| 1780 | str = | | ||
| 1781 | foo: | ||
| 1782 | bar: baz | ||
| 1783 | return str | ||
| 1784 | ``` | ||
| 1785 | <YueDisplay> | ||
| 1786 | <pre> | ||
| 1787 | fn = -> | ||
| 1788 | str = | | ||
| 1789 | foo: | ||
| 1790 | bar: baz | ||
| 1791 | return str | ||
| 1792 | </pre> | ||
| 1793 | </YueDisplay> | ||
| 1794 | |||
| 1795 | Internal indentation is preserved relative to the removed common prefix, allowing clean nested structures. | ||
| 1796 | |||
| 1797 | All special characters like quotes (`"`) and backslashes (`\`) in the YAMLMultiline block are automatically escaped so that the generated Lua string is syntactically valid and behaves as expected. | ||
| 1798 | |||
| 1799 | ```moonscript | ||
| 1800 | str = | | ||
| 1801 | path: "C:\Program Files\App" | ||
| 1802 | note: 'He said: "#{Hello}!"' | ||
| 1803 | ``` | ||
| 1804 | <YueDisplay> | ||
| 1805 | <pre> | ||
| 1806 | str = | | ||
| 1807 | path: "C:\Program Files\App" | ||
| 1808 | note: 'He said: "#{Hello}!"' | ||
| 1809 | </pre> | ||
| 1810 | </YueDisplay> | ||
| 1811 | |||
| 1705 | ## Function Literals | 1812 | ## Function Literals |
| 1706 | 1813 | ||
| 1707 | All functions are created using a function expression. A simple function is denoted using the arrow: **->**. | 1814 | All functions are created using a function expression. A simple function is denoted using the arrow: **->**. |
diff --git a/doc/docs/zh/doc/README.md b/doc/docs/zh/doc/README.md index 61b746c..15f4768 100755 --- a/doc/docs/zh/doc/README.md +++ b/doc/docs/zh/doc/README.md | |||
| @@ -442,6 +442,54 @@ print "有效的枚举类型:", $BodyType Static | |||
| 442 | </pre> | 442 | </pre> |
| 443 | </YueDisplay> | 443 | </YueDisplay> |
| 444 | 444 | ||
| 445 | ### 宏参数检查 | ||
| 446 | |||
| 447 | 可以直接在参数列表中声明期望的 AST 节点类型,并在编译时检查传入的宏参数是否符合预期。 | ||
| 448 | |||
| 449 | ```moonscript | ||
| 450 | macro printNumAndStr = (num `Num, str `String) -> | | ||
| 451 | print( | ||
| 452 | #{num} | ||
| 453 | #{str} | ||
| 454 | ) | ||
| 455 | |||
| 456 | $printNumAndStr 123, "hello" | ||
| 457 | ``` | ||
| 458 | <YueDisplay> | ||
| 459 | <pre> | ||
| 460 | macro printNumAndStr = (num `Num, str `String) -> | | ||
| 461 | print( | ||
| 462 | #{num} | ||
| 463 | #{str} | ||
| 464 | ) | ||
| 465 | |||
| 466 | $printNumAndStr 123, "hello" | ||
| 467 | </pre> | ||
| 468 | </YueDisplay> | ||
| 469 | |||
| 470 | 如果需要做更加灵活的参数检查操作,可以使用内置的 `$is_ast` 宏函数在合适的位置进行手动检查。 | ||
| 471 | |||
| 472 | ```moonscript | ||
| 473 | macro printNumAndStr = (num, str) -> | ||
| 474 | error "expected Num as first argument" unless $is_ast Num, num | ||
| 475 | error "expected String as second argument" unless $is_ast String, str | ||
| 476 | "print(#{num}, #{str})" | ||
| 477 | |||
| 478 | $printNumAndStr 123, "hello" | ||
| 479 | ``` | ||
| 480 | <YueDisplay> | ||
| 481 | <pre> | ||
| 482 | macro printNumAndStr = (num, str) -> | ||
| 483 | error "expected Num as first argument" unless $is_ast Num, num | ||
| 484 | error "expected String as second argument" unless $is_ast String, str | ||
| 485 | "print(#{num}, #{str})" | ||
| 486 | |||
| 487 | $printNumAndStr 123, "hello" | ||
| 488 | </pre> | ||
| 489 | </YueDisplay> | ||
| 490 | |||
| 491 | 更多关于可用 AST 节点的详细信息,请参考 [yue_parser.cpp](https://github.com/IppClub/YueScript/blob/main/src/yuescript/yue_parser.cpp) 中大写的规则定义。 | ||
| 492 | |||
| 445 | ## 操作符 | 493 | ## 操作符 |
| 446 | 494 | ||
| 447 | Lua的所有二元和一元操作符在月之脚本中都是可用的。此外,**!=** 符号是 **~=** 的别名,而 **\\** 或 **::** 均可用于编写链式函数调用,如写作 `tb\func!` 或 `tb::func!`。此外月之脚本还提供了一些其他特殊的操作符,以编写更具表达力的代码。 | 495 | Lua的所有二元和一元操作符在月之脚本中都是可用的。此外,**!=** 符号是 **~=** 的别名,而 **\\** 或 **::** 均可用于编写链式函数调用,如写作 `tb\func!` 或 `tb::func!`。此外月之脚本还提供了一些其他特殊的操作符,以编写更具表达力的代码。 |
| @@ -1700,6 +1748,65 @@ binary = 0B10011 | |||
| 1700 | </pre> | 1748 | </pre> |
| 1701 | </YueDisplay> | 1749 | </YueDisplay> |
| 1702 | 1750 | ||
| 1751 | ### YAML 风格字符串 | ||
| 1752 | |||
| 1753 | 使用 `|` 前缀标记一个多行 YAML 风格字符串: | ||
| 1754 | |||
| 1755 | ```moonscript | ||
| 1756 | str = | | ||
| 1757 | key: value | ||
| 1758 | list: | ||
| 1759 | - item1 | ||
| 1760 | - #{expr} | ||
| 1761 | ``` | ||
| 1762 | <YueDisplay> | ||
| 1763 | <pre> | ||
| 1764 | str = | | ||
| 1765 | key: value | ||
| 1766 | list: | ||
| 1767 | - item1 | ||
| 1768 | - #{expr} | ||
| 1769 | </pre> | ||
| 1770 | </YueDisplay> | ||
| 1771 | |||
| 1772 | 其效果类似于原生 Lua 的多行拼接,所有文本(含换行)将被保留下来,并支持 `#{...}` 语法,通过 `tostring(expr)` 插入表达式结果。 | ||
| 1773 | |||
| 1774 | YAML 风格的多行字符串会自动检测首行后最小的公共缩进,并从所有行中删除该前缀空白字符。这让你可以在代码中对齐文本,但输出字符串不会带多余缩进。 | ||
| 1775 | |||
| 1776 | ```moonscript | ||
| 1777 | fn = -> | ||
| 1778 | str = | | ||
| 1779 | foo: | ||
| 1780 | bar: baz | ||
| 1781 | return str | ||
| 1782 | ``` | ||
| 1783 | <YueDisplay> | ||
| 1784 | <pre> | ||
| 1785 | fn = -> | ||
| 1786 | str = | | ||
| 1787 | foo: | ||
| 1788 | bar: baz | ||
| 1789 | return str | ||
| 1790 | </pre> | ||
| 1791 | </YueDisplay> | ||
| 1792 | |||
| 1793 | 输出字符串中的 foo: 对齐到行首,不会带有函数缩进空格。保留内部缩进的相对结构,适合书写结构化嵌套样式的内容。 | ||
| 1794 | |||
| 1795 | 支持自动处理字符中的引号、反斜杠等特殊符号,无需手动转义: | ||
| 1796 | |||
| 1797 | ```moonscript | ||
| 1798 | str = | | ||
| 1799 | path: "C:\Program Files\App" | ||
| 1800 | note: 'He said: "#{Hello}!"' | ||
| 1801 | ``` | ||
| 1802 | <YueDisplay> | ||
| 1803 | <pre> | ||
| 1804 | str = | | ||
| 1805 | path: "C:\Program Files\App" | ||
| 1806 | note: 'He said: "#{Hello}!"' | ||
| 1807 | </pre> | ||
| 1808 | </YueDisplay> | ||
| 1809 | |||
| 1703 | ## 函数字面量 | 1810 | ## 函数字面量 |
| 1704 | 1811 | ||
| 1705 | 所有函数都是使用月之脚本的函数表达式创建的。一个简单的函数可以用箭头表示为:**->**。 | 1812 | 所有函数都是使用月之脚本的函数表达式创建的。一个简单的函数可以用箭头表示为:**->**。 |
diff --git a/spec/inputs/macro.yue b/spec/inputs/macro.yue index 5d5f1a9..191f09f 100644 --- a/spec/inputs/macro.yue +++ b/spec/inputs/macro.yue | |||
| @@ -60,6 +60,11 @@ macro NumAndStr = (num, str) -> | |||
| 60 | 60 | ||
| 61 | print $NumAndStr 123, 'xyz' | 61 | print $NumAndStr 123, 'xyz' |
| 62 | 62 | ||
| 63 | macro NumAndStr2 = (num`Num, str`SingleString) -> | | ||
| 64 | [#{num}, #{str}] | ||
| 65 | |||
| 66 | print $NumAndStr2 456, 'abc' | ||
| 67 | |||
| 63 | $asserts item == nil | 68 | $asserts item == nil |
| 64 | 69 | ||
| 65 | $myconfig false | 70 | $myconfig false |
| @@ -100,13 +105,14 @@ macro filter = (items, action)-> | |||
| 100 | $showMacro "filter", "[_ for _ in *#{items} when #{action}]" | 105 | $showMacro "filter", "[_ for _ in *#{items} when #{action}]" |
| 101 | 106 | ||
| 102 | macro reduce = (items, def, action)-> | 107 | macro reduce = (items, def, action)-> |
| 103 | $showMacro "reduce", "if ##{items} == 0 | 108 | $showMacro "reduce", | |
| 104 | #{def} | 109 | if ##{items} == 0 |
| 105 | else | 110 | #{def} |
| 106 | _1 = #{def} | 111 | else |
| 107 | for _2 in *#{items} | 112 | _1 = #{def} |
| 108 | _1 = #{action} | 113 | for _2 in *#{items} |
| 109 | _1" | 114 | _1 = #{action} |
| 115 | _1 | ||
| 110 | 116 | ||
| 111 | macro foreach = (items, action)-> | 117 | macro foreach = (items, action)-> |
| 112 | $showMacro "foreach", "for _ in *#{items} | 118 | $showMacro "foreach", "for _ in *#{items} |
| @@ -154,13 +160,15 @@ macro curry = (...)-> | |||
| 154 | f = $curry x,y,z,do | 160 | f = $curry x,y,z,do |
| 155 | print x,y,z | 161 | print x,y,z |
| 156 | 162 | ||
| 157 | macro get_inner = (var)-> "do | 163 | macro get_inner = (var)-> | |
| 158 | a = 1 | 164 | do |
| 159 | a + 1" | 165 | a = 1 |
| 166 | a + 1 | ||
| 160 | 167 | ||
| 161 | macro get_inner_hygienic = (var)-> "(-> | 168 | macro get_inner_hygienic = (var)-> | |
| 162 | local a = 1 | 169 | (-> |
| 163 | a + 1)!" | 170 | local a = 1 |
| 171 | a + 1)! | ||
| 164 | 172 | ||
| 165 | do | 173 | do |
| 166 | a = 8 | 174 | a = 8 |
| @@ -196,6 +204,18 @@ end | |||
| 196 | 204 | ||
| 197 | print x | 205 | print x |
| 198 | 206 | ||
| 207 | import "yue" | ||
| 208 | macro lua = (code`YAMLMultiline) -> { | ||
| 209 | code: yue.loadstring(code)! | ||
| 210 | type: "lua" | ||
| 211 | } | ||
| 212 | |||
| 213 | $lua | | ||
| 214 | local function f2(a) | ||
| 215 | return a + 1 | ||
| 216 | end | ||
| 217 | x = x + f2(3) | ||
| 218 | |||
| 199 | macro def = (fname, ...)-> | 219 | macro def = (fname, ...)-> |
| 200 | args = {...} | 220 | args = {...} |
| 201 | last = table.remove args | 221 | last = table.remove args |
| @@ -317,7 +337,13 @@ $chainC( | |||
| 317 | Destroy! | 337 | Destroy! |
| 318 | ) | 338 | ) |
| 319 | 339 | ||
| 320 | macro tb = -> "{'abc', a:123, <call>:=> 998}" | 340 | macro tb = -> | |
| 341 | { | ||
| 342 | 'abc' | ||
| 343 | a: 123 | ||
| 344 | <call>: => 998 | ||
| 345 | } | ||
| 346 | |||
| 321 | print $tb[1], $tb.a, ($tb)!, $tb! | 347 | print $tb[1], $tb.a, ($tb)!, $tb! |
| 322 | 348 | ||
| 323 | print "current line: #{ $LINE }" | 349 | print "current line: #{ $LINE }" |
diff --git a/spec/inputs/macro_export.yue b/spec/inputs/macro_export.yue index 75fd813..22905b5 100644 --- a/spec/inputs/macro_export.yue +++ b/spec/inputs/macro_export.yue | |||
| @@ -8,13 +8,12 @@ export macro config = (debugging = true)-> | |||
| 8 | "" | 8 | "" |
| 9 | 9 | ||
| 10 | export macro showMacro = (name, res)-> | 10 | export macro showMacro = (name, res)-> |
| 11 | if debugMacro then " | 11 | if debugMacro then | |
| 12 | do | 12 | do |
| 13 | txt = #{res} | 13 | txt = #{res} |
| 14 | print '[macro ' .. #{name} .. ']' | 14 | print '[macro #{name}]' |
| 15 | print txt | 15 | print txt |
| 16 | txt | 16 | txt |
| 17 | " | ||
| 18 | else | 17 | else |
| 19 | res | 18 | res |
| 20 | 19 | ||
| @@ -35,14 +34,16 @@ export macro copy = (src, dst, ...)-> | |||
| 35 | src != "_src_" and src != "_dst_" and dst != "_src_" and dst != "_dst_" | 34 | src != "_src_" and src != "_dst_" and dst != "_src_" and dst != "_dst_" |
| 36 | "copy targets can not be _src_ or _dst_" | 35 | "copy targets can not be _src_ or _dst_" |
| 37 | ) | 36 | ) |
| 38 | " | 37 | copyFields = table.concat( |
| 39 | do | 38 | ["_dst_.#{field} = _src_.#{field}" for field in *{...}] |
| 40 | local _src_, _dst_ | 39 | "\n\t\t\t" |
| 41 | with _dst_ := #{dst} | 40 | ) |
| 42 | with _src_ := #{src} | 41 | | |
| 43 | #{table.concat for field in *{...} do " | 42 | do |
| 44 | _dst_.#{field} = _src_.#{field} | 43 | local _src_, _dst_ |
| 45 | "}" | 44 | with _dst_ := #{dst} |
| 45 | with _src_ := #{src} | ||
| 46 | #{copyFields} | ||
| 46 | 47 | ||
| 47 | export macro enum = (...) -> | 48 | export macro enum = (...) -> |
| 48 | items = {...} | 49 | items = {...} |
diff --git a/spec/inputs/macro_teal.yue b/spec/inputs/macro_teal.yue index 0cfd862..e51bcd7 100644 --- a/spec/inputs/macro_teal.yue +++ b/spec/inputs/macro_teal.yue | |||
| @@ -4,11 +4,16 @@ $ -> | |||
| 4 | options.target_extension = "tl" | 4 | options.target_extension = "tl" |
| 5 | package.path ..= ";./spec/lib/?.lua" | 5 | package.path ..= ";./spec/lib/?.lua" |
| 6 | 6 | ||
| 7 | macro to_lua = (code)-> | 7 | macro to_lua = (code)-> | |
| 8 | "require('yue').to_lua(#{code}, reserve_line_number:false, same_module:true)" | 8 | require('yue').to_lua #{code}, |
| 9 | reserve_line_number: false | ||
| 10 | same_module: true | ||
| 9 | 11 | ||
| 10 | macro trim = (name)-> | 12 | macro trim = (name)-> | |
| 11 | "if result := #{name}\\match '[\\'\"](.*)[\\'\"]' then result else #{name}" | 13 | if result := #{name}\match '[\'"](.*)[\'"]' |
| 14 | result | ||
| 15 | else | ||
| 16 | #{name} | ||
| 12 | 17 | ||
| 13 | export macro local = (decl, value = nil)-> | 18 | export macro local = (decl, value = nil)-> |
| 14 | import "yue" as {options:{:tl_enabled}} | 19 | import "yue" as {options:{:tl_enabled}} |
diff --git a/spec/inputs/macro_todo.yue b/spec/inputs/macro_todo.yue index 752c9cb..c9c8f77 100644 --- a/spec/inputs/macro_todo.yue +++ b/spec/inputs/macro_todo.yue | |||
| @@ -5,9 +5,6 @@ export macro todoInner = (module, line, msg)-> | |||
| 5 | type: "lua" | 5 | type: "lua" |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | export macro todo = (msg)-> | 8 | export macro todo = (msg)-> | |
| 9 | if msg | 9 | $todoInner $FILE, $LINE#{msg and ", #{msg}" or ""} |
| 10 | "$todoInner $FILE, $LINE, #{msg}" | ||
| 11 | else | ||
| 12 | "$todoInner $FILE, $LINE" | ||
| 13 | 10 | ||
diff --git a/spec/inputs/string.yue b/spec/inputs/string.yue index f91383e..1f0fba8 100644 --- a/spec/inputs/string.yue +++ b/spec/inputs/string.yue | |||
| @@ -74,3 +74,76 @@ _ = "hello" | |||
| 74 | something"hello"\world! | 74 | something"hello"\world! |
| 75 | something "hello"\world! | 75 | something "hello"\world! |
| 76 | 76 | ||
| 77 | do | ||
| 78 | str = | | ||
| 79 | key: value | ||
| 80 | str = | | ||
| 81 | config: | ||
| 82 | enabled: true | ||
| 83 | level: 5 | ||
| 84 | str = | | ||
| 85 | header: start | ||
| 86 | |||
| 87 | footer: end | ||
| 88 | str = | | ||
| 89 | name: #{username} | ||
| 90 | str = | | ||
| 91 | count: #{total} items | ||
| 92 | str = | | ||
| 93 | user: #{name} | ||
| 94 | id: #{id} | ||
| 95 | str = | | ||
| 96 | path: "C:\\Program Files\\App" | ||
| 97 | desc: 'single "quote" test' | ||
| 98 | str = | | ||
| 99 | key: value | ||
| 100 | next: 123 | ||
| 101 | str = | | ||
| 102 | list: | ||
| 103 | - "one" | ||
| 104 | - "two" | ||
| 105 | str = | | ||
| 106 | -- comment | ||
| 107 | content text | ||
| 108 | -- comment | ||
| 109 | str = | | ||
| 110 | #{1 + 2} | ||
| 111 | #{2 + 3} | ||
| 112 | #{"a" .. "b"} | ||
| 113 | obj = | ||
| 114 | settings: | | ||
| 115 | mode: #{mode} | ||
| 116 | flags: | ||
| 117 | - #{flag1} | ||
| 118 | - default | ||
| 119 | fn = -> | | ||
| 120 | Hello | ||
| 121 | name: #{userName} | ||
| 122 | str = | | ||
| 123 | result: | ||
| 124 | status: #{if ok then "pass" else "fail"} | ||
| 125 | code: #{code} | ||
| 126 | summary = | | ||
| 127 | date: #{os.date()} | ||
| 128 | values: | ||
| 129 | - | ||
| 130 | a: #{aVal} | ||
| 131 | b: #{bVal or defaultB} | ||
| 132 | msg = send | | ||
| 133 | Hello, #{user}! | ||
| 134 | Today is #{os.date("%A")}. | ||
| 135 | desc = do | ||
| 136 | prefix = "Result" | ||
| 137 | | | ||
| 138 | #{prefix}: | ||
| 139 | value: #{compute!} | ||
| 140 | (| | ||
| 141 | 1 | ||
| 142 | 2 | ||
| 143 | 3 | ||
| 144 | |||
| 145 | |||
| 146 | export yaml = | | ||
| 147 | version: #{ver} | ||
| 148 | ok: true | ||
| 149 | |||
diff --git a/spec/outputs/macro.lua b/spec/outputs/macro.lua index 9f5507c..89c6e63 100644 --- a/spec/outputs/macro.lua +++ b/spec/outputs/macro.lua | |||
| @@ -26,6 +26,10 @@ print({ | |||
| 26 | 123, | 26 | 123, |
| 27 | 'xyz' | 27 | 'xyz' |
| 28 | }) | 28 | }) |
| 29 | print({ | ||
| 30 | 456, | ||
| 31 | 'abc' | ||
| 32 | }) | ||
| 29 | do | 33 | do |
| 30 | assert(item == nil) | 34 | assert(item == nil) |
| 31 | end | 35 | end |
| @@ -213,6 +217,13 @@ function tb:func() | |||
| 213 | end | 217 | end |
| 214 | end | 218 | end |
| 215 | print(x) | 219 | print(x) |
| 220 | local yue = require("yue") | ||
| 221 | do | ||
| 222 | local function f2(a) | ||
| 223 | return a + 1 | ||
| 224 | end | ||
| 225 | x = x + f2(3) | ||
| 226 | end | ||
| 216 | local sel | 227 | local sel |
| 217 | sel = function(a, b, c) | 228 | sel = function(a, b, c) |
| 218 | if a then | 229 | if a then |
| @@ -317,7 +328,7 @@ print((setmetatable({ | |||
| 317 | return 998 | 328 | return 998 |
| 318 | end | 329 | end |
| 319 | })) | 330 | })) |
| 320 | print("current line: " .. tostring(323)) | 331 | print("current line: " .. tostring(349)) |
| 321 | do | 332 | do |
| 322 | do | 333 | do |
| 323 | -- TODO | 334 | -- TODO |
diff --git a/spec/outputs/string.lua b/spec/outputs/string.lua index febea62..b536e6d 100644 --- a/spec/outputs/string.lua +++ b/spec/outputs/string.lua | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | local _module_0 = { } | ||
| 1 | local hi = "hello" | 2 | local hi = "hello" |
| 2 | local hello = "what the heckyes" | 3 | local hello = "what the heckyes" |
| 3 | print(hi) | 4 | print(hi) |
| @@ -41,4 +42,42 @@ local _ = "hello"; | |||
| 41 | ("hello"):format().hello(1, 2, 3); | 42 | ("hello"):format().hello(1, 2, 3); |
| 42 | ("hello"):format(1, 2, 3) | 43 | ("hello"):format(1, 2, 3) |
| 43 | something("hello"):world() | 44 | something("hello"):world() |
| 44 | return something(("hello"):world()) | 45 | something(("hello"):world()) |
| 46 | do | ||
| 47 | local str = "key: value" | ||
| 48 | str = "config:\n\tenabled: true\n\tlevel: 5" | ||
| 49 | str = "header: start\nfooter: end" | ||
| 50 | str = "name: " .. tostring(username) | ||
| 51 | str = "count: " .. tostring(total) .. " items" | ||
| 52 | str = "user: " .. tostring(name) .. "\nid: " .. tostring(id) | ||
| 53 | str = "path: \"C:\\\\Program Files\\\\App\"\ndesc: 'single \"quote\" test'" | ||
| 54 | str = "key: value \nnext: 123 " | ||
| 55 | str = "list:\n - \"one\"\n - \"two\"" | ||
| 56 | str = "-- comment\ncontent text\n-- comment" | ||
| 57 | str = tostring(1 + 2) .. '\n' .. tostring(2 + 3) .. '\n' .. tostring("a" .. "b") | ||
| 58 | local obj = { | ||
| 59 | settings = "mode: " .. tostring(mode) .. "\nflags:\n\t- " .. tostring(flag1) .. "\n\t- default" | ||
| 60 | } | ||
| 61 | local fn | ||
| 62 | fn = function() | ||
| 63 | return "Hello\nname: " .. tostring(userName) | ||
| 64 | end | ||
| 65 | str = "result:\n\tstatus: " .. tostring((function() | ||
| 66 | if ok then | ||
| 67 | return "pass" | ||
| 68 | else | ||
| 69 | return "fail" | ||
| 70 | end | ||
| 71 | end)()) .. "\n\tcode: " .. tostring(code) | ||
| 72 | local summary = "date: " .. tostring(os.date()) .. "\nvalues:\n\t-\n\t\ta: " .. tostring(aVal) .. "\n\t\tb: " .. tostring(bVal or defaultB) | ||
| 73 | local msg = send("Hello, " .. tostring(user) .. "!\nToday is " .. tostring(os.date("%A")) .. ".") | ||
| 74 | local desc | ||
| 75 | do | ||
| 76 | local prefix = "Result" | ||
| 77 | desc = tostring(prefix) .. ":\nvalue: " .. tostring(compute()) | ||
| 78 | end | ||
| 79 | print(("1\n2\n3")) | ||
| 80 | end | ||
| 81 | local yaml = "version: " .. tostring(ver) .. "\nok: true" | ||
| 82 | _module_0["yaml"] = yaml | ||
| 83 | return _module_0 | ||
diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 3768fc9..a8d222d 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp | |||
| @@ -5440,7 +5440,7 @@ private: | |||
| 5440 | if (def->defaultValue) { | 5440 | if (def->defaultValue) { |
| 5441 | defVal = _parser.toString(def->defaultValue); | 5441 | defVal = _parser.toString(def->defaultValue); |
| 5442 | Utils::trim(defVal); | 5442 | Utils::trim(defVal); |
| 5443 | defVal = '=' + Utils::toLuaString(defVal); | 5443 | defVal = '=' + Utils::toLuaDoubleString(defVal); |
| 5444 | } | 5444 | } |
| 5445 | newArgs.emplace_back(_parser.toString(def->name) + defVal); | 5445 | newArgs.emplace_back(_parser.toString(def->name) + defVal); |
| 5446 | } | 5446 | } |
| @@ -9185,15 +9185,34 @@ private: | |||
| 9185 | auto line = static_cast<YAMLLine_t*>(line_); | 9185 | auto line = static_cast<YAMLLine_t*>(line_); |
| 9186 | if (!line->segments.empty()) { | 9186 | if (!line->segments.empty()) { |
| 9187 | str_list segs; | 9187 | str_list segs; |
| 9188 | bool firstSeg = true; | ||
| 9188 | for (auto seg_ : line->segments.objects()) { | 9189 | for (auto seg_ : line->segments.objects()) { |
| 9189 | auto content = static_cast<YAMLLineContent_t*>(seg_)->content.get(); | 9190 | auto content = static_cast<YAMLLineContent_t*>(seg_)->content.get(); |
| 9190 | switch (content->get_id()) { | 9191 | switch (content->get_id()) { |
| 9191 | case id<YAMLLineInner_t>(): { | 9192 | case id<YAMLLineInner_t>(): { |
| 9192 | auto str = _parser.toString(content); | 9193 | auto seg = _parser.toString(content); |
| 9193 | Utils::replace(str, "\r\n"sv, "\n"sv); | 9194 | if (!indent) { |
| 9194 | Utils::replace(str, "\n"sv, "\\n"sv); | 9195 | auto pos = seg.find_first_not_of("\t "sv); |
| 9195 | Utils::replace(str, "\\#"sv, "#"sv); | 9196 | if (pos == std::string::npos) { |
| 9196 | segs.push_back('\"' + str + '\"'); | 9197 | indent = seg; |
| 9198 | firstSeg = false; | ||
| 9199 | continue; | ||
| 9200 | } else { | ||
| 9201 | indent = std::string{seg.c_str(), pos}; | ||
| 9202 | } | ||
| 9203 | } | ||
| 9204 | if (firstSeg) { | ||
| 9205 | firstSeg = false; | ||
| 9206 | if (std::string_view{seg}.substr(0, indent.value().size()) != indent.value()) { | ||
| 9207 | throw CompileError("inconsistent indent"sv, line); | ||
| 9208 | } | ||
| 9209 | auto seqStr = seg.substr(indent.value().size()); | ||
| 9210 | if (!seqStr.empty()) { | ||
| 9211 | segs.push_back(Utils::toLuaDoubleString(seqStr)); | ||
| 9212 | } | ||
| 9213 | } else { | ||
| 9214 | segs.push_back(Utils::toLuaDoubleString(seg)); | ||
| 9215 | } | ||
| 9197 | break; | 9216 | break; |
| 9198 | } | 9217 | } |
| 9199 | case id<Exp_t>(): { | 9218 | case id<Exp_t>(): { |
| @@ -9204,20 +9223,7 @@ private: | |||
| 9204 | default: YUEE("AST node mismatch", content); break; | 9223 | default: YUEE("AST node mismatch", content); break; |
| 9205 | } | 9224 | } |
| 9206 | } | 9225 | } |
| 9207 | auto lineStr = join(segs, " .. "sv); | 9226 | temp.push_back(join(segs, " .. "sv)); |
| 9208 | if (!indent) { | ||
| 9209 | auto pos = lineStr.find_first_not_of("\t "sv, 1); | ||
| 9210 | if (pos == std::string::npos) { | ||
| 9211 | throw CompileError("expecting first line indent"sv, line); | ||
| 9212 | } | ||
| 9213 | indent = std::string{lineStr.c_str(), pos}; | ||
| 9214 | } else { | ||
| 9215 | if (std::string_view{lineStr}.substr(0, indent.value().size()) != indent.value()) { | ||
| 9216 | throw CompileError("inconsistent indent"sv, line); | ||
| 9217 | } | ||
| 9218 | } | ||
| 9219 | lineStr = '"' + lineStr.substr(indent.value().size()); | ||
| 9220 | temp.push_back(lineStr); | ||
| 9221 | } | 9227 | } |
| 9222 | } | 9228 | } |
| 9223 | auto str = join(temp, " .. '\\n' .. "sv); | 9229 | auto str = join(temp, " .. '\\n' .. "sv); |
diff --git a/src/yuescript/yue_parser.cpp b/src/yuescript/yue_parser.cpp index eebc676..2e21a52 100644 --- a/src/yuescript/yue_parser.cpp +++ b/src/yuescript/yue_parser.cpp | |||
| @@ -645,7 +645,7 @@ YueParser::YueParser() { | |||
| 645 | YAMLLine = check_indent_match >> Seperator >> +YAMLLineContent | | 645 | YAMLLine = check_indent_match >> Seperator >> +YAMLLineContent | |
| 646 | advance_match >> Seperator >> ensure(+YAMLLineContent, pop_indent) | | 646 | advance_match >> Seperator >> ensure(+YAMLLineContent, pop_indent) | |
| 647 | Seperator >> *set(" \t") >> and_(line_break); | 647 | Seperator >> *set(" \t") >> and_(line_break); |
| 648 | YAMLMultiline = '|' >> Seperator >> +space_break >> advance_match >> ensure(YAMLLine >> *(*set(" \t") >> line_break >> YAMLLine), pop_indent); | 648 | YAMLMultiline = '|' >> space >> Seperator >> +(*set(" \t") >> line_break) >> advance_match >> ensure(YAMLLine >> *(*set(" \t") >> line_break >> YAMLLine), pop_indent); |
| 649 | 649 | ||
| 650 | String = DoubleString | SingleString | LuaString | YAMLMultiline; | 650 | String = DoubleString | SingleString | LuaString | YAMLMultiline; |
| 651 | 651 | ||
| @@ -1185,7 +1185,7 @@ void trim(std::string& str) { | |||
| 1185 | str.erase(str.find_last_not_of(" \t\r\n") + 1); | 1185 | str.erase(str.find_last_not_of(" \t\r\n") + 1); |
| 1186 | } | 1186 | } |
| 1187 | 1187 | ||
| 1188 | std::string toLuaString(const std::string& input) { | 1188 | std::string toLuaDoubleString(const std::string& input) { |
| 1189 | std::string luaStr = "\""; | 1189 | std::string luaStr = "\""; |
| 1190 | for (char c : input) { | 1190 | for (char c : input) { |
| 1191 | switch (c) { | 1191 | switch (c) { |
diff --git a/src/yuescript/yue_parser.h b/src/yuescript/yue_parser.h index 15f9277..f4e0ab1 100644 --- a/src/yuescript/yue_parser.h +++ b/src/yuescript/yue_parser.h | |||
| @@ -457,7 +457,7 @@ private: | |||
| 457 | namespace Utils { | 457 | namespace Utils { |
| 458 | void replace(std::string& str, std::string_view from, std::string_view to); | 458 | void replace(std::string& str, std::string_view from, std::string_view to); |
| 459 | void trim(std::string& str); | 459 | void trim(std::string& str); |
| 460 | std::string toLuaString(const std::string& input); | 460 | std::string toLuaDoubleString(const std::string& input); |
| 461 | } // namespace Utils | 461 | } // namespace Utils |
| 462 | 462 | ||
| 463 | } // namespace yue | 463 | } // namespace yue |
