Nuevo lenguaje de programación Mash

Durante varios años intenté desarrollar mi propio lenguaje de programación. Quería crear, en mi opinión, el lenguaje más simple, completamente funcional y conveniente posible.

En este artículo quiero resaltar las principales etapas de mi trabajo y, para empezar, describir el concepto creado del lenguaje y su primera implementación, en la que estoy trabajando actualmente.

Déjame decirte de antemano que escribí todo el proyecto en Free Pascal, porque... Los programas que contiene se pueden ensamblar para una gran cantidad de plataformas, y el compilador en sí produce binarios muy optimizados (colecciono todos los componentes del proyecto con la bandera O2).

Tiempo de ejecución del idioma

En primer lugar, vale la pena hablar de la máquina virtual que tuve que escribir para ejecutar futuras aplicaciones en mi idioma. Decidí implementar una arquitectura de pila, tal vez porque era la forma más sencilla. No encontré ni un solo artículo normal sobre cómo hacer esto en ruso, así que después de familiarizarme con el material en inglés, me senté a diseñar y escribir mi propia bicicleta. A continuación expondré mis ideas y desarrollos “avanzados” en esta materia.

Implementación de pila

Obviamente, en la parte superior de la VM está la pila. En mi implementación funciona en bloques. Básicamente, se trata de una matriz simple de punteros y una variable para almacenar el índice de la parte superior de la pila.
Cuando se inicializa, se crea una matriz de 256 elementos. Si se insertan más punteros en la pila, su tamaño aumenta en los siguientes 256 elementos. En consecuencia, al retirar elementos de la pila, se ajusta su tamaño.

La VM utiliza varias pilas:

  1. Pila principal.
  2. Una pila para almacenar puntos de retorno.
  3. Pila de recolector de basura.
  4. Pruebe/capture/finalmente bloquee la pila de controladores.

Constantes y variables

Éste es sencillo. Las constantes se manejan en un pequeño fragmento de código separado y están disponibles en aplicaciones futuras a través de direcciones estáticas. Las variables son una matriz de punteros de cierto tamaño, el acceso a sus celdas se realiza mediante índice, es decir, dirección estática. Las variables se pueden enviar a la parte superior de la pila o leer desde allí. En realidad, porque Si bien nuestras variables esencialmente almacenan punteros a valores en la memoria de la VM, en el lenguaje predomina el trabajo con punteros implícitos.

Recolector de basura

En mi VM es semiautomático. Aquellos. el propio desarrollador decide cuándo llamar al recolector de basura. No funciona con un contador de puntero normal, como en Python, Perl, Ruby, Lua, etc. Se implementa a través de un sistema de marcadores. Aquellos. Cuando se pretende asignar un valor temporal a una variable, se agrega un puntero a este valor a la pila del recolector de basura. En el futuro, el recopilador revisa rápidamente la lista de sugerencias ya preparada.

Manejo de bloques try/catch/finally

Como ocurre con cualquier lenguaje moderno, el manejo de excepciones es un componente importante. El núcleo de la VM está envuelto en un bloque try..catch, que puede volver a la ejecución del código después de detectar una excepción al insertar cierta información sobre ella en la pila. En el código de la aplicación, puede definir bloques de código try/catch/finalmente, indicando puntos de entrada en catch (controlador de excepciones) y finalmente/end (final del bloque).

subprocesos múltiples

Es compatible a nivel de VM. Es simple y cómodo de usar. Funciona sin un sistema de interrupciones, por lo que el código debería ejecutarse en varios subprocesos varias veces más rápido, respectivamente.

Bibliotecas externas para máquinas virtuales

No hay manera de prescindir de esto. VM admite importaciones, de forma similar a cómo se implementa en otros idiomas. Puede escribir parte del código en Mash y parte del código en idiomas nativos y luego vincularlos en uno.

Traductor del lenguaje Mash de alto nivel al código de bytes para máquinas virtuales

lenguaje intermedio

Para escribir rápidamente un traductor desde un lenguaje complejo a código VM, primero desarrollé un lenguaje intermedio. El resultado fue un espectáculo terrible, parecido al de un ensamblador, que no tiene sentido considerar aquí. Sólo diré que en este nivel el traductor procesa la mayoría de las constantes y variables, calcula sus direcciones estáticas y las direcciones de los puntos de entrada.

Arquitectura del traductor

No elegí la mejor arquitectura para la implementación. El traductor no construye un árbol de código como lo hacen otros traductores. Mira el comienzo de la estructura. Aquellos. Si el fragmento de código que se está analizando se parece a “ while <condición>:”, entonces es obvio que se trata de una construcción de bucle while y debe procesarse como una construcción de bucle while. Algo así como una compleja caja de interruptores.

Gracias a esta solución arquitectónica, el traductor resultó no ser muy rápido. Sin embargo, la facilidad de su modificación ha aumentado significativamente. Agregué las estructuras necesarias más rápido de lo que mi café podía enfriarse. El soporte completo de programación orientada a objetos se implementó en menos de una semana.

Optimización de código

Aquí, por supuesto, se podría haber implementado mejor (y se implementará, pero más tarde, cuando uno se ponga manos a la obra). Hasta ahora, el optimizador solo sabe cómo eliminar el código, las constantes y las importaciones no utilizadas del ensamblado. Además, varias constantes con el mismo valor se reemplazan por una. Eso es todo.

