Emacs Lisp

Anotações dos meus estudos

Conteúdo

Sobre a série

O que pretendemos aprender

  • Fundamentos da linguagem.
  • Criação de funções.
  • Manipulação de variáveis.
  • Criar novas funcionalidades para o Emacs.
  • Utilizar o Emacs como interpretador no sistema.
  • Trabalhar com macros.
  • Criar e compartilhar pacotes (git e MELPA).

Este site é gerado por um programa em Emacs Lisp interpretado pelo Emacs invocado em um shell script!

Para quem é a série

  • Para mim: eu quero muito aprender elisp 😎!
  • Para quem quer customizar os mínimos detalhes do seu Emacs.
  • Para quem quer exercer plenamente sua liberdade de computação.
  • Para quem quer contribuir com pacotes para a comunidade Emacs.

Ambiente de estudo

GNU Emacs
É fundamental ter ou desenvolver um conhecimento mínimo do Emacs. Nisso, o tutorial pode ajudar muito: C-h t.
REPL (ielm)
O Emacs conta com um REPL (Read Eval Print Loop) para avaliar interativamente códigos em elisp: M-x ielm
Buffer scratch

O scratch buffer é um buffer temporário no modo Emacs Lisp, ou seja, um modo em que o Emacs aciona todas as funcionalidades para a escrita de código em elisp. Por ser temporário, o que escrevermos nele será perdido com o fim da sessão do Emacs, o que o torna ideal para os nossos experimentos.

O problema, é que o scratch buffer, embora disponível, não é o buffer visível no início, e isso requer, no mínimo, o conhecimento sobre como navegar entre os buffers (atalho C-x b). Além disso, os códigos precisarão ser avaliados pelo interpretador elisp para terem efeito:

  • Avaliar todo o buffer: M-x eval-buffer
  • Avaliar seleção (região): M-x eval-region
  • Avaliar última expressão antes do cursor: M-x eval-last-sexp
Sistema interno de descrições
  • C-h v: Descrição de variáveis
  • C-h f: Descrição de funções
  • C-h o: Descrição de símbolos
  • C-h m: Descrição de modos
Deixe o Emacs mais confortável

Ocultar/exibir barra de menu:

M-x menu-bar-mode RET

Ocultar/exibir barra de ferramentas:

M-x tool-bar-mode RET

Ocultar/exibir barras de rolagem:

M-x scroll-bar-mode RET

Alterar tema padrão:

M-x load-themne RET <nome do tema> RET

Essas alterações podem ser tornadas permanentes se escritas no arquivo init.el:

