Campa
Um LISP implementado em Ruby.
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
- (assert x y) Retorna true se x e y forem eq
Extras em Ruby (runtime)
- (load a-file another-file) Lê e evalua os arquivos dados como parâmetro
- (print "some" "stuff" 4 '("even" "lists")) Printa o parâmetro em uma representação amigável para humanos
- (println stuffz here)
Mesmo que print mas adiciona nova linha (
\n
) após cada parâmetro
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.