criando sua própria linguagem de programação

49
Criando sua própria linguagem de programação Dev In Sampa São Paulo, 28 de novembro de 2009

Upload: ronaldoferraz

Post on 06-Jun-2015

13.153 views

Category:

Technology


2 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Criando sua própria linguagem de programação

Criando sua própria linguagem de programaçãoDev In SampaSão Paulo, 28 de novembro de 2009

Page 2: Criando sua própria linguagem de programação

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

Page 3: Criando sua própria linguagem de programação

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 ;)

Page 4: Criando sua própria linguagem de programação

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

Page 5: Criando sua própria linguagem de programação

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

Page 6: Criando sua própria linguagem de programação

Um pouco de conceitos

Page 7: Criando sua própria linguagem de programação

Linguagem

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

— Wikipedia

Page 8: Criando sua própria linguagem de programação

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

Page 9: Criando sua própria linguagem de programação

Gramáticas formais

Value ← [0-9]+ / '(' Expr ')'Product ← Value (('*' / '/') Value)*Sum ← Product (('+' / '-') Product)*Expr ← Sum

Page 10: Criando sua própria linguagem de programação

Do texto à execuçãoSAÍDA

ANÁLISE SINTÁTICA

ANÁLISE LÉXICA

TOKENS

AST

COMPILADORINTERPRETADOR

TRADUTOR

CÓDIGO FONTE

PARSING

Page 11: Criando sua própria linguagem de programação

Introduzindo “Mirror”

Page 12: Criando sua própria linguagem de programação

Inspiração

Sintaxe baseada em Smalltalk e IO

Slot-based como Self

Forte e dinamicamente tipada

Interpretada, via bytecodes

Page 13: Criando sua própria linguagem de programação

Mirror

World mirrorInto: "Fib".

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

(Fib of: 10) transcribeAndBreak.

Page 14: Criando sua própria linguagem de programação

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

Page 15: Criando sua própria linguagem de programação

AST #1

(Fib of: 2 + 3)transcribeAndBreak.

Page 16: Criando sua própria linguagem de programação

AST #2

(Fib of: 2 + 3)transcribeAndBreak.

Page 17: Criando sua própria linguagem de programação

Bytecode

push 3push 2send +load Fibsend of:send transcribeAndBreakpop

Page 18: Criando sua própria linguagem de programação

A gramática

Page 19: Criando sua própria linguagem de programação

Blocos básicosgrammar 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

Page 20: Criando sua própria linguagem de programação

Keywords

grammar Mirror

rule keyword_expression variable:binary_expression? keywords:(spaces? keyword spaces expression:binary_expression)+ <KeywordExpression> end

# Account deposit: 100 from: user. # ...

end

Page 21: Criando sua própria linguagem de programação

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

Page 22: Criando sua própria linguagem de programação

Expressões unárias

grammar Mirror

rule unary_expression variable:primary selectors:(spaces selector:identifier !colon)+ <UnaryExpression> / primary end

# (Account current balance) transcribeAndBreak. # ...

end

Page 23: Criando sua própria linguagem de programação

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"

Page 24: Criando sua própria linguagem de programação

Convertendo a AST

Page 25: Criando sua própria linguagem de programação

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 endend

class Statement def initialize(expression) @expression = expression endend

Page 26: Criando sua própria linguagem de programação

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) endend

class Message def initialize(target, selector, *arguments) @target = target @selector = selector @arguments = arguments endend

Page 27: Criando sua própria linguagem de programação

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>]>>]

Page 28: Criando sua própria linguagem de programação

Geração de código

Page 29: Criando sua própria linguagem de programação

Double dispatchclass 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

Page 30: Criando sua própria linguagem de programação

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

Page 31: Criando sua própria linguagem de programação

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

Page 32: Criando sua própria linguagem de programação

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

Page 33: Criando sua própria linguagem de programação

Juntando as peças

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

irb> CodeGenerator.new(ast).generate

[ push 3, push 2, send +, pop]

Page 34: Criando sua própria linguagem de programação

Modelo de execução

Page 35: Criando sua própria linguagem de programação

Containers & Slots

0

BLOCK CONTEXT

BLOCK CONTEXT

ACCOUNT

DEPOSIT:

BALANCE

WITHDRAW:

USER USER

Page 36: Criando sua própria linguagem de programação

Universe & WorldUNIVERSE

ERROR

WORLD

WORLD

SET: TO:

MIRRORINTO:

Page 37: Criando sua própria linguagem de programação

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

Page 38: Criando sua própria linguagem de programação

Máquina virtual

Page 39: Criando sua própria linguagem de programação

Máquina Virtualclass VM def initialize(instructions) @instructions = instructions end

def run reset_instruction_pointer while has_instructions? execute(next_instruction) end end # ...

end

Page 40: Criando sua própria linguagem de programação

Máquina Virtualclass 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

Page 41: Criando sua própria linguagem de programação

Juntando as peças

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

[4]

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

[4]

Page 42: Criando sua própria linguagem de programação

Próximos passos

Page 43: Criando sua própria linguagem de programação

Próximos passos

Arrays

Inlining de mensagens comuns

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

ifTrue:ifFalse et al

to:do et al

Melhor uso de blocos

Page 44: Criando sua própria linguagem de programação

LLVM

Uma estratégia de compilação

Um conjunto de instruções virtualizado

Uma infra-estrutura de compilação

Um conjunto de ferramentas

Page 45: Criando sua própria linguagem de programação

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

Page 46: Criando sua própria linguagem de programação

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

Page 47: Criando sua própria linguagem de programação

LLVM: Uso

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

entry_block = function.create_blockexit_block_true = function.create_blockexit_block_false = function.create_block

builder = entry_block.buildercmp = builder.icmp_sgt(-1.llvm, 1.llvm)builder.cond_br(cmp, exit_block_true, exit_block_false)

Page 48: Criando sua própria linguagem de programação

LLVM: Uso

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

builder = exit_block_false.builderbuilder.return(0.llvm)

ExecutionEngine.get(module)ExecutionEngine.run_autoconvert(function)

Page 49: Criando sua própria linguagem de programação

Questões?

@rferrazhttp://logbr.reflectivesurface.com