Your SlideShare is downloading. ×
0
Criando sua própria
linguagem de programação
Dev In Sampa
São Paulo, 28 de novembro de 2009
Por quê?


Cada nova linguagem é um campo aberto para
livre experimentação

Compilação é uma arena em que todas as suas
ha...
A verdade verdadeira

“Por que escrever um programa se você pode
escrever um programa para escrever um
programa?”

       ...
Na prática

Você pode usar um parser mais sofisticado para
suas DSLs

Você pode resolver problemas de portar código de
um d...
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
pe...
Um pouco de conceitos
Linguagem


“Uma notação para escrever programas, que são
especificações para a computação de uma
algoritmo”

             ...
Elementos


Sintaxe: o que é escrito, descrita em uma
gramática formal

Semântica: o que isso significa, especificado em
ter...
Gramáticas formais


Value ← [0-9]+ / '(' Expr ')'
Product ← Value (('*' / '/') Value)*
Sum ← Product (('+' / '-') Product...
Do texto à execução
  CÓDIGO FONTE              SAÍDA

              PARSING

 ANÁLISE LÉXICA           COMPILADOR
       ...
Introduzindo “Mirror”
Inspiração

Sintaxe baseada em Smalltalk e IO

Slot-based como Self

Forte e dinamicamente tipada

Interpretada, via bytec...
Mirror
World mirrorInto: "Fib".


Fib set: "of:" to: [ n |
     n <= 2
       ifTrue: [ 1. ]
       ifFalse: [ (of: n - 1)...
Análise

Via Treetop, um packrat parser em Ruby

  PEGs

  Análise versus geração

  Sem ambigüidades

Gera uma árvore sin...
AST #1


(Fib of: 2 + 3)
  transcribeAndBreak.
AST #2


(Fib of: 2 + 3)
  transcribeAndBreak.
Bytecode

push 3
push 2
send +
load Fib
send of:
send transcribeAndBreak
pop
A gramática
Blocos básicos
grammar Mirror


  rule statements
      (spaces? statement spaces? "." spaces?)* <Statements>
  end


  ru...
Keywords

grammar Mirror


  rule keyword_expression
      variable:binary_expression?
        keywords:(spaces? keyword s...
Expressões binárias
grammar Mirror


  rule binary_expression
      variable:unary_expression spaces?
        selector:bin...
Expressões unárias
grammar Mirror


  rule unary_expression
        variable:primary
          selectors:(spaces selector:...
Juntando as peças
irb> MirrorParser.new.parse('2 + 3.')


SyntaxNode+Statements offset=0, "2 + 3." (build):
  SyntaxNode+S...
Convertendo a AST
Blocos básicos
#     rule statements
#         (spaces? statement spaces? "." spaces?)* <Statements>
#     end


module St...
Expressões binárias
#     rule binary_expression
#         variable:unary_expression spaces?
#           selector:binary_s...
Juntando as peças

irb> MirrorParser.new.parse('2 + 3.').build


[
    #<Ast::Statement
      @expression =
        #<Ast:...
Geração de código
Double dispatch
class CodeGenerator


  def initialize(ast)
      @ast = ast
  end


  def generate
      @ast.collect { |...
Blocos básicos
class CodeGenerator


  def generate_statement(ast)
      ([generate_any(ast.expression)] + [Bytecode::Pop....
Mensagens
class CodeGenerator


  def generate_message(ast)
      instructions = []
      ast.arguments.reverse.each do |a...
Bytecodes
class Pop
  def inspect
      "pop"
  end
end


class Message
  def initialize(selector)
      @selector = selec...
Juntando as peças

irb> ast = MirrorParser.new.parse('2 + 3.').build


irb> CodeGenerator.new(ast).generate


[
    push 3...
Modelo de execução
Containers & Slots
   ACCOUNT

   BALANCE           0

    DEPOSIT:   BLOCK CONTEXT

   WITHDRAW:   BLOCK CONTEXT

     US...
Universe & World
  UNIVERSE         WORLD

   WORLD       MIRRORINTO:

   ERROR           SET: TO:
Detalhes

O envio de mensagens acontece em um contexto
que é gerado para cada mensagem

Blocos geram contextos empilhados
...
Máquina virtual
Máquina Virtual
class VM


  def initialize(instructions)
      @instructions = instructions
  end


  def run
      reset...
Máquina Virtual
class VM


  def execute(instruction)
      case instruction
      when Bytecode::Implicit
        stack_p...
Juntando as peças

irb> Interpreter.run(true, "World offload: 2 + 2.")


[4]


irb> Interpreter.run(true, "World offload: ...
Próximos passos
Próximos passos
Arrays

Inlining de mensagens comuns

  Primitivas: + - * / at: at:put:

  ifTrue:ifFalse et al

  to:do e...
LLVM

Uma estratégia de compilação

Um conjunto de instruções virtualizado

Uma infra-estrutura de compilação

Um conjunto...
LLVM

Efetivamente uma DSL para geração de código
intermediário otimizado e portável

Estático ou JIT

Usado por MacRuby, ...
LLVM: Uso

Transformar slots de código em funções

Transformar closures em funções quando não
fizer sentido que os mesmos s...
LLVM: Uso

module = LLVM::Module.new("mirror")
type = Type::function(MACHINE_WORD, [])
function = module.get_or_insert_fun...
LLVM: Uso


builder = exit_block_true.builder
builder.return(1.llvm)


builder = exit_block_false.builder
builder.return(0...
Questões?

@rferraz
http://logbr.reflectivesurface.com
Upcoming SlideShare
Loading in...5
×

Criando sua própria linguagem de programação

13,500

Published on

Uma rápida aventura pelo processo de criar uma pequena linguagem de programação para demonstrar Treetop e interpretação de código.

Published in: Technology
0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
13,500
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
182
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide

Transcript of "Criando sua própria linguagem de programação"

  1. 1. Criando sua própria linguagem de programação Dev In Sampa São Paulo, 28 de novembro de 2009
  2. 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. 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. 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. 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
  6. 6. Um pouco de conceitos
  7. 7. Linguagem “Uma notação para escrever programas, que são especificações para a computação de uma algoritmo” — Wikipedia
  8. 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. 9. Gramáticas formais Value ← [0-9]+ / '(' Expr ')' Product ← Value (('*' / '/') Value)* Sum ← Product (('+' / '-') Product)* Expr ← Sum
  10. 10. Do texto à execução CÓDIGO FONTE SAÍDA PARSING ANÁLISE LÉXICA COMPILADOR INTERPRETADOR TRADUTOR TOKENS ANÁLISE SINTÁTICA AST
  11. 11. Introduzindo “Mirror”
  12. 12. Inspiração Sintaxe baseada em Smalltalk e IO Slot-based como Self Forte e dinamicamente tipada Interpretada, via bytecodes
  13. 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. 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
  15. 15. AST #1 (Fib of: 2 + 3) transcribeAndBreak.
  16. 16. AST #2 (Fib of: 2 + 3) transcribeAndBreak.
  17. 17. Bytecode push 3 push 2 send + load Fib send of: send transcribeAndBreak pop
  18. 18. A gramática
  19. 19. Blocos básicos grammar Mirror rule statements (spaces? statement spaces? "." spaces?)* <Statements> end rule statement message_expression end rule message_expression keyword_expression / binary_expression / unary_expression end # ... end
  20. 20. Keywords grammar Mirror rule keyword_expression variable:binary_expression? keywords:(spaces? keyword spaces expression:binary_expression)+ <KeywordExpression> end # Account deposit: 100 from: user. # ... end
  21. 21. Expressões binárias grammar Mirror rule binary_expression variable:unary_expression spaces? selector:binary_selector spaces? expression:binary_expression <BinaryExpression> / unary_expression end # 2 + 3 * (account balance). # ... end
  22. 22. Expressões unárias grammar Mirror rule unary_expression variable:primary selectors:(spaces selector:identifier !colon)+ <UnaryExpression> / primary end # (Account current balance) transcribeAndBreak. # ... end
  23. 23. Juntando as peças irb> MirrorParser.new.parse('2 + 3.') SyntaxNode+Statements offset=0, "2 + 3." (build): SyntaxNode+Statements0 offset=0, "2 + 3." (statement): SyntaxNode+BinaryExpression0+BinaryExpression offset=0, "2 + 3": SyntaxNode+IntegerLiteral offset=0, "2" (build): SyntaxNode offset=0, "2" SyntaxNode+Spaces2 offset=1, " ": SyntaxNode offset=2, "+": SyntaxNode offset=2, "+" SyntaxNode+IntegerLiteral offset=4, "3" (build): SyntaxNode offset=4, "3"
  24. 24. Convertendo a AST
  25. 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. 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
  27. 27. Juntando as peças irb> MirrorParser.new.parse('2 + 3.').build [ #<Ast::Statement @expression = #<Ast::Message @selector = "+", @target = #<Ast::Literal @value = "2", @type = :integer>, @arguments = [#<Ast::Literal @value = "3", @type = :integer>]>> ]
  28. 28. Geração de código
  29. 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. 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. 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. 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
  33. 33. Juntando as peças irb> ast = MirrorParser.new.parse('2 + 3.').build irb> CodeGenerator.new(ast).generate [ push 3, push 2, send +, pop ]
  34. 34. Modelo de execução
  35. 35. Containers & Slots ACCOUNT BALANCE 0 DEPOSIT: BLOCK CONTEXT WITHDRAW: BLOCK CONTEXT USER USER
  36. 36. Universe & World UNIVERSE WORLD WORLD MIRRORINTO: ERROR SET: TO:
  37. 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
  38. 38. Máquina virtual
  39. 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. 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
  41. 41. Juntando as peças irb> Interpreter.run(true, "World offload: 2 + 2.") [4] irb> Interpreter.run(true, "World offload: [ 2 + 2. ] value.") [4]
  42. 42. Próximos passos
  43. 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. 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. 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. 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
  47. 47. LLVM: Uso module = LLVM::Module.new("mirror") type = Type::function(MACHINE_WORD, []) function = module.get_or_insert_function("main", type) entry_block = function.create_block exit_block_true = function.create_block exit_block_false = function.create_block builder = entry_block.builder cmp = builder.icmp_sgt(-1.llvm, 1.llvm) builder.cond_br(cmp, exit_block_true, exit_block_false)
  48. 48. LLVM: Uso builder = exit_block_true.builder builder.return(1.llvm) builder = exit_block_false.builder builder.return(0.llvm) ExecutionEngine.get(module) ExecutionEngine.run_autoconvert(function)
  49. 49. Questões? @rferraz http://logbr.reflectivesurface.com
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×