Pegjs: Suporte para análise de linguagens baseadas em indentação

Criado em 16 out. 2013  ·  34Comentários  ·  Fonte: pegjs/pegjs

Eu estava usando um monte de linguagens baseadas em indentação, como CoffeeScript, Jade, e quero criar DSLs sozinho.
Encontro alguns truques para manter recuos nos pegjs pesquisando, queria saber se existe uma solução consistente:
http://stackoverflow.com/questions/11659095/parse-indentation-level-with-peg-js
http://stackoverflow.com/questions/4205442/peg-for-python-style-indentation
https://gist.github.com/jakubkulhan/3192844
https://groups.google.com/forum/#!searchin/pegjs/indent/pegjs/RkbAB4rPlfU/xxafrY5wGCEJ
Mas os pegjs suportam esse recurso?

feature

Comentários muito úteis

É muito perigoso confiar nos efeitos colaterais que você adiciona em manipuladores personalizados para analisar gramáticas baseadas em indentação. Só não faça isso. Pegjs teria que adicionar alguma capacidade de empurrar e pop estado condicional a fim de tornar as indentações de análise (e outras gramáticas sensíveis ao contexto) seguras.

Isso é o que eu faço por agora e recomendo que você faça o seguinte: Pré-processe o arquivo de entrada e insira seus próprios tokens de recuo / recuo. Eu uso {{{{e}}}} respectivamente. Então sua gramática é livre de contexto e pode ser analisada normalmente. Isso pode bagunçar seus valores de linha / coluna, mas você pode corrigi-los em um pós-processador.

Todos 34 comentários

É muito perigoso confiar nos efeitos colaterais que você adiciona em manipuladores personalizados para analisar gramáticas baseadas em indentação. Só não faça isso. Pegjs teria que adicionar alguma capacidade de empurrar e pop estado condicional a fim de tornar as indentações de análise (e outras gramáticas sensíveis ao contexto) seguras.

Isso é o que eu faço por agora e recomendo que você faça o seguinte: Pré-processe o arquivo de entrada e insira seus próprios tokens de recuo / recuo. Eu uso {{{{e}}}} respectivamente. Então sua gramática é livre de contexto e pode ser analisada normalmente. Isso pode bagunçar seus valores de linha / coluna, mas você pode corrigi-los em um pós-processador.

Se você não precisa direcionar o javascript, Pegasus , meu clone do pegjs para C #, tem suporte para o estado push / popping. Aqui está um artigo wiki sobre como fazer exatamente o que você deseja: https://github.com/otac0n/Pegasus/wiki/Significant-Whitespace-Parsing

Gostaria de propor que os pegjs usem minha sintaxe como ponto de partida para a análise baseada em estado.

A capacidade de empurrar e abrir o estado com segurança é boa. Eu usaria isso se fosse baseado em Javascript. Simplesmente não vale a pena integrar um CLR apenas para análise.

Isso é o que eu imaginei. Acho, nesse caso, que provavelmente devo tentar fazer back-port minhas melhorias em pegjs.

No entanto, não quero necessariamente fazer isso sem ter uma conversa com @dmajda.

@ otac0n É bom. Eu não escrevo C #. JavaScript é muito melhor para mim.

Linguagens baseadas em indentação são importantes. Eu quero simplificar sua análise após 1.0.0.

Eu acho que este problema é melhor resolvido permitindo o estado em geral, assim como Pegasus faz e como sugerido em # 285. Aqui está uma ideia (o seguinte é a gramática de espaços em branco significativa de Pegasus traduzida para pegjs e com minha ideia de sintaxe adicionada):

{var indentation = 0}

program
  = s:statements eof { return s }

statements
  = line+

line
  = INDENTATION s:statement { return s }

statement
  = s:simpleStatement eol { return s }
  / "if" _ n:name _? ":" eol INDENT !"bar " s:statements UNDENT {
      return { condition: n, statements: s }
    }
  / "def" _ n:name _? ":" eol INDENT s:statements UNDENT {
      return { name: n, statements: s }
    }

simpleStatement
  = a:name _? "=" _? b:name { return { lValue: a, expression: b } }

name
  = [a-zA-Z] [a-zA-Z0-9]* { return text() }

_ = [ \t]+

eol = _? comment? ("\r\n" / "\n\r" / "\r" / "\n" / eof)