lenguaje puré

Concepto básico del lenguaje.

La idea principal era desarrollar el lenguaje más funcional y sencillo posible. Creo que el desarrollo hace frente a su tarea a la perfección.

Bloques de código, procedimientos y funciones.

Todas las construcciones del idioma se abren con dos puntos. : y son cerrados por el operador final.

Los procedimientos y funciones se declaran como proc y func, respectivamente. Los argumentos se enumeran entre paréntesis. Todo es como la mayoría de los otros idiomas.

Operador volvemos puedes devolver un valor de una función, operador romper le permite salir del procedimiento/función (si está fuera de los bucles).

Ejemplo de código:

...

func summ(a, b):
  return a + b
end

proc main():
  println(summ(inputln(), inputln()))
end

Diseños compatibles

  • Bucles: for..end, while..end, hasta...end
  • Condiciones: if..[else..]end, switch..[case..end..][else..]end
  • Métodos: proc <nombre>():... fin, func <nombre>():... fin
  • Etiqueta y ir a: <nombre>:, saltar <nombre>
  • Enumeraciones enumeradas y matrices constantes.

Variables

El traductor puede determinarlos automáticamente o si el desarrollador escribe var antes de definirlos.

Ejemplos de código:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

Se admiten variables globales y locales.

OOP

Bueno, hemos llegado al tema más delicioso. Mash soporta todos los paradigmas de programación orientada a objetos. Aquellos. clases, herencia, polimorfismo (incluida la dinámica), reflexión automática dinámica e introspección (completa).

Sin más preámbulos, es mejor dar ejemplos de código.

Una clase simple y trabajando con ella:

uses <bf>
uses <crt>

class MyClass:
  var a, b
  proc Create, Free
  func Summ
end

proc MyClass::Create(a, b):
  $a = new(a)
  $b = new(b)
end

proc MyClass::Free():
  Free($a, $b)
  $rem()
end

func MyClass::Summ():
  return $a + $b
end

proc main():
  x ?= new MyClass(10, 20)
  println(x->Summ())
  x->Free()
end

Salida: 30.

Herencia y polimorfismo:

uses <bf>
uses <crt>

class MyClass:
  var a, b
  proc Create, Free
  func Summ
end

proc MyClass::Create(a, b):
  $a = new(a)
  $b = new(b)
end

proc MyClass::Free():
  Free($a, $b)
  $rem()
end

func MyClass::Summ():
  return $a + $b
end

class MyNewClass(MyClass):
  func Summ
end

func MyNewClass::Summ():
  return ($a + $b) * 2
end

proc main():
  x ?= new MyNewClass(10, 20)
  println(x->Summ())
  x->Free()
end

Salida: 60.

¿Qué pasa con el polimorfismo dinámico? ¡Sí, esto es reflexión!:

uses <bf>
uses <crt>

class MyClass:
  var a, b
  proc Create, Free
  func Summ
end

proc MyClass::Create(a, b):
  $a = new(a)
  $b = new(b)
end

proc MyClass::Free():
  Free($a, $b)
  $rem()
end

func MyClass::Summ():
  return $a + $b
end

class MyNewClass(MyClass):
  func Summ
end

func MyNewClass::Summ():
  return ($a + $b) * 2
end

proc main():
  x ?= new MyClass(10, 20)
  x->Summ ?= MyNewClass::Summ
  println(x->Summ())
  x->Free()
end

Salida: 60.

Ahora tomemos un momento para hacer introspección en busca de valores y clases simples:

uses <bf>
uses <crt>

class MyClass:
  var a, b
end

proc main():
  x ?= new MyClass
  println(BoolToStr(x->type == MyClass))
  x->rem()
  println(BoolToStr(typeof(3.14) == typeReal))
end

Salida: verdadero, verdadero.

Acerca de los operadores de asignación y los punteros explícitos

El operador ?= se utiliza para asignar a una variable un puntero a un valor en la memoria.
El operador = cambia un valor en la memoria usando un puntero de una variable.
Y ahora un poco sobre sugerencias explícitas. Los agregué al idioma para que existan.
@<variable>: toma un puntero explícito a una variable.
?<variable>: obtiene una variable mediante un puntero.
@=: asigna un valor a una variable mediante un puntero explícito a ella.

Ejemplo de código:

uses <bf>
uses <crt>

proc main():
  var a = 10, b
  b ?= @a
  PrintLn(b)
  b ?= ?b
  PrintLn(b)
  b++
  PrintLn(a)
  InputLn()
end

Salida: algún número, 10, 11.

Intenta...[capturar...][finalmente...]finalizar

Ejemplo de código:

uses <bf>
uses <crt>

proc main():
  println("Start")
  try:
    println("Trying to do something...")
    a ?= 10 / 0
  catch:
    println(getError())
  finally:
    println("Finally")
  end
  println("End")
  inputln()
end

Planes para el futuro

Sigo mirando y mirando GraalVM & Truffle. Mi entorno de ejecución no tiene un compilador JIT, por lo que en términos de rendimiento actualmente solo compite con Python. Espero poder implementar la compilación JIT basada en GraalVM o LLVM.

repositorio

Puedes jugar con los desarrollos y seguir el proyecto tú mismo.

sitio web
Repositorio en GitHub

Gracias por leer hasta el final si lo hiciste.

Fuente: habr.com

Añadir un comentario