Skip to content

Commit

Permalink
Add support for literate ruby files like GHC
Browse files Browse the repository at this point in the history
GHC, of Haskell fame, supports .lhs files that either use code marked with
'bird feet' (> puts 5) or LaTeX style (\begin{code}...\end{code}). These files
can be either run directly or required, but if run directly, the .lrb ending
is mandatory, and if required, .rb and .rbc files have higher precedence
over .lrb files. See http://www.haskell.org/haskellwiki/Literate_programming
for minformation. N.B. not all of Haskell's literate programming features are
implemented.
  • Loading branch information
seydar committed Aug 20, 2009
1 parent a44e405 commit ef33c41
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 2 deletions.
9 changes: 9 additions & 0 deletions kernel/compiler/compile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ def self.compile_feature(rb, requiring, &block)

# Internally used by #unified_load. This attempts to load the
# designated file from a single prefix path.
#
# N.B. +rb+ could be a .lrb file, so we have to check for that and
# produce an appropriate .rbc file
def self.single_load(dir, rb, rbc, ext, requiring, options)
if rb
return false if requiring and $LOADED_FEATURES.include? rb
Expand Down Expand Up @@ -306,6 +309,9 @@ def self.load_from_extension(path, options = {})
rb, rbc, ext = nil, path, nil
elsif path.suffix? '.rb'
rb, rbc, ext = path, "#{path}c", nil
elsif path.suffix? '.lrb'
new_path = path.match(/^(.+).lrb$/)[1] + '.rb'
rb, rbc, ext = path, "#{new_path}c", nil
elsif path.suffix? "#{Rubinius::LIBSUFFIX}"
rb, rbc, ext = nil, nil, path
else
Expand All @@ -331,6 +337,9 @@ def self.split_path(path)
rb, rbc, ext = nil, path, nil
elsif path.suffix? '.rb'
rb, rbc, ext = path, "#{path}c", nil
elsif path.suffix? '.lrb'
new_path = path.match(/^(.+).lrb$/)[1] + '.rb'
rb, rbc, ext = path, "#{new_path}c", nil
elsif path.suffix? "#{Rubinius::LIBSUFFIX}"
rb, rbc, ext = nil, nil, path
else
Expand Down
64 changes: 62 additions & 2 deletions kernel/compiler/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,57 @@ def set_eval_local(name, val)
dynamic_locals[name] = val
end
end

class LiterateRB

def self.to_code(file, options); new(file).to_code(options); end

attr_accessor :file
attr_accessor :code
attr_accessor :comments

def initialize(file)
@file = file
@code = []
@comments = []
@in_code = false

parse
end

def to_code(options)
coms = @comments.map {|c| !c || c.empty? ? nil : "# #{c}" }
coms = [nil] * @code.size unless options[:comments]
tree = @code.zip(coms).map {|(a, b)| a || b }.compact
tree.join "\n"
end

private

def parse
File.open @file do |file| # used in keeping memory footprint down
file.each_line do |line| # keep it constant space
case line
when /\s*>(.*)/
@code << $1
@comments << nil
when /\s*\\begin\{code\}\s*/
@in_code = true
when /\s*\\end\{code\}\s*/
@in_code = false
else
if @in_code
@code << line.chomp
@comments << nil
else
@comments << line.chomp
@code << nil
end
end
end
end
end
end

def self.process_flags(flags)
flags.each { |f| Config[f] = true } if flags
Expand All @@ -47,9 +98,18 @@ def self.parse_flags(stream)
to_clear.each { |t| stream.delete(t) }
end

def self.compile_file(path, flags=nil)
##
# +path+ is either a normal ruby file or a literate ruby file.
def self.compile_file(path, flags=nil, options={})
process_flags(flags)
sexp = File.to_sexp(path)

sexp = nil # scoping fun so that +sexp+ isn't restricted to the block below
if path =~ /\.lrb$/ or options[:lrb]
# we produce a string that is the code, and then convert it to sexps
sexp = LiterateRB::to_code(path, :comments => false).to_sexp
else
sexp = File.to_sexp(path)
end

comp = new(Generator)
node = comp.into_script(sexp)
Expand Down

0 comments on commit ef33c41

Please sign in to comment.