comment = "//" [^\r\n]*

eof = !.

INDENTATION
  = spaces:" "* &{ return spaces.length == indentation }

INDENT
  = #STATE{indentation}{ indentation += 4 }

UNDENT
  = #STATE{indentation}{ indentation -= 4 }

Observe os blocos de #STATE{indentation} próximos à parte inferior (obviamente inspirados em Pegasus). Eu chamo esses blocos de estado. A ideia é permitir um bloqueio de estado antes das ações. Aqui está um bloco de estado mais complicado:

#STATE{a, b, arr: {arr.slice()}, obj: {shallowCopy(obj)}, c}

É uma abreviação de:

#STATE{a: {a}, b: {b}, arr: {arr.slice()}, obj: {shallowCopy(obj)}, c: {c}}

Em outras palavras, após a expansão abreviada ter sido aplicada, o conteúdo de um bloco de estado é uma lista de identifier ":" "{" code "}" . Adicionar um bloco de estado antes de uma ação diz aos pegjs que essa ação modificará os identificadores listados e, se a regra for retrocedida, esses identificadores devem ser redefinidos para o código entre as chaves.

Aqui estão as funções compiladas para INDENT e UNDENT da gramática acima, com redefinição da variável indentation adicionada:

    function peg$parseINDENT() {
      var s0, s1, t0;

      s0 = peg$currPos;
      t0 = indentation;
      s1 = [];
      if (s1 !== peg$FAILED) {
        peg$reportedPos = s0;
        s1 = peg$c41();
      } else {
        indentation = t0;
      }
      s0 = s1;

      return s0;
    }

    function peg$parseUNDENT() {
      var s0, s1, t0;

      s0 = peg$currPos;
      t0 = indentation;
      s1 = [];
      if (s1 !== peg$FAILED) {
        peg$reportedPos = s0;
        s1 = peg$c42();
      } else {
        indentation = t0;
      }
      s0 = s1;

      return s0;
    }

E aqui está um pouco de como o "bloco de estado complicado" acima pode ser compilado:

s0 = peg$currPos;
t0 = a;
t1 = b;
t2 = arr.slice();
t3 = shallowCopy(obj);
t4 = c;
// ...
if (s1 !== peg$FAILED) {
  // ...
} else {
  peg$currPos = s0;
  a = t0;
  b = t1;
  arr = t2;
  obj = t3;
  c = t4;
}

O que você acha dessa ideia de ser capaz de:

  • Informe aos pegjs sobre quais variáveis ​​com estado serão modificadas por uma ação.
  • Forneça o código necessário para armazenar essas variáveis ​​se elas precisarem ser redefinidas. (Incluindo sintaxe abreviada para o caso simples em que a variável é um valor primitivo.)

E o que você acha da sintaxe?

Editar: aqui está a gramática de sintaxe proposta (apenas por diversão):

diff --git a/src/parser.pegjs b/src/parser.pegjs
index 08f6c4f..09e079f 100644
--- a/src/parser.pegjs
+++ b/src/parser.pegjs
@@ -116,12 +116,31 @@ ChoiceExpression
     }

 ActionExpression
