Aprenda EELisp construindo projetos reais. Da sua primeira expressão a uma calculadora de juros compostos, controle de despesas e um organizador pessoal inspirado no Lotus Agenda.
Abra a barra lateral do REPL no EEditor com ⌘⇧L. Ela aparece como um painel no lado direito do editor e permanece aberta enquanto você troca de arquivo. Digite uma expressão e pressione ⌘Return para avaliá-la. Seu histórico de comandos é salvo entre sessões — use as setas para cima/baixo para recuperar comandos anteriores.
No EELisp, tudo é uma expressão envolvida em parênteses. O primeiro item é a função, o restante são os argumentos:
;; Aritmética — o operador vem primeiro (+ 1 2) ; → 3 (+ 1 2 3 4) ; → 10 (variádico!) (* 6 7) ; → 42 (/ 100 3) ; → 33.333... ;; Aninhamento — expressões internas são avaliadas primeiro (+ (* 3 4) (- 10 5)) ; → 17 (12 + 5) ;; Strings (str "Hello, " "world!") ; → "Hello, world!" (str-upper "hello") ; → "HELLO" ;; Funções matemáticas (pow 2 10) ; → 1024 (round 3.14159 2) ; → 3.14 (arredonda para N casas decimais) (abs -42) ; → 42 ;; Comparações retornam true ou false (> 10 5) ; → true (= 1 1) ; → true
(+1 2) funciona da mesma forma que (+ 1 2).
Use def para vincular um nome a um valor:
(def name "Alice") (def age 30) (def pi 3.14159) (str "Hi, " name "! You are " age " years old.") ; → "Hi, Alice! You are 30 years old."
Use defn para criar funções nomeadas:
;; Uma função que dobra um número (defn double (x) (* x 2)) (double 21) ; → 42 ;; Uma função com múltiplos parâmetros (defn greet (name greeting) (str greeting ", " name "!")) (greet "Bob" "Good morning") ; → "Good morning, Bob!" ;; Lógica condicional com if (defn abs-val (n) (if (< n 0) (- 0 n) n)) (abs-val -5) ; → 5 (abs-val 3) ; → 3
Use fn para funções avulsas sem nome:
(map (fn (x) (* x x)) '(1 2 3 4 5)) ; → (1 4 9 16 25)
Listas são o pão com manteiga do Lisp. Elas armazenam sequências ordenadas de valores:
;; Criando listas (def numbers (list 1 2 3 4 5)) (def fruits '("apple" "banana" "cherry")) ;; Acessando elementos (first numbers) ; → 1 (second numbers) ; → 2 (last numbers) ; → 5 (nth fruits 1) ; → "banana" (length numbers) ; → 5 ;; Construindo listas (cons 0 numbers) ; → (0 1 2 3 4 5) (append numbers '(6 7)) ; → (1 2 3 4 5 6 7) (range 1 11) ; → (1 2 3 4 5 6 7 8 9 10)
As três operações de lista mais poderosas:
;; map — aplica uma função a cada elemento (map double (range 1 6)) ; → (2 4 6 8 10) ;; filter — mantém elementos que atendem a uma condição (filter even? (range 1 11)) ; → (2 4 6 8 10) ;; reduce — combina todos os elementos em um único valor (reduce + 0 (range 1 101)) ; → 5050 (soma de 1 a 100) ;; Componha-os: soma dos quadrados dos números pares de 1-10 (reduce + 0 (map (fn (x) (* x x)) (filter even? (range 1 11)))) ; → 220 (4 + 16 + 36 + 64 + 100)
Dicionários armazenam pares chave-valor usando palavras-chave (prefixadas com :):
;; Criar um dicionário (def person {:name "Alice" :age 30 :city "NYC"}) ;; Acessar valores (dict-get person :name) ; → "Alice" (dict-get person :age) ; → 30 ;; Atualizar (retorna um novo dict) (def older (dict-set person :age 31)) (dict-get older :age) ; → 31 ;; Inspecionar (dict-keys person) ; → (:name :age :city) (dict-values person) ; → ("Alice" 30 "NYC") (dict-has person :email) ; → false ;; Mesclar dois dicts (o segundo prevalece em conflitos) (dict-merge person {:email "alice@example.com" :age 31}) ; → {:name "Alice" :age 31 :city "NYC" :email "alice@example.com"}
Vamos construir uma calculadora financeira real que computa juros compostos anuais. Esta é a fórmula:
A = P × (1 + r/n)^(n×t)
Onde P = capital inicial, r = taxa anual, n = composições por ano, t = anos.
;; Compound interest formula using pow ;; A = P * (1 + r/n)^(n*t) (defn compound-interest (principal rate compounds-per-year years) (let ((r (/ rate 100)) (n compounds-per-year)) (* principal (pow (+ 1 (/ r n)) (* n years))))) ;; Teste: $1000 a 5% ao ano, composição mensal, por 10 anos (round (compound-interest 1000 5 12 10) 2) ; → 1647.01 (você ganhou $647.01 em juros!)
;; Show balance at the end of each year (defn yearly-breakdown (principal rate compounds-per-year years) (println (str "Investment: $" principal " at " rate "%")) (println (str "Compounding: " compounds-per-year "x/year for " years " years")) (println "---") (for-each year (range 1 (+ years 1)) (let ((balance (compound-interest principal rate compounds-per-year year)) (interest (- balance principal))) (println (str "Year " year ": $" (round balance 2) " (+" (round interest 2) " interest)"))))) (yearly-breakdown 10000 7 12 5)
;; Compare multiple interest rates side by side (defn compare-rates (principal years . rates) (println (str "Comparing $" principal " over " years " years:")) (for-each rate rates (let ((final (compound-interest principal rate 12 years)) (gain (round (- final principal) 2))) (println (str " " rate "% → $" (round final 2) " (+" gain ")"))))) (compare-rates 10000 20 3 5 7 10)
. rates) para aceitar um número variável de argumentos. Isso é perfeito para comparar múltiplos valores.
O EELisp possui um banco de dados SQLite integrado. Vamos usá-lo para gerenciar contatos — como um mini dBASE.
;; Define a contacts table with typed fields (deftable contacts (name:string email:string phone:string city:string age:number)) ;; The table is created. You can verify: (describe contacts) (tables) ; → ("contacts")
;; Insert contacts — each returns the new record with an auto-ID (insert contacts {:name "Alice" :email "alice@mail.com" :phone "555-0101" :city "New York" :age 30}) (insert contacts {:name "Bob" :email "bob@mail.com" :phone "555-0102" :city "San Francisco" :age 25}) (insert contacts {:name "Carol" :email "carol@mail.com" :phone "555-0103" :city "New York" :age 35}) (insert contacts {:name "Dan" :email "dan@mail.com" :phone "555-0104" :city "Chicago" :age 28}) (insert contacts {:name "Eve" :email "eve@mail.com" :phone "555-0105" :city "San Francisco" :age 32})
;; See all contacts (displayed as a formatted table) (query contacts) ;; Find contacts in New York (query contacts :where "city = ?" :params (list "New York")) ;; Find contacts over 30, sorted by name (query contacts :where "age > ?" :params (list 30) :order "name") ;; Count contacts (count-records contacts) ; → 5 ;; Select only specific columns (query contacts :select "name, city" :order "city")
;; Update Bob's city (record ID 2) (update contacts 2 {:city "Los Angeles"}) ;; Verify the change (query contacts :where "name = ?" :params (list "Bob")) ;; Soft-delete Dan (he's marked deleted but not gone) (delete contacts 4) (count-records contacts) ; → 4 (Dan is hidden) ;; Purge deleted records permanently (pack contacts) (count-records contacts) ; → 4
;; Extract records from a query and process them (def nyc-people (records (query contacts :where "city = ?" :params (list "New York")))) ;; Get names of NYC contacts (map (fn (r) (field-get r :name)) nyc-people) ; → ("Alice" "Carol") ;; Average age of all contacts (let ((all (records (query contacts))) (ages (map (fn (r) (field-get r :age)) all))) (/ (reduce + 0 ages) (length ages))) ; → 30.5
Vamos construir um controle de despesas pessoal com resumos por categoria.
(deftable expenses (description:string amount:number category:string date:string)) ;; Add some expenses (insert expenses {:description "Groceries" :amount 85.50 :category "food" :date "2026-02-01"}) (insert expenses {:description "Electric bill" :amount 120.00 :category "utilities" :date "2026-02-03"}) (insert expenses {:description "Coffee shop" :amount 12.75 :category "food" :date "2026-02-05"}) (insert expenses {:description "Bus pass" :amount 75.00 :category "transport" :date "2026-02-01"}) (insert expenses {:description "Restaurant" :amount 45.00 :category "food" :date "2026-02-10"}) (insert expenses {:description "Internet" :amount 65.00 :category "utilities" :date "2026-02-03"}) (insert expenses {:description "Books" :amount 32.99 :category "education" :date "2026-02-15"})
;; Total spending (let ((all (records (query expenses)))) (reduce + 0 (map (fn (r) (field-get r :amount)) all))) ; → 436.24 ;; Spending by category (defn category-total (cat) (let ((rows (records (query expenses :where "category = ?" :params (list cat))))) (reduce + 0 (map (fn (r) (field-get r :amount)) rows)))) (println (str "Food: $" (category-total "food"))) (println (str "Utilities: $" (category-total "utilities"))) (println (str "Transport: $" (category-total "transport"))) (println (str "Education: $" (category-total "education")))
;; Top 3 expenses by amount (query expenses :order "amount" :desc true :limit 3)
Um mini-projeto clássico que mostra como combinar tabelas com funções auxiliares.
(deftable todos (title:string priority:number done:bool)) ;; Helper: add a todo (defn add-todo (title priority) (insert todos {:title title :priority priority :done false}) (println (str "Added: " title))) ;; Helper: mark done (defn done! (id) (update todos id {:done true}) (println "Done!")) ;; Helper: show pending todos (defn pending () (query todos :where "done = ?" :params (list false) :order "priority")) ;; Helper: show completed todos (defn completed () (query todos :where "done = ?" :params (list true)))
;; Add tasks (lower priority number = more important) (add-todo "Write README" 1) (add-todo "Fix login bug" 1) (add-todo "Update tests" 2) (add-todo "Refactor CSS" 3) (add-todo "Deploy to staging" 2) ;; View pending tasks (pending) ;; Complete a task (done! 1) ; mark "Write README" as done ;; See what's left (pending) ;; See what's completed (completed)
.el e carregá-las com :load todo-helpers.el toda vez que abrir o REPL.
O EELisp possui dois comandos de visualização interativa: browse mostra uma tabela/grade navegável, e edit abre um formulário CRUD. Vamos construir um livro de receitas usando ambos.
Nossa tabela de receitas usa vários tipos de campo: string para texto curto, number para quantidades, bool para indicadores, e memo para notas mais longas.
(deftable recipes
(name:string
cuisine:string
servings:number
vegetarian:bool
notes:memo))
(insert recipes {:name "Pasta Carbonara" :cuisine "Italian" :servings 4 :vegetarian false :notes "Use guanciale, not bacon."}) (insert recipes {:name "Vegetable Curry" :cuisine "Indian" :servings 6 :vegetarian true :notes "Toast spices first."}) (insert recipes {:name "Miso Soup" :cuisine "Japanese" :servings 2 :vegetarian true :notes "Don't boil the miso."})
O comando browse abre uma tabela/grade navegável mostrando todos os registros de uma vez:
;; Open the table view (browse recipes)
A visualização em tabela mostra todos os registros em uma grade. Clique em uma linha para selecioná-la, ou use Anterior/Próximo para navegar.
browse, edit e defform a partir de um bloco ```eelisp nas suas notas Markdown. Pressione ⌘⇧Return e o formulário ou tabela abre como um painel lateral interativo ao lado do editor — assim você pode ver seus dados e suas notas ao mesmo tempo.
O comando edit abre um formulário estilo dBASE para operações CRUD completas:
;; Open the CRUD form (edit recipes)
O formulário mostra um registro por vez. Você pode:
Tanto browse quanto edit suportam :where, :params e :order:
;; Browse only vegetarian recipes (browse recipes :where "vegetarian = ?" :params (list true)) ;; Edit sorted by cuisine (edit recipes :order "cuisine") ;; Compare with text table output (query recipes :select (list "name" "cuisine" "servings"))
Por padrão, os dados ficam na memória e são perdidos quando você fecha o aplicativo. Para persistir suas receitas:
;; Create a database file in your folder :db new recipes ;; Now all deftable/insert/update calls are persisted ;; Check current database :db ;; Switch back to in-memory :db memory
recipes.lisp e carregue com :load recipes.lisp. Se sua pasta tiver um arquivo eelisp.db, o REPL conecta-se a ele automaticamente quando você abre a pasta.
O comando defform cria formulários interativos independentes com campos computados. Campos computados atualizam automaticamente quando você altera valores de entrada — eles não são salvos em nenhum banco de dados.
Vamos construir uma calculadora que computa juros compostos a partir de três entradas:
(defform compound-interest (principal:number rate:number years:number) :computed ((total-amount (* principal (pow (+ 1 (/ rate 100)) years))) (interest (- total-amount principal))))
Isso cria um formulário com três campos editáveis (principal, rate, years) e dois campos computados (total-amount, interest). Experimente alterar os valores — os campos computados atualizam instantaneamente.
A sintaxe é:
(defform form-name (field1:type field2:type ...) :computed ((computed1 expression1) (computed2 expression2)))
deftable: number, string, bool, date, memoConversor de temperatura:
(defform temperature (celsius:number) :computed ((fahrenheit (+ (* celsius 1.8) 32)) (kelvin (+ celsius 273.15))))
Calculadora de IMC:
(defform bmi (weight-kg:number height-cm:number) :computed ((height-m (/ height-cm 100)) (bmi (/ weight-kg (* height-m height-m)))))
:load compound-calculator.lisp para ver isso em ação.
O EELisp inclui um gerenciador de informações pessoais inspirado no Lotus Agenda construído sobre a camada de banco de dados. Pense nele como uma lista de tarefas programável combinada com um organizador de conhecimento — você digita texto livre e a estrutura emerge das categorias.
Itens são focados em texto. Apenas digite o que está na sua mente:
;; Simple item (add-item "Finish quarterly report for Sarah") ;; With due date and priority (add-item "Call dentist to reschedule" :when "2026-03-01" :priority 2) ;; With a category and notes (add-item "Buy groceries" :category "personal" :notes "milk, eggs, bread, coffee") ;; High-priority work item (add-item "Deploy v2.1 to staging" :when "2026-02-26" :priority 1) ;; A meeting(add-item "Team standup" :when "2026-02-24" :priority 3)
O comando items mostra todos os itens em uma visualização de tabela:
;; Ver todos os itens (items)
As categorias são hierárquicas — use separadores /. Você também pode definir grupos exclusivos onde um item só pode pertencer a um filho:
;; Categorias de trabalho (defcategory work) (defcategory work/projects) (defcategory work/meetings) (defcategory work/admin) ;; Categorias pessoais (defcategory personal) (defcategory personal/health) (defcategory personal/errands) ;; Grupo exclusivo: o item só pode ser high, medium OU low (defcategory priority :exclusive true :children (high medium low)) ;; Visualizar a árvore (categories)
Use assign para etiquetar itens. Categorias exclusivas removem automaticamente as irmãs:
;; Etiquetar itens com categorias (assign 1 "work/projects") (assign 2 "personal/health") (assign 4 "work/projects") (assign 5 "work/meetings") ;; Definir prioridades (assign 1 "priority/high") (assign 4 "priority/high") (assign 2 "priority/medium") ;; Verificar um item (item-get 1)
;; Encontrar todos os itens de trabalho (items :category "work") ;; Pesquisar por texto (items :search "quarterly") ;; Apenas itens de prioridade 1 (items :priority 1) ;; Itens com vencimento antes de março (items :when-before "2026-03-01") ;; Quantos itens eu tenho? (item-count)
;; Atualizar propriedades (item-set 1 :priority 1 :when "2026-02-28") ;; Mudar prioridade — exclusiva! Remove "high" automaticamente (assign 1 "priority/low") (item-get 1) ; agora mostra priority/low, não priority/high ;; Marcar item como concluído (exclusão lógica) (item-done 3) ; "Buy groceries" — pronto! (item-count) ; → 4 (era 5) ;; Remover uma categoria (unassign 2 "personal/health")
_items, _categories, _rules), criadas automaticamente. Isso significa que sua agenda persiste se você usar um arquivo de banco de dados (:db myagenda.db). As categorias são arrays JSON dentro de cada registro de item, e as propriedades (when, priority, etc.) são armazenadas como um objeto JSON.
Defina regras que categorizam automaticamente itens com base em seu conteúdo. Isso é o que tornou o Lotus Agenda revolucionário em 1988 — e agora está no EELisp.
;; Regra: itens contendo "URGENT" recebem prioridade alta (defrule urgent-flag :when (str-contains text "URGENT") :assign "priority/high") ;; Regra: reuniões e ligações → work/meetings (defrule meeting-detect :when (or (str-contains text "meeting") (str-contains text "call with") (str-contains text "standup")) :assign "work/meetings") ;; Regra: itens de compras → personal/errands (defrule errand-detect :when (or (str-contains text "buy ") (str-contains text "groceries")) :assign "personal/errands")
;; Aplicar regras a todos os itens (retorna contagem de alterações) (apply-rules) ;; → 3 (itens que corresponderam às regras) ;; Aplicar a um único item (apply-rules 2) ;; Verificar os resultados (item-get 2) ;; → agora mostra Categories: priority/high (se continha "URGENT")
;; Ativar auto-aplicação — regras executam em cada add-item (auto-categorize true) ;; Agora novos itens são categorizados automaticamente! (add-item "URGENT: Deploy hotfix to production") ;; → automaticamente atribuído priority/high (add-item "Buy milk and eggs this weekend") ;; → automaticamente atribuído personal/errands (add-item "Standup with the team at 9am") ;; → automaticamente atribuído work/meetings
;; Extrair datas ISO do texto do item usando :action (defrule date-extract :when (str-matches text "\\b(\\d{4}-\\d{2}-\\d{2})\\b") :action (item-set id :when (match 1))) ;; Itens com datas recebem :when automaticamente (add-item "Deadline is 2026-04-01") (item-get 8) ;; → When: 2026-04-01
str-matches retorna grupos de captura, e (match 1) extrai o primeiro grupo.;; Listar todas as regras (rules) ;; → urgent-flag: (str-contains text "URGENT") ;; meeting-detect: (or (str-contains text "meeting") ...) ;; errand-detect: (or (str-contains text "buy ") ...) ;; date-extract: (str-matches text "\\b(\\d{4}-\\d{2}-\\d{2})\\b") ;; Excluir uma regra que você não precisa mais (drop-rule "errand-detect") ;; Desativar auto-categorização (auto-categorize false)
_rules. Quando avaliadas, os campos de cada item (text, notes, id, categories, e todas as propriedades) são vinculados como símbolos no ambiente de avaliação da regra. O atalho :assign gera ações (assign id "category"), enquanto :action permite executar qualquer expressão Lisp.
and/or/not, use has-category para verificar atribuições existentes, (get :property) para testes de propriedades, e str-matches para padrões regex. Você pode construir lógica de categorização arbitrariamente complexa.
Visualizações são consultas salvas que filtram, ordenam e agrupam seus itens. Defina uma visualização uma vez e use show a qualquer momento para ver os itens correspondentes mais recentes — como um painel ao vivo para sua agenda.
Visualizações usam expressões de filtro — o mesmo contexto das regras (text, categories, propriedades, has-category, overdue?):
;; Um quadro de todos os itens de trabalho, agrupados por categoria (defview work-board :source items :filter (has-category "work") :group-by category :sort-by when) ;; Apenas itens de alta prioridade (defview urgent :source items :filter (= priority "1") :sort-by when) ;; Itens sem categorias (caixa de entrada não categorizada) (defview inbox :source items :filter (= (length categories) 0)) ;; Itens atrasados (defview overdue :source items :filter (overdue?) :sort-by when)
Use show para exibir os resultados de uma visualização. Visualizações agrupadas são renderizadas como uma árvore com cabeçalhos de categoria:
;; Exibir o quadro de trabalho (show work-board)
;; Exibir itens urgentes (show urgent)
O EELisp fornece helpers de data que são perfeitos para filtros de visualização:
;; Data de hoje como string ISO (today) ; → "2026-02-21" ;; Adicionar dias, semanas ou meses (date-add (today) 7 :days) ; → "2026-02-28" (date-add (today) 1 :months) ; → "2026-03-21" ;; Diferença em dias (date-diff "2026-02-21" "2026-03-01") ; → 8
;; Listar todas as visualizações definidas (views) ;; → work-board: filter=(has-category "work") group-by=category sort-by=when ;; urgent: filter=(= priority "1") sort-by=when ;; inbox: filter=(= (length categories) 0) ;; overdue: filter=(overdue?) sort-by=when ;; Excluir uma visualização (drop-view "inbox")
_views com suas expressões de filtro serializadas como Lisp. Quando você usa show em uma visualização, ela busca todos os itens, avalia o filtro em cada um (vinculando todos os campos do item como símbolos), ordena os resultados e opcionalmente os agrupa. Itens com propriedades ausentes são silenciosamente excluídos — sem erros.
overdue? em filtros para encontrar itens que passaram da data :when. Combine com and/or para visualizações complexas: :filter (and (has-category "work") (overdue?)) mostra itens de trabalho atrasados.
Conecte sua agenda com datas do calendário. Visualize o que está agendado para um dia específico, navegue por um intervalo de datas e adicione rapidamente itens para hoje — tudo a partir do REPL.
Use items-on para ver tudo agendado para uma data específica:
;; O que está acontecendo na segunda-feira? (items-on "2026-02-24") ;; Use (today) para datas dinâmicas (items-on (today))
Use items-between para ver itens em um intervalo de datas (inclusivo em ambas as extremidades):
;; O que tem esta semana? (items-between "2026-02-24" "2026-02-28") ;; Próximos 7 dias usando helpers de data (items-between (today) (date-add (today) 7 :days)) ;; O mês inteiro (items-between "2026-02-01" "2026-02-28")
add-item-today cria um item com :when automaticamente definido para a data de hoje:
;; Adição rápida para hoje (add-item-today "Review pull requests" :priority 1) (add-item-today "Quick grocery run" :notes "milk, bread") (add-item-today "Team sync" :category "work/meetings")
Helpers de data e comandos de calendário funcionam muito bem junto com visualizações:
;; Data de hoje como string ISO (today) ; → "2026-02-21" ;; Aritmética de datas (date-add (today) 7 :days) ; → "2026-02-28" (date-add (today) 1 :months) ; → "2026-03-21" (date-diff "2026-02-21" "2026-03-01") ; → 8 (dias) ;; Definir uma visualização para os itens desta semana (defview this-week :source items :filter (and (>= when (today)) (<= when (date-add (today) 7 :days))) :sort-by when)
add-item-today é um atalho para (add-item text :when (today)). Ele suporta as mesmas opções :priority, :category e :notes que add-item. Combine com regras de auto-categorização para organização sem esforço.
(items-on "date") — mostrar itens para uma data específica(items-between "start" "end") — mostrar itens em um intervalo de datas(add-item-today text ...) — adicionar item com a data de hoje(today) — data atual como string ISO(date-add date n :days/:weeks/:months) — aritmética de datas(date-diff date1 date2) — dias entre duas datasO comando add usa análise de linguagem natural baseada em regex para extrair automaticamente datas, prioridades e pessoas do seu texto. Combinado com builtins de HTTP e JSON, o EELisp se torna uma poderosa ferramenta de automação.
Basta digitar o que está em mente — add descobre os metadados:
;; Datas são extraídas automaticamente (add "Meet Alice tomorrow for coffee") ;; → :when = data de amanhã (add "Call Bob next Monday about the project") ;; → :when = data da próxima segunda, :who = Bob (add "Review docs end of month") ;; → :when = último dia do mês atual (add "Deploy hotfix in 3 days") ;; → :when = 3 dias a partir de agora
Expresse urgência com palavras ou pontos de exclamação:
;; Prioridade baseada em palavras (add "URGENT fix server crash") ;; → :priority 1 ;; Pontos de exclamação: !!! = 1, !! = 2, ! = 3 (add "Review PR !!") ;; → :priority 2 (add "Update README low priority") ;; → :priority 4
Pessoas são detectadas a partir de @menções, preposições e verbos de ação:
;; @menções (add "Review code for @carlos") ;; → :who = carlos ;; Padrões Verbo + Nome (add "Email Sarah March 15 about renewal") ;; → :who = Sarah, :when = March 15 ;; Padrões Preposição + Nome (add "Meeting with John this weekend") ;; → :who = John, :when = Saturday
Use smart-parse para ver o que seria extraído sem criar um item:
(smart-parse "Meet Alice tomorrow for coffee !!")
O EELisp inclui builtins genéricos de HTTP e JSON para integração com APIs:
;; Analisar JSON para dados EELisp (json-parse "{\"name\": \"Alice\", \"scores\": [95, 87]}") ;; → {:name "Alice" :scores (95 87)} ;; Converter EELisp para JSON (json-stringify {:name "Bob" :active true}) ;; → "{\"active\":true,\"name\":\"Bob\"}" ;; Requisições HTTP (http-get "https://api.example.com/data") ;; → {:status 200 :body "..."} (http-post "https://api.example.com/items" (json-stringify {:title "New item"}) :content-type "application/json") ;; → {:status 201 :body "..."}
2026-03-15), tomorrow/today/yesterday, next Monday, this weekend, in 3 days, in 2 weeks, end of week, end of month, March 15.
add funciona perfeitamente com regras de auto-categorização. Após o smart-parsing extrair metadados, as regras ainda são executadas — então (add "URGENT meeting with Bob tomorrow") pode simultaneamente definir a data, prioridade e acionar sua regra "meeting-detect" para atribuir work/meetings.
Itens que se repetem em um cronograma e blueprints reutilizáveis para tarefas comuns.
Adicione :recur a qualquer chamada add-item. Padrões embutidos: :daily, :weekly, :monthly.
(add-item "Team standup" :when "2026-02-24" :recur :weekly :category "work") ;; Item #1: Team standup ;; When: 2026-02-24 ;; Recurrence: weekly ;; Categories: work
Use (every N :unit) para padrões de recorrência personalizados:
(add-item "Quarterly review" :when "2026-04-01" :recur (every 3 :months)) (add-item "Biweekly report" :when "2026-02-28" :recur (every 2 :weeks))
Quando você marca um item recorrente como concluído, o sistema cria automaticamente a próxima ocorrência:
(item-done 1) ;; Item #4: Team standup ;; When: 2026-03-03 ← avançou 7 dias ;; Recurrence: weekly ;; Categories: work
Modelos são blueprints reutilizáveis para itens que você cria com frequência:
(deftemplate weekly-review :text "Weekly review: reflect on goals" :category "work/admin" :priority 2 :notes "1. What went well?\n2. What to improve?") ;; "Template 'weekly-review' defined"
Use from-template com substituições opcionais:
(from-template weekly-review :when "2026-03-07") ;; Item #5: Weekly review: reflect on goals ;; When: 2026-03-07 ;; Priority: 2 ;; Categories: work/admin ;; Substituir qualquer campo (from-template weekly-review :when "2026-03-14" :priority 1)
Liste e remova modelos:
(templates) ;; weekly-review — "Weekly review: reflect on goals" [work/admin] !2 (drop-template weekly-review) ;; "Template 'weekly-review' removed"
:recur — ex. (deftemplate standup :text "Daily standup" :recur :daily). Todo item criado a partir dele será automaticamente recorrente.Gerencie agendas separadas para trabalho, pessoal e projetos. Cada agenda é um banco de dados SQLite independente com seus próprios itens, categorias, regras, visualizações e modelos.
Use open-agenda para criar um novo arquivo de banco de dados e alternar para ele:
;; Sua agenda padrão já está ativa (add-item "Personal task" :category "personal") ;; Abrir uma agenda de trabalho — cria o arquivo se não existir (open-agenda "work.db") ;; "Opened agenda 'work' at /path/to/work.db" (add-item "Deploy to staging" :when "2026-03-01" :priority 1)
Use use-agenda para alternar. Os itens são completamente isolados entre agendas:
;; Voltar para a padrão (use-agenda memory) (items) ;; → apenas "Personal task" ;; Alternar para trabalho (use-agenda work) (items) ;; → apenas "Deploy to staging"
;; Ver todas as agendas abertas (agendas) ;; Agendas: ;; memory — :memory: ;; work [active] — /path/to/work.db ;; Fechar uma agenda que você não precisa mais (close-agenda work)
Faça backup da sua agenda para JSON, ou transfira itens entre agendas:
;; Exportar agenda atual (export-agenda "memory" :format :json :path "backup.json") ;; "Exported agenda 'memory' to /path/to/backup.json" ;; Importar em uma agenda diferente (open-agenda "archive.db") (import-agenda "backup.json") ;; "Imported: 15 items, 4 categories, 3 rules, 2 views, 1 templates"
work.db se torna work, :memory: se torna memory. Use nomes de arquivo descritivos para facilitar a alternância.A barra lateral do EEditor tem três modos: Arquivos, Calendário e Agenda. Os dois últimos se integram diretamente com seu banco de dados de agenda EELisp, oferecendo uma visão geral visual sem precisar escrever consultas.
Clique no ícone de prancheta no controle segmentado da barra lateral para alternar para o modo Agenda. Ele consulta automaticamente seu banco de dados e organiza os itens em três seções:
:when é anterior a hoje (cabeçalho vermelho)Cada item mostra um ponto de prioridade (vermelho / laranja / amarelo / cinza), o texto do item, categorias e uma data formatada. Pressione o botão de atualizar para recarregar após adicionar itens no REPL.
A barra lateral da agenda não duplica a lógica do banco de dados. Em vez disso, o AgendaViewModel chama interpreter.eval() com os mesmos builtins EELisp que você usaria no REPL:
;; O que a barra lateral da Agenda executa internamente: (items :when-before "2026-02-23") ;; → Seção Atrasados (items-on "2026-02-23") ;; → Seção Hoje (items-between "2026-02-24" "2026-03-02") ;; → Seção Próximos
A barra lateral do Calendário também se integra com sua agenda. Cada célula de dia mostra indicadores de ponto duplo:
Quando você clica em um dia, o painel de detalhes mostra as seções Arquivos e Agenda, para que você possa ver sua atividade de escrita junto com suas tarefas agendadas.
;; 1. Adicione alguns itens com datas (add-item "Review PR" :when (today) :priority 1) (add-item "Team meeting" :when (date-add (today) 1 :days) :category "work/meetings") (add-item "Deploy v2" :when (date-add (today) 3 :days) :priority 2) (add-item "Overdue task" :when "2026-01-15") ;; 2. Alterne para a aba Agenda na barra lateral ;; → Você verá: Atrasados (1) | Hoje (1) | Próximos 7 Dias (2) ;; 3. Alterne para a aba Calendário na barra lateral ;; → Hoje tem um ponto laranja; clique nele para ver o item
Use a macro pipe para encadear operações da esquerda para a direita em vez de aninhar:
;; Em vez desta expressão profundamente aninhada: (str-join (map str-upper (filter (fn (s) (str-starts-with s "a")) (str-split "apple,avocado,banana,apricot" ","))) ", ") ;; Escreva isto: (pipe "apple,avocado,banana,apricot" (str-split ",") (filter (fn (s) (str-starts-with s "a"))) (map str-upper) (str-join ", ")) ; → "APPLE, AVOCADO, APRICOT"
Escreva suas funções em um arquivo e carregue-as no REPL:
;; No REPL, carregue um arquivo de script: :load ~/scripts/finance.el ;; Ou clique com o botão direito em um arquivo .el na barra lateral e ;; selecione "Run in REPL"
| Comando | Descrição |
|---|---|
:help | Mostrar todos os comandos do REPL |
:load <file> | Carregar e executar um script |
:db | Mostrar caminho do banco de dados atual |
:db <path> | Alternar para um arquivo de banco de dados |
:db new <name> | Criar um novo arquivo de banco de dados |
:db memory | Alternar para banco de dados em memória |
:env | Listar todos os símbolos definidos |
:clear | Limpar a saída |
:reset | Reiniciar o interpretador |
(tables) | Listar todas as tabelas no banco de dados atual |
(describe tablename) | Mostrar o esquema de uma tabela |
(drop-table tablename) | Excluir uma tabela |
| Padrão | Exemplo |
|---|---|
| Transformar uma lista | (map inc numbers) |
| Filtrar uma lista | (filter even? numbers) |
| Agregar uma lista | (reduce + 0 numbers) |
| Consultar + processar | (map (fn (r) (field-get r :name)) (records (query ...))) |
| Condicional | (if (> x 0) "positive" "non-positive") |
| Escopo local | (let ((x 1) (y 2)) (+ x y)) |
| Loop | (loop ((i 0)) (= i 10) (recur (inc i))) |
| Matemática | (pow 2 10), (round 3.14159 2), (abs -5) |
| Visualização em tabela | (browse tasks) |
| Formulário CRUD | (edit tasks) |
| Formulário calculadora | (defform name (fields) :computed ((name expr))) |
| Adicionar item de agenda | (add-item "text" :when "date" :priority n) |
| Navegar itens | (items :category "work" :search "text") |
| Definir categoria | (defcategory work/projects) |
| Atribuir categoria | (assign 1 "work/projects") |
| Concluir item | (item-done 1) |
| Definir regra | (defrule name :when (str-contains text "URGENT") :assign "priority/high") |
| Auto-categorizar | (auto-categorize true) |
| Aplicar regras | (apply-rules) |
| Regra com regex | (defrule name :when (str-matches text "pattern") :action expr) |
| Definir visualização | (defview name :source items :filter (has-category "work") :group-by category) |
| Exibir visualização | (show work-board) |
| Data de hoje | (today), (date-add (today) 7 :days) |
| Itens para uma data | (items-on "2026-02-24") |
| Itens em intervalo | (items-between (today) (date-add (today) 7 :days)) |
| Adição rápida hoje | (add-item-today "text" :priority 1) |
| Adição inteligente (NLP) | (add "Meet Alice tomorrow !!") |
| Pré-visualizar análise | (smart-parse "Call Bob next Monday") |
| Analisar JSON | (json-parse "{\"a\": 1}") |
| Serializar JSON | (json-stringify {:name "Alice"}) |
| HTTP GET | (http-get "https://api.example.com/data") |
| Item recorrente | (add-item "Task" :when "2026-03-01" :recur :weekly) |
| Intervalo personalizado | (add-item "Review" :when "2026-04-01" :recur (every 3 :months)) |
| Definir modelo | (deftemplate standup :text "Standup" :recur :daily) |
| Usar modelo | (from-template standup :when "2026-03-01") |
| Abrir agenda | (open-agenda "work.db") |
| Alternar agenda | (use-agenda work) |
| Listar agendas | (agendas) |
| Exportar agenda | (export-agenda "work" :format :json :path "backup.json") |
| Importar agenda | (import-agenda "backup.json") |