#!/usr/bin/env ruby # ebnf --- Process EBNF to generate the following: # * S-Expression # * Turtle # * Either of the above, transformed to BNF # * And with First/Follow rules $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", 'lib'))) require "bundler/setup" require 'rubygems' require 'getoptlong' require 'ebnf' def dump_tables(grammarFile, ebnf, output, options) ebnf.build_tables unless output == STDOUT output.puts "# This file is automatically generated by #{__FILE__}" output.puts "# BRANCH derived from #{grammarFile}" unless ebnf.errors.empty? output.puts "# Note, tables completed with errors, may need to be resolved manually:" #output.puts "# #{pp.conflicts.map{|c| c.join("\n# ")}.join("\n# ")}" end output.puts "module #{options.fetch(:mod_name, 'Branch')}" output.puts " START = #{ebnf.start.inspect}" output.puts end ebnf.outputTable(output, 'BRANCH', ebnf.branch, 1) ebnf.outputTable(output, 'TERMINALS', ebnf.terminals, 1) ebnf.outputTable(output, 'FIRST', ebnf.first, 1) ebnf.outputTable(output, 'FOLLOW', ebnf.follow, 1) ebnf.outputTable(output, 'PASS', [ebnf.pass], 1) if ebnf.pass unless output == STDOUT output.puts "end" end "" end options = { :output_format => :sxp, :prefix => "ttl", :namespace => "http://www.w3.org/ns/formats/Turtle#", } out = STDOUT OPT_ARGS = [ ["--dbg", GetoptLong::NO_ARGUMENT, "Turn on debugging output"], ["--bnf", GetoptLong::NO_ARGUMENT, "Transform EBNF to BNF"], ["--evaluate","-e", GetoptLong::REQUIRED_ARGUMENT,"Evaluate argument as a JSON-LD document"], ["--ll1", GetoptLong::REQUIRED_ARGUMENT,"Generate First/Follow rules, argument is start symbol"], ["--format", "-f", GetoptLong::REQUIRED_ARGUMENT,"Specify output format one of ebnf, html, ttl, sxp, or rb"], ["--input-format", GetoptLong::REQUIRED_ARGUMENT,"Specify input format one of ebnf or sxp"], ["--mod-name", GetoptLong::REQUIRED_ARGUMENT,"Module name used when creating ruby tables"], ["--output", "-o", GetoptLong::REQUIRED_ARGUMENT,"Output to the specified file path"], ["--prefix", "-p", GetoptLong::REQUIRED_ARGUMENT,"Prefix to use when generating Turtle"], ["--progress", "-v", GetoptLong::NO_ARGUMENT, "Detail on execution"], ["--namespace", "-n", GetoptLong::REQUIRED_ARGUMENT,"Namespace to use when generating Turtle"], ["--help", "-?", GetoptLong::NO_ARGUMENT, "This message"] ] def usage STDERR.puts %{#{$0} Version #{EBNF::VERSION}} STDERR.puts %{Usage: #{$0} [options] file ...} width = OPT_ARGS.map do |o| l = o.first.length l += o[1].length + 2 if o[1].is_a?(String) l end.max OPT_ARGS.each do |o| s = " %-*s " % [width, (o[1].is_a?(String) ? "#{o[0,2].join(', ')}" : o[0])] s += o.last STDERR.puts s end exit(1) end opts = GetoptLong.new(*OPT_ARGS.map {|o| o[0..-2]}) opts.each do |opt, arg| case opt when '--dbg' then options[:debug] = true when '--bnf' then options[:bnf] = true when '--evaluate' then input = arg when '--input-format' then options[:format] = arg.to_sym when '--format' then options[:output_format] = arg.to_sym when '--ll1' then options[:ll1] = arg.to_sym when '--mod-name' then options[:mod_name] = arg when '--output' then out = File.open(arg, "w") when '--prefix' then options[:prefix] = arg when '--namespace' then options[:namespace] = arg when '--progress' then options[:progress] = true when '--help' then usage end end if options[:output_format] == :rb && !options[:ll1] STDERR.puts "outputing in .rb format requires -ll" exit(1) end input = File.open(ARGV[0]) if ARGV[0] ebnf = EBNF.parse(input || STDIN, options) ebnf.make_bnf if options[:bnf] || options[:ll1] ebnf.first_follow(options[:ll1]) if options[:ll1] res = case options[:output_format] when :ebnf then ebnf.to_s when :html then ebnf.to_html when :sxp then ebnf.to_sxp when :ttl then ebnf.to_ttl(options[:prefix], options[:namespace]) when :rb then dump_tables(ARGV[0], ebnf, out, options) else ebnf.ast.inspect end out.puts res