docs: update usage instructions and improve validation error messages in generate_mbap.py
This commit is contained in:
parent
90ba49d613
commit
0abbae93a4
159
docs/LRM/avap.md
159
docs/LRM/avap.md
|
|
@ -115,7 +115,41 @@ AVAP utiliza una gramática estructural mixta. Combina la fluidez de las palabra
|
||||||
La estructura `if()` evalúa una expresión lógica o de comparación. Todo bloque condicional requiere un cierre explícito utilizando el comando `end()`.
|
La estructura `if()` evalúa una expresión lógica o de comparación. Todo bloque condicional requiere un cierre explícito utilizando el comando `end()`.
|
||||||
|
|
||||||
El comando `if()` soporta dos modos de invocación:
|
El comando `if()` soporta dos modos de invocación:
|
||||||
* **Modo 1 (comparación estructurada):** `if(variable, valor, comparador)` — evalúa la comparación entre variable y valor usando el operador indicado como string (ej. `"=="`, `">"`, `"!="`). Los dos primeros argumentos deben ser identificadores simples o literales, nunca expresiones de acceso como `dict['clave']`. Si se necesita comparar un valor extraído de una estructura, debe asignarse primero a una variable.* **Modo 2 (expresión libre):** `if(None, None, "expresion_compleja")` — evalúa directamente una expresión booleana compleja proporcionada como string.
|
* **Modo 1 (comparación estructurada):** `if(variable, valor, comparador)` — evalúa la comparación entre variable y valor usando el operador indicado como string (ej. `"=="`, `">"`, `"!="`). Los dos primeros argumentos deben ser identificadores simples o literales, nunca expresiones de acceso como `dict['clave']`. Si se necesita comparar un valor extraído de una estructura, debe asignarse primero a una variable.* **Modo 2 (expresión libre):** `if(None, None, expresion_compleja)` — evalúa directamente una expresión booleana compleja proporcionada como string encapsulado entre `.
|
||||||
|
|
||||||
|
## SECCIÓN III: Lógica de Control y Estructuras de Decisión
|
||||||
|
|
||||||
|
AVAP utiliza una gramática estructural mixta. Combina la fluidez de las palabras clave para abrir bloques funcionales con la seguridad matemática de cierres estrictos.
|
||||||
|
|
||||||
|
### 3.1 El Bloque Condicional (if() / else() / end())
|
||||||
|
El comando `if()` gestiona la lógica condicional mediante dos modos de invocación estrictamente diferenciados. Es imperativo respetar los delimitadores y la posición de los argumentos.
|
||||||
|
|
||||||
|
#### Modo 1: Comparación Estructurada (Atómica)
|
||||||
|
Se utiliza para comparaciones directas entre dos valores simples.
|
||||||
|
* **Sintaxis:** `if(átomo_1, átomo_2, "operador")`
|
||||||
|
* **Argumentos 1 y 2:** Deben ser identificadores simples (variables) o literales (strings/números). **No se permite el uso de `None` en este modo.**
|
||||||
|
* **Argumento 3:** El operador de comparación debe ir obligatoriamente entre **comillas dobles** (`"=="`, `"!="`, `">"`, `"<"`, `">="`, `"<="`).
|
||||||
|
* **Restricción:** No se permiten expresiones de acceso (ej. `data.user` o `list[0]`). Estos valores deben asignarse previamente a una variable.
|
||||||
|
* **Ejemplo correcto:** `if(reintentos, 5, "<")`
|
||||||
|
|
||||||
|
#### Modo 2: Expresión Libre (Evaluación Compleja)
|
||||||
|
Se utiliza para evaluar expresiones lógicas que no encajan en la estructura atómica.
|
||||||
|
* **Sintaxis:** `if(None, None, `expresión_compleja`)`
|
||||||
|
* **Argumentos 1 y 2:** Deben ser literalmente la palabra `None` (sin comillas).
|
||||||
|
* **Argumento 3:** La expresión completa **debe** estar encapsulada entre **acentos graves (backticks)**. Esto permite incluir lógica interna, operadores `and/or` y accesos a estructuras de datos.
|
||||||
|
* **Ejemplo correcto:** `if(None, None, `user.id > 10 and email.contains("@")`)`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Tabla de Validación para el Modelo
|
||||||
|
|
||||||
|
| Entrada | Estado | Razón |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `if(count, 10, "==")` | ✅ VÁLIDO | Modo 1: Átomos válidos y operador entre comillas. |
|
||||||
|
| `if(None, None, `val > 0`)` | ✅ VÁLIDO | Modo 2: Uso correcto de `None` y backticks. |
|
||||||
|
| `if(username, None, "==")` | ❌ ERROR | El Modo 1 prohíbe el uso de `None`. Debe usarse el Modo 2. |
|
||||||
|
| `if(None, None, "val > 0")` | ❌ ERROR | El Modo 2 requiere backticks (`` ` ``), no comillas. |
|
||||||
|
| `if(user.id, 10, "==")` | ❌ ERROR | El Modo 1 no permite expresiones de acceso (`.`). |
|
||||||
|
|
||||||
### 3.2 Iteraciones Estrictas y Deterministas (startLoop / endLoop)
|
### 3.2 Iteraciones Estrictas y Deterministas (startLoop / endLoop)
|
||||||
Para garantizar el determinismo y evitar el colapso de memoria:
|
Para garantizar el determinismo y evitar el colapso de memoria:
|
||||||
|
|
@ -137,15 +171,17 @@ Diseñada para proteger la estabilidad del servidor ante fallos de I/O.
|
||||||
[ "else()" <EOL> <block> ]
|
[ "else()" <EOL> <block> ]
|
||||||
"end()" <EOL>
|
"end()" <EOL>
|
||||||
|
|
||||||
/* if() soporta dos modos:
|
<if_condition> ::= <if_structured> | <if_free_expression>
|
||||||
Modo 1 — comparación estructurada: los dos primeros argumentos deben ser
|
|
||||||
identificadores simples o literales, nunca expresiones de acceso.
|
<if_structured> ::= "if" "(" <strict_atom> "," <strict_atom> "," <backtick_string> ")"
|
||||||
Si se necesita comparar un valor extraído de una estructura (ej. dict['clave']),
|
<if_free_expression> ::= "if" "(" "None" "," "None" "," <backtick_string> ")"
|
||||||
debe asignarse previamente a una variable.
|
|
||||||
Modo 2 — expresión libre: None, None, expresión compleja como string */
|
<strict_atom> ::= <identifier> | <non_null_literal>
|
||||||
<if_condition> ::= <if_atom> "," <if_atom> "," <stringliteral>
|
<backtick_string> ::= "`" <text_content> "`"
|
||||||
| "None" "," "None" "," <stringliteral>
|
|
||||||
<if_atom> ::= <identifier> | <literal>
|
<identifier> ::= [a-zA-Z_][a-zA-Z0-9_]*
|
||||||
|
<non_null_literal>::= <number> | <string_literal_double_quotes>
|
||||||
|
/* Nota: <non_null_literal> NO incluye la palabra "None" */
|
||||||
|
|
||||||
<loop_stmt> ::= "startLoop(" <identifier> "," <expression> "," <expression> ")" <EOL>
|
<loop_stmt> ::= "startLoop(" <identifier> "," <expression> "," <expression> ")" <EOL>
|
||||||
<block>
|
<block>
|
||||||
|
|
@ -261,59 +297,116 @@ AVAP utiliza `avapConnector("TOKEN")` para la hidratación segura de credenciale
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## SECCIÓN VI: Utilidades, Criptografía y Manipulación de Datos
|
# SECCIÓN VI: Utilidades, Criptografía y Manipulación de Datos
|
||||||
|
|
||||||
AVAP incluye un set de comandos integrados de alto nivel para manipular tipos complejos (JSON y Listas), tiempos, textos y generar hashes.
|
AVAP incluye un set de comandos integrados de alto nivel para manipular tipos complejos (JSON y Listas), tiempos, textos y generar hashes.
|
||||||
|
|
||||||
### 6.1 Manipulación Nativa de Listas y Objetos JSON
|
---
|
||||||
Para extraer y mutar estructuras complejas, AVAP provee comandos nativos específicos:
|
|
||||||
* **`variableToList(elemento, destino)`**: Fuerza a que una variable escalar se convierta en una estructura iterable de lista.
|
|
||||||
* **`itemFromList(lista_origen, indice, destino)`**: Extrae de forma segura el elemento contenido en la posición `indice` de una lista.
|
|
||||||
* **`variableFromJSON(json_origen, clave, destino)`**: Parsea un objeto JSON en memoria y extrae el valor correspondiente a la `clave`.
|
|
||||||
* **`AddVariableToJSON(clave, valor, json_destino)`**: Inyecta dinámicamente una nueva propiedad dentro de un objeto JSON existente.
|
|
||||||
|
|
||||||
### 6.2 Criptografía y Expresiones Regulares
|
## 6.1 Manipulación Nativa de Listas y Objetos JSON
|
||||||
* **`encodeSHA256` y `encodeMD5(origen, destino)`**: Funciones criptográficas que encriptan de forma irreversible un texto. Vitales para el almacenamiento seguro de contraseñas.
|
|
||||||
* **`getRegex(origen, patron, destino)`**: Aplica una Expresión Regular (`patron`) sobre la variable de origen, extrayendo las coincidencias exactas.
|
|
||||||
|
|
||||||
### 6.3 Transformación de Tiempo y Cadenas
|
Para extraer y mutar estructuras complejas, AVAP provee comandos nativos específicos. En AVAP, las listas **no se instancian con literales de array**, sino que se construyen y recorren a través de un conjunto cerrado de comandos especializados:
|
||||||
* **Fechas:** `getTimeStamp` (convierte un string a Epoch), `getDateTime` (Epoch a string legible), y `stampToDatetime` (Epoch a objeto datetime estructurado). Soportan formatos de calendario y cálculos con TimeDeltas.
|
|
||||||
* **Cadenas:** `replace` (saneamiento y sustitución de texto) y `randomString` (generación determinista de claves/tokens aleatorios).
|
|
||||||
|
|
||||||
### Especificación BNF (Sección VI)
|
* **`variableToList(elemento, destino)`**: Fuerza a que una variable escalar se convierta en una estructura iterable de lista de un único elemento. Es el punto de entrada canónico para construir una lista desde cero a partir de un valor existente.
|
||||||
|
|
||||||
|
* **`itemFromList(lista_origen, indice, destino)`**: Extrae de forma segura el elemento contenido en la posición `indice` (base 0) de una lista. Equivale a un acceso por índice controlado.
|
||||||
|
|
||||||
|
* **`getListLen(lista, destino)`**: Calcula el número total de elementos contenidos en `lista` y almacena el resultado entero en `destino`. Imprescindible para construir bucles de recorrido seguro y para validar listas antes de acceder a sus índices. Se recomienda llamar siempre a `getListLen` antes de `itemFromList` para evitar accesos fuera de rango.
|
||||||
|
|
||||||
|
* **`variableFromJSON(json_origen, clave, destino)`**: Parsea un objeto JSON en memoria y extrae el valor correspondiente a la `clave`, almacenándolo en `destino`. El acceso es directo por nombre de propiedad.
|
||||||
|
|
||||||
|
* **`AddVariableToJSON(clave, valor, json_destino)`**: Inyecta dinámicamente una nueva propiedad dentro de un objeto JSON existente. Si la clave ya existe, su valor es sobreescrito.
|
||||||
|
|
||||||
|
**Patrón de recorrido típico en AVAP:**
|
||||||
|
|
||||||
|
```avap
|
||||||
|
// 1. Obtener longitud de la lista
|
||||||
|
getListLen(myList, len)
|
||||||
|
|
||||||
|
// 2. Iterar con índice controlado
|
||||||
|
i = 0
|
||||||
|
while (i < len) {
|
||||||
|
itemFromList(myList, i, currentItem)
|
||||||
|
// ... procesar currentItem ...
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6.2 Criptografía y Expresiones Regulares
|
||||||
|
|
||||||
|
* **`encodeSHA256(origen, destino)`** y **`encodeMD5(origen, destino)`**: Funciones criptográficas que encriptan de forma irreversible un texto. Vitales para el almacenamiento seguro de contraseñas y la verificación de integridad de datos. SHA-256 produce un digest de 64 caracteres hexadecimales y ofrece mayor resistencia criptográfica que MD5 (32 caracteres); se recomienda SHA-256 para nuevos desarrollos.
|
||||||
|
|
||||||
|
* **`getRegex(origen, patron, destino)`**: Aplica una Expresión Regular (`patron`) sobre la variable de origen, extrayendo la primera coincidencia exacta encontrada. El patrón sigue la sintaxis estándar compatible con Python `re`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6.3 Transformación de Tiempo y Cadenas
|
||||||
|
|
||||||
|
### Fechas y Timestamps
|
||||||
|
|
||||||
|
AVAP provee tres comandos complementarios para cubrir todas las conversiones posibles entre representaciones de tiempo. Los tres soportan formatos de calendario en notación `strftime` de Python y cálculos con `TimeDelta` expresados en segundos (positivo para sumar, negativo para restar):
|
||||||
|
|
||||||
|
| Comando | Entrada | Salida |
|
||||||
|
|---|---|---|
|
||||||
|
| `getTimeStamp(fecha_string, formato, timedelta, destino)` | String de fecha | Epoch (entero) |
|
||||||
|
| `stampToDatetime(epoch, formato, timedelta, destino)` | Epoch (entero) | String de fecha |
|
||||||
|
| `getDateTime(formato, timedelta, zona_horaria, destino)` | — (ahora mismo) | String de fecha |
|
||||||
|
|
||||||
|
* **`getTimeStamp(fecha_string, formato, timedelta, destino)`**: Convierte un string de fecha legible a su valor Epoch (entero Unix). Útil para almacenar fechas y realizar cálculos aritméticos sobre ellas.
|
||||||
|
|
||||||
|
* **`stampToDatetime(epoch, formato, timedelta, destino)`**: Convierte un valor Epoch a un string de fecha con el formato especificado. Útil para presentar timestamps almacenados de forma legible.
|
||||||
|
|
||||||
|
* **`getDateTime(formato, timedelta, zona_horaria, destino)`**: Captura la fecha y hora actuales del sistema, aplica el ajuste `timedelta` y las convierte a la `zona_horaria` indicada antes de almacenar el resultado. Acepta cualquier zona horaria reconocida por la librería `pytz` de Python.
|
||||||
|
|
||||||
|
### Cadenas de Texto
|
||||||
|
|
||||||
|
* **`randomString(patron, longitud, destino)`**: Genera una cadena aleatoria de `longitud` caracteres cuyos símbolos están restringidos al conjunto definido por `patron` (expresión regular de caracteres). Útil para generar tokens de sesión, contraseñas temporales o identificadores únicos.
|
||||||
|
|
||||||
|
* **`replace(origen, patron_busqueda, reemplazo, destino)`**: Localiza todas las ocurrencias de `patron_busqueda` dentro de `origen` y las sustituye por `reemplazo`, almacenando el resultado en `destino`. Facilita el saneamiento y normalización de datos de entrada antes de su procesamiento o almacenamiento.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BNF — Gramática Formal de los Comandos de Utilidad
|
||||||
|
|
||||||
```bnf
|
```bnf
|
||||||
/* [CORRECCIÓN] Todas las subreglas de <util_command> están ahora completamente expandidas. */
|
<util_command> ::= <json_list_cmd> | <crypto_cmd> | <regex_cmd>
|
||||||
<util_command> ::= <json_list_cmd> | <crypto_cmd> | <regex_cmd> | <datetime_cmd> | <stamp_cmd> | <string_cmd> | <replace_cmd>
|
| <datetime_cmd> | <stamp_cmd> | <string_cmd> | <replace_cmd>
|
||||||
|
|
||||||
/* Manipulación de listas y JSON */
|
/* Manipulación de listas y JSON */
|
||||||
<json_list_cmd> ::= "variableToList(" <expression> "," <identifier> ")"
|
<json_list_cmd> ::= "variableToList(" <expression> "," <identifier> ")"
|
||||||
| "itemFromList(" <identifier> "," <expression> "," <identifier> ")"
|
| "itemFromList(" <identifier> "," <expression> "," <identifier> ")"
|
||||||
|
| "getListLen(" <identifier> "," <identifier> ")"
|
||||||
| "variableFromJSON(" <identifier> "," <expression> "," <identifier> ")"
|
| "variableFromJSON(" <identifier> "," <expression> "," <identifier> ")"
|
||||||
| "AddVariableToJSON(" <expression> "," <expression> "," <identifier> ")"
|
| "AddVariableToJSON(" <expression> "," <expression> "," <identifier> ")"
|
||||||
|
|
||||||
/* Criptografía */
|
/* Criptografía */
|
||||||
<crypto_cmd> ::= "encodeSHA256(" <identifier_or_string> "," <identifier> ")"
|
<crypto_cmd> ::= "encodeSHA256(" <expression> "," <identifier> ")"
|
||||||
| "encodeMD5(" <identifier_or_string> "," <identifier> ")"
|
| "encodeMD5(" <expression> "," <identifier> ")"
|
||||||
|
|
||||||
/* Expresiones regulares */
|
/* Expresiones regulares */
|
||||||
<regex_cmd> ::= "getRegex(" <identifier> "," <stringliteral> "," <identifier> ")"
|
<regex_cmd> ::= "getRegex(" <identifier> "," <expression> "," <identifier> ")"
|
||||||
|
|
||||||
|
/* Fecha/hora actual → string */
|
||||||
<datetime_cmd> ::= "getDateTime(" <stringliteral> "," <expression> "," <stringliteral> "," <identifier> ")"
|
<datetime_cmd> ::= "getDateTime(" <stringliteral> "," <expression> "," <stringliteral> "," <identifier> ")"
|
||||||
/* Argumentos: formato_salida, epoch_origen, zona_horaria, destino */
|
/* Argumentos: formato_salida, timedelta, zona_horaria, destino */
|
||||||
|
|
||||||
|
/* Conversiones epoch ↔ string */
|
||||||
<stamp_cmd> ::= "stampToDatetime(" <expression> "," <stringliteral> "," <expression> "," <identifier> ")"
|
<stamp_cmd> ::= "stampToDatetime(" <expression> "," <stringliteral> "," <expression> "," <identifier> ")"
|
||||||
/* Argumentos: epoch_origen, formato, timedelta, destino */
|
/* Argumentos: epoch_origen, formato, timedelta, destino */
|
||||||
| "getTimeStamp(" <stringliteral> "," <stringliteral> "," <expression> "," <identifier> ")"
|
| "getTimeStamp(" <stringliteral> "," <stringliteral> "," <expression> "," <identifier> ")"
|
||||||
/* Argumentos: fecha_string, formato_entrada, timedelta, destino */
|
/* Argumentos: fecha_string, formato_entrada, timedelta, destino */
|
||||||
|
|
||||||
<string_cmd> ::= "randomString(" <expression> "," <identifier> ")"
|
/* Cadenas */
|
||||||
/* Argumentos: longitud, destino */
|
<string_cmd> ::= "randomString(" <expression> "," <expression> "," <identifier> ")"
|
||||||
|
/* Argumentos: patron, longitud, destino */
|
||||||
|
|
||||||
<replace_cmd> ::= "replace(" <identifier_or_string> "," <stringliteral> "," <stringliteral> "," <identifier> ")"
|
<replace_cmd> ::= "replace(" <identifier> "," <stringliteral> "," <stringliteral> "," <identifier> ")"
|
||||||
/* Argumentos: origen, patron_busqueda, reemplazo, destino */
|
/* Argumentos: origen, patron_busqueda, reemplazo, destino */
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## SECCIÓN VII: Arquitectura de Funciones y Ámbitos (Scopes)
|
## SECCIÓN VII: Arquitectura de Funciones y Ámbitos (Scopes)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Use:
|
Use:
|
||||||
python generate_mbpp_avap.py
|
python generate_mbap.py
|
||||||
python generate_mbpp_avap.py --lrm path/to/avap.md
|
python generate_mbap.py --lrm path/to/avap.md
|
||||||
python generate_mbpp_avap.py --lrm avap.md --output output/mbpp_avap.json --problems 300
|
python generate_mbap.py --lrm avap.md --output output/mbpp_avap.json --problems 300
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
pip install anthropic
|
pip install anthropic
|
||||||
|
|
@ -53,7 +53,7 @@ REGLAS ESTRICTAS para el código AVAP generado:
|
||||||
5. if() Modo 1: if(var_o_literal, var_o_literal, "operador")
|
5. if() Modo 1: if(var_o_literal, var_o_literal, "operador")
|
||||||
— los argumentos NO pueden ser expresiones de acceso como dict['key'];
|
— los argumentos NO pueden ser expresiones de acceso como dict['key'];
|
||||||
hay que extraer el valor a una variable propia primero.
|
hay que extraer el valor a una variable propia primero.
|
||||||
6. if() Modo 2: if(None, None, "expresion_completa_como_string")
|
6. if() Modo 2: if(None, None, `expresion_completa_como_string`)
|
||||||
7. _status se asigna con: addVar(_status, 404)
|
7. _status se asigna con: addVar(_status, 404)
|
||||||
8. ormAccessSelect firma: ormAccessSelect(campos, "tabla", selector, varTarget)
|
8. ormAccessSelect firma: ormAccessSelect(campos, "tabla", selector, varTarget)
|
||||||
— selector puede ser cadena vacía.
|
— selector puede ser cadena vacía.
|
||||||
|
|
@ -62,7 +62,7 @@ REGLAS ESTRICTAS para el código AVAP generado:
|
||||||
|
|
||||||
MODO DE EJECUCIÓN — MUY IMPORTANTE:
|
MODO DE EJECUCIÓN — MUY IMPORTANTE:
|
||||||
- El código se ejecuta DIRECTAMENTE, línea a línea, sin servidor ni registro de endpoints.
|
- El código se ejecuta DIRECTAMENTE, línea a línea, sin servidor ni registro de endpoints.
|
||||||
- NUNCA uses registerEndpoint(), NUNCA uses mainHandler(), NUNCA envuelvas el código en funciones solo para ejecutarlo salvo que queramos probar la funcionalidad de funciones.
|
- NUNCA uses registerEndpoint(), NUNCA uses mainHandler(), NUNCA envuelvas el código en funciones solo para ejecutarlo.
|
||||||
- El código correcto es simplemente las instrucciones en línea, por ejemplo:
|
- El código correcto es simplemente las instrucciones en línea, por ejemplo:
|
||||||
result = "Hello World"
|
result = "Hello World"
|
||||||
addResult(result)
|
addResult(result)
|
||||||
|
|
@ -82,29 +82,48 @@ Estructura exacta de cada elemento:
|
||||||
"task_id": <número entero>,
|
"task_id": <número entero>,
|
||||||
"text": "<enunciado del problema en español>",
|
"text": "<enunciado del problema en español>",
|
||||||
"code": "<código AVAP con saltos de línea como \\n>",
|
"code": "<código AVAP con saltos de línea como \\n>",
|
||||||
|
"test_inputs": { "<param1>": <valor1>, "<param2>": <valor2> },
|
||||||
"test_list": ["<expr_python_1>", "<expr_python_2>"]
|
"test_list": ["<expr_python_1>", "<expr_python_2>"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FORMATO DE test_inputs — MUY IMPORTANTE:
|
||||||
|
- Es un objeto JSON con un valor fijo para cada variable que el código recibe via addParam().
|
||||||
|
- Los nombres de las claves deben coincidir EXACTAMENTE con el nombre de variable usado en addParam().
|
||||||
|
- Los valores deben ser concretos y representativos del problema (no genéricos como "test" o 123).
|
||||||
|
- Si el código no tiene ningún addParam(), el campo test_inputs debe ser un objeto vacío: {}
|
||||||
|
- Estos valores son los que el evaluador inyectará en el stack antes de ejecutar el código,
|
||||||
|
de modo que las aserciones de test_list puedan validar las variables de salida resultantes.
|
||||||
|
|
||||||
|
Ejemplo con addParam:
|
||||||
|
código: addParam("password", password)\\nencodeSHA256(password, hashed)\\naddResult(hashed)
|
||||||
|
test_inputs: { "password": "secret123" }
|
||||||
|
test_list: ["re.match(r'^[a-f0-9]{64}$', hashed)"]
|
||||||
|
|
||||||
|
Ejemplo sin addParam:
|
||||||
|
código: randomString(16, token)\\naddResult(token)
|
||||||
|
test_inputs: {}
|
||||||
|
test_list: ["re.match(r'^[a-zA-Z0-9]{16}$', token)"]
|
||||||
|
|
||||||
FORMATO DE test_list — MUY IMPORTANTE:
|
FORMATO DE test_list — MUY IMPORTANTE:
|
||||||
Cada aserción debe ser una expresión Python con re.match() o re.search()
|
Cada aserción debe ser una expresión Python con re.match()
|
||||||
evaluable directamente sobre las variables del stack AVAP (disponibles como
|
evaluable directamente sobre las variables del stack AVAP (disponibles como
|
||||||
variables Python locales). El módulo 're' está siempre disponible.
|
variables Python locales). El módulo 're' está siempre disponible.
|
||||||
La expresión debe devolver un match object (truthy) si el test pasa.
|
La expresión debe devolver un match object (truthy) si el test pasa.
|
||||||
|
|
||||||
Reglas estrictas:
|
Reglas estrictas:
|
||||||
- USA ÚNICAMENTE re.match(r'<patrón>', <variable>) o re.search(r'<patrón>', str(<variable>))
|
- USA ÚNICAMENTE re.match(r'<patrón>', <variable>)
|
||||||
|
- NO combines expresiones re.match en una aserción, cada asercion tiene que ser un unico re.match(r'<patrón>', <variable>)
|
||||||
- Convierte a string si es necesario: re.match(r'^\\d+$', str(result))
|
- Convierte a string si es necesario: re.match(r'^\\d+$', str(result))
|
||||||
- Puedes encadenar con 'and': re.match(r'^[a-zA-Z0-9]{32}$', token) and re.match(r'.{32}', token)
|
- Puedes encadenar con 'and': re.match(r'^[a-zA-Z0-9]{32}$', token) and re.match(r'.{32}', token)
|
||||||
- Las variables referenciadas deben existir en el stack tras ejecutar el código.
|
- Las variables referenciadas deben existir en el stack tras ejecutar el código.
|
||||||
- NUNCA uses comparaciones directas (==, !=, >, <).
|
- NUNCA uses comparaciones directas (==, !=, >, <).
|
||||||
- NUNCA uses isinstance(), len(), assert, ni texto descriptivo.
|
- NUNCA uses isinstance(), len(), assert, ni texto descriptivo.
|
||||||
- NUNCA uses nada que no sea re.match() o re.search().
|
- NUNCA uses nada que no sea re.match().
|
||||||
|
|
||||||
Ejemplos correctos de test_list:
|
Ejemplos correctos de test_list:
|
||||||
"re.match(r'^[a-f0-9]{64}$', hashed)"
|
"re.match(r'^[a-f0-9]{64}$', hashed)"
|
||||||
"re.match(r'^[a-zA-Z0-9]{32}$', token)"
|
"re.match(r'^[a-zA-Z0-9]{32}$', token)"
|
||||||
"re.match(r'^\\d{4}-\\d{2}-\\d{2}$', date_str)"
|
"re.match(r'^\\d{4}-\\d{2}-\\d{2}$', date_str)"
|
||||||
"re.search(r'Hello', result)"
|
|
||||||
"re.match(r'^-?\\d+(\\.\\d+)?$', str(result))"
|
"re.match(r'^-?\\d+(\\.\\d+)?$', str(result))"
|
||||||
"re.match(r'^(par|impar)$', result)"
|
"re.match(r'^(par|impar)$', result)"
|
||||||
"re.match(r'^40[134]$', str(_status))"
|
"re.match(r'^40[134]$', str(_status))"
|
||||||
|
|
@ -138,22 +157,26 @@ Responde ÚNICAMENTE con el array JSON. Sin texto antes ni después.
|
||||||
|
|
||||||
def parse_response(raw: str):
|
def parse_response(raw: str):
|
||||||
text = raw.strip()
|
text = raw.strip()
|
||||||
|
|
||||||
if text.startswith("```"):
|
if text.startswith("```"):
|
||||||
lines = text.splitlines()
|
lines = text.splitlines()
|
||||||
inner = lines[1:]
|
inner = lines[1:]
|
||||||
if inner and inner[-1].strip() == "```":
|
if inner and inner[-1].strip() == "```":
|
||||||
inner = inner[:-1]
|
inner = inner[:-1]
|
||||||
text = "\n".join(inner).strip()
|
text = "\n".join(inner).strip()
|
||||||
|
|
||||||
problems = json.loads(text)
|
problems = json.loads(text)
|
||||||
|
|
||||||
if not isinstance(problems, list):
|
if not isinstance(problems, list):
|
||||||
raise ValueError("answer is not a JSON.")
|
raise ValueError("response is not an JSON array")
|
||||||
|
|
||||||
for p in problems:
|
for p in problems:
|
||||||
for field in ("task_id", "text", "code", "test_list"):
|
for field in ("task_id", "text", "code", "test_list"):
|
||||||
if field not in p:
|
if field not in p:
|
||||||
raise ValueError(f"field '{field}' not found in a problem.")
|
raise ValueError(f"Field missing '{field}' in task_id={p.get('task_id','?')}.")
|
||||||
|
if "test_inputs" not in p:
|
||||||
|
p["test_inputs"] = {}
|
||||||
|
if not isinstance(p["test_inputs"], dict):
|
||||||
|
raise ValueError(f"'test_inputs' must by a JSON Object (task_id={p.get('task_id','?')}).")
|
||||||
|
|
||||||
return problems
|
return problems
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue