Module: Kotodama

Defined in:
lib/rule.rb,
lib/type.rb,
lib/change.rb,
lib/kotodama.rb,
lib/language.rb,
lib/kotodama_grammar.rb

Defined Under Namespace

Classes: Change, Language, Rule, Type

Constant Summary collapse

VERSION =
'1.2.5'
GRAMMAR =
Kaiseki::Grammar.new do
  starting :document
  skipping :WS | :COMMENT
  
  rule :document do
    parses proc { @language = Language.new } & :options_block.optional & :content_block.zero_or_more & :EOF.skip
    filter { @language }
  end
  
  rule :options_block do
    parses 'options'.skip & '{'.skip & :option.zero_or_more & '}'.skip
  end
  
  rule :option do
    parses :LITERAL & '=>'.skip & (:ATOM | :INT) & ';'.skip
    node [:key, :value]
    action do
      @language.options[@key] = @value
    end
  end
  
  rule :content_block do
    parses :type_block | :rule_block | :change_block | :spelling_rule | :zipf_block
  end
  
  rule :type_block do
    parses 'type'.skip & :ATOM.set(:id) & :get_type & '{'.skip & :symbol_declaration.zero_or_more & '}'.skip
  end
  
  action :get_type do
    if @language.types.key? @id
      @type = @language.types[@id]
    else
      @type = @language.types[@id] = Type.new(@language)
    end
  end
  
  rule :symbol_declaration do
    parses :LIST & :WEIGHT.optional & ';'.skip
    node [:symbols, :weight]
    action  do
      @symbols.each {|n| @type.add n, @weight }
    end
  end
  
  rule :rule_block do
    parses 'rule'.skip & :ATOM.set(:id) & :get_rule & '{'.skip & (:exclude_expression | :rule_expression).zero_or_more & '}'.skip
  end
  
  action :get_rule do
    if @language.rules.key? @id
      @rule = @language.rules[@id]
    else
      @rule = @language.rules[@id] = Rule.new(@language)
    end
  end
  
  rule :exclude_expression do
    parses 'exclude'.skip & :ID.one_or_more & ';'.skip
    action { @rule.excludes << @result.join }
  end
  
  rule :rule_expression do
    parses :ATOM.one_or_more & :WEIGHT.optional & ';'.skip
    node [:expression, :weight]
    action { @rule.add @expression, @weight }
  end
  
  rule :change_block do
    parses 'change'.skip & :ATOM.set(:id) & :get_change & '{'.skip & (:ATOM.set(:id).action { @change.add [:load, @id, nil] } | :sound_change).zero_or_more & '}'.skip
  end
  
  action :get_change do
    if @language.changes.key? @id
      @change = @language.changes[@id]
    else
      @change = @language.changes[@id] = Change.new(@language)
    end
  end
  
  rule :sound_change do
    parses (:insert_change | :delete_change | :convert_change) & :change_env.optional & ';'.skip
    node [:type, :env]
    action do
      @type[2] = @env
      @change.add @type
    end
  end
  
  rule :insert_change do
    parses 'insert'.skip & :ATOM.one_or_more
    filter { [:insert, (@result.is_a?(Array) ? @result : [@result]), nil] }
  end
  
  rule :delete_change do
    parses 'delete'.skip & :ATOM.one_or_more
    filter { [:delete, (@result.is_a?(Array) ? @result : [@result]), nil] }
  end
  
  rule :convert_change do
    parses :ATOM.one_or_more & '>'.skip & :ATOM.one_or_more
    node [:from, :to]
    filter { [:convert, [@from, @to], nil] }
  end
  
  rule :change_env do
    parses '/'.skip & '#'.optional & :ATOM.zero_or_more & '_'.skip & :ATOM.zero_or_more & '#'.optional
  end
  
  rule :spelling_rule do
    parses 'spell'.skip & :ID & 'as'.skip & :LITERAL & ';'.skip
    node [:symbol, :spelling]
    action do
      @language.spellings[@symbol] = @spelling[1..-1]
    end
  end
  
  rule :zipf_block do
    parses 'zipf'.skip & :FLOAT.set(:float) & '{'.skip & (:LIST & ';'.skip).one_or_more.set(:lists) & '}'.skip
    action do
      zipf_list = []
      @lists.each {|list1| list1.each {|n| zipf_list << n } }
      zipf_list.length.times do |i|
        value = 1.0 / (i + 1) ** @float
        value = (value * 100).to_i
        @language.zipf[zipf_list[i]] = value
      end
    end
  end
  
  rule :ID do
    parses /[a-zA-Z][0-9]?/
    filter do
      if @result.length == 1
        @result + '0'
      else
        @result
      end
    end
  end
  
  rule :LIST do
    parses (:ATOM & (','.skip & :ATOM).zero_or_more).merge
  end
  
  rule :LITERAL do
    parses /\"([^"]*)\"/ | /\'([^']*)\'/
    simplify false
    filter do
      '$' + @result.captures[0]
    end
  end
  
  rule :ATOM do
    parses :ID | :LITERAL
  end
  
  rule :WEIGHT do
    parses '('.skip & :INT & ')'.skip
  end
  
  rule :INT do
    parses /\d+/
    cast Integer
  end
  
  rule :FLOAT do
    parses /\d+\.\d+/
    cast Float
  end
  
  rule :WS do
    parses /\s+/
  end
  
  rule :COMMENT do
    parses /%[^\n]*\n/
  end
end