27 diciembre, 2011


PRESENTACIÓN DEL LENGUAJE ABDLAB-MANCER


Mancer es un lenguaje de nivel medio-alto, procedural y simple. Permite crear códigos complejos para la máquina virtual abdlab turbotron (y estoy trabajando en una versión para (jenova). La estructura interna del mismo tiende a ser minimalista, una órden se compila a muy pocas líneas en turbotronasm (a partir de ahora ttasm) lo que permite además un alto control del lenguaje máquina por parte del programador. Es algo diferente a la mayor parte de lenguajes en varios aspectos, la diferencia más notable es la sintaxis de las ordenes matemáticas ya que estas se deben ejecutar de una en una y el programador indicará en qué bloque de memoria guardará el resultado; Esto es así para evitar que los métodos planeados en el codigo fuente sean muy similares a los del código objeto, Mancer enmascara Ttasm. A pesar de ser ejecutadas de una en una, para facilitar la lectura las órdenes pueden ser apiladas en la misma línea para agrupar cada serie de pasos de una gran operación. Ésta propiedad del lenguaje se usa también en las definiciones de variables, de nuevo por facilidad de lectura se apilan los parámetros tras el nombre de la función.

Otra parte interesante es el interface de entrada-salida (al que llamo de movimientos de datos) que se escribe usando los operadores ">>" (mover desde origen) y "<<" (mover a destino) lo cual simplifica todo bastante al equiparar abstractamente todas las entradas y salidas. Las cadenas de texto se escriben sin usar "", en caso de que el nombre introducido coincida con el nombre de una variable el valor será sustituido por dicho valor (en jenova incluiré un caracter para introducir literales). Los tipos de datos se administran internamente, el usuario no tiene un control directo sobre éstos, pero existen y la máquina los usa.

los tipos son:

var: no es un tipo, indica que se aceptará cualquier tipo de datos. (3, 5.5, cualquier

cosa, incluso esto)

int: número entero de longitud variable. (5, 10500, 7585895)

flo: número de coma flotante, es decir, fraccionado. (1.0, 0.03, 3.1416)

str: cadena de texto, de longitud variable. (A, hola mundo)

Todas las órdenes deben de contener los símbolos "(" y ")" aunque no se utilicen en cierto caso concreto, ya que son los encargados de indicar al intérprete que deben de leer la siguiente órden. Hay dos maneras de introducir un comentario, Un comentario del primer modo será desechado por el intérprete #(str_comentario), es decir, que desaparecerá al ser compilado, el otro modo es la función comentario(str_comentario) que incluirá el comentario en el código ttasm en el lugar correspondiente, puede ser muy útil para etiquetar zonas para su posterior analisis o edición a mano.


Está estructurado en bloques, cada bloque puede contener procedimientos, funciones o código lineal. Un bloque puede siempre llamar a las funciones contenidas en cualquier otro bloque sin restricciones lo cual facilita la reutilización de bloques de funciones (bloque-librería o bloque sin main). La primera línea se usa para indicar el nombre del programa y no se aconseja introducir código en ella.

A grandes rasgos hay 4 tipos de órdenes en Mancer e if:

1- sentencias y movimientos de datos.
2- llamadas y definiciones de funciones o procedimientos
3- tests lógicos
4- operaciones aritméticas y lógicas
5- if

1- sentencias y movimientos de datos:
Sentenciar una variable es darle un valor. En caso de que la variable no exista esta será creada, pero pueden ser creadas con antelación usando la función crear (str_nombre,var_valor). Los movimientos de datos, como he dicho antes, son un interface para simplificar las entradas y las salidas más usadas, hay dos tipos, desde orígenes y hasta destinos. Es como si se tratara de una operación entre dos operadores, el primero es la parte estática y el segundo el valor o parte dinámica, en el modo desde origen la parte estática representa una entrada y la parte dinámica un nombre de variable donde guardar el dato de la estática. En el modo hasta destino la parte estática representa la salida a la que se enviarán los datos del valor. Las partes estáticas disponibles hacen referencia a las entradas o salidas, algunas, como el counter, pueden existir en los dos modos, otros, como la entrada de keypad sólamente sirven para uno de los modos.

 - entradas mover a: estática >> dinámica()

La parte estática puede ser una de las siguientes:

pointer -guarda la posición actual del cabezal lector del programa en la variable indicada.

key -guarda el id de ultima tecla de keypad pulsada en la variable indicada. (NO COMPATIBLE CON JENOVA, usará fakedma *1)

bus -guarda el valor del ultimo dato de hardware recibido en la variable indicada. (NO COMPATIBLE CON JENOVA, usará fakedma)

archivo -guarda todo el texto del archivo indicado (entre paréntesis) en la variable indicada. (REQUISITO EN JENOVA, necesitará periférico *2)

counter -guarda el valor de counter en la variable indicada. (NO COMPATIBLE CON JENOVA, no usa counter)

ejemplos:

pointer >> V_tempP()
archivo >> Todo_El_Texto(str_nombrearchivo)

 - salidas mover de: estática << valor()

chat -envía el valor a la salida de chat (canal indicado en el paréntesis final).

consola -envía el valor a la salida de consola (ftlout).

bus -envía el valor a través del bus indicado entre el paréntesis [ ej. bus << datos(8) ]. (DIFERENCIAS EN JENOVA, el protocolo es diferente *3)

archivo -envía el valor al arvchivo indicado entre el paréntesis [ ej. archivo << texto texto texto fin(file.txt)].

counter -envía el valor (que debe de ser integer) al contador del procesador.

ejemplos:

chat << hola_mundo()
archivo << linea de texto 1 /n linea2 /n linea3()
bus << datos_xxxx(64)

Así pues, si queremos hacer un miniprograma que lea un archivo de texto y lo escriba por el chat haremos lo siguiente:

archivo >> tmp_var1(str_archivo_ejemplo)
chat << tmp_var1()

¿Sencillo, no?, ésta no es la única forma de manejar las entradas o salidas, existen también unas pocas funciones con ese fin, pero esta sintaxis puede simplificar el código muchísimo a la vista del programador.

2- llamadas y definiciones de funciones o procedimientos:
Como en la mayoría de lenguajes mancer incluye una serie de funciones o procedimientos

básicos integrados para la interacción básica con la máquina. Mancer es un lenguaje pequeño, lo que quiere decir que se integran muy pocos procedimientos nativos, pero son más que suficientes para crear comportamientos mucho más complejos.

bloque(str_block): ejecutar el bloque indicado en "str_block"

llamar(str_proc,p1,p2): ejecutar el procedimiento indicado en primer parametro (p1,p2) son opcionales, puede haber cualquier numero de ellos

fin(): finaliza el programa o returna el proceso
_________
usar(str_bib): carga la biblioteca indicada por nombre para poder usar sus funciones (una biblioteca es un código sin funcion main)

#(str_txt): deja un comentario en el programa resultante con el valor indicado en "txt"

variable(str_nombre,var_valor): guardar o crear una variable con el nombre y valor indicados para ser usada posteriormente
_________
escribir(str_texto): escribe en pantalla o consola el texto o variable guardada indicado

chat(str_texto): escribe "str_texto" en el chat

pedir(str_texto,destino): muestra "str_texto" y espera a que el usuario introduzca datos que se guardaran en la variable "destino"
_________
posicion(int_pos): da un nombre a ésta posición para poder volver luego

saltar(int_pos): salta a la posicion indicada en pos y continua el programa desde ahí

las definiciones de procedimientos son una pequeña herramienta para crear tus propias variables y reutilizarlas cuando se quiera, el mecanismo es muy diferente a otros lenguajes, pero el resultado es conceptualmente el mismo. La principal diferencia está en el modo de indicar los parámetros que necesitará nuestro procedimiento. Para declarar un procedimiento sin parámetros se usará la función >(str_nombre_proc) para indicar el final de la misma se usará <(), podremos llamar a dicha función usando call(str_nombre_proc) desde cualquier parte del código. De este modo, si queremos crear un procedimiento con el

código del ejemplo anterior haremos lo siguiente.

>(leer_archivo)
archivo >> tmp_var1(archivo_ejemplo)
chat << tmp_var1()
<()

Y para llamarla símplemente: call(leer_archivo), pero quizas queremos indicar expresamente qué archivo leer en cada momento, para eso necesitaremos añadir parámetros al código, se consigue con el uso de param() o -() (son equivalentes). Entonces, si queremos añadir un parámetro que indique el nombre del archivo a leer el código quedaría así.

>(leer_archivo)-(nA1)
archivo >> tmp_var1(nA1)
chat << tmp_var1()
<()

Y la llamaremos así:

call(leer_archivo,archivo_ejemplo)

podemos añadir cualquier número de parámetros a nuestras funciones y siempre que las llamemos deberemos introducir los valores deseados para estos separados por una coma, igual que en el último ejemplo.

3- tests lógicos:
Los tests lógicos son pequeñas comprobaciones que dan como resultado un sólo bit, hay cuatro de ellos y dos sólo funcionan sobre integer o float. Son sencillos de entender, es como hacer una pregunta simple a la máquina sobre la relación entre dos valores y ésta nos responde con un Si o un No. Para la explicación llamaremos "a" al primer valor y "b" al segundo, aunque en realidad pueden ser cualquier valor o variable. En el paréntesis se indicará el nombre de la variable en la que quedará la respuesta para ser utilizada a posteriori, aquí usaré "bit_check" pero puede ser usado cualquier nombre.

¿es a igual que b? a == b(bit_check)

¿es a distinto que b? a != b(bit_check)

¿es a mayor que b? a > b(bit_check)

¿es a menor que b? a < b(bit_check)


por ejemplo, tras ejecutar esto: 1 > 100(tmp_bit) en tmp_bit tendrémos el valor 0, ya que es falso que 1 sea mayor que 100.

4- operaciones con aritmética y lógica:
Es quizás la parte que menos se parece a otros lenguajes. Mancer ejecuta las operaciones siempre de una en una, y siempre deberemos indicar en el paréntesis dónde quedará la respuesta. Los operadores admitidos hasta ahora son:

aritméticos:
+ sumar
- restar
* multiplicar
/ dividir
& concatenar

lógicos:
AND y boleano
OR o boleano
NOT no boleano

Por ejemplo, si queremos hacer una operación simple cómo 5*5 y guardar el resultado en la variable a_res:

5 * 5(a_res)

Y como las operaciones se pueden apilar y son leidas en orden podemos hacer operaciones más complejas controlando el órden de las mismas,  por ejemplo:

1-5*2-(2+6)=

5 * 2(a_res)1 - a_res(a_res)2 + 6(b_res)a_res - b_res(a_res)

2*pi*r=

2 * pi(a_res)a_res * r(a_res)

(1 and 0) or ((0 and 1) or 1)

0 and 1(a_res)a_res or 1(a_res)1 and 0(b_res)a_res or b_res(a_res)

Es cierto que queda menos legible, pero se tiene control total en el órden de las operaciones y sobre todo en la administración de la memoria temporal. Yo he utilizado el mínimo posible de variables en este ejemplo, pero es posible que se busque guardar los números intermedios con los que se ha llegado hasta el resultado final, por ejemplo si queremos guardar 2*pi y hacer 2*pi*r de una misma vez:

2 * pi(b_res)b_res * r(a_res)

así b_res contiene 2*pi y a_res la operación completa.

5- if
Mancer usa un sistema if minimalista el cual sobre todo simplifica el intérprete (lo siento, programador), if sólo tiene un parámetro que debe de ser un valor boleano, "1" o "0" y no una expresión completa, si se apila tras tests lógicos y operaciones se consigue una funcionalidad igual:

si 3+2 es mayor que "variable" di "OH MY GOD" por el chat:

3 + 2(var1)var1 > variable(var1)if(var1)
chat << OH MY GOD()
end if()

Esta parte puede parecer poco intuitiva, pero con un poco de costumbre se asimila bien, hay que asegurarse siempre de reducir bien la expresión a un sólo bit ya que en caso contrario recibiremos como respuesta siempre 0.

EJEMPLOS DE CÓDIGO MANCER:


chatlogger (guarda todo lo que escucha por el chat en un archivo):




(chatlogger)
 call(escuchar)
>(escuchar)
 posicion(escuchar_more)
 pedir(,tmp001)
 archivo >> tmp002(chat-logs)
 tmp002 & /n(tmp002)tmp002 & tmp001(tmp002)
 archivo << tmp002(chat-logs)
 saltar(escuchar_more)
 <()

propiedades del programa objeto:
1 procedimiento de usuario
35 bloques de memoria (líneas de código maquina)
3512 bits brutos aprox


chatbot simple y escalable para Patxi1 (responde a preguntas simples, no analiza, solo coincidencias exactas)

(stupid chatbot simple)
 pedir(escuchando,msg)
 msg == help(bit)if(bit)
 chat << aqui podria dar ayuda o listar las preguntas aceptadas... mi creador es abderramah()
 asm(goto main)
 end if()
 msg == hola(bit)msg == hola bot(bit1)bit or bit1(bit)if(bit)
 chat << hola!()
 asm(goto main)
 end if()
msg == ¿como te llamas?(bit)msg == ¿cual es tu nombre?(bit1)bit or bit1(bit)if(bit)
 chat << me llamo PATXI I()
 asm(goto main)
 end if()
msg == ¿quien te ha creado?(bit)msg == ¿quien te ha fabricado?(bit1)msg == ¿quien te ha construido?(bit2)bit or bit1(bit)bit or bit2(bit)if(bit)
 chat << mi creador es ABDERRAMAH()
 end if()
msg == ¿que tal?(bit)msg == ¿como estas?(bit1)bit or bit1(bit)if(bit)
 chat << bien, gracias, y tu?()
 asm(goto main)
 end if()
 msg == estoy bien(bit)if(bit)
 chat << me alegro()
 asm(goto main)
 end if()
 asm(goto main)




propiedades del programa objeto:
0 procedimiento de usuario
304 bloques de memoria (líneas de código maquina)
32544 bits brutos aprox

Se puede comprobar que, comparando con el lenguaje lsl, mancer es mucho más corto e
intuitivo (a excepción de las operaciones), ya que se ejecuta sobre una máquina virtual multipropósito no orientada a eventos (como lo es el lenguaje lsl). El código es más lineal en un principio, aunque se puede crear la ilusión de eventos, tan sólo hace falta un poco de estructura. Mancer está diseñado para simplificar la creación de código en turbotron y jenova y en cuanto a eso hace su trabajo muy bien, la abstracción es máxima mientras la diferencia entre un lenguaje y el otro es mínima (mínima dentro de lo que cabe esperar entre un ensamblador y  un lenguaje estructurado de nivel medio-alto). Por otro lado es perfecto para diseñar lenguajes de más alto nivel que en lugar de compilar diréctamente a ttasm utilice mancer como lenguaje intermedio y para cualquier código que use gran número de operaciones (lo cual es muy tedioso en ttasm).



NOTAS:

*- A partir de la versión jenova espero añadir compatiblidad para arrays, en arquitectura turbotron se hacía demasiado complicado.

*- En la versión jenova estoy reestructurando el sistema de movimientos de datos y de entrada y salida de bus.

*- La versión turbotron quedará desmantenida en cuanto jenova sea un hecho.

*- Existe un editor llamado Electromancer que tiene tres versiones, beta, postbeta y godmode. La más completa es godmode, que incluye compatibilidad con el bot patxi1 y con el micro sistema operativo abdlab CCP.

NOTAS 2:

*1- fakedma es el sistema de entrada cacheada activa que permite escuchar en cualquier momento y procesar con "multi", inspirado en la arquitectura dma, aunque no tiene mucho que ver en cuanto a su protocolo y funcionamiento.

*2- jenova necesita un periférico para escribir texto, ya que si no se crearía un requisito especial de permisos no deseado como el de turbotron por usar osfunctions.

*3- mientras que turbotron solo puede enviar una cadena simple de texto a traves de sus buses jenova envía dos simultaneamente, ( bus << datos, datos2(bus) )


ABDLAB SOFTWARE EXPERIMENTAL - 27 dic 2011 - ( abderramah@gmail.com -en
osgrid ABDERRAMAH Scripter )

HAPPY CODE
Publicar un comentario en la entrada