/* [The "BSD license"] Copyright (c) 2006 Martin Traverso All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ group Ruby implements ANTLRCore; /** The overall file structure of a recognizer; stores methods for rules * and cyclic DFAs plus support code. */ outputFile(LEXER,PARSER,TREE_PARSER, actionScope, actions, docComment, recognizer, name, tokens, tokenNames, rules, cyclicDFAs, bitsets, buildTemplate, buildAST, rewrite, profile, backtracking, synpreds, memoize, numRules, fileName, ANTLRVersion, generatedTimestamp, trace, scopes, superClass, literals) ::= << # () # Generated by ANTLR on >> /** * Inherits parameters from outputFile(...) * * labelType is not used for Ruby (no explicit type declarations) */ lexer(grammar, name, tokens, scopes, rules, numRules, labelType, filterMode) ::= << class require 'stringio' =}; separator="\n"> def initialize(input) input = StringIO.new(input) if input.respond_to?(:to_str) @input = CharStream.new(input) @backtracking = 0 @failed = false end def next_token # TODO: catch exceptions @token = nil @channel = nil @text = nil @start = @input.index @line = @input.line @pos = @input.column @type = nil @type_int = nil return :EOF if == :EOF match_Tokens() if @token == nil @text ||= @input.substring(@start, @input.index - 1) @token = Token.new(@type, @type_int, @line, @pos, @text, @channel) end puts @token.inspect return @token end class Token attr_reader :token_type attr_reader :int_type attr_reader :line attr_reader :pos attr_reader :text attr_reader :channel def initialize(token_type, int_type, line, pos, text, channel = nil) @token_type = token_type @int_type = int_type @line = line @pos = pos @text = text @channel = channel end alias :to_i :int_type end private class CharStream attr_reader :line attr_reader :column attr_reader :index def initialize(input) @buffer = "" @input = input @line = 1 @column = 0 @index = 0; end # returns a Fixnum between 0 and 0xFFFF or :EOF def look_ahead(pos) offset = @index + pos - 1 if @buffer.length \< offset + 1 char = @input.read(offset + 1 - @buffer.length) @buffer \<\< char if not char.nil? end if offset \< @buffer.length @buffer[offset] else :EOF end end def mark @state = { :index => @index, :line => @line, :column => @column } return 0 end def rewind(marker) @index = @state[:index] @line = @state[:line] @column = @state[:column] end def consume look_ahead(1) # force a read from the input if necessary @column = @column + 1 if @buffer[@index] == ?\n @line = @line + 1 @column = 0 end @index = @index + 1 end def substring(start, stop) @buffer.slice(start, stop - start + 1) end end def match(value = nil) @failed = false case when value.nil? @input.consume() when value.respond_to?(:to_str) catch(:done) do value.each_byte do |c| @failed ||= !() @input.consume() if !@failed throw :done if @failed end end else @failed = !() @input.consume() if !@failed end if @failed && @backtracking \<= 0 raise "Expected #{value.respond_to?(:chr) ? value.chr : value}" end end def match_range(from, to) char = if char != :EOF && (char \>= from || char \<= to) @failed = false match() elsif @backtracking > 0 @failed = true else raise "Expected [#{from.chr}..#{to.chr}]" end end end >> parser(grammar, name, scopes, tokens, tokenNames, rules, numRules, bitsets, ASTLabelType, superClass, labelType, members) ::= << require 'Lexer' class attr_reader :lexer TOKENS = [ , ]}; separator=",\n"> ].inject({}) { |hash, pair| name = pair[0] index = pair[1] + 3 # hardcoded for now... no way to get this value from ANTLR if name[0] == ?' hash[:"T#{index}"] = index else hash[:"#{name}"] = index end hash } TOKENS[:EOF] = -1 def initialize(input) if input.respond_to?(:to_str) || input.respond_to?(:read) input = Lexer.new(input) end @lexer = input @input = TokenStream.new(input) @backtracking = 0 @failed = false @indent = 0 end private class TokenStream attr_reader :index def initialize(input) @buffer = [] @input = input @channel = nil @index = 0; end # returns a Token def look_ahead(pos) offset = @index + pos - 1 while @buffer[-1] != :EOF && @buffer.length \< offset + 1 token = @input.next_token if token == :EOF || token.channel == @channel @buffer \<\< token end end offset = -1 if offset >= @buffer.length if offset \< @buffer.length @buffer[offset] end end def mark @state = { :index => @index } return 0 end def rewind(marker) @index = @state[:index] end def consume look_ahead(1) # force a read from the input if necessary @index = @index + 1 end end def match(token = nil) if token.nil? || == token @input.consume @failed = false return elsif @backtracking > 0 @failed = true else raise "Expected #{token}" end end def look_ahead(k) token = @input.look_ahead(k) if token != :EOF token = token.token_type end token end end >> /** How to generate a tree parser; same as parser except the input * stream is a different type. */ treeParser(grammar, name, scopes, tokens, tokenNames, globalAction, rules, numRules, bitsets, labelType, ASTLabelType, superClass, members) ::= << raise "treeParser not implemented" >> /** A simpler version of a rule template that is specific to the imaginary * rules created for syntactic predicates. As they never have return values * nor parameters etc..., just give simplest possible method. Don't do * any of the normal memoization stuff in here either; it's a waste. * As predicates cannot be inlined into the invoking rule, they need to * be in a rule by themselves. */ synpredRule(ruleName, ruleDescriptor, block, description, nakedBlock) ::= << # def _fragment end >> /** How to generate code for a rule. This includes any return type * data aggregates required for multiple return values. */ rule(ruleName,ruleDescriptor,block,emptyRule,description,exceptions,finally,memoize) ::= << # def () = nil }; separator = "\n" > puts " " * @indent + "+" @indent += 1 @indent -= 1 puts " " * @indent + "-" return { => _retval_ }; separator = ",">} # TODO: need "Attribute.index" for this to work: return }; separator = ","> return _retval_ end >> ruleLabelDefs() ::= << = nil}; separator="\n"> <[ruleDescriptor.tokenListLabels, ruleDescriptor.ruleListLabels] :{list_ = nil}; separator="\n" > >> /** How to generate a rule in the lexer; naked blocks are used for * fragment rules. */ lexerRule(ruleName,nakedBlock,ruleDescriptor,block,memoize) ::= << def match_() <\n> @type = : @type_int = end >> lexerRuleLabelDefs() ::= << <[ruleDescriptor.tokenLabels, ruleDescriptor.ruleLabels] :{ = nil}; separator="\n" > = nil}; separator="\n"> <[ruleDescriptor.tokenListLabels, ruleDescriptor.ruleListLabels] :{list_ = nil}; separator="\n" > >> /** How to generate code for the implicitly-defined lexer grammar rule * that chooses between lexer rules. */ tokensRule(ruleName,nakedBlock,args,block,ruleDescriptor) ::= << >> filteringNextToken() ::= << raise "filteringNextToken not implemented" >> filteringActionGate() ::= << raise "filteringActionGate not implemented" >> // S U B R U L E S /** A (...) subrule with multiple alternatives */ block(alts,decls,decision,enclosingBlockLevel,blockLevel,decisionNumber,maxK,maxAlt,description) ::= << >> /** A rule block with multiple alternatives */ ruleBlock(alts,decls,decision,enclosingBlockLevel,blockLevel,decisionNumber,maxK,maxAlt,description) ::= << >> /** * decision, decisionNumber don't seem to be relevant in this template * alts actually has a single element */ ruleBlockSingleAlt(alts,decls,decision,enclosingBlockLevel,blockLevel,decisionNumber,description) ::= << >> /** A special case of a (...) subrule with a single alternative */ blockSingleAlt(alts,decls,decision,enclosingBlockLevel,blockLevel,decisionNumber,description) ::= << >> /** A (..)+ block with 0 or more alternatives */ positiveClosureBlock(alts,decls,decision,enclosingBlockLevel,blockLevel,decisionNumber,maxK,maxAlt,description) ::= << # matchedOnce = false while true alt = case alt else break end matchedOnce = true end if !matchedOnce raise "Expected at least one match: " end >> positiveClosureBlockSingleAlt ::= positiveClosureBlock /** A (..)* block with 0 or more alternatives */ closureBlock(alts,decls,decision,enclosingBlockLevel,blockLevel,decisionNumber,maxK,maxAlt,description) ::= << # while true alt = case alt else break end end >> closureBlockSingleAlt ::= closureBlock /** Optional blocks (x)? are translated to (x|) by before code generation * so we can just use the normal block template */ optionalBlock ::= block optionalBlockSingleAlt ::= block /** An alternative is just a list of elements; at outermost level */ alt(elements,altNum,description,autoAST,outerAlt)::= << # >> // E L E M E N T S /** match a token optionally with a label in front */ tokenRef(token,label,elementIndex)::= << _