Анализатор Treetop: синтаксис определения функции - n аргументов - PullRequest
3 голосов
/ 08 ноября 2011

В настоящее время я пытаюсь описать некоторую базовую грамматику Ruby, но теперь я застрял с определением функции.Действительно, я не знаю, как обращаться с аргументом «n».Вот код, который я использую для обработки функций, содержащих от 0 до 2 аргументов:

  rule function_definition
    'def' space? identifier space? '(' space? expression? space? ','? expression? space? ')'
      block
    space? 'end' <FunctionDefinition>
  end  

Как я могу обработать аргумент 'n'?Есть ли какой-нибудь рекурсивный способ сделать это?

РЕДАКТИРОВАТЬ:

Я хотел бы подчеркнуть тот факт, что мне нужно, чтобы аргументы были в дереве результатов.Например:

 Argument offset=42, "arg1"
 Argument offset=43, "arg2"
 Argument offset=44, "arg3"

Так что мне нужно сделать cstom объявление подкласса SyntaxNode, как, например, я сделал для правила function_definition, например.

Ответы [ 2 ]

1 голос
/ 08 ноября 2011

Вы хотите что-то вроде (не проверено):

'def' space? identifier space? '(' space? ( expression ( space? ',' expression )* )? space?  ')'

(NB, если это стиль рубина def, тогда паренсы также необязательны в случае, когда нет аргументов)

Изменить, чтобы продемонстрировать извлечение аргументов из дерева разбора - здесь я выплюнул text_value каждого синтаксического узла аргумента (FunctionArg), но вы, конечно, могли бы сделать что-нибудь:

foo.rb:

# Prepend current directory to load path
$:.push('.')

# Load treetop grammar directly without compilation
require 'polyglot'
require 'treetop'
require 'def'

# Classes for bespoke nodes
class FunctionDefinition < Treetop::Runtime::SyntaxNode ; end
class FunctionArg < Treetop::Runtime::SyntaxNode ; end

# Some tests
[
  'def foo() block end',
  'def foo(arg1) block end',
  'def foo(arg1, arg2) block end',
  'def foo(arg1, arg2, arg3) block end',
].each do |test|
  parser = DefParser.new
  tree = parser.parse( test )
  raise RuntimeError, "Parsing failed on line:\n#{test}" unless tree
  puts test
  puts "identifier=#{tree.function_identifier}"
  puts "args=#{tree.function_args.inspect}"
  puts
end

def.tt:

grammar Def

  # Top level rule: a function
  rule function_definition
    'def' space identifier space? '(' space? arg0 more_args space? ')' space block space 'end' <FunctionDefinition>
    {
      def function_identifier
        identifier.text_value
      end
      def function_args
        arg0.is_a?( FunctionArg ) ? [ arg0.text_value ] + more_args.args : []
      end
    }
  end

  # First function argument
  rule arg0
    argument?
  end

  # Second and further function arguments
  rule more_args
    ( space? ',' space? argument )* 
    {
      def args
        elements.map { |e| e.elements.last.text_value }
      end
    }
  end

  # Function identifier
  rule identifier
    [a-zA-Z_] [a-zA-Z0-9_]*
  end

  # TODO Dummy rule for function block
  rule block
    'block'
  end

  # Function argument
  rule argument
    [a-zA-Z_] [a-zA-Z0-9_]* <FunctionArg>
  end

  # Horizontal whitespace (htab or space character).
  rule space
    [ \t]
  end

end

Выход:

def foo() block end
identifier=foo
args=[]

def foo(arg1) block end
identifier=foo
args=["arg1"]

def foo(arg1, arg2) block end
identifier=foo
args=["arg1", "arg2"]

def foo(arg1, arg2, arg3) block end
identifier=foo
args=["arg1", "arg2", "arg3"]
0 голосов
/ 17 августа 2015

Лучшим методом может быть использование рекурсии.

rule function_definition
  'def' space identifier space? '(' space? argList? space? ')' block 'end'
end

rule argList
   identifier space? ',' space? argList
   / identifier
end
...