Building DSL in Semi-Natural Language in Ruby

I am interested in creating DSL in Ruby for use in parsing microblog updates. In particular, I thought I could translate text into a Ruby string in the same way that the Rails gem allows "4.days.ago". I already have a regex code that will translate the text

@USER_A: give X points to @USER_B for accomplishing some task
@USER_B: take Y points from @USER_A for not giving me enough points

into something like

Scorekeeper.new.give(x).to("USER_B").for("accomplishing some task").giver("USER_A")
Scorekeeper.new.take(x).from("USER_A").for("not giving me enough points").giver("USER_B")

It is acceptable for me to formalize the syntax of updates so that only standardized text is provided and analyzed, which allows me to intensively process updates. So it seems like it is more a question of how to implement the DSL class. I have the following stub class (removed all error checking and replaced some with comments to minimize insertion):

class Scorekeeper

  attr_accessor :score, :user, :reason, :sender

  def give(num)
    # Can 'give 4' or can 'give a -5'; ensure 'to' called
    self.score = num
    self
  end

  def take(num)
    # ensure negative and 'from' called
    self.score = num < 0 ? num : num * -1
    self
  end

  def plus
    self.score > 0
  end

  def to (str)
    self.user = str
    self
  end

  def from(str)
    self.user = str
    self
  end

  def for(str)
    self.reason = str
    self
  end

  def giver(str)
    self.sender = str
    self
  end

  def command
    str = plus ? "giving @#{user} #{score} points" : "taking #{score * -1} points from @#{user}"
    "@#{sender} is #{str} for #{reason}"
  end

end

Execution of the following commands:

t = eval('Scorekeeper.new.take(4).from("USER_A").for("not giving me enough points").giver("USER_B")')
p t.command
p t.inspect

Sets the expected results:

"@USER_B is taking 4 points from @USER_A for not giving me enough points"
"#<Scorekeeper:0x100152010 @reason=\"not giving me enough points\", @user=\"USER_A\", @score=4, @sender=\"USER_B\">"

, : -, , ? - DSL - ?

, eval, sub/gsub regex, , , .

+5
3

@David_James, , DSL - , , . , :

SEARCH_STRING = "@Scorekeeper give a healthy 4 to the great @USER_A for doing something 
really cool.Then give the friendly @USER_B a healthy five points for working on this. 
Then take seven points from the jerk @USER_C."

PATTERN_A = /\b(give|take)[\s\w]*([+-]?[0-9]|one|two|three|four|five|six|seven|eight|nine|ten)[\s\w]*\b(to|from)[\s\w]*@([a-zA-Z0-9_]*)\b/i

PATTERN_B = /\bgive[\s\w]*@([a-zA-Z0-9_]*)\b[\s\w]*([+-]?[0-9]|one|two|three|four|five|six|seven|eight|nine|ten)/i

SEARCH_STRING.scan(PATTERN_A) # => [["give", "4", "to", "USER_A"],
                              #     ["take", "seven", "from", "USER_C"]]
SEARCH_STRING.scan(PATTERN_B) # => [["USER_B", "five"]]

, , , , "name- > points" "points- > name". , , , , . "" .

, , ( , gobbledygook :)

0

: - ?

, , , .

, :

@USER_A: give X points to @USER_B for accomplishing some task

Ruby:

input = "@abe: give 2 points to @bob for writing clean code"
PATTERN = /^@(.+?): give ([0-9]+) points to @(.+?) for (.+?)$/
input =~ PATTERN
user_a = $~[1] # => "abe"
x      = $~[2] # => "2"
user_b = $~[3] # => "bob"
why    = $~[4] # => "writing clean code"

, - . , Ruby, Treetop: http://treetop.rubyforge.org/

, , . eval - , , . . , .

DSL, : ? ? , , , DSL, .

+5

( MOO ).

, . - " ", " " .. - , - DSL , .

, , , DSL . , , DSL. , DSL; .

, ? , , , . " ", . "to" "from" , .

, , IMO.

, . , : Ruby .

+1
source

All Articles