Expressões Avançadas

O Lazuli oferece diversas expressões avançadas para tornar seu código mais conciso e expressivo. Estas features incluem operador ternário inline, ranges, optional chaining, pipe operator e muito mais.

Operador Ternário Inline

O operador ternário permite expressões condicionais inline sem precisar de blocos if/else.

# Sintaxe: X if CONDITION else Y
set {result} to "maior" if {value} > 10 else "menor ou igual"

# Pode ser usado em qualquer lugar que espera um valor
send "Você é %"admin" if player has permission "admin" else "player"%" to player

# Aninhado
set {status} to "excelente" if {score} > 90 else ("bom" if {score} > 60 else "regular")

# Com null coalescing: X ? Y (retorna Y se X é null/undefined)
set {name} to {custom-name} ? player's name

Expressões de Range

Ranges permitem criar sequências de números de forma concisa.

# Range inclusivo: X..Y (inclui ambos X e Y)
loop number in 1..10:
    send "%number%"  # 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

# Range exclusivo: X..<Y (não inclui Y)
loop i in 0..<5:
    send "%i%"  # 0, 1, 2, 3, 4

# Range com step: X..Y step Z
loop n in 0..100 step 10:
    send "%n%"  # 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100

# Range reverso (step negativo)
loop n in 10..1 step -1:
    send "%n%"  # 10, 9, 8, 7, 6, 5, 4, 3, 2, 1

# Em condições
if {level} is in 1..10:
    send "Level iniciante"

# Atribuição
set {range} to 1..100

Optional Chaining

Optional chaining (?.) permite acessar propriedades de forma segura sem causar erros quando o valor é null.

# Sem optional chaining (pode dar erro se player-data não existe)
set {level} to {player-data::%uuid%::level}

# Com optional chaining (retorna null seguramente)
set {level} to {player-data::%uuid%}?.level

# Encadeado
set {guild-name} to {player}?.guild?.name

# Com fallback
set {name} to {player}?.custom-name ? player's name

# Em condições
if {target}?.health is set:
    send "Target health: %{target}?.health%"

Pipe Operator

O pipe operator (|>) permite encadear funções de forma fluente, passando o resultado de uma expressão como entrada para a próxima.

# Sintaxe: value |> function1 |> function2 |> ...

# Exemplo: processar texto
set {result} to "  Hello World  " |> trim |> toLowerCase |> replace " " with "_"
# Resultado: "hello_world"

# Exemplo: processar lista
set {result} to {players::*} |> filter(p -> p.hasPermission("vip")) |> map(p -> p.getName()) |> join(", ")

# Exemplo: matemática
set {result} to 10 |> add(5) |> multiply(2) |> sqrt
# (10 + 5) * 2 = 30, sqrt(30) ≈ 5.47

# Comparação com estilo tradicional:
# Tradicional:
set {temp1} to trim("  Hello World  ")
set {temp2} to toLowerCase({temp1})
set {result} to replace {temp2} " " with "_"

# Com pipe (mais legível):
set {result} to "  Hello World  " |> trim |> toLowerCase |> replace " " with "_"

Destructuring

Destructuring permite extrair valores de arrays e objetos diretamente em variáveis.

# Array destructuring
set [{first}, {second}, {third}] to {my-list::*}

# Ignorar elementos com _
set [{name}, _, {level}] to ["Player1", "unused", 10]

# Object destructuring
set {{name}, {level}, {rank}} to {player-data}

# Nested destructuring
set [{x}, {y}, {z}] to player's location
send "You are at %{x}%, %{y}%, %{z}%"

# Com valores padrão
set [{a}, {b} = 10] to [5]  # {a} = 5, {b} = 10

# Em parâmetros de função
function processPlayer([{name}, {level}]):
    send "%{name}% is level %{level}%"

Spread Operator

O spread operator (...) expande arrays e objetos.

# Expandir array em outro array
set {combined::*} to [...{list1::*}, "middle", ...{list2::*}]

# Passar elementos como argumentos separados
call myFunction(...{args::*})

# Copiar array
set {copy::*} to [...{original::*}]

# Merge de objetos
set {merged} to {...{defaults}, ...{custom}}

# Em loops
loop element in [...{list1::*}, ...{list2::*}]:
    send "%element%"

Memoization

Memoization permite cachear resultados de funções caras para evitar recálculos.

