> ## Documentation Index
> Fetch the complete documentation index at: https://docs.prodexlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# El lenguaje de expresiones

> El lenguaje de fórmulas estilo Excel de ProDex para hacer modelos de simulación dinámicos y basados en datos.

## Resumen

El lenguaje de Expresiones (Expressions) (o DSL) es la forma de hacer dinámicos los modelos de ProDex. En cualquier lugar donde esperarías ingresar un número fijo (condiciones de enrutamiento, prioridades de cola, requisitos de recursos, valores de atributos de entidad, disparadores de eventos y los *parámetros* de las distribuciones de tiempo de procesamiento) puedes ingresar una expresión en su lugar. Las expresiones referencian el estado de simulación, atributos de entidad, constantes, tablas de búsqueda (Lookup Tables) y variables de estado, de modo que tu modelo responde a los datos que fluyen a través de él en lugar de comportarse idénticamente para cada entidad.

Un usuario que solo ingresa números fijos está usando una pequeña fracción de lo que el Modelador (Modeler) puede hacer. Aprender el lenguaje de expresiones es lo que convierte un diagrama de flujo estático en un modelo vivo de tus operaciones.

## Dónde aparecen las expresiones

La mayoría de los campos numéricos y lógicos en la configuración de un componente aceptan tanto un valor literal como una expresión. Lugares comunes:

