Transform a template file into a new file by processing the %-directives
and replacing symbol references <?FOO> with values.
Supported directives: %#, %if, %elif, %else, %endif, %define, %undef, and
%include.
:errlist |
[R] |
|
:fileFinder |
[RW] |
|
:outtext |
[R] |
|
:sytab |
[RW] |
|
Setup symbol table, conditional evaluation stack, and map of token bit
values to handler Procs
# File fileproc.rb, line 201
def initialize()
@abend = false
@goteof = false
@sytab = SyTab.new(VarDelims)
@allowed = Array.new
@allowed.push(NonCond | CondBegin)
@condstk = Array.new
@condstk.push(true)
@filestk = Array.new
@filestk.push(nil)
@metastk = Array.new
@metastk.push(DefMetaStr)
@metastr = @metastk.last
@metalen = @metastk.last.size
@outtext = Array.new
@errlist = Array.new
@fileFinder = nil
@linebuf = ""
# Map from token bit values to Proc objects which invoke the handler
# in the context of the current object.
@handlers = {
Comment => proc { |ln| true },
IfTok => proc { |ln| do_if(ln) },
ElifTok => proc { |ln| do_elif(ln) },
ElseTok => proc { |ln| do_else(ln) },
EndifTok => proc { |ln| do_endif(ln) },
EofTok => proc { |ln| do_eof(ln) if @condstk.last },
DefTok => proc { |ln| do_define(ln) if @condstk.last },
UndefTok => proc { |ln| do_undef(ln) if @condstk.last },
InclTok => proc { |ln| do_include(ln) if @condstk.last },
LicenseTok=> proc { |ln| do_license(ln) if @condstk.last },
MetaStrTok => proc { |ln| do_metastr(ln) if @condstk.last },
MetaPushTok => proc { |ln| do_metapush(ln) if @condstk.last },
MetaPopTok => proc { |ln| do_metapop(ln) if @condstk.last },
}
end
Process file, returning array of output text or nil if errors occurred
- fn
- path to file to read
# File fileproc.rb, line 538
def process(fn)
readfile(fn)
@errlist.size == 0 ? @outtext : nil
end
Shorthand for process(fn)
# File fileproc.rb, line 545
def <<(fn)
process(fn)
end
Report an error
- msg
- Text describing error condition
# File fileproc.rb, line 246
def error(msg)
trace = []
@filestk.each {
|io|
trace.push(io ? [ io.path, io.lineno ] : [ "<<start>>", 0 ])
}
@errlist.push(ParseError.new(msg, trace))
if @errlist.size > MaxErrors
@abend = true
@errlist.push(ParseError.new("Bailing out: too many errors", trace))
end
end
Process %if
- ln
- Remainder of line
# File fileproc.rb, line 262
def do_if(ln)
@condstk.push(@condstk.last && Cond.new(ln, @sytab).value)
@allowed.push(NonCond | CondBegin | ElifTok | ElseTok | EndifTok)
end
Process %else
- ln
- Remainder of line (ignored)
# File fileproc.rb, line 270
def do_else(ln)
@condstk[-1] = !@condstk[-1]
@condstk[-1] &&= @condstk[-2]
@allowed[-1] = NonCond | CondBegin | EndifTok
end
Process %elif
- ln
- Remainder of line
# File fileproc.rb, line 279
def do_elif(ln)
do_else(ln)
do_if(ln)
@condstk[-1] &&= @condstk[-2]
@condstk.delete_at(-2)
@allowed.delete_at(-2)
end
Process %endif
- ln
- Remainder of line (ignored)
# File fileproc.rb, line 290
def do_endif(ln)
@condstk.pop
@allowed.pop
end
Process %end
- ln
- Remainder of line (ignored)
# File fileproc.rb, line 298
def do_eof(ln)
@goteof = true
end
Process %undef
- ln
- Remainder of line (all but first word ignored)
# File fileproc.rb, line 305
def do_undef(ln)
@sytab.delete(first_word(ln))
end
Remove quotes from string
- s
- String to be unquoted
# File fileproc.rb, line 312
def unquote(s)
if /^\\["']/ =~ s
s = s[1..-1]
elsif /^(["']).*\1$/ =~ s
s = s[1..-2]
end
s
end
Define/redefine a variable
- ln
- Remainder of line: first word is the variable name; rest, without leading
or trailing spaces, is value. One layer of quotes is removed unless
preceded by \.
# File fileproc.rb, line 327
def do_define(ln)
args = ln.strip.split(>\s+>,2)
@sytab[ args[0] ] = unquote(args[1])
end
Find a file specified to %include in search path; returns full path to file
or nil.
- fn
- Name of file to find
# File fileproc.rb, line 336
def findfile(fn)
if fn == nil || fn == ""
"/dev/null"
elsif fn[0] == ?/
File.file?(fn) ? fn : nil
else
@fileFinder ? @fileFinder.findFile(fn) : nil
end
end
Process %include
- ln
- Remainder of line, used as filename to include; may be enclosed in
<\> brackets, as in C.
# File fileproc.rb, line 351
def do_include(ln)
ln = ln.strip
ln = ln[1..-2] if /^<.*>$/ =~ ln
ln = '"' + ln + '"' if ln !~ /^["<'"]/
ln = Cond.new(ln, @sytab).value()
fn = findfile(ln)
if (fn)
readfile(fn)
else
error("can't find include file: \"#{ln}\"")
end
end
Process %license
- ln
- Remainder of line, ignored.
# File fileproc.rb, line 368
def do_license(ln)
fn = @sytab["LICENSE"] + "@license.inc"
path = findfile(fn)
if (path)
readfile(path)
else
error("can't find include file: \"#{fn}\"")
end
end
Process %metastr
- ln
- Remainder of line (all but first word ignored)
# File fileproc.rb, line 381
def do_metastr(ln)
do_metapush(ln)
end
Process %metapush
- ln
- Remainder of line (all but first word ignored)
# File fileproc.rb, line 388
def do_metapush(ln)
@metastk.push(first_word(ln))
@metastr = @metastk.last
@metalen = @metastk.last.size
end
Process %metapop
- ln
- Remainder of line (ignored)
# File fileproc.rb, line 397
def do_metapop(ln)
@metastk.pop
@metastr = @metastk.last
@metalen = @metastk.last.size
end
Handle a line beginning with @metastr by calling a proc
- tok
- Number representing token
- ln
- Remainder of line after token and whitespace
# File fileproc.rb, line 407
def do_token(tok, ln)
@handlers[ tok ].call(ln)
end
Get the first word of a line
- ln
- Line of text
# File fileproc.rb, line 414
def first_word(ln)
ln.sub(/\s.*/,"")
end
Handle a line beginning with @metastr by validating and either calling #do_token or #error
- ln
- Line of text with leading % removed
# File fileproc.rb, line 422
def do_meta(ln)
word = first_word(ln)
token = TOKENS[ word ]
if token != nil
do_token(token, @sytab << ln.sub(/^[a-z]+\s*/,""))
else
error("unknown directive: %#{word}")
end
end
Process input text: replace symbols and copy to output if top of condition
stack is not false
- ln
- Line of input text
# File fileproc.rb, line 436
def do_text(ln)
@outtext.push(@sytab << ln) if @condstk.last
end
Decide what to do with a line and call appropriate method
- ln
- Line of input text
# File fileproc.rb, line 443
def do_line(ln)
i = ln.index(@metastr)
if i != 0
if i == 1 && ln[0,1] = "\\"
ln = ln[ 1, ln.size - 1 ]
end
do_text(ln)
else
do_meta(ln[ @metalen, ln.size - @metalen ])
end
end
Build logical lines: if a physical line ends with "%+\n", then
the next line is a continuation, and the "%+\n" is replaced by a
single space. Calls do_line once a
logical line is assembled.
- ln
- partial or complete logical line read from input
# File fileproc.rb, line 461
def cons_line(ln)
if @linebuf.size == 0
@linebuf = ln
else
@linebuf += " "
if }^\s*(\S+.*)} =~ ln
@linebuf += $1
end
end
if @linebuf.size < 2 || @linebuf[0,1] != "%" || @linebuf[-2,2] != "%+"
tmp = @linebuf
@linebuf = ""
do_line(tmp)
else
@linebuf.slice!(-2,2)
end
end
Process an input file (does not search for file)
- fn
- path to file to read
# File fileproc.rb, line 482
def readfile(fn)
if File::file?(fn) && File::readable?(fn)
File.open(fn) {
|io|
@filestk.push(io)
@metastk.push(@metastr)
ccond = @condstk.size
cmeta = @metastk.size
io.each_line { |ln|
if @abend
break
else
cons_line(ln.chomp)
if @goteof
@goteof = false; break
end
end # if abend
}
if @linebuf.size > 0
error("File ends with line continuation")
cons_line("") # force the partial line out
end
if @condstk.size != ccond
error("Mismatched conditionals in file")
if @condstk.size > ccond
error("Unclosed %if(s): recovering ...")
while @condtstk.size > ccond
@condstk.pop
end
else
@abend = true
error("Too many %endifs (corrupted): bailing out");
end
end
if @metastk.size < cmeta
@abend = true
error("Too many %metapops (corrupted): bailing out");
end
while @metastk.size > cmeta
@metastk.pop
end
@metastr = @metastk.last
@metalen = @metastr.size
@filestk.pop
}
else
error("File '#{fn}' not found/readable")
end
end