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.6'
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
		tag_error :error
	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
		tag_error :error
	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 }
		tag_error :error
	end
	
	rule :rule_expression do
		parses :ATOM.one_or_more & :WEIGHT.optional & ';'.skip
		node [:expression, :weight]
		action { @rule.add @expression, @weight }
		tag_error :error
	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