O documento discute a criação de uma nova linguagem de programação chamada Mirror. Apresenta os conceitos-chave de linguagens de programação como sintaxe, semântica e gramáticas formais. Descreve elementos da linguagem Mirror como slots, contextos e mensagens. Explica como a linguagem é interpretada usando um parser, AST, bytecodes e máquina virtual.
2. Por quê?
Cada nova linguagem é um campo aberto para
livre experimentação
Compilação é uma arena em que todas as suas
habilidades são necessárias
3. A verdade verdadeira
“Por que escrever um programa se você pode
escrever um programa para escrever um
programa?”
— Autor desconhecido
A verdade verdadeira é que é divertido ;)
4. Na prática
Você pode usar um parser mais sofisticado para
suas DSLs
Você pode resolver problemas de portar código de
um domínio para outro com um tradutor
Você pode usar um interpretador para construir
geradores mais sofisticados
5. Na pior das hipóteses...
“Se uma linguagem não é capaz de afetar o modo
como você pensa sobre programação, não vale a
pena aprendê-la”
— Alan Perlis
7. Linguagem
“Uma notação para escrever programas, que são
especificações para a computação de uma
algoritmo”
— Wikipedia
8. Elementos
Sintaxe: o que é escrito, descrita em uma
gramática formal
Semântica: o que isso significa, especificado em
termos de formalizações de compilação e
execução
9. Gramáticas formais
Value ← [0-9]+ / '(' Expr ')'
Product ← Value (('*' / '/') Value)*
Sum ← Product (('+' / '-') Product)*
Expr ← Sum
10. Do texto à execução
CÓDIGO FONTE SAÍDA
PARSING
ANÁLISE LÉXICA COMPILADOR
INTERPRETADOR
TRADUTOR
TOKENS
ANÁLISE SINTÁTICA AST
12. Inspiração
Sintaxe baseada em Smalltalk e IO
Slot-based como Self
Forte e dinamicamente tipada
Interpretada, via bytecodes
13. Mirror
World mirrorInto: "Fib".
Fib set: "of:" to: [ n |
n <= 2
ifTrue: [ 1. ]
ifFalse: [ (of: n - 1) + (of: n - 2). ].
].
(Fib of: 10) transcribeAndBreak.
14. Análise
Via Treetop, um packrat parser em Ruby
PEGs
Análise versus geração
Sem ambigüidades
Gera uma árvore sintática que é compilada para
uma representação em bytecodes
25. Blocos básicos
# rule statements
# (spaces? statement spaces? "." spaces?)* <Statements>
# end
module Statements
def build
elements.collect do |element|
Ast::Statement.new(element.statement.build)
end
end
end
class Statement
def initialize(expression)
@expression = expression
end
end
26. Expressões binárias
# rule binary_expression
# variable:unary_expression spaces?
# selector:binary_selector spaces?
# expression:binary_expression <BinaryExpression> /
# unary_expression
module BinaryExpression
def build
Ast::Message.new(variable.build, selector.text_value, expression.build)
end
end
class Message
def initialize(target, selector, *arguments)
@target = target
@selector = selector
@arguments = arguments
end
end
29. Double dispatch
class CodeGenerator
def initialize(ast)
@ast = ast
end
def generate
@ast.collect { |statement| generate_any(statement) }.flatten
end
def generate_any(ast)
send("generate_#{ast.class.name.demodulize.underscore}", ast)
end
# ...
end
30. Blocos básicos
class CodeGenerator
def generate_statement(ast)
([generate_any(ast.expression)] + [Bytecode::Pop.new]).flatten
end
def generate_variable(ast)
Bytecode::Load.new(ast.name)
end
# ...
end
31. Mensagens
class CodeGenerator
def generate_message(ast)
instructions = []
ast.arguments.reverse.each do |argument|
instructions += [generate_any(argument)].flatten
end
instructions += [generate_any(ast.target)].flatten
instructions << Bytecode::Message.new(ast.selector)
instructions
end
# ...
end
32. Bytecodes
class Pop
def inspect
"pop"
end
end
class Message
def initialize(selector)
@selector = selector
@selector_name = get_selector_name(selector)
@selector_method = get_selector_method(selector)
@arity = get_selector_arity(selector)
end
# ...
end
35. Containers & Slots
ACCOUNT
BALANCE 0
DEPOSIT: BLOCK CONTEXT
WITHDRAW: BLOCK CONTEXT
USER USER
36. Universe & World
UNIVERSE WORLD
WORLD MIRRORINTO:
ERROR SET: TO:
37. Detalhes
O envio de mensagens acontece em um contexto
que é gerado para cada mensagem
Blocos geram contextos empilhados
O interpretador percorre os contextos até
encontrar o objeto apropriado para enviar a
mensagem
39. Máquina Virtual
class VM
def initialize(instructions)
@instructions = instructions
end
def run
reset_instruction_pointer
while has_instructions?
execute(next_instruction)
end
end
# ...
end
40. Máquina Virtual
class VM
def execute(instruction)
case instruction
when Bytecode::Implicit
stack_push_and_wrap(current_context)
when Bytecode::Pop
stack.pop
when Bytecode::Push
stack_push_and_wrap(instruction.value)
when Bytecode::Load
stack_push_and_wrap(walk_contexts(instruction.name))
# ...
end
end
end
43. Próximos passos
Arrays
Inlining de mensagens comuns
Primitivas: + - * / at: at:put:
ifTrue:ifFalse et al
to:do et al
Melhor uso de blocos
44. LLVM
Uma estratégia de compilação
Um conjunto de instruções virtualizado
Uma infra-estrutura de compilação
Um conjunto de ferramentas
45. LLVM
Efetivamente uma DSL para geração de código
intermediário otimizado e portável
Estático ou JIT
Usado por MacRuby, Rubinius, Unladden
Swallow e outros
46. LLVM: Uso
Transformar slots de código em funções
Transformar closures em funções quando não
fizer sentido que os mesmos sejam inline
Compilar o próprio interpretador para ser
parcialmente jitted