-  = expression:SequenceExpression code:(__ CodeBlock)? {
+  = expression:SequenceExpression code:((__ StateBlock)? __ CodeBlock)? {
       return code !== null
-        ? { type: "action", expression: expression, code: code[1] }
+        ? {
+            type:       "action",
+            expression: expression,
+            code:       code[2],
+            stateVars:  (code[0] !== null ? code[0][1] : [])
+          }
         : expression;
     }

+StateBlock "state block"
+  = "#STATE{" __ first:StateBlockItem rest:(__ "," __ StateBlockItem)* __ "}" {
+      return buildList(first, rest, 3);
+    }
+
+StateBlockItem
+  = varName:Identifier expression:(__ ":" __ CodeBlock)? {
+      return {
+        type:       "stateVar",
+        name:       varName,
+        expression: expression !== null ? expression[3] : varName
+      };
+    }
+
 SequenceExpression
   = first:LabeledExpression rest:(__ LabeledExpression)* {
       return rest.length > 0

Oi, pessoal,
Apenas para esclarecer, estou correto que é melhor não usar PEG.js (com soluções alternativas desde o início deste problema) com linguagens baseadas em indentação até que este problema seja resolvido?
Obrigado.

@hoho , não entendi ... Mas depois encontrei outra solução para analisar indentações com o combinador de analisador como soluções e funcionou. E acho que meu recuo original para analisar indentações sem PEG.js.

Quer dizer, existem soluções alternativas para analisar o recuo, mas os comentários dizem que essas soluções irão falhar em alguns casos.

Deixe-me esclarecer a situação: a análise de linguagens baseadas em indentação no PEG.js é possível. Existem várias soluções mencionadas acima e acabei de criar outra enquanto tentava "sentir" isso (é uma gramática de uma linguagem simples com duas declarações, uma das quais pode conter sub-declarações recuadas - semelhante a, por exemplo, if em Python).

Uma coisa comum a todas as soluções é que elas precisam rastrear o estado de indentação manualmente (porque o PEG.js não pode fazer isso). Isso significa que existem duas limitações:

  1. Você não pode compilar a gramática com cache de forma segura (porque o analisador pode usar resultados armazenados em cache em vez de executar código de manipulação de estado).
  2. Você não pode retroceder pelos níveis de indentação (porque atualmente não há como desenrolar o estado ao retroceder). Em outras palavras, você não pode analisar um idioma onde há duas construções válidas que podem ser desambigadas somente após uma mudança de linha e nível de indentação.

A Limitação 1 pode causar problemas de desempenho em alguns casos, mas não acho que haja muitos idiomas para os quais a limitação 2 seria um problema.

Estou bem com esse estado até 1.0.0 e pretendo voltar a este tópico algum tempo depois. O primeiro nível de melhoria poderia ser livrar-se da limitação 2 usando um rastreamento de estado mais explícito (como sugerido acima) ou fornecendo um gancho de retrocesso (para que se possa desenrolar o estado corretamente). O segundo nível poderia eliminar a necessidade de rastrear o estado de indentação manualmente, fornecendo uma maneira declarativa de fazer isso. Isso pode ajudar com a limitação 1.

H, eu escrevi um patch (minúsculo, hacky) para PEG.js que suporta backtracking adequado, como expliquei aqui: https://github.com/pegjs/pegjs/issues/45

desculpe pelo solavanco 😜

Eu estava tentando criar analisadores CSON e YAML para uma linguagem que estou desenvolvendo e, enquanto procurava maneiras de criar um analisador baseado em indentação com PEG.js, propus um método simples que:

1) não depende de estados push / pop
2) afirmando níveis de recuo por meio de código dentro das ações

Ocorreu-me que qualquer uma das 2 soluções acima na verdade adiciona problemas de desempenho aos analisadores gerados. Além disso, em minha opinião:

1) confiar no estado não apenas adiciona uma sintaxe PEG.js feia, mas também pode afetar o tipo de analisador que pode ser gerado, pois seria necessário para suportar a manipulação de estado baseada em ação.
2) às vezes adicionar algum código em ações resulta em uma regra dependente de linguagem, e para alguns desenvolvedores isso significa que eles não podem usar plugins para gerar analisadores para outras linguagens como C ou PHP sem recorrer a mais plugins para lidar com ações em regras, que apenas significa um sistema de construção maior apenas para suportar 1 ou 2 alterações.

Depois de um tempo, comecei a criar minha própria variante do analisador PEG.js e pensei: por que não usar apenas operadores de prefixo de incremento (“++”) e decremento (“-”) (__ ++ expressão__ e __-- expressão__ ) para lidar com os resultados das expressões de correspondência (__expression * __ ou __expression + __).

O seguinte é um exemplo de gramática com base na linguagem simples baseada em intentação de @dmajda , reescrita para usar a nova expressão __ ++__ e __-- expressão__ em vez de __ & {predicado} __:

Start
  = Statements

Statements
  = Statement*

Statement
  = Indent* statement:(S / I) { return statement; }

S
  = "S" EOS {
      return "S";
    }

I
  = "I" EOL ++Indent statements:Statements --Indent { return statements; }
  / "I" EOS { return []; }

Indent "indent"
  = "\t"
 / !__ "  "

__ "white space"
 = " \t"
 / " "

EOS
  = EOL
  / EOF

EOL
  = "\n"

EOF
  = !.

Muito mais agradável aos olhos, não? Mais fácil de entender também, tanto para humanos quanto para software.

Como funciona? simples:

