propane/lib/propane.rb

101 lines
2.5 KiB
Ruby

require "erb"
require "set"
require_relative "propane/cli"
require_relative "propane/code_point_range"
require_relative "propane/fa"
require_relative "propane/fa/state"
require_relative "propane/fa/state/transition"
require_relative "propane/grammar"
require_relative "propane/lexer"
require_relative "propane/lexer/dfa"
require_relative "propane/parser"
require_relative "propane/parser/item"
require_relative "propane/parser/item_set"
require_relative "propane/regex"
require_relative "propane/regex/nfa"
require_relative "propane/regex/unit"
require_relative "propane/rule_set"
require_relative "propane/rule"
require_relative "propane/token"
require_relative "propane/version"
class Propane
# EOF.
TOKEN_EOF = 0xFFFFFFFC
# Decoding error.
TOKEN_DECODE_ERROR = 0xFFFFFFFD
# Token ID for a "dropped" token.
TOKEN_DROP = 0xFFFFFFFE
# Invalid token ID.
TOKEN_NONE = 0xFFFFFFFF
class Error < RuntimeError
end
def initialize(input)
grammar = Grammar.new(input)
@classname = grammar.classname
@modulename = grammar.modulename
@tokens = grammar.tokens
@rule_sets = grammar.rule_sets
end
def generate(output_file, log_file)
expand_rules
lexer = Lexer.new(@tokens)
parser = Parser.new(@tokens, @rule_sets)
classname = @classname || File.basename(output_file).sub(%r{[^a-zA-Z0-9].*}, "").capitalize
erb = ERB.new(File.read(File.join(File.dirname(File.expand_path(__FILE__)), "../assets/parser.d.erb")), trim_mode: "<>")
result = erb.result(binding.clone)
File.open(output_file, "wb") do |fh|
fh.write(result)
end
end
private
def expand_rules
@rule_sets.each do |rule_name, rule_set|
if @tokens.include?(rule_name)
raise Error.new("Rule name collides with token name #{rule_name}")
end
end
unless @rule_sets["Start"]
raise Error.new("Start rule not found")
end
@rule_sets.each do |rule_name, rule_set|
rule_set.rules.each do |rule|
rule.components.map! do |component|
if @tokens[component]
@tokens[component]
elsif @rule_sets[component]
@rule_sets[component]
else
raise Error.new("Symbol #{component} not found")
end
end
end
end
end
class << self
def run(input_file, output_file, log_file)
begin
propane = Propane.new(File.read(input_file))
propane.generate(output_file, log_file)
rescue Error => e
$stderr.puts e.message
return 2
end
return 0
end
end
end