require 'wlang/ruby_extensions' require 'stringio' require 'wlang/rule' require 'wlang/rule_set' require 'wlang/encoder_set' require 'wlang/dialect' require 'wlang/dialect_dsl' require 'wlang/dialect_loader' require 'wlang/parser' require 'wlang/parser_context' require 'wlang/intelligent_buffer' # # Main module of the _wlang_ code generator/template engine, providing a facade # on _wlang_ tools. See also the Roadmap section of {README}[link://files/README.html] # to enter the library. # module WLang # Current version of WLang VERSION = "0.8.5".freeze # Reusable string for building dialect name based regexps DIALECT_NAME_REGEXP_STR = "[-a-z]+" # # Regular expression for dialect names. # DIALECT_NAME_REGEXP = /^([-a-z]+)*$/ # Reusable string for building dialect name based regexps QUALIFIED_DIALECT_NAME_REGEXP_STR = "[-a-z]+([\/][-a-z]+)*" # # Regular expression for dialect qualified names. Dialect qualified names are # '/' seperated names, where a name is [-a-z]+. # Examples: wlang/xhtml/uri, wlang/plain-text, ... # QUALIFIED_DIALECT_NAME_REGEXP = /^[-a-z]+([\/][-a-z]+)*$/ # Reusable string for building encoder name based regexps ENCODER_NAME_REGEXP_STR = "[-a-z]+" # # Regular expression for encoder names. # ENCODER_NAME_REGEXP = /^([-a-z]+)*$/ # # Regular expression for encoder qualified names. Encoder qualified names are # '/' seperated names, where a name is [-a-z]+. # Examples: xhtml/entities-encoding, sql/single-quoting, ... # QUALIFIED_ENCODER_NAME_REGEXP = /^([-a-z]+)([\/][-a-z]+)*$/ # Reusable string for building qualified encoder name based regexps QUALIFIED_ENCODER_NAME_REGEXP_STR = "[-a-z]+([\/][-a-z]+)*" # # Provides installed {file extension => dialect} mappings. File extensions # (keys) contain the first dot (like .wtpl, .whtml, ...). Dialects (values) are # qualified names, not Dialect instances. # FILE_EXTENSIONS = {} # # Provides installed {file extension => data loader} mapping. File extensions # (keys) contain the first dot (like .wtpl, .whtml, ...). Data loades are # Proc instances that take a single |uri| argument. # DATA_EXTENSIONS = {} # # Main anonymous dialect. All installed dialects are children of this one, # which is anonymous because it does not appear in qualified names. # @dialect = Dialect.new("", nil) # # Installs or query a dialect. # # When called with a block, this method installs a _wlang_ dialect under # _name_ (which cannot be qualified). Extensions can be provided to let _wlang_ # automatically recognize files that are expressed in this dialect. The block # is interpreted as code in the dialect DSL (domain specific language, see # WLang::Dialect::DSL). Returns nil in this case. # # When called without a block this method returns a Dialect instance # installed under name (which can be a qualified name). Extensions are ignored # in this case. Returns nil if not found, a Dialect instance otherwise. # def self.dialect(name, *extensions, &block) if block_given? raise "Unsupported qualified names in dialect installation"\ unless name.index('/').nil? Dialect::DSL.new(@dialect).dialect(name, *extensions, &block).build! else @dialect.dialect(name) end end # # Adds a data loader for file extensions. # def self.data_loader(*exts, &block) raise(ArgumentError, "Block expected") unless block_given? raise(ArgumentError, "Block of arity 1 expected") unless block.arity==1 exts.each do |ext| DATA_EXTENSIONS[ext] = block end end # # Loads data from a given URI. If _extension_ is omitted, tries to infer it # from the uri, otherwise use it directly. Returns loaded data. # def self.load_data(uri, extension=nil) if extension.nil? extension = File.extname(uri) raise("Unable to infer data loader from #{uri}") if extension.nil? end loader = DATA_EXTENSIONS[extension] raise("No data loader for #{extension}") if loader.nil? loader.call(uri) end # Infers a dialect from a file extension def self.infer_dialect(uri) WLang::FILE_EXTENSIONS[File.extname(uri)] end # # Returns an encoder installed under a qualified name. Returns nil if not # found. # def self.encoder(name) @dialect.encoder(name) end # # Instantiates a template written in some _wlang_ dialect, using a given _context_ # (providing instantiation data). Returns instantiatiation as a String. If you want # to instantiate the template in a specific buffer (a file or console for example), # use Template. _template_ is expected to be a String and _context_ a Hash. To # know available dialects, see WLang::StandardDialects. block_symbols # can be :braces ('{' and '}' pairs), :brackets ('[' and ']' # pairs) or :parentheses ('(' and ')' pairs). # # Examples: # WLang.instantiate "Hello ${who} !", {"who" => "Mr. Jones"} # WLang.instantiate "SELECT * FROM people WHERE name='{name}'", {"who" => "Mr. O'Neil"}, "wlang/sql" # WLang.instantiate "Hello $(who) !", {"who" => "Mr. Jones"}, "wlang/active-string", :parentheses # def self.instantiate(template, context=nil, dialect="wlang/active-string", block_symbols = :braces) WLang::Template.new(template, dialect, context, block_symbols).instantiate end # # Instantiates a file written in some _wlang_ dialect, using a given _context_ # (providing instantiation data). Outputs instantiation in _buffer_ (any object # responding to <<, usually a IO; String is supported as well). If # _dialect_ is nil, tries to infer it from the file extension; otherwise _dialect_ # is expected to be a qualified dialect name. See instantiate about block_symbols. # Returns _buffer_. # # Examples: # Wlang.file_instantiate "template.wtpl", {"who" => "Mr. Jones"} # Wlang.file_instantiate "template.wtpl", {"who" => "Mr. Jones"}, STDOUT # Wlang.file_instantiate "template.xxx", {"who" => "Mr. Jones"}, STDOUT, "wlang/xhtml" # def self.file_instantiate(file, context=nil, buffer=nil, dialect=nil, block_symbols = :braces) raise "File '#{file}' does not exists or is unreadable"\ unless File.exists?(file) and File.readable?(file) if dialect.nil? dialect = infer_dialect(file) if dialect.nil? raise("No known dialect for file extension '#{File.extname(file)}'\n"\ "Known extensions are: " << WLang::FILE_EXTENSIONS.keys.join(", "))\ if dialect.nil? end t = WLang::Template.new(File.read(file), dialect, context, block_symbols) t.source_file = file t.instantiate(buffer) end end require 'wlang/dialects/standard_dialects'