* **Arrival rate** en una Fuente (Source): puede depender del tiempo de simulación, calendario (Schedule) o estado
* **Routing conditions** en un Enrutador (Router): ramifica según atributos de entidad, disponibilidad de recursos o estado de cola
* **Resource requirements** en un Proceso (Process): cuántas unidades de un recurso (Resource) se necesitan, condicionado a la entidad
* **Queue priority** en un Búfer (Buffer): ordena entidades en espera por una puntuación calculada
* **Resource allocation priority**: elige entre demandas en competencia cuando la capacidad está restringida
* **Event Hook condition** y argumentos de Action: consulta [Event Hooks](/es/reference/events#event-hooks)
* **Event Listener condition** y argumentos de Action: consulta [Event Listeners](/es/reference/events)
* **Scheduled action condition**: controla si un `assign` / `emit` programado dispara (consulta [Calendarios](/es/reference/schedules))
* Asignaciones de atributo de Entidad (Entity) en una salida de Source, Transformador (Transformer), Combinador (Combiner) o Separador (Separator): calcula valores iniciales o actualizados de atributos
* **Combinador `batch_expr`**: el disparador booleano que cierra un lote de tamaño dinámico
* **Separador output count**: expresión DSL que retorna el número de salidas por entrada
* **Parámetros de distribución (Distribution)**: cada campo numérico que acepta una distribución tiene un toggle `</>` que cambia la entrada del parámetro a modo de expresión. Consulta [Distribuciones vs. Expresiones](#distributions-vs-expressions) más adelante.

## Sintaxis básica

Las expresiones siguen una sintaxis tipo Excel: literales numéricos, literales de cadena en comillas simples O dobles, llamadas de función en forma `FUNCTION(args)`, operadores aritméticos, operadores de comparación y operadores lógicos.

```
5
"TypeA"
'TypeB'
FUNCTION(arg1, arg2)
a + b * c
IF(condition, then_value, else_value)
priority > 5 AND qc_passed
```

Los literales booleanos no distinguen entre mayúsculas y minúsculas: `TRUE`, `True`, `true`, `FALSE`, `False`, `false` todos se analizan correctamente.

### Precedencia de operadores

Las expresiones siguen la precedencia estándar de operadores (de mayor a menor):

1. **`NOT`**, **`-` unario**
2. **Multiplicación, división**: `*`, `/`
3. **Suma, resta**: `+`, `-`
4. **Comparación**: `<`, `<=`, `>`, `>=`, `=`, `==`, `!=`
5. **`AND`**
6. **`OR`**

`AND`, `OR` y `NOT` son **operadores infijo y prefijo**, no funciones: escribe `a AND b`, `a OR b`, `NOT a`, nunca `AND(a, b)`. Tanto `=` como `==` se aceptan como operadores de igualdad. Usa paréntesis para agrupación explícita cuando quieras anular la precedencia.

## Contextos de expresión

El **contexto** en el que se ejecuta una expresión determina qué identificadores están disponibles para referenciar. Equivocarse de contexto produce errores de validación como *"identifier not available here"* que son confusos sin entender el modelo. Hay tres contextos.

### Contexto sin Entidad

Se ejecuta sin una entidad específica en alcance. Identificadores disponibles:

* [Variables de estado integradas](#built-in-state-variables) como `SIM_TIME`, `SIM_DURATION`
* [Funciones de consulta de componentes](#component-query-functions) como `BUFFER_LEVEL(...)`, `RESOURCE_AVAILABLE(...)`
* [Constantes](/es/reference/constants-and-lookups) por nombre
* [Variables de estado](/es/reference/events#state-variables) por nombre
* `SELF`: cuando la expresión está en un event hook o event listener (consulta [Disponibilidad de SELF](#self-availability) más adelante)
* Los atributos de entidad **no** están disponibles

Usado en: tasas de llegada de Source (antes de que exista cualquier entidad), condiciones y acciones de Event Listener (los listeners siempre se ejecutan sin entidad), eventos de Resource, acciones programadas y verificaciones de estado global.

### Contexto de Entidad única

Se ejecuta con una entidad específica en alcance. Todo lo del contexto sin entidad, más:

* **Atributos de entidad por nombre simple**: `priority`, `product_class`, `weight`
* `ENTITY_TYPE`, `ENTITY_AGE`

Usado en: tiempo de procesamiento de Process, condiciones de Router, asignaciones de atributo de Transformer, Event Hooks por entidad (p. ej., `entity created`, `process started`) y lugares similares donde una entidad está claramente en alcance.

### Contexto de múltiples Entidades

Se ejecuta con múltiples entidades en alcance. Las **cuatro** superficies multi-entidad son:

* **Asignación de atributo de Combiner**: atributos de la entidad de salida derivados de las entradas
* **Combiner `batch_expr`**: el disparador booleano que cierra un lote dinámico
* **Hook `on_batch_assembled` de Combiner**: dispara una vez por lote ensamblado con todas las entradas en alcance
* **Hook `on_batch_created` de Separator**: dispara una vez por separación con todas las salidas en alcance

En cualquier contexto multi-entidad, las referencias de atributo simples como `priority` son ambiguas (*¿de qué entidad?*). Debes envolverlas en una [función de agregación](#aggregation-functions): `MAX(priority) > 5`, `ANY(qc_passed)`.

## Variables de estado integradas

* `SIM_TIME`: tiempo actual de simulación
* `SIM_DURATION`: duración configurada de la simulación
* `ENTITY_TYPE`: slug de tipo de entidad de la entidad actual (solo contexto de entidad única). Retorna el **slug** (p. ej., `widget`), no el nombre visible (p. ej., `Widget`).
* `ENTITY_AGE`: tiempo que la entidad actual ha estado en el sistema (solo contexto de entidad única)
* `SELF`: el componente actual (consulta [Disponibilidad de SELF](#self-availability))

### Disponibilidad de SELF

`SELF` es **solo** válido dentro de expresiones de event hook y event listener. **No** está disponible en:

* Acciones programadas
* Campos de configuración de componente fuera de hooks/listeners (processing\_time, arrival\_logic, reglas de enrutamiento, prioridad de cola, prioridad de asignación de recursos, asignaciones de atributos)

Si necesitas referenciar un componente específico desde fuera de un hook, usa el nombre de ese componente explícitamente dentro de una [función de consulta de componente](#component-query-functions).

## Funciones de consulta de componentes

Estas funciones leen el estado vivo de simulación para un componente específico. Toman el campo **`id`** del componente (el id del esquema, no el nombre visible) como primer argumento.

* `BUFFER_LEVEL(buffer_id)`: número actual de entidades en el búfer nombrado
* `BUFFER_CAPACITY(buffer_id)`: capacidad configurada del búfer nombrado
* `RESOURCE_AVAILABLE(resource_id)`: unidades actualmente disponibles del recurso nombrado
* `RESOURCE_CAPACITY(resource_id)`: capacidad configurada del recurso nombrado
* `STATION_WIP(station_id [, entity_type])`: trabajo en curso en la estación (Station) nombrada; segundo argumento opcional filtra a un tipo de entidad específico

<Warning>
  **Las funciones de consulta de componentes toman el `id` del componente, no el nombre visible mostrado en el Modelador.** Si el nombre visible de tu búfer es *"WIP Buffer"* y su id es `wip-buffer`, debes escribir `BUFFER_LEVEL("wip-buffer")`. Pasar el nombre visible retorna silenciosamente `0` y tu modelo se comporta como si el búfer estuviera siempre vacío.
</Warning>

### Valor por defecto

Todas las funciones de consulta de componentes aceptan un argumento de palabra clave opcional `default:=` que anula el valor de respaldo cuando el componente no se encuentra:

```
BUFFER_LEVEL("wip-buffer", default:=-1)
```

Sin `default:=`, los componentes faltantes retornan el numérico `0`. A diferencia de LOOKUP, las consultas de componentes **no** retornan cero específico de tipo (`""` o `FALSE`), solo `0`.

## Variables de estado personalizadas

Las [variables de estado](/es/reference/events#state-variables) definidas en el modelo son legibles por su nombre simple en SCREAMING\_SNAKE\_CASE en cualquier contexto DSL, sin entidad, entidad única o multi-entidad:

```
current_shift_efficiency
UNITS_COMPLETED + 1
IF(SHIFT_MODE == "night", base_time * 1.2, base_time)
```

Las variables de estado se leen directamente en las expresiones y se escriben a través de la acción `assign` en event hooks, event listeners o acciones programadas. Persisten a través de entidades y de toda la ejecución.

Para la distinción entre variables de estado (a nivel de modelo, mutables) y atributos de entidad (por entidad, se mueven con la entidad), consulta [Atributos de Entidad](#entity-attributes) más adelante.

## Atributos de Entidad

Las entidades llevan **atributos tipados** a través del flujo: booleanos, números, texto, listas de texto, listas de números. Referencia los atributos de la entidad actual **por su nombre simple** en cualquier expresión que se ejecute en un contexto de entidad única:

```
priority
product_class
weight
```

Sin prefijo `self.`, la entidad en alcance es implícita. Las expresiones referencian atributos directamente por el nombre definido en el tipo de entidad.

En contextos multi-entidad, los nombres simples de atributo se refieren al conjunto de entidades y deben envolverse en una función de agregación como `SUM(weight)` o `MAX(priority)`. Consulta [Funciones de agregación](#aggregation-functions).

Los atributos de entidad se establecen en una Source, pueden ser modificados por un Transformer y pueden ser leídos desde cualquier expresión aguas abajo. Persisten con la entidad a través del flujo completo. Consulta [Entidades](/es/reference/entities) para el sistema completo de tipos de atributos y las estrategias de asignación.

## Distribuciones vs. Expresiones

Una confusión común: **las distribuciones no son funciones DSL.** No puedes escribir `NORMAL(60, 10)` como una expresión. Las distribuciones se seleccionan de un **menú desplegable** en cada campo numérico que las admite: Normal, Uniform, Triangular, Beta y el resto de las diez opciones. Seleccionar una revela los campos de parámetros debajo del selector.

Cada campo de parámetro tiene un toggle `</>` que lo cambia a modo de expresión, ahí entra el DSL. La forma de la distribución permanece estática; los valores de los parámetros se vuelven dinámicos.

Por ejemplo, en lugar de una media codificada en `60`, puedes cambiar el campo Mean a modo de expresión y escribir:

```
base_time + weight * 0.5
```

O hacer que la media dependa de una tabla de búsqueda por tipo de entidad:

```
LOOKUP(cycle_times_by_product, ENTITY_TYPE) * complexity_factor
```

El campo Standard Deviation puede permanecer como literal `10`, o también ser una expresión. Consulta [Distribuciones](/es/reference/distributions) para la lista completa de tipos de distribución admitidos.

## Funciones comunes

### Flujo de control

* `IF(condition, then, else)`: ramifica entre dos valores
* `AND`, `OR`, `NOT`: combinaciones lógicas (operadores infijo/prefijo, no funciones)
* Operadores de comparación: `==`, `!=`, `<`, `<=`, `>`, `>=`, `=`

### Matemáticas

* `ABS(x)`: valor absoluto
* `POW(x, y)`: `x` elevado a `y`
* `MOD(x, y)`: resto de `x` dividido entre `y`
* Aritmética estándar: `+`, `-`, `*`, `/`

### Cadenas

* `CONTAINS(text, substring)`: si una cadena contiene una subcadena (distingue mayúsculas/minúsculas)
* `LEN(text)`: longitud de una cadena, como número

### Búsquedas

* `LOOKUP(table_name, key1, key2, ...)`: busca un valor de una [Tabla de búsqueda](/es/reference/constants-and-lookups#lookup-tables). Suministra una clave por dimensión. Acepta un argumento de palabra clave opcional `default:=`:

```
LOOKUP(setup_times, product_class, default:=300)
```

Sin `default:=`, las filas faltantes retornan el cero específico de tipo (`0` para tablas numéricas, `""` para texto, `FALSE` para booleano). Consulta [Constantes y Tablas de búsqueda](/es/reference/constants-and-lookups) para el esquema completo.

### Funciones de agregación

Válidas solo en **contextos multi-entidad** (asignación de atributo de Combiner, `batch_expr` de Combiner, `on_batch_assembled` de Combiner, `on_batch_created` de Separator). Las agregaciones operan sobre el conjunto de entidades en alcance:

* `SUM(expr)`: suma
* `MEAN(expr)`: promedio
* `COUNT(expr)`: cuenta de entidades en alcance (canónicamente `COUNT(1)`)
* `MAX(expr)`: valor máximo
* `MIN(expr)`: valor mínimo
* `MODE(expr)`: valor más común
* `N_UNIQUE(expr)`: cuenta de valores distintos
* `ANY(expr)`: verdadero si la expresión es verdadera para alguna entidad
* `ALL(expr)`: verdadero si la expresión es verdadera para cada entidad

Cada agregación acepta dos argumentos de palabra clave opcionales:

* `filter:=<boolean expr>`: solo agrega entidades donde el filtro evalúa verdadero
* `type:=<entity_type_slug>`: solo agrega entidades del tipo nombrado (slug sin comillas)

```
SUM(weight, filter:=qc_passed)
COUNT(1, type:=tube)
MAX(priority, filter:=is_rush)
```

<Note>
  **`type:=` toma un slug sin comillas, y los slugs con guión no se admiten en esta posición.** Un tipo con un guión en su slug no puede filtrarse con `type:=`, soluciónalo con `filter:=ENTITY_TYPE == "my-type"` en su lugar.
</Note>

**Ejemplo: un Combiner produciendo un ensamblaje, estableciendo atributos de salida desde sus entradas:**

```
# Atributo de salida: total_weight
SUM(weight)

# Atributo de salida: priority
MAX(priority)

# Condición de salida: emitir solo si cada entrada pasó QC
ALL(qc_passed)
```

## Seguridad de tipos

El DSL es **estáticamente tipado**. Cada expresión se verifica de tipo en tiempo de validación del modelo, no en tiempo de ejecución. Un campo de condición espera un booleano; una asignación de atributo para un atributo Number debe producir un número; una expresión de cantidad debe producir un entero. Mezclar tipos falla la validación con un error específico apuntando a la expresión ofensiva: consulta [Validación](/es/reference/validation).

No hay coerción implícita de tipos. `5 + "x"` no se convierte silenciosamente en `"5x"`, falla la validación. Convierte explícitamente cuando lo necesites.

## Patrones comunes

**Enrutamiento condicional**: envía cada entidad por la ruta correcta:

```
IF(product_class = "rush", "express_line", "standard_line")
```

**Parámetro de distribución impulsado por atributos**: cambia el campo Mean de una distribución Normal a modo de expresión para que se adapte a cada entidad, mientras Standard Deviation permanece en `10` fijo:

```
LOOKUP(cycle_times_by_product, ENTITY_TYPE) * complexity_factor
```

**Contrapresión**: frena las llegadas cuando aguas abajo está congestionado, vía una expresión en la tasa de llegada de Source:

```
IF(BUFFER_LEVEL("wip-buffer") > 100, 0, 1)
```

**Puntuación de prioridad**: ordena entidades en espera por importancia calculada:

```
due_priority * 0.7 + customer_tier * 0.3
```

**Salida de ensamblaje**: combina varias entradas en una (Combiner, contexto multi-entidad):

```
# El peso de la entidad ensamblada es la suma de sus entradas
SUM(weight)

# Emitir solo si todas las entradas pasaron QC
ALL(qc_passed)
```

## Consejos y trampas

* **Las expresiones se evalúan en cada entidad, cada vez.** Mantenlas simples donde sea posible, las expresiones complejas en rutas de alto rendimiento se acumulan.
* **Las consultas de componentes retornan cero numérico.** Un nombre de componente faltante produce `0`, no nulo. Usa el argumento de palabra clave `default:=` si necesitas un centinela diferente.
* **LOOKUP retorna cero específico de tipo.** `0`, `""` o `FALSE` dependiendo del tipo de valor de la tabla. Envuelve con `default:=` para un respaldo diferente.
* **Los atributos de entidad no son variables de estado.** Los atributos pertenecen a una entidad y se mueven con ella. Las [Variables de estado](/es/reference/events#state-variables) pertenecen al modelo y persisten a través de las entidades.
* **El contexto importa.** Si encuentras un error de validación como *"identifier `priority` not available here"*, la expresión se está ejecutando en un contexto que no tiene una entidad en alcance. Consulta [Contextos de expresión](#expression-contexts).
* **`SELF` vs. nombres simples de atributo.** `SELF` se refiere al *componente* al que está adjunta la expresión (solo hooks y listeners). Los nombres simples se refieren a *atributos de entidad*. No se superponen.
* **`ENTITY_TYPE` retorna el slug, no el nombre visible.** Compara contra `"widget"`, no `"Widget"`.
* **Prefiere [Constantes y Tablas de búsqueda](/es/reference/constants-and-lookups)** sobre valores codificados. Cambia una constante en un solo lugar; no puedes fácilmente buscar-y-reemplazar a través de decenas de expresiones.
