YueScript Documentation
repository·main·Indexed 20 days ago
https://github.com/ippclub/yuescriptYueScript is a modern language that compiles to Lua, designed as an evolution of MoonScript to be more expressive and productive. It features macros, pipe operators, and improved performance. The documentation covers the YueScript Lua API, CLI compilation and execution options, and advanced language features including line decorators, scoping with do expressions, and a comprehensive macro system with AST type validation and annotation statements.
What's inside YueScript
- YueScript is a language designed to provide modern syntax while compiling down to Lua. It aims to offer a delightful developer experience by providing features like pipes, pattern matching, slicing, and destructuring, all while maintaining high interoperability with existing Lua workflows. The compiled output is designed to be readable Lua, ensuring predictable behavior.
What is YueScript
mainYueScript is a dynamic language and a dialect of MoonScript that compiles to Lua. It is designed to be expressive and concise, making it suitable for writing maintainable application logic that runs in embedded Lua environments, such as game engines or website servers. It is developed alongside the Dora SSR game engine.Overview of YueScript Syntax
mainYueScript is a concise, expressive dialect of Lua (similar to MoonScript). Key syntax features include:
- Importing:
import p, to_lua from "yue" - Object Literals: Uses indentation-based structure.
- List Comprehensions:
[action item for item in *arr] - Pipe Operator:
[1, 2, 3] |> map (x) -> x * 2 - Metatable Manipulation: Use
withand.for concise access. - Exporting:
export 🌛 = "Script of Moon"
-- list comprehension map = (arr, action) -> [action item for item in *arr] -- pipe operator [1, 2, 3] |> map (x) -> x * 2 |> filter (x) -> x > 4 |> reduce 0, (a, b) -> a + b |> print- Importing:
Use Prefixed Return Expressions for cleaner logic
mainTo avoid writing a trailing return statement at the end of deeply nested functions, you can use the Prefixed Return Expression syntax. By placing the desired implicit return value before the
->or=>token, you declare what the function returns if no explicitreturnis triggered within the body.# The ': nil' prefix indicates the implicit return value if the body finishes findFirstEven = (list): nil -> for item in *list if type(item) == "table" for sub in *item if sub % 2 == 0 return subUse the `in` operator for range and membership checks
mainThe
inoperator allows for concise membership testing against lists, tables, or discrete values.Membership Testing
- Lists/Arrays:
a in [1, 3, 5]checks ifais one of those values. - Tables:
item in {key: val}checks ifitemis a key in the table. - Negation: Use
not into check for absence.
Special Cases
- Single-element check:
a in [1,]ora in {1}checks ifa == 1. - Warning:
a in [1](without a comma) is treated as an index access (tb[1]) rather than a membership check.
a = 5 if a in [1, 3, 5] print "Match found" if item not in list print "Not in list"if a in [1, 3, 5] print "Match" not_exist = item not in list- Lists/Arrays:
Use line decorators for loops and conditionals
mainYueScript allows applying
for,if,while, anduntilloops/conditionals to a single statement at the end of a line for conciseness.ifdecorator:print "msg" if conditionfordecorator:print item for item in *itemswhiledecorator:update! while conditionuntildecorator:parse! until condition
print "hello world" if name == "Rob" print "item: ", item for item in *items game\update! while game\isRunning! reader\parse_line! until reader\eof!Use Table Comprehensions to create key-value maps
mainTable comprehensions allow you to construct a new table with specific key-value pairs. They use curly braces
{}and require two values per iteration (a key and a value).Key features:
- Key-Value Mapping: The syntax
{k, v for k, v in pairs thing}maps keys and values from an existing table. - Filtering: Use a
whenclause to exclude specific keys or values. - Shorthand via Expressions: If an expression returns two values (like a tuple), it can be used directly to define the key and value.
- The
*Operator: Works with the shorthand iteration for numeric tables to create lookup tables.
thing = { color: "red", name: "fast", width: 123 } -- Copy a table thing_copy = {k, v for k, v in pairs thing} -- Filter keys no_color = {k, v for k, v in pairs thing when k != "color"} -- Create lookup table using * operator numbers = [1, 2, 3, 4] sqrts = {i, math.sqrt i for i in *numbers} -- Convert array of pairs to a table tuples = [ ["hello", "world"], ["foo", "bar"] ] tbl = {unpack tuple for tuple in *tuples}- Key-Value Mapping: The syntax
How macros work in YueScript
mainMacro functions evaluate a string at compile-time and inject the generated code into the final compilation. You invoke a macro using the
$prefix (e.g.,$MY_MACRO).Macros can return:
- A YueScript string.
- A configuration table containing Lua code (using
type: "lua").
To generate multi-line code, it is recommended to use the
|operator (YAML-style multi-line string) instead of quoted strings to ensure stable indentation and support for comments.macro PI2 = -> math.pi * 2 area = $PI2 * 5 macro luaFunc = (var) -> { code: "local function #{var}() end" type: "lua" } $luaFunc funcB macro default_conf = (conf) -> | -- useful; only set once #{conf}.identity = 'LOVE' #{conf}.version = "11.5"Prevent accidental variable shadowing with `using` statements
mainIn YueScript, the
usingstatement allows you to explicitly define which external variables a function is allowed to access and modify. This prevents accidental assignment to global or outer-scope variables that share the same name.- To prevent all assignments from affecting the outer scope, use
(using nil)immediately after the parameter list or inside the parentheses if there are no parameters. - To allow specific variables to be modified, use
(add using var1, var2, ...)to list the names of the external variables you wish to access/mutate.
i = 100 -- Prevents modifying outer 'i' my_func = (using nil) -> i = "hello" my_func! print i -- Prints 100 -- Allows modifying specific variables tmp = 1213 i, k = 100, 50 my_func = (add using k, i) -> tmp = tmp + add -- Creates a new local 'tmp' i += tmp k += tmp my_func(22) print i, k -- These are updated- To prevent all assignments from affecting the outer scope, use
Use automatic global variable import
mainBy placing
import globalat the top of a block, all names that have not been explicitly declared or assigned within that scope are automatically imported as localconstreferences to the corresponding globals.Important Rules:
- Immutability: Imported globals are
const. You cannot reassign them (e.g.,print = nilwill error). - Exclusion: If you explicitly declare a global variable in the same scope using the
globalkeyword, it will not be imported by the automatic mechanism, allowing you to assign to it.
do import global print "hello" math.random 3 -- print = nil -- error: imported globals are const end do -- explicit global variable will not be imported import global global FLAG print FLAG FLAG = 123 end- Immutability: Imported globals are
Define classes with custom logic and private variables
mainIn YueScript, class declaration bodies can contain ordinary expressions in addition to key/value pairs. These expressions execute after all properties are added to the class's base object. Within the class body,
selfrefers to the class object itself, not an instance. Variables declared in the class body are scoped only to that class declaration, making them useful for private helper functions or values.class Things @class_var = "hello world" class MoreThings secret = 123 log = (msg) -> print "LOG:", msg some_method: => log "hello world: " .. secretMatch tables and arrays in switch statements
mainYueScript allows powerful pattern matching within
switchclauses:- Table Destructuring: Match tables by their structure. You can use keys (e.g.,
:x,:y) or nested structures. If a field is missing, the match fails unless you use default values. - Array Matching: Match array elements by position or value. You can use a variable (e.g.,
b) to capture a value at a specific index. - Default Values in Matching: Use
b = 3within a pattern to provide a default value for a captured variable. - Nested Structures: Match complex, nested tables and arrays.
- Spread Operator: Use
...to capture a range of elements (e.g.,[...groups, resource, action]).
# Table destructuring switch item when :x, :y print "Vec2 #{x}, #{y}" # Array matching with capture and default switch tb when [1, 2, b = 3] print "1, 2, #{b}" # Spread operator for ranges segments = ["admin", "users", "logs", "view"] switch segments when [...groups, resource, action] print "Group:", groups print "Resource:", resource print "Action:", action- Table Destructuring: Match tables by their structure. You can use keys (e.g.,