-
Notifications
You must be signed in to change notification settings - Fork 4
Interfaz para el desarrollo
Escribí una interfaz mínima de lo que necesitamos, que incluye cómo usaríamos el interactive y los snapshots. Acá va:
index.js
podría exportar un objeto con las siguientes funciones:
{
config: {
setLanguage,
setInfiniteLoopTimeout,
setXGobstonesEnabled
},
gbb: {
read,
write
},
getAst,
parse
}
Detalle de qué sería cada una:
Clave | Tipo retorno | Descripción |
---|---|---|
setLanguage(code) |
- |
Setea el código de lenguaje code (por ej. "es" o "en" ). Si no se invoca, todo debería usar "es" como default. |
setInfiniteLoopTimeout(milliseconds) |
- |
Setea el tiempo de espera antes de que un loop infinito tire un error en tiempo de ejecución. Default: 3000 . |
setXGobstonesEnabled(isEnabled) |
- |
Setea un flag que activa o desactiva XGobstones. Default: false . |
read(gbb) |
Board (*1) |
Recibe el gbb como string y retorna su tablero equivalente. |
write(board) |
String |
Recibe el board (mismo formato que (*1)) y retorna un string con el GBB. |
getAst(sourceCode) |
{ tag: Symbol("Name"), contents: [...] } |
Recibe el sourceCode como string y retorna el AST. |
parse(sourceCode) |
ParseResult (*2) |
Recibe el sourceCode como string y retorna un objeto con el resultado. Si falla, tira una excepción ParseError (*3). Inicialmente lo vamos a usar así y por eso preferí dejarlo simple, más adelante podemos extender esto para que reciba más sourceCode s (con argumentos variables) y que pueda recibir o strings u objetos { sourceCode, fileName } para incluir el nombre de archivo. |
{
width: 2,
height: 2,
head: { x: 1, y: 2 },
table: [ // mismo formato que en gs-weblang-cli
[ { red: 1, blue: 4, green: 3, black: 2 }, {} ], // celdas con posiciones (0; 1) y (1; 1)
[ { blue: 9 }, { red: 1, black: 8 } ] // celdas con posiciones (0; 0) y (1; 0)
]
}
(en los métodos que requieran un Board
yo voy a mandar un objeto plano con esas keys)
{
program: {
alias: "interactiveProgram" // o "program" si no es interactivo,
interpret: function(board) { ... } // recibe un Board (*1) y retorna un ExecutionResult (*4) o tira una excepción ExecutionError (*5)
}, // o null si no hay programa, (*6)
declarations: [
// información de funciones y procedimientos (*6)
]
}
{
message: "Token inesperado 'asdasf'", // el mensaje de error localizado al idioma actual
reason: {
code: "unexpected_token", // un código de error único en inglés
detail: ["asdasf"] // (opcional) un objeto cualquiera que dé más detalles sobre el error
},
on: {
range: {
start: {
row: 0,
column: 1
},
end: {
row: 0,
column: 10
}
},
region: "ASD" // idem como en las Snapshots (*7)
}
}
Esto pueden ser dos cosas: NormalExecutionResult
(*4.1) o InteractiveExecutionResult
(*4.2)
{
finalBoard: ..., // un Board (*1) normalito con el último resultado
// (esta key existe solo por comodidad), debería ser igual al (último snapshot).board
snapshots: [
... // un array de varios Snapshot (*7)
],
returnValue: { // valor de retorno del programa
type: "Color", // o Number, o Direction, o Boolean o los que se me estén escapando
value: "Rojo", // o 2 (como Number, sorry D=), o "Norte", o true (como booleano)
} // o null si no retornó nada
}
{
keys: ["K_ARROW_LEFT", "K_ENTER"], // las teclas usadas por el programa
timeout: 400, // la cantidad de ms del timeout, o null si no hay timeout
onInit: function() { },
onKey: function(keyCode) { }, // recibe el keyCode como string, ej: "K_ENTER"
onTimeout: function() { }
// estas tres funciones...
// devuelven un `Board` (*1) o tiran excepción ExecutionError (*5)
// van a ser invocadas al principio, cuando el usuario presione una tecla, y en cada timeout
// ... pero a diferencia de interpret(...) no reciben el tablero (este debe ser guardado en el estado interno del InteractiveExecutionResult)
}
Misma idea que ParserError
(*3) pero con snapshots...
{
message: "El mensaje de error en tiempo de ejecución",
reason: {
code: "boom_called",
detail: { message: "El mensaje de error en tiempo de ejecución" }
},
on: {
range: {
start: {
row: 0,
column: 1
},
end: {
row: 0,
column: 10
}
},
region: "ASD" // idem como en las Snapshots (*7)
},
snapshots: []
}
Es importante que .parse
no rompa si no hay definición de programa, porque actualmente se usa para ver si cosas aisladas (como la biblioteca) tienen errores de compilación o no. Es decir, hacer:
gobstonesInterpreter.parse("procedure Hola() { Poner(Rojo) } \n function devolverDos() { return(2) }")
debería retornar:
{
program: null,
declarations: [
{
alias: "procedureDeclaration",
name: "Hola"
},
{
alias: "functionDeclaration",
name: "devolverDos"
}
]
}
Cada snapshot es un objeto:
{
contextNames: [ 'program', 'Hey2-107430', 'Hey1-104697' ],
board: ..., // el Board (*1) correspondiente a este snapshot
region: "ASD", // esto lo agregué para que cada snapshot diga en qué región pasó cada snapshot (o null si no había ninguna región) para que podamos highlightear bloques
}
La idea es que:
- Cada acción que modifica en algo al tablero final crea un snapshot luego de hacerla. Esto no aplica para funciones ya que su contexto es temporal.
- El snapshot tiene el
contextNames
que representa el stack de llamadas hasta ese punto. A los procedimientos se les agrega un guión "-" y un id pseudo aleatorio, necesario para que desde la GUI se puedan ignorar algunos snapshots y ande bien con los repetidos. - El array de snapshots siempre contiene el tablero inicial (el que se le pasó a
.interpret(...)
al principio y el tablero final al final.
Al ejecutar procedure Hey1() { Mover(Norte) } \n procedure Hey2() { Hey1() \n Mover(Este) } \n program { Poner(Rojo) \n Hey2() \n Poner(Azul) }
esto debería generar los siguientes snapshots:
[
{
contextNames: [],
board: ... // el Board (*1) correspondiente al tablero inicial recibido
},
{
contextNames: [ 'program' ],
board: ... // el Board (*1) luego del primer Poner(Rojo)
},
{
contextNames: [ 'program', 'Hey2-107430', 'Hey1-104697' ],
board: ... // el Board (*1) luego del Mover(Norte)
},
{
contextNames: [ 'program', 'Hey2-107430' ],
board: .... // el Board (*1) luego del Mover(Este)
},
{
contextNames: [ 'program' ],
board: ... // el Board (*1) luego del Poner(Azul)
}
]
-> Donde corresponda podrías agregar métodos para interactuar con los pragmas, como no vi todavía aún cómo está hecho eso te lo dejo a vos.
Fijate si tiene sentido y cualquier cosa me decís @foones 😃