Вы хотите что-то вроде (не проверено):
'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"]