1) Indent* diz ao analisador que queremos 0 ou mais do que Indent está retornando
2) ++Indent diz ao analisador para aumentar a quantidade mínima de correspondências necessárias para Indent
3) Agora, a qualquer momento que o analisador estiver prestes a retornar as correspondências para Indent , ele primeiro espera que seja __1 mais__ correspondência do que antes, caso contrário, _peg $ SyntaxError_ será acionado.
4) --Indent diz ao analisador para diminuir a quantidade mínima de correspondências necessárias para Indent
5) Agora, a qualquer momento o analisador procura Indent e retorna as correspondências que espera __1 menos__ correspondência do que antes, caso contrário, _peg $ SyntaxError_ será acionado.

Esta solução é a melhor maneira de adicionar suporte para 'Análise de espaço em branco significativa' sem adicionar uma sintaxe feia às gramáticas PEG.js ou bloquear geradores de terceiros.

Aqui estão as regras alteradas para adicionar suporte para análise em _src / parser.pegjs_:

{
  const OPS_TO_PREFIXED_TYPES = {
    "$": "text",
    "&": "simple_and",
    "!": "simple_not",
    "++": "increment_match",
    "--": "decrement_match"
  };
}

PrefixedOperator
  = "$"
  / "&"
  / "!"
  / "++"
  / "--"

SuffixedOperator
  = "?"
  / "*"
  / "+" !"+"

Estou certo em supor que, para oferecer suporte ao lado do compilador / gerador, teremos que:

1) adicionar uma passagem de compilador que garanta que __ ++ expression__ ou __-- expression__ estão sendo usados ​​apenas em __expression * __ ou __expression + __, onde __expression__ deve ser dos tipos: escolha, sequência ou regra_ref
2) adicionar uma verificação baseada em cache no analisador gerado para __expression * __ ou __expression + __ que afirma que a correspondência mínima necessária é atendida antes de retornar as correspondências
3) opcionalmente, adicionar um método auxiliar para analisadores gerados para implementar que retorne o número de correspondências necessárias para uma determinada regra, por exemplo. nMatches( name: String ): Number

@futagoza , isso é limpo e inteligente. Eu gosto disso. Estou trabalhando em um analisador que controla o estado, mas o único estado de que realmente precisamos são os níveis de indentação. Posso usar essa ideia e dar-lhe crédito por isso. Rastrear o nível de indentação ainda requer efetivamente o estado de push / popping e, portanto, ainda pode impedir algumas otimizações, mas a semântica disso é muito boa.

Se você estiver adicionando operadores a uma gramática, recomendo adicionar o operador de prefixo @ também. Seu objetivo é simplesmente extrair um único resultado de regra de uma sequência. Usando isso, a gramática de exemplo fica ainda mais limpa. Não há mais ações {return x} triviais.

Start
  = Statements

Statements
  = Statement*

Statement
  = Indent* @(S / I)

S
  = "S" EOS {
      return "S";
    }

I
  = "I" EOL ++Indent <strong i="8">@Statements</strong> --Indent
  / "I" EOS { return []; }

Indent "indent"
  = "\t"
 / !__ "  "

__ "white space"
 = " \t"
 / " "

EOS
  = EOL
  / EOF

EOL
  = "\n"

EOF
  = !.

@kodyjking o que você acha disso?

@futagoza Você tem um fork / branch com o patch de indentação habilitado e um pequeno exemplo de gramática?

Estou trabalhando neste recuo de fork / branch

@krinye "Eu recomendo adicionar o operador de prefixo @ também. Seu propósito é simplesmente extrair um único resultado de regra de uma sequência"

Um de vocês poderia dar uma olhada e fazer um comentário ou relações públicas com a correção. Obrigado :)

Leia-me: mudanças de garfo

Eu depurei ... increment_match-not-found

Ah, não percebi a advertência:

Estou certo em supor que, para oferecer suporte ao lado do compilador / gerador, teremos que:

  • adicione uma passagem de compilador que garanta que ++ expressão ou - expressão sejam usados ​​apenas em expressão * ou expressão +, onde expressão deve ser dos tipos: escolha, sequência ou regra_ref
  • adicione uma verificação baseada em cache no analisador gerado para a expressão * ou expressão + que afirma que a correspondência mínima necessária é atendida antes de retornar as correspondências
  • opcionalmente, adicione um método auxiliar para analisadores gerados para implementar que retorne o número de correspondências necessárias para uma determinada regra, por exemplo. nMatches (nome: String): Número

