Campa

travis build (shildes.io) badge coverage (shildes.io) badge gem version (shildes.io) badge yard docs

Um LISP implementado em Ruby.

XKCD lisp cycles comic strip

Vem equipado com um REPL e um framework de testes (extremamente rudimentar).

Você pode instalar essa gem normalmente com:

$ gem install campa

E depois é possível iniciar o REPL:

$ campa

Um prompt será exibido sinalizando que você pode verificar se Campa está funcionando através de um oferecimento aos deuses da programação:

=> (defun hello-world () (print "hello world"))
(lambda () (print "hello world"))
=> (hello-world)
"hello world"NIL

O que é isso tudo afinal?

Antes de mais nada você provavelmente precisa saber que, em termos de implementação de LISP, essa aqui certamente está do lado EXAGERADAMENTE simples do dispostivo de medição - seja lá qual for essa ferramenta.

O principal propósito desse projeto é a criação de um ambiente para que o autor possa aprender sobre LISP e implementação de linguagens de programação. Eu aconselharia uma baixa na proverbial expectativa antes de usar/ler isso aqui.

Paul Graham's The Roots of Lisp

Existe esse artigo por Paul Graham chamado The Roots of Lisp. Até onde consigo dizer esse é um trabalho seminal que auxiliou muitos a entenderem LISP melhor e talvez até tenha feito as ideias originais de McCarthy ainda mais acessíveis.

Essa implementação de LISP cobre apenas as funções exemplificadas pelo artigo de Graham. O que quer dizer que alguém pode implementar Campa nela mesmo (ou poderiam furar a fila e ver aqui como isso já foi feito).

Usando

Executando arquivos .cmp

Considere um arquivo hello.cmp com o seguinte código Campa:

(defun hello (stuff) (print "hello" stuff))
(label thing "Marvin")

(hello thing)

Você pode executar esse código usando o comando campa:

$ campa hello.cmp
hello Marvin

Note que as funções print e println são oferecidas "gratuitamente" ao usuário já que não são especificadas em Roots of Lisp. Mas mais sobre isso depois.

Brincando no REPL

Se você está interessado em testar algumas ideias antes de se comprometer com elas a ponto de criar um arquivo (ou adicioná-las ao controle de versão) é possível fazê-lo em uma sessão do repl através do comando campa:

$ campa
=>

O símbolo => é o prompt esperando pela entrada de código.

Vou usar essa oportunidade para mostrar outra função que vem com essa implementação mas não é parte de Roots of Lisp. Vamos carregar (load) o mesmo arquivo que foi usado no exemplo anterior na atual sessão do REPL.

=> (load "./hello.cmp")
hello  MarvinNIL

O NIL mostrado logo depois da mensagem (hello Marvin) é o valor returnado pela função print.

Note também que a função hello, declarada no arquivo hello.cmp, agora está disponível na sessão.

=> (hello "World")
hello  WorldNIL

As Raízes

As seguintes funções estão disponíveis e são consideradas o cerne (core) dessa implementação de LISP. Todas elas tem o comportamento especificado no já mencionado The Roots of Lisp.

(atom thing)
(car list)
(cdr list)
(cond ((condition) some-value) ((other-condition) other-value))
(cons 'new-head '(list with stuffz))
(defun func-name (params, more-params) (do-something))
(eq 'meaning-of-life 42)
(label a-symbol 4,20)
(lambda (params, more-params) (do-something))
(list 'this 'creates 'a 'list 'with 'all 'given 'params)
(quote '(a b c))

Além das funções em si o uso de aspas simples (') como notação para quote de objetos também foi implementado em runtime.

Detalhes de implementação

Essas funções foram todas implementadas em Ruby e vivem aqui.

Além das Raízes

Nesse Readme algumas menções foram feitas a respeito de funções/funcionalidades de Campa que não foram especificadas em Roots of Lisp.

São basicamente dois tipos de "coisas": as implementadas em código Campa e as implementadas em runtime (código Ruby).

Extras em Campa

Extras em Ruby (runtime)

Testes

Há também um framework de testes muito simples implementado em runtime. Isto é disponibilizado através do comando campa seguido da opção test. O comando recebe como parâmetro arquivos contendo... testes.

A definição de um teste nesse contexto é qualquer função que comece como test- ou test_ (case insensitive) e retorna true para sucesso ou false para falha. A "parte LISP" de Campa usa essa ferramenta e você pode verificar como aqui.

$ campa test test/core_test.cmp

10 tests ran
Success: none of those returned false

Internamente esse "framework" é formado pelas duas funções a seguir:

(tests-run)

A função tests-run encontra qualquer função com nomes no padrão test- ou test_ (case insensitive) no contexto atual e as executa. Uma função que retorne false é considerada uma falha.

Podemos simular isso claramente no REPL:

$ campa
=> (defun test-one () "do nothing" false)
(lambda () "do nothing" false)
=> (defun test-two () "great success" true)
(lambda () "great success" true)
=> (tests-run)
((success (test-two)) (failures (test-one)))

Essa estrutura de dados retornada por tests-run é muito conhecida de uma segunda função...

(tests-report)

... podemos usar a função tests-report para aparesentar um bom resumo dos testes executados:

=> (tests-report (tests-run))
2 tests ran
FAIL!
  1 tests failed
  they are:
    - test-one
false

Perceba que tests-report retorna false se houver falha ou true se todos os testes passam. Isso é um jeito fácil de integrar essa ferramenta com ambientes de CI ou qualquer tipo de build "progressivo".

Um exemplo de como isso é usado nessa implementação pode ser lido aqui.

Desenvolvimento

Depois de fazer o check out do repositório, rode bin/setup para instalar dependências. Então execute rake spec para rodar os testes. Você também pode executar bin/console para iniciar um prompt interativo para experimentos.

Para instalar essa gem localmente execute bundle exec rake install.

Contributing

Bug reports e pull requests são bem vindos no GitHub em https://github.com/mistersourcerer/campa.

License

The gem is available as open source under the terms of the MIT License.