# Decorator @memoize em funções
@memoize
function expensiveCalculation({input}) :: number:
    # Esta função só é executada uma vez para cada input único
    # Resultados são cacheados automaticamente
    wait 1 second  # Simula operação cara
    return {input} * {input}

# Uso
set {result1} to expensiveCalculation(10)  # Calcula e cacheia
set {result2} to expensiveCalculation(10)  # Retorna do cache instantaneamente

# Memoize inline
set {result} to memoize(heavyComputation({value}))

# Lazy evaluation - só executa quando necessário
set {lazy-value} to lazy(expensiveSetup())
# ...
# Só aqui a função é realmente chamada:
set {actual} to evaluate {lazy-value}

# Cache com TTL (time-to-live)
@memoize(ttl: 5 minutes)
function fetchData({url}) :: text:
    return http get {url}

Guards & Assert

Guards e assertions ajudam a validar condições e sair de funções cedo.

# Guard clause - sai se a condição falhar
function processPlayer({player}):
    guard {player} is online else return
    guard {player} has permission "use.feature" else:
        send "&cNo permission!" to {player}
        return
    
    # Código principal aqui...
    do stuff with {player}

# Guard com valor de retorno
function getValue({data}) :: number:
    guard {data} is set else return 0
    guard {data} > 0 else return -1
    return {data} * 2

# Assert - para debugging e validação
function divide({a}, {b}) :: number:
    assert {b} != 0: "Cannot divide by zero!"
    return {a} / {b}

# Assert com ação
assert player's health > 0: 
    log "Player %player% has invalid health!"
    kick player due to "Invalid state"

# Guard em loops
loop player in all players:
    guard player has permission "see.info" else continue
    send "Secret info" to player

Pattern Guards

Pattern guards adicionam condições extras em cases e loops.

# Guard em switch/case
switch {value}:
    case "admin" if player has permission "admin.access":
        # Só entra se value é "admin" E player tem permissão
        grant admin access
    
    case n if n > 100:
        # Pattern matching com guard
        send "High value: %n%"
    
    case _ if {debug-mode}:
        # Default com guard
        log "Unhandled value: %{value}%"

# Guard em loops
loop player in all players if player has permission "notify":
    send "Notification" to player

loop item in {inventory::*} if item's type is diamond:
    add 1 to {diamond-count}

# Combinar com destructuring
loop [{name}, {level}] in {players::*} if {level} > 10:
    send "%{name}% is high level!"

Design by Contract

Contracts permitem definir pré-condições, pós-condições e invariantes.

# require - pré-condição (deve ser verdade antes da execução)
# ensure - pós-condição (deve ser verdade após a execução)
# invariant - deve ser verdade sempre

function withdraw({amount}) :: boolean:
    require {amount} > 0: "Amount must be positive"
    require {balance} >= {amount}: "Insufficient funds"
    
    set {old-balance} to {balance}
    subtract {amount} from {balance}
    
    ensure {balance} >= 0: "Balance cannot be negative"
    ensure {balance} equals {old-balance} - {amount}: "Balance mismatch"
    
    return true

# Invariant em classes/objetos
invariant {health} >= 0 and {health} <= {max-health}:
    "Health must be between 0 and max"

# Em eventos
on damage:
    require victim is player: "Only players can be damaged by this"
    require damage > 0: "Damage must be positive"
    
    # processar dano...
    
    ensure victim's health >= 0: "Health cannot go negative"

Lambda / Arrow Functions

Funções anônimas compactas para callbacks e expressões funcionais.

# Sintaxe básica: (params) => expression
set {_double} to (x) => {_x} * 2

# Múltiplos parâmetros
set {_add} to (a, b) => {_a} + {_b}

# Parâmetro único (parênteses opcionais)
set {_increment} to x => {_x} + 1

# Com operações de lista
set {_names} to {_players::*} |> map(p => p.getName())
set {_adults} to {_users::*} |> filter(u => u.age >= 18)

# Em sort
set {_sorted} to {_items::*} |> sort by (i => i.value) descending

List Comprehensions

Crie listas de forma declarativa com sintaxe inspirada em Python.

# Sintaxe: [expressão for variável in iterável]
set {_squares} to [{_x} * {_x} for {_x} in 1..10]
# Resultado: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# Com condição (filter)
set {_evens} to [{_x} for {_x} in 1..20 if {_x} % 2 == 0]
# Resultado: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

# Transformar lista existente
set {_names} to [player.getName() for player in all players]

# Filtrar e transformar
set {_vip-names} to [p.getName() for p in all players if p.hasPermission("vip")]