Só por diversão, tentei adicionar isso em visitor.js

      increment_match: visitExpression,
      decrement_match: visitExpression,

Agora recebo Invalid opcode: undefined.

@kristianmandrup em relação ao operador @ para extrair valores únicos de sequências, tenho um fork apenas com esse recurso adicionado ao PegJS disponível aqui:

https://github.com/krisnye/pegjs

É uma adição muito simples.

@krisnye +1 para a implementação baseada em gramática, agradável e simples. Se você não se importar, vou adicionar isso à minha variante do PEG.js 😄

@kristianmandrup vejo você se comprometer com minha sugestão

@futagoza Por favor, faça.

Eu estava discutindo a lógica de indentação com um colega de trabalho e sugerimos os seguintes elementos sintáticos

// incrementa a variável de estado nomeada
identificador ++
// decrementa a variável de estado nomeada
identificador--

// repita a quantidade constante ou variável de estado (com padrão zero se a variável ainda não foi incrementada)
regra {inteiro | identificador}
// repita min / max usando constantes ou variáveis ​​de estado
regra {inteiro | identificador, inteiro | identificador}

O analisador no qual estamos trabalhando pode lidar com o estado arbitrário, mas, honestamente, o acima exposto é tudo o que é necessário para a análise de indentação.

Muito obrigado pessoal! Se é tão fácil de fazer, por que não fazer um fork "dedicado" onde as coisas que você mencionou "simplesmente funcionam";)
Saúde!

@krisnye

  1. Usar __identifier ++ __ pode facilmente causar um erro confuso se o desenvolvedor quis dizer __identifier + __, é por isso que optei por __ ++ identifier__ e, para consistência, __-- identifier__
  2. Conforme mencionado em uma questão diferente sobre intervalos, rule{ STATE_REPEAT / RANGE } pode ser confundido com rule{ ACTION } , especialmente se você estiver construindo um realce de sintaxe para PEG.js, então esta abordagem foi negada por @dmajda

@kristianmandrup _ (FORA

@futagoza Obrigado amigo :) Olhando para as extensões de recurso, mas não menciona o suporte a indentação. Isso está incluído, mas não documentado ou está diminuindo?

Outra pessoa dessa lista me indicou outras soluções de gerador / construtor de analisador que eu também poderia examinar. Mantenha-me informado! Saúde!

@kristianmandrup Não está incluído até onde eu posso dizer, mas @dmajda disse 3 anos atrás que ele vai investigar depois de lançar PEG.js v1, mas pelo que eu posso dizer que não será por mais 2 anos, a menos que ele planeje libere mais versões secundárias do PEG.js v0 (_0.12_, _0.13_, _etc_)

Eu quis dizer se você já tivesse incluído o suporte de recuo no ePEG ou no roteiro?

@kristianmandrup oh 😆, está no roteiro. Não atualizei o repositório ePEG.js por um tempo e recentemente decidi transformá-lo em uma reescrita completa do PEG.js em vez de um plug-in.

@futagoza concordou em ++ / - como pré-operações, e esqueci a sintaxe da ação, mudamos para [min, max]

Então

++identifier
--identifier
rule[integer | identifier]
rule[integer | identifier, integer | identifier]

@krisnye [ ... ] é usado para classes de personagens, consulte https://github.com/pegjs/pegjs#characters

O que estou fazendo no ePEG.js é adicionar intervalos (também no roteiro) para alcançar o que acho que você está descrevendo:

space = [ \t]*
rule = range|expression
  • __expression__ pode ser qualquer um destes: __ ++ space__, __-- space__ ou __space__
  • __range__ pode ser __ min.. __, __ min..max __, __ ..max __ ou __ exact __
  • __min__, __max__ ou __exact__ só pode ser um __ inteiro sem sinal__
  • usar uma __range__ com uma __expressão__ (por exemplo, 2 | expressão) pode nos permitir definir a quantidade total de __expressão__ necessária para que __rule__ analise com êxito.
  • usando o intervalo __exact__ com __ ++ expression__ ou __-- expression__ (por exemplo, 3 | ++ expression) pode nos permitir definir o valor de __integer__ para __ ++ __ ou __ - __, que é por padrão __1__
  • usar o intervalo __min__ ou __max__ com __ ++ expression__ ou __-- expression__ gera um erro de sintaxe.

