YueScript Documentation

repository·main·Indexed 20 days ago

https://github.com/ippclub/yuescript

YueScript 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.

Tokens
77.8K
Snippets
376
Records
388
Agent score
58%

What's inside YueScript

  1. Overview of YueScript

    main
    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.
  2. Overview of YueScript Syntax

    main

    YueScript 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 with and . 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
  3. Use Prefixed Return Expressions for cleaner logic

    main

    To 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 explicit return is 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 sub
  4. Use the `in` operator for range and membership checks

    main

    The in operator allows for concise membership testing against lists, tables, or discrete values.

    Membership Testing

    • Lists/Arrays: a in [1, 3, 5] checks if a is one of those values.
    • Tables: item in {key: val} checks if item is a key in the table.
    • Negation: Use not in to check for absence.

    Special Cases

    • Single-element check: a in [1,] or a in {1} checks if a == 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
  5. Use line decorators for loops and conditionals

    main

    YueScript allows applying for, if, while, and until loops/conditionals to a single statement at the end of a line for conciseness.

    • if decorator: print "msg" if condition
    • for decorator: print item for item in *items
    • while decorator: update! while condition
    • until decorator: 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!
  6. Use Table Comprehensions to create key-value maps

    main

    Table 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 when clause 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}
  7. How macros work in YueScript

    main

    Macro 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:

    1. A YueScript string.
    2. 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"
  8. Prevent accidental variable shadowing with `using` statements

    main

    In YueScript, the using statement 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
  9. Use automatic global variable import

    main

    By placing import global at the top of a block, all names that have not been explicitly declared or assigned within that scope are automatically imported as local const references to the corresponding globals.

    Important Rules:

    1. Immutability: Imported globals are const. You cannot reassign them (e.g., print = nil will error).
    2. Exclusion: If you explicitly declare a global variable in the same scope using the global keyword, 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
  10. Define classes with custom logic and private variables

    main

    In 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, self refers 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: " .. secret
  11. Match tables and arrays in switch statements

    main

    YueScript allows powerful pattern matching within switch clauses:

    • 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 = 3 within 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