# Nested
set {_coords} to [[{_x}, {_y}] for {_x} in 1..3 for {_y} in 1..3]

Dict Comprehensions

Crie dicionários/mapas de forma declarativa.

# Sintaxe: {chave: valor for variável in iterável}
set {_scores} to {{_name}: 0 for {_name} in {_player-names::*}}

# Com condição
set {_high-scores} to {{_name}: {_score} for {_name}, {_score} in {_all-scores} if {_score} > 100}

# Inverter mapa
set {_inverted} to {{_v}: {_k} for {_k}, {_v} in {_original}}

# Criar lookup table
set {_player-lookup} to {p.getUUID(): p for p in all players}

String Templates (F-Strings)

Interpolação de strings poderosa com prefixo f.

# Sintaxe: f"texto {expressão} mais texto"
send f"Hello {player}, you have {_coins} coins!" to player

# Com expressões complexas
send f"Your level is {_level} ({_xp}/{_xp-needed} XP)" to player

# Formatação
send f"Balance: {_balance |> formatNumber}" to player

# Multi-line (com aspas simples também)
set {_message} to f'
Welcome, {player}!
You joined on {_join-date}.
Current rank: {_rank}
'

Default Parameters

Defina valores padrão para parâmetros de funções.

# Valor padrão simples
function greet(name = "Guest"):
    send "Hello, {_name}!"

# Com tipo e valor padrão
function heal(player, amount: number = 10):
    heal {_player} by {_amount}

# Múltiplos defaults
function createItem(type = "stone", count: number = 1, name = "Item"):
    give {_count} {_type} named {_name} to player

# Uso
call greet()                    # "Hello, Guest!"
call greet("Steve")             # "Hello, Steve!"
call heal(player)               # Cura 10
call heal(player, 20)           # Cura 20

Named Arguments

Chame funções com argumentos nomeados para maior clareza.

function createPlayer(name, level: number = 1, rank = "member"):
    # ...

# Chamada posicional (tradicional)
call createPlayer("Steve", 10, "admin")

# Chamada com argumentos nomeados
call createPlayer(name: "Steve", level: 10, rank: "admin")

# Ordem não importa com named args
call createPlayer(rank: "vip", name: "Alex", level: 5)

# Misturar posicional com named (posicionais primeiro)
call createPlayer("Steve", rank: "admin")  # level usa default

Rest Parameters

Aceite um número variável de argumentos com rest parameters.

# Sintaxe: ...nome
function sum(...numbers):
    set {_total} to 0
    loop {_n} in {_numbers}:
        add {_n} to {_total}
    return {_total}

# Uso
set {_result} to sum(1, 2, 3, 4, 5)  # 15

# Com parâmetros normais antes
function log(prefix, ...messages):
    loop {_msg} in {_messages}:
        send "[{_prefix}] {_msg}" to console

call log("INFO", "Server started", "Players: 0", "Memory: 50%")

# Com tipo
function broadcast(...players: Player):
    loop {_p} in {_players}:
        send "Announcement!" to {_p}

Elvis Operator

O operador Elvis (?:) retorna um default se o valor é "falsy" (null, false, 0, string vazia, array vazio).

# Sintaxe: valor ?: default
set {_name} to {_custom-name} ?: "Unknown"

# Diferença entre Elvis e Null Coalescing:
# Elvis (?:) - retorna default se FALSY (null, false, 0, "", [])
# Null Coalescing (?) - retorna default apenas se NULL

set {_count} to 0
set {_a} to {_count} ?  10   # {_a} = 0 (não é null)
set {_b} to {_count} ?: 10   # {_b} = 10 (0 é falsy)

# Casos práticos
set {_display} to {_nickname} ?: player's name
set {_items} to {_inventory::*} ?: ["empty"]
set {_message} to {_custom-msg} ?: "Default message"

Null Coalescing Assignment

O operador ??= só atribui se a variável for null ou não existir.

# Sintaxe: {var} ??= defaultValue
{_config} ??= loadDefaultConfig()

# Só executa se {_config} é null
# Equivalente a:
# if {_config} is not set:
#     set {_config} to loadDefaultConfig()

# Casos práticos
{player-data::%uuid%} ??= createNewPlayerData()
{_cache} ??= []
{settings::timeout} ??= 30

In-Place Operations

Operadores compostos para modificar variáveis de forma concisa.

# Adição
{_score} += 10       # {_score} = {_score} + 10