Não estou usando variáveis ​​de estado, pois isso seria apenas confuso com identificadores de regra.

Usando uma combinação de __range____, __ ++ __, __ - __ ou __ @__ , espero criar arquivos de gramática PEG que dependam menos de regras __action__ para seus resultados, o que deve aumentar o tempo de desenvolvimento de espaços em branco (por exemplo, recuo baseado em arte ASCII, etc), pois os designers e / ou implementadores de linguagem não precisam se preocupar em tentar confirmar se a quantidade correta de espaços em branco foi analisada.
Isso também deve permitir que os desenvolvedores de plugins criem geradores de analisadores que podem otimizar com _sem medo_ de qual é a linguagem de nossa __action __ (por padrão, JavaScript, mas com plug-ins pode alterá-lo para CoffeeScript, PHP, etc).

Portanto, parece que ainda não é possível analisar Python com PEG.js fora da caixa, certo?

Se não, isso é algo que virá em breve? Existe um conjunto de tarefas necessárias para fazer este trabalho com as quais as pessoas possam contribuir?

Tenho um projeto em que adoraria conseguir um Python AST em JS, modificá-lo e convertê-lo de volta ao código-fonte com a mesma formatação, então, estou interessado em fazer isso acontecer se houver um claro roteiro.

@mindjuice Ainda não, está planejado para o pós-1.0.

Se você não pode esperar, meu sobrinho e eu criamos um analisador escrito em TypeScript que usa a mesma sintaxe e controla o recuo. Ainda não está documentado, mas é muito simples. Ainda há trabalho em andamento, pois o estamos usando como analisador para um novo design de linguagem.

https://github.com/krisnye/pegs

Você também pode tentar outro gerador de analisador com suporte, como chevrotain

Exemplo de recuo Python

Eu acredito que é muito possível analisar indentação semelhante a python com PEGjs.
O exemplo a seguir usa apenas recuo baseado em quatro espaços, mas pode ser estendido para cobrir tabulações com espaçamento arbitrário e outros caracteres de espaço em branco.
Na verdade, a linguagem em que estou trabalhando tem uma história de indentação um pouco mais complicada do que a do Python, e essa gramática funciona bem para ela.

{
    let prevIndentCount = 0;
    function print(...s) { console.log(...s); }
}

Indent 'indent'
    = i:("    "+) { 
        let currentIndentCount = i.toString().replace(/,/g, "").length;
        if (currentIndentCount === prevIndentCount + 4) { 
            // DEBUG //
            print("=== Indent ===");
            print("    current:"+currentIndentCount); 
            print("    previous:"+prevIndentCount);
            print("    lineNumber:"+location().start.line); 
            // DEBUG //
            prevIndentCount += 4;
            return "[indent]";
        }
        error("error: expected a 4-space indentation here!")
    } // 4 spaces 

Samedent 'samedent'
    = s:("    "+ / "") &{
        let currentIndentCount = s.toString().replace(/,/g, "").length;
        if (currentIndentCount === prevIndentCount) {
            print("=== Samedent ===");
            return true;
        }
        return false;
    }

Dedent 'dedent'
    = d:("    "+ / "") {
        let currentIndentCount = d.toString().replace(/,/g, "").length;
        if (currentIndentCount < prevIndentCount) {
            // DEBUG //
            print("=== Dedent ===");
            print("    current:"+currentIndentCount); 
            print("    previous:"+prevIndentCount);
            print("    lineNumber:"+location().start.line); 
            // DEBUG //
            prevIndentCount -= 4;
            return "[dedent]";
        }
        error("error: expected a 4-space dedentation here!");
    }

Com a gramática acima, você pode criar uma regra de bloqueio recuada como esta:

FunctionDeclaration 
    = 'function' _ Identifier _ FunctionParameterSection _ ":" _ FunctionBody

FunctionBody
    = Newline Indent FunctionSourceCode (Newline Samedent FunctionSourceCode)* Dedent 
Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

ghost picture ghost  ·  22Comentários

krisnye picture krisnye  ·  21Comentários

log4b0at picture log4b0at  ·  25Comentários

futagoza picture futagoza  ·  13Comentários

doersino picture doersino  ·  15Comentários