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 nameExpressõ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..100Optional 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 playerPattern 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) descendingList 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 20Named 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 defaultRest 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} ??= 30In-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ênciaChained 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 # 30Combinando 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
| Feature | Sintaxe | Exemplo |
|---|---|---|
| Ternário | X if COND else Y | "yes" if {flag} else "no" |
| Null Coalescing | X ? Y | {name} ? "Unknown" |
| Range Inclusivo | X..Y | 1..10 |
| Range Exclusivo | X..<Y | 0..<5 |
| Range Step | X..Y step Z | 0..100 step 10 |
| Optional Chain | {var}?.prop | {player}?.guild?.name |
| Pipe | X |> fn | "text" |> trim |> upper |
| Destructure | [{a}, {b}] | set [{x}, {y}] to pos |
| Spread | [...{arr}] | [...{a::*}, ...{b::*}] |
| Guard | guard X else Y | guard online else return |
| Assert | assert 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-String | f"text {expr}" | f"Hello {name}" |
| Default Param | name = value | function f(x = 10) |
| Named Args | name: value | call f(count: 5) |
| Rest Params | ...name | function sum(...nums) |
| Elvis | X ?: Y | {val} ?: "default" |
| Null Assign | {var} ??= X | {cache} ??= [] |
| In-Place Ops | +=, -=, *=, /= | {score} += 10 |
| Chain Compare | a < b < c | 0 <= {x} <= 100 |
| Safe Assign | set? {var} to X | set? {name} to data |
| Tuple | (a, b, c) | (10, 20, 30) |
| Set Literal | #{a, b} | #{1, 2, 3} |
| Map Literal | %{k: v} | %{name: "test"} |