# Subtração
{_health} -= 5       # {_health} = {_health} - 5

# Multiplicação
{_gold} *= 2         # {_gold} = {_gold} * 2

# Divisão
{_value} /= 4        # {_value} = {_value} / 4

# Módulo
{_remainder} %= 3    # {_remainder} = {_remainder} % 3

# Com strings (concatenação)
{_message} += " more text"

# Com arrays (append)
{_items} += "new item"   # Adiciona ao final do array

# Subtração em arrays (remove)
{_items} -= "old item"   # Remove primeira ocorrência

Chained Comparisons

Compare múltiplos valores em uma única expressão, como em Python.

# Sintaxe: a < b < c (equivale a: a < b and b < c)
if 1 < {_x} < 10:
    send "x is between 1 and 10"

# Inclusivo
if 0 <= {_health} <= 100:
    send "Health is valid"

# Qualquer combinação de operadores
if 0 <= {_score} < {_max-score}:
    send "Score in range"

# Com variáveis
if {_min} <= {_value} <= {_max}:
    send "Value is within bounds"

Safe Assignment

O operador set? só atribui se o valor for válido (não null/vazio).

# Sintaxe: set? {var} to value
set? {_result} to fetchData()

# Só atribui se fetchData() retornar algo válido
# Se retornar null ou "", a variável não é modificada

# Casos práticos
set? {player-name} to player's display name
set? {_config} to loadConfig("custom.yml")

# Útil para fallbacks encadeados
set? {_value} to {_primary}
set? {_value} to {_secondary}
set? {_value} to {_fallback}

Tuple, Set e Map Literals

Literais especiais para estruturas de dados imutáveis e com valores únicos.

# Tuple - array imutável
set {_point} to (10, 20, 30)
set {_rgb} to (255, 128, 0)

# Set - valores únicos (sem duplicatas)
set {_unique} to #{{"{1, 2, 2, 3, 3, 3}"}}
# Resultado: #{1, 2, 3}

set {_tags} to #{"vip", "builder", "vip"}
# Resultado: #{"vip", "builder"}

# Map literal - pares chave:valor
set {_config} to %{timeout: 30, retries: 3, enabled: true}

# Acessar valores
set {_timeout} to {_config}.timeout  # 30

Combinando Features

# Exemplo completo combinando várias features
@memoize(ttl: 1 minute)
function getTopPlayers({count}) :: list:
    require {count} > 0: "Count must be positive"
    
    set {result::*} to all players 
        |> filter(p -> p?.stats?.score is set)
        |> sort by (p -> p.stats.score) descending
        |> take({count})
        |> map(p -> [p.getName(), p.stats.score])
    
    ensure size of {result::*} <= {count}: "Result exceeds count"
    return {result::*}

# Uso
loop [{name}, {score}] in getTopPlayers(10) if {score} > 1000:
    send "&6%{name}%: &e%{score}% points"

Referência Rápida

FeatureSintaxeExemplo
TernárioX if COND else Y"yes" if {flag} else "no"
Null CoalescingX ? Y{name} ? "Unknown"
Range InclusivoX..Y1..10
Range ExclusivoX..<Y0..<5
Range StepX..Y step Z0..100 step 10
Optional Chain{var}?.prop{player}?.guild?.name
PipeX |> fn"text" |> trim |> upper
Destructure[{a}, {b}]set [{x}, {y}] to pos
Spread[...{arr}][...{a::*}, ...{b::*}]
Guardguard X else Yguard online else return
Assertassert X: "msg"assert {n} > 0: "Invalid"
Memoize@memoize@memoize function...
Lambda(x) => expr(x) => {x} * 2
List Comp[X for Y in Z][{x}*2 for {x} in 1..5]
Dict Comp{k: v for x in y}{n: 0 for n in names}
F-Stringf"text {expr}"f"Hello {name}"
Default Paramname = valuefunction f(x = 10)
Named Argsname: valuecall f(count: 5)
Rest Params...namefunction sum(...nums)
ElvisX ?: Y{val} ?: "default"
Null Assign{var} ??= X{cache} ??= []
In-Place Ops+=, -=, *=, /={score} += 10
Chain Comparea < b < c0 <= {x} <= 100
Safe Assignset? {var} to Xset? {name} to data
Tuple(a, b, c)(10, 20, 30)
Set Literal#{a, b}#{1, 2, 3}
Map Literal%{k: v}%{name: "test"}