View on GitHub

Como criar uma linguagem no GNU/Linux

Exemplo do livro 'The Unix Programming Environment' com explicações em PT-BR

Construção da linguagem hoc

Porque isso é interessante

No capítulo 8 do livro The Unix Programming Environment (UPE), Brian W. Kernighan e Rob Pike mostram como construir uma uma pequena linguagem Turing-completa chamada hoc (higher-order calculator).

Em UPE, hoc serve para apresentar as seguintes ferramentas do ambiente UNIX:

Estudar a implementação de hoc em C é uma boa maneira de aprender como funciona um interpretador por dentro. Um conhecimento básico de C é suficiente para acompanhar este exemplo.

🗒 Desde que o GNU Linux substituiu no mercado os UNIX proprietários, yacc e lex também foram superados por ferramentas livres mais modernas: GNU bison e flex. Em muitos ambientes, ao instalar bison e flex você ganha também atalhos chamados yacc e lex que emulam o funcionamento das ferramentas antigas.

Minha motivação

Decidi estudar este exemplo para aprender o básico de lex e yacc, antes de estudar o pacote SLY de David Beazley, que implementa funcionalidade semelhante em Python, usando metaprogramação em vez de geração de código. Meu plano é usar SLY na Oficina de Linguagens de Programação do Garoa Hacker Clube. Como achei o exemplo hoc muito interessante, e não encontrei o livro UPE em português, resolvi contribuir para a nossa cultura de computação apresentando esse exemplo em nosso idioma.

Características de hoc

Exemplo

Exibir números da sequência de Fibonacci menores do que 1000:

$ ./hoc fib.hoc 
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987

Código-fonte de fib.hoc:

proc fib() {
	a = 0
	b = 1
	while (b < $1) {
		print b
		c = b
		b = a+b
		a = c
	}
	print "\n"
}
fib(1000)

No código acima, a variável $1 é o primeiro argumento passado para fib. A palavra reservada proc serve para declarar um procedimento: uma sub-rotina que não devolve um valor, assim como um médodo do tipo void em Java. Para declarar uma função que devolve um número em hoc, usa-se func.

🗒 A distinção entre procedimentos e funções é natural para quem já programou em Pascal ou Delphi. Muitas linguagens modernas não separam os dois conceitos claramente. Por exemplo, em Python não há procedimentos, há apenas funções que não devolvem nenhum valor explicitamente mas, implicitamente, devolvem o valor None, que a gente ignora.

O que há neste repositório

Os textos em português não são uma tradução direta do original e sim uma recriação com minhas próprias palavras. Em alguns pontos minhas explicações são mais detalhadas, em outros pontos são mais resumidas. O capítulo original tem cerca de 50 páginas, e o assunto é bem amplo, então em vários pontos os autores se desculpam pela abordagem rápida e superficial. Eu certamente não conheço o assunto melhor que eles.

Os códigos em C e yacc são razoavelmente fiéis ao original, exceto por algumas simplificações e também porque eu traduzi todos os identificadores de variáveis e funções que podiam ser traduzidos. Isso torna mais fácil para o leitor saber o que são identificadores nossos, como NUMERO e aviso, e o que são identificadores externos que somos obrigados a preservar, como main ou yylex.

O diretório /complete é um fork do repositório richardfearn/hoc no GitHub. Richard Fearn modificou código original de 1984 para compilar e executar em um sistema GNU/Linux em 2012. Em 1/jan/2019, não está acessível o site onde o código original foi publicado (http://netlib.bell-labs.com/cm/cs/upe/).

🗒 Você pode compilar o programa rodando make no diretório /complete, desde que tenha instalado as ferramentas de desenvolvimento do seu sistema, incluindo bison e flex.

O diretório /docs contém esta página que você está lendo e as explicações das etapas do desenvolvimento de hoc.

Os demais diretórios contém as 6 etapas da construção de hoc, como descrito em The Unix Programming Environment.

Índice de páginas

LR