(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
(load-theme 'modus-vivendi)

O arquivo init.el pode ser criado em ~/.config/emacs/ ou em ~/.emacs.d/.

Para o Emacs não exibir o buffer de boas-vindas e utilizar o scratch buffer como buffer inicial, nós podemos incluir a definição da variável inhibit-startup-message no arquivo init.el:

(setq inhibit-startup-message t)

(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
(load-theme 'modus-vivendi)

Sobre o Emacs

Esta é uma história de hackers

  • Em 1972, Carl Mikkelsen adiciona capacidade de edição visual ao editor TECO.
  • Em 1974, Richard Stallman adicionou funcionalidades de macros ao TECO.
  • As coleções de macros para o TECO foram se acumulando no laboratório de IA do MIT.
  • Em 1976, Stallman organizou essas macros em comandos no TECO EMACS (Editor MACroS).
  • Entre as novidades do EMACS, estava o recurso de auto-documentação das macros.
  • O TECO EMACS tornou-se o editor padrão do sistema ITS (Incompatible Time Sharing).
  • Nos anos seguintes, surgiram várias implementações do EMACS.
  • Em 1984, Stallman começou a desenvolver o GNU Emacs: o primeiro programa do Projeto GNU.

Sobre o Emacs Lisp

  • É um dialeto Lisp para expandir o Emacs.
  • Fortemente inspirado no dialeto MACLISP (MIT, anos 1960).
  • Tem influências do ANSI Common Lisp, mas é bem mais simples.
  • Requer um conhecimento mínimo de uso do Emacs (tutorial: C-h t).

Conceitos básicos

Sintaxe

  • Sintaxe simples (poucas regras) e uniforme, o que facilita muito o aprendizado.
  • O código em elisp se resume a três elementos de sintaxe: listas, símbolos e valores.
  • No Lisp, em geral, código também é dado.
  • O próprio Emacs nos ajuda a lidar com os parêntesis.
  • Múltiplos espaços e quebras de linha são ignorados: são os parêntesis que delimitam as expressões.
(defun salve (nome)
  "Exibe uma saudação para NOME."
  (message "Salve, %s!" nome))

Valores

  • Todos os elementos (objetos) possuem um valor de um determinado tipo.
  • São os valores avaliados que possuem um tipo, não as suas representações!

Alguns tipos do Lisp:

  • Números
  • Strings
  • Símbolos
  • Listas

Alguns tipos do Emacs Lisp:

  • Buffers
  • Windows
  • Frames
  • Keymaps

Os tipos do elisp são principalmente os componentes do Emacs e da sua interface!

Formas

São todos os objetos do Lisp que podem ser avaliados (termo geralmente aplicado a funções e macros).

Objetos autoavaliáveis

Tipos primitivos, como números e strings:

ELISP> 42
42 (#o52, #x2a, ?*)
ELISP> "Salve, simpatia!"
"Salve, simpatia!"
ELISP> ?B
65 (#o102, #x42, ?B)

Funções e macros são avaliáveis

ELISP> (defun fala () (print "Falaê!"))
fala
ELISP> (fala)

"Falaê!"

"Falaê!"
ELISP> (defun soma2 (a b) (+ a b))
soma2
ELISP> (soma2 17 25)
42 (#o52, #x2a, ?*)

Se o primeiro elemento da lista não for uma função ou uma macro, ela não poderá ser avaliada:

ELISP> (1 2 3 4)
*** Eval error *** Invalid function: 1

Devido à notação prefixa (ou polonesa), o interpretador presume que o primeiro elemento da lista seja o nome de uma função.

Para ser avaliada como tal, a lista teria que ser citada (quoted):

ELISP> '(1 2 3 4)
(1 2 3 4)

A aspa simples é a sintaxe reduzida da chamada da forma especial quote:

ELISP> (quote (1 2 3 4))
(1 2 3 4)

A forma quote também serve para avaliar símbolos:

ELISP> soma2
*** Eval error ***  Symbol’s value as variable is void: soma2
ELISP> 'soma2
soma2

O erro aconteceu porque, quando tentamos avaliar um símbolo pelo seu nome, ele é presumido como o identificador de uma variável.

ELISP> (defvar bicho "zebra")
bicho
ELISP> bicho
"zebra"

Objetos especiais t e nil

No elisp, o símbolo nil tem três significados:

  • É um símbolo nomeado como nil;
  • É o valor lógico falso;
  • E é uma lista vazia (com zero elementos).

Quanto à representação de um valor lógico verdadeiro, qualquer valor diferente de nil pode cumprir com este papel. Contudo, o símbolo especial t é preferível quando queremos ser explícitos ou quando não houver algo a ser avaliado para determinar uma verdade lógica.

Predicados

Predicados são funções que avaliam apenas t (verdadeiro) ou nil (falso).

(null ())  ;; Avalia `t' se argumento for `nil'.
(zerop 0)  ;; Avalia `t' se argumento for zero.

Predicados de tipos

Predicados são funções que avaliam apenas verdadeiro (t) ou falso (nil). Os predicados de tipos determinam se o argumento é do tipo especificado pela função, por exemplo:

stringp
Avalia t se o argumento for uma string.
floatp
Avalia t se o argumento for um número de ponto flutuante.
integerp
Avalia t se o argumento for um número inteiro.
numberp
Avalia t se o argumento for um número.
listp
Avalia t se o argumento for uma lista.
symbolp
Avalia t se o argumento for um símbolo.
bufferp
Avalia t se o argumento for um buffer.
windowp
Avalia t se o argumento for uma janela.

Lista completa de predicados de tipos.

Função type-of

Avalia um símbolo representando o nome do tipo do objeto primitivo passado como argumento.

ELISP> (type-of 42)
integer
ELISP> (type-of ?B)
integer
ELISP> (type-of (print "banana"))

"banana"

string
ELISP> (type-of (get-buffer "*scratch*"))
buffer

Predicados de igualdade

Determinam se dois objetos são idênticos em todos ou em algum dos seus atributos.

Determinando se dois objetos são o mesmo objeto

O predicado eq avalia t apenas se os dois objetos forem o mesmo objeto em termos de referência na memória.

ELISP> (eq 15 15)
t
ELISP> (eq 'defun 'defun)
t
ELISP> (eq minha-var minha-var)
t

Alguns objetos são referenciados em instâncias separadas na memória, mesmo que sejam escritos da mesma forma!

ELISP> (eq 3.0 3.0)
nil
ELISP> (eq "banana" "banana")
nil
ELISP> (eq '(a b c) '(a b c))
nil

Determinando se dois objetos são o mesmo objeto ou são números

Com eql, a avaliação é t se os objetos forem o mesmo objeto ou se forem números iguais.

ELISP> (eql 1.5 1.5)
t

Determinando se dois objetos são equivalentes

O predicado equal avalia t se os dois objetos possuírem os mesmos componentes.

ELISP> (equal 15 15)
t
ELISP> (equal 1.5 1.5)
t
ELISP> (equal "banana" "banana")
t
ELISP> (equal 'defun 'defun)
t
ELISP> (equal minha-var minha-var)
t
ELISP> (equal '(a b c) '(a b c))
t

Predicados de comparação numérica

(= 15 15)                 ;; Igualdade
(< 15 52)                 ;; Menor que...
(> 15 42)                 ;; Maior que...
(<= 15 42)                ;; Menor ou igual
(>= 15 42)                ;; Maior ou igual
(char-equal ?😊 128522)   ;; Comparação numérica usando caracateres

Operações aritméticas

Operações básicas

(+ 17 25)      ;; Soma
(- 126 84)     ;; Subtração
(* 10.5 4)     ;; Multiplicação
(/ 126 3)      ;; Divisão
(% 342 100)    ;; Módulo (resto) int
(mod 21.98 3)  ;; Módulo (resto) float

Raiz quadrada

ELISP> (sqrt 1764)
42

Potenciação

ELISP> (expt 2 16)  ;; Base 2, expoente 16...
65536

Incremento e decremento

ELISP> (1+ 41)
42
ELISP> (1- 43)
42

Número pseudoaleatório

(random 100)  ;; Avalia um inteiro entre 0 e o `limite-1'.

Máximo e mínimo

(max 1 3 5 7)  ;; Valor máximo entre os argumentos
(min 1 3 5 7)  ;; Valor mínimo entre os argumentos

Aproximações (converter float para int)

ELISP> (truncate 21.98)
21
ELISP> (floor 21.9999)
21
ELISP> (floor -21.9999)
-22
ELISP> (ceiling 21.0001)
22
ELISP> (ceiling -21.0001)
-21
ELISP> (round 21.5)
22
ELISP> (round 21.49999)
21

Expressões condicionais

Forma especial `if'

A forma if avalia uma expressão, caso a CONDIÇÃO avalie t, ou várias expressões, caso a CONDIÇÃO avalie nil.

(if CONDIÇÃO
  SE-VERDADEIRO
  SE-FALSO
  SE-FALSO
  ...)

Alternativamente, podemos avaliar mais de uma expressão se a CONDIÇÃO avaliar t com a forma especial progn:

(if CONDIÇÃO
  (progn SE-VERDADEIRO
         SE-VERDADEIRO
         ...)
  SE-FALSO
  SE-FALSO
  ...)

A forma prog avalia todas as expressões passadas como argumentos.

Macro `unless'

Se a CONDIÇÃO for avaliada nil, as demais expressões serão avaliadas; caso contrário, avalia nil.

(unless CONDIÇÃO
  CÓDIGO)

Macro `when'

Se CONDIÇÃO avaliar algo diferente de nil, as demais expressões serão avaliadas; caso contrário, avalia nil.

(when CONDIÇÃO
  CÓDIGO)

Forma especial `cond'

Avalia cláusulas na forma (CONDIÇÃO CÓDIGO) e avalia a primeira em que CONDIÇÃO for diferente de nil. Se nenhuma cláusula for avaliada, avalia nil.

(cond (CONDIÇÃO CÓDIGO)
      (CONDIÇÃO CÓDIGO)
      ...)

A forma cond pode ser utilizada como um if "sem else" se houver apenas uma cláusula. Nesse caso, se a única condição avaliar nil, toda a expressão avaliará nil.

Repetições (loops)

Existem quatro formas de implementar repetições no elisp:

  • while
  • dotimes
  • dolist
  • Funções recursivas

Forma especial `while'

Avalia o CÓDIGO enquanto a CONDIÇÃO continuar sendo avaliada como diferente de nil.

(while CONDIÇÃO
  CÓDIGO)

Macro `dotimes'

Avalia o CÓDIGO enquanto associa VAR a uma CONTAGEM de inteiros sucessivos de zero a CONTAGEM - 1.

(dotimes (VAR CONTAGEM)
  CÓDIGO)

Macro `dolist'

Itera pelos ELEMENTOS de uma lista na forma (VAR '(ÍTENS)) associando o ÍTEM corrente a VAR e avaliando o CÓDIGO a cada iteração.

(dolist (VAR '(ÍTEM1 ÍTEM2 ...))
  CÓDIGO)

Variáveis

Em Lisp, variáveis são símbolos associados a valores.

Definindo variáveis de escopo global

  • set
  • setq
  • defvar

Definindo variáveis de escopo global no buffer

  • setq-local

Definindo variáveis de escopo local

  • let
  • let*

Definindo variáveis customizáveis pela UI

  • defcustom