Nova linguagem de programação Mash

Durante vários anos, tentei desenvolver minha linguagem de programação. Eu queria criar na minha opinião a linguagem mais simples, totalmente funcional e conveniente.

Neste artigo, quero destacar as principais etapas do meu trabalho e começar descrevendo o conceito criado da linguagem e sua primeira implementação na qual estou trabalhando atualmente.

Eu digo com antecedência que escrevi o projeto inteiro no Free Pascal, porque Os programas nele podem ser montados para um grande número de plataformas, e o próprio compilador produz binários muito otimizados (eu coleciono todos os componentes do projeto com o sinalizador O2).

Tempo de execução do idioma


A primeira coisa a se falar é sobre uma máquina virtual que precisei escrever para executar aplicativos futuros no meu idioma. Decidi implementar uma arquitetura de pilha, talvez, porque era a maneira mais fácil. Não encontrei um único artigo normal sobre como fazer isso em russo; portanto, depois de ler o material em inglês, sentei-me para projetar e escrever minha bicicleta. Além disso, darei minhas idéias e desenvolvimentos "avançados" neste assunto.

Implementação de pilha


Obviamente, a VM está no topo da pilha. Na minha implementação, ele funciona em blocos. Em essência, essa é uma matriz simples de ponteiros e uma variável para armazenar o índice da parte superior da pilha.
Quando é inicializado, uma matriz de 256 elementos é criada. Se mais ponteiros forem lançados na pilha, seu tamanho será aumentado pelos próximos 256 elementos. Assim, ao remover itens da pilha, seu tamanho é ajustável.

Uma VM usa várias pilhas:

  1. A pilha principal.
  2. Pilha para armazenar pontos de retorno.
  3. Pilha de coletor de lixo.
  4. O manipulador de pilha dos blocos try / catch / finalmente.

Constantes e variáveis


Com isso, tudo é simples. As constantes são processadas em um pequeno pedaço de código separado e estão disponíveis em aplicativos no futuro em endereços estáticos. As variáveis ​​são uma matriz de ponteiros de um determinado tamanho; o acesso às células é realizado pelo índice - ou seja, endereço estático. As variáveis ​​podem ser colocadas no topo da pilha ou lidas a partir daí. Na verdade, porque nossas variáveis ​​essencialmente armazenam ponteiros para valores na memória da VM e, em seguida, trabalham com ponteiros implícitos predominando no idioma.

Coletor de lixo


Na minha VM é semi-automático. I.e. o desenvolvedor decide quando chamar o coletor de lixo. Ele não funciona de acordo com o contador comum de ponteiros, como no mesmo Python, Perl, Ruby, Lua, etc. É implementado através de um sistema de marcadores. I.e. quando se entende que uma variável recebe um valor temporário, um ponteiro para esse valor é adicionado à pilha do coletor de lixo. No futuro, o coletor percorre rapidamente uma lista já preparada de indicadores.

Manipulando blocos try / catch / finalmente


Como em qualquer linguagem moderna, o tratamento de exceções é um componente importante. O kernel da VM é agrupado em um bloco try..catch, que pode retornar à execução do código após capturar a exceção, colocando um pouco de informação sobre ele na pilha. No código do aplicativo, você pode especificar blocos de código try / catch / finalmente, indicando os pontos de entrada a serem capturados (manipulador de exceções) e finalmente / fim (fim do bloco).

Multithreading


É suportado no nível da VM. É simples e conveniente de usar. Como funciona sem um sistema de interrupção, o código deve ser executado em vários threads várias vezes mais rápido, respectivamente.

Bibliotecas externas para VM


Não há como ficar sem isso. A VM suporta importações, assim como é implementada em outros idiomas. Você pode escrever parte do código no Mash e parte do código em idiomas nativos e vinculá-los.

Tradutor do idioma de alto nível Mash para bytecode for VM


Linguagem intermediária


Para escrever rapidamente um tradutor de um idioma complexo para o código da VM, primeiro desenvolvi um idioma intermediário. Acabou sendo uma visão assustadora, como uma montadora, que não há nenhum sentido especial a considerar aqui. Só posso dizer que, nesse nível, o tradutor processa a maioria das constantes, variáveis, calcula seus endereços estáticos e endereços de pontos de entrada.

Arquitetura do tradutor


Eu não escolhi a melhor arquitetura para implementação. O tradutor não cria uma árvore de códigos, como convém a outros tradutores. Ele olha para o início da construção. I.e. se o trecho de código analisado se parecer com "while <condition>:", é óbvio que essa é uma construção while do loop e precisa ser processada como a construção do loop while. Algo como um caso de troca complicado.

Graças a essa solução arquitetônica, o tradutor não foi muito rápido. No entanto, a simplicidade de seu refinamento aumentou significativamente. Adicionei os desenhos necessários mais rapidamente do que meu café poderia esfriar. O suporte total ao POO foi implementado em menos de uma semana.

Otimização de código


Aqui, é claro, poderia ser realizado melhor (e será realizado, mas mais tarde, quando as mãos chegarem). Até o momento, o otimizador sabe apenas como cortar código, constantes e importações não usadas do assembly. Além disso, várias constantes com o mesmo valor são substituídas por uma. Isso é tudo.

Idioma mash


O conceito básico de linguagem


A idéia principal era desenvolver a linguagem mais funcional e simples. Eu acredito que o desenvolvimento lida com sua tarefa com um estrondo.

Blocos de código, procedimentos e funções


Todas as construções no idioma são abertas com dois pontos : e fechadas com o operador final .

Procedimentos e funções são declarados como proc e func, respectivamente. Os argumentos estão listados entre parênteses. Assim como a maioria dos outros idiomas.

A instrução return pode retornar um valor de uma função, a instrução break permite que você saia de um procedimento / função (se estiver fora dos loops).

Exemplo de código:

...

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

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


  • : for..end, while..end, until..end
  • : if..[else..]end, switch..[case..end..][else..]end
  • : proc <>():… end, func <>():… end
  • Label & goto: <>:, jump <>
  • Enum .


, var .

:

a ?= 10
b ?= a + 20

var a = 10, b = a + 20

.


. Mash - . .. , , ( .. ), ().

, .

:

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

: 30.

:

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

: 60.

? !:

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

: 60.

:

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

: true, true.


?= .
= .
. .
@<> — .
?<> — .
@= — .

:

uses <bf>
uses <crt>

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

: - , 10, 11.

Try..[catch..][finally..]end


:

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


GraalVM & Truffle. JIT , . , JIT GraalVM LLVM.


.


GitHub

, , .

Source: https://habr.com/ru/post/pt434966/


All Articles