mirror of
https://github.com/marcin-szczepanski/jFuzzyLogic.git
synced 2024-08-17 10:16:56 +02:00
1352 lines
32 KiB
Plaintext
1352 lines
32 KiB
Plaintext
/*
|
|
[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) ::=
|
|
<<
|
|
# <name> (<fileName>)
|
|
# Generated by ANTLR <ANTLRVersion> on <generatedTimestamp>
|
|
|
|
<docComment>
|
|
<recognizer>
|
|
>>
|
|
|
|
/**
|
|
* Inherits parameters from outputFile(...)
|
|
*
|
|
* labelType is not used for Ruby (no explicit type declarations)
|
|
*/
|
|
lexer(grammar, name, tokens, scopes, rules, numRules, labelType, filterMode) ::=
|
|
<<
|
|
class <name>
|
|
require 'stringio'
|
|
|
|
<tokens:{<it.name>=<it.type>}; separator="\n">
|
|
|
|
def initialize(input)
|
|
input = StringIO.new(input) if input.respond_to?(:to_str)
|
|
@input = CharStream.new(input)
|
|
@backtracking = 0
|
|
@failed = false
|
|
|
|
<actions.lexer.init>
|
|
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 <LA(1)> == :EOF
|
|
|
|
match_Tokens()
|
|
|
|
if @token == nil
|
|
@text ||= @input.substring(@start, @input.index - 1)
|
|
@token = Token.new(@type, @type_int, @line, @pos, @text, @channel)
|
|
end
|
|
|
|
<if(trace)>
|
|
puts @token.inspect
|
|
<endif>
|
|
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
|
|
|
|
<actions.lexer.members>
|
|
|
|
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 ||= !(<isolatedLookaheadTest(atom="c", k=1)>)
|
|
@input.consume() if !@failed
|
|
throw :done if @failed
|
|
end
|
|
end
|
|
else
|
|
@failed = !(<isolatedLookaheadTest(atom="value", k=1)>)
|
|
@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 = <LA(1)>
|
|
|
|
if char != :EOF && (char \>= from || char \<= to)
|
|
@failed = false
|
|
match()
|
|
elsif @backtracking > 0
|
|
@failed = true
|
|
else
|
|
raise "Expected [#{from.chr}..#{to.chr}]"
|
|
end
|
|
end
|
|
|
|
<rules; separator="\n\n">
|
|
|
|
<synpreds: synpred(); separator="\n\n">
|
|
|
|
<dfaClass()>
|
|
<cyclicDFAs: cyclicDFA()>
|
|
end
|
|
>>
|
|
|
|
parser(grammar, name, scopes, tokens, tokenNames, rules, numRules,
|
|
bitsets, ASTLabelType, superClass,
|
|
labelType, members) ::=
|
|
<<
|
|
require '<grammar.name>Lexer'
|
|
|
|
class <name>
|
|
attr_reader :lexer
|
|
|
|
TOKENS = [
|
|
<tokenNames: {[<it>, <i>]}; 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 = <grammar.name>Lexer.new(input)
|
|
end
|
|
|
|
@lexer = input
|
|
@input = TokenStream.new(input)
|
|
@backtracking = 0
|
|
@failed = false
|
|
|
|
<actions.parser.init>
|
|
|
|
<if(trace)>
|
|
@indent = 0
|
|
<endif>
|
|
end
|
|
|
|
<rules; separator="\n\n">
|
|
|
|
<actions.parser.members>
|
|
|
|
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? || <LA(1)> == 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
|
|
|
|
<synpreds: synpred(); separator="\n\n">
|
|
|
|
<dfaClass()>
|
|
<cyclicDFAs: cyclicDFA()>
|
|
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) ::=
|
|
<<
|
|
# <description>
|
|
def <ruleName>_fragment
|
|
<block>
|
|
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) ::=
|
|
<<
|
|
# <description>
|
|
def <ruleName>(<ruleDescriptor.parameterScope:parameterScope(scope=it)>)
|
|
<ruleDescriptor.returnScope.attributes:
|
|
{ _retval_<it.name> = nil }; separator = "\n"
|
|
>
|
|
<ruleLabelDefs()>
|
|
|
|
<if(trace)>
|
|
puts " " * @indent + "+<ruleName>"
|
|
@indent += 1
|
|
<endif>
|
|
|
|
<ruleDescriptor.actions.init>
|
|
|
|
<block>
|
|
|
|
<if(trace)>
|
|
@indent -= 1
|
|
puts " " * @indent + "-<ruleName>"
|
|
<endif>
|
|
|
|
|
|
<if(!ruleDescriptor.isSynPred)>
|
|
<if(ruleDescriptor.hasReturnValue)>
|
|
<if(ruleDescriptor.hasMultipleReturnValues)>
|
|
return {<ruleDescriptor.returnScope.attributes:{ a | :<a.name> => _retval_<a.name> }; separator = ",">}
|
|
# TODO: need "Attribute.index" for this to work: return <ruleDescriptor.returnScope.attributes:{ a | _retval_<a.name> }; separator = ",">
|
|
<else>
|
|
return _retval_<ruleDescriptor.singleValueReturnName>
|
|
<endif>
|
|
<endif>
|
|
<endif>
|
|
end
|
|
>>
|
|
|
|
ruleLabelDefs() ::= <<
|
|
<ruleDescriptor.tokenLabels :{_<it.label.text> = nil}; separator="\n">
|
|
<[ruleDescriptor.tokenListLabels, ruleDescriptor.ruleListLabels]
|
|
:{list_<it.label.text> = 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_<ruleName>(<ruleDescriptor.parameterScope:parameterScope(scope=it)>)
|
|
<ruleDescriptor.actions.init>
|
|
|
|
<lexerRuleLabelDefs()>
|
|
<if(nakedBlock)>
|
|
<block><\n>
|
|
<else>
|
|
@type = :<ruleName>
|
|
@type_int = <ruleName>
|
|
<block>
|
|
<endif>
|
|
end
|
|
>>
|
|
|
|
lexerRuleLabelDefs() ::= <<
|
|
<[ruleDescriptor.tokenLabels, ruleDescriptor.ruleLabels]
|
|
:{<it.label.text> = nil}; separator="\n"
|
|
>
|
|
<ruleDescriptor.charLabels:{<it.label.text> = nil}; separator="\n">
|
|
<[ruleDescriptor.tokenListLabels, ruleDescriptor.ruleListLabels]
|
|
:{list_<it.label.text> = 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) ::=
|
|
<<
|
|
<lexerRule(...)>
|
|
>>
|
|
|
|
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) ::=
|
|
<<
|
|
<switchBlock(...)>
|
|
>>
|
|
|
|
/** A rule block with multiple alternatives */
|
|
ruleBlock(alts,decls,decision,enclosingBlockLevel,blockLevel,decisionNumber,maxK,maxAlt,description) ::=
|
|
<<
|
|
<switchBlock(...)>
|
|
>>
|
|
|
|
|
|
/**
|
|
* 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) ::=
|
|
<<
|
|
<plainBlock(...)>
|
|
>>
|
|
|
|
/** A special case of a (...) subrule with a single alternative */
|
|
blockSingleAlt(alts,decls,decision,enclosingBlockLevel,blockLevel,decisionNumber,description) ::=
|
|
<<
|
|
<plainBlock(...)>
|
|
>>
|
|
|
|
/** A (..)+ block with 0 or more alternatives */
|
|
positiveClosureBlock(alts,decls,decision,enclosingBlockLevel,blockLevel,decisionNumber,maxK,maxAlt,description) ::=
|
|
<<
|
|
# <description>
|
|
matchedOnce<decisionNumber> = false
|
|
<decls>
|
|
while true
|
|
alt<decisionNumber> = <maxAlt>
|
|
<decision>
|
|
case alt<decisionNumber>
|
|
<alts:switchCase(); separator="\n">
|
|
else
|
|
break
|
|
end
|
|
matchedOnce<decisionNumber> = true
|
|
end
|
|
|
|
if !matchedOnce<decisionNumber>
|
|
raise "Expected at least one match: <description>"
|
|
end
|
|
>>
|
|
|
|
positiveClosureBlockSingleAlt ::= positiveClosureBlock
|
|
|
|
/** A (..)* block with 0 or more alternatives */
|
|
closureBlock(alts,decls,decision,enclosingBlockLevel,blockLevel,decisionNumber,maxK,maxAlt,description) ::=
|
|
<<
|
|
# <description>
|
|
<decls>
|
|
while true
|
|
alt<decisionNumber> = <maxAlt>
|
|
<decision>
|
|
case alt<decisionNumber>
|
|
<alts:switchCase(); separator="\n">
|
|
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)::=
|
|
<<
|
|
# <description>
|
|
<elements: element(); separator="\n">
|
|
>>
|
|
|
|
// E L E M E N T S
|
|
|
|
/** match a token optionally with a label in front */
|
|
tokenRef(token,label,elementIndex)::=
|
|
<<
|
|
<if(label)>
|
|
_<label> = @input.look_ahead(1)<\n>
|
|
<endif>
|
|
match(:<token>)
|
|
>>
|
|
|
|
/** ids+=ID */
|
|
tokenRefAndListLabel(token,label,elementIndex)::=
|
|
<<
|
|
<tokenRef(...)>
|
|
<listLabel(elem=label, ...)>
|
|
>>
|
|
|
|
|
|
listLabel(label,elem)::=
|
|
<<
|
|
list_<label> ||= []
|
|
list_<label> \<\< _<elem>
|
|
>>
|
|
|
|
/** match a character */
|
|
charRef(char,label)::=
|
|
<<
|
|
<if(label)>
|
|
_<label> = <char><\n>
|
|
<endif>
|
|
match(<char>)
|
|
>>
|
|
|
|
/** match a character range */
|
|
charRangeRef(a,b,label)::=
|
|
<<
|
|
<if(label)>
|
|
_<label> = <LA(1)><\n>
|
|
<endif>
|
|
match_range(<a>, <b>)
|
|
>>
|
|
|
|
/** For now, sets are interval tests and must be tested inline */
|
|
matchSet(s,label,elementIndex,postmatchCode)::=
|
|
<<
|
|
<if(label)>
|
|
_<label> = <LA(1)><\n>
|
|
<endif>
|
|
if <s>
|
|
match()
|
|
<postmatchCode>
|
|
else
|
|
raise "Expected set"
|
|
end
|
|
>>
|
|
|
|
matchSetAndListLabel(s,label,elementIndex,postmatchCode)::=
|
|
<<
|
|
<matchSet(...)>
|
|
<listLabel(elem=label,...)>
|
|
>>
|
|
|
|
/** Match a string literal */
|
|
lexerStringRef(string,label)::=
|
|
<<
|
|
<if(label)>
|
|
_<label> = <string><\n>
|
|
<endif>
|
|
match(<string>)<\n>
|
|
>>
|
|
|
|
wildcard(label, elementIndex)::=
|
|
<<
|
|
<if(label)>
|
|
_<label> = <LA(1)><\n>
|
|
<endif>
|
|
match()
|
|
>>
|
|
|
|
|
|
wildcardAndListLabel(label,elementIndex)::=
|
|
<<
|
|
<wildcard(...)>
|
|
<listLabel(elem=label,...)>
|
|
>>
|
|
|
|
|
|
/** Match . wildcard in lexer */
|
|
wildcardChar(label, elementIndex)::=
|
|
<<
|
|
<if(label)>
|
|
_<label> = <LA(1)><\n>
|
|
<endif>
|
|
match()
|
|
>>
|
|
|
|
wildcardCharListLabel(label, elementIndex)::=
|
|
<<
|
|
raise "wildcardCharListLabel not implemented"
|
|
>>
|
|
|
|
/** Match a rule reference by invoking it possibly with arguments
|
|
* and a return value or values.
|
|
*/
|
|
ruleRef(rule,label,elementIndex,args)::=
|
|
<<
|
|
<if(label)>
|
|
_<label> = <rule>(<args; separator=", ">)<\n>
|
|
<else>
|
|
<rule>(<args; separator=", ">)<\n>
|
|
<endif>
|
|
>>
|
|
|
|
/** ids+=ID */
|
|
ruleRefAndListLabel(rule,label,elementIndex,args)::=
|
|
<<
|
|
<ruleRef(...)>
|
|
<listLabel(elem=label,...)>
|
|
>>
|
|
|
|
|
|
/**
|
|
|
|
A: b=B;
|
|
B: .;
|
|
|
|
TODO: Should we use a real token type instead of :invalid? How do we get it?
|
|
|
|
*/
|
|
lexerRuleRef(rule,label,args,elementIndex)::=
|
|
<<
|
|
<if(label)>
|
|
_<label>_start_<elementIndex> = @input.index
|
|
_<label>_line_<elementIndex> = @input.line
|
|
_<label>_pos_<elementIndex> = @input.column
|
|
match_<rule>(<args; separator=", ">)
|
|
_<label> = Token.new(:invalid, 0,
|
|
_<label>_line_<elementIndex>,
|
|
_<label>_pos_<elementIndex>,
|
|
@input.substring(_<label>_start_<elementIndex>, @input.index - 1), nil)
|
|
<else>
|
|
match_<rule>(<args; separator=", ">)
|
|
<endif>
|
|
>>
|
|
|
|
|
|
lexerRuleRefAndListLabel(rule,label,args,elementIndex) ::=
|
|
<<
|
|
<lexerRuleRef(...)>
|
|
<listLabel(elem=label,...)>
|
|
>>
|
|
|
|
/** EOF in the lexer */
|
|
lexerMatchEOF(label,elementIndex)::=
|
|
<<
|
|
<if(label)>
|
|
_<label> = :EOF<\n>
|
|
<endif>
|
|
match(:EOF)
|
|
>>
|
|
|
|
/** match ^(root children) in tree parser */
|
|
tree(root, actionsAfterRoot, children, nullableChildList) ::=
|
|
<<
|
|
raise "tree not implemented"
|
|
>>
|
|
|
|
/** Every predicate is used as a validating predicate (even when it is
|
|
* also hoisted into a prediction expression).
|
|
*/
|
|
validateSemanticPredicate(pred,description)::=
|
|
<<
|
|
# <description>
|
|
if !<evalPredicate(...)>
|
|
raise "Semantic predicate failed: #{<description>}"
|
|
end
|
|
>>
|
|
|
|
// F i x e d D F A (if-then-else)
|
|
|
|
dfaState(k,edges,eotPredictsAlt,description,stateNumber,semPredState)::=
|
|
<<
|
|
# <description>
|
|
look_ahead<decisionNumber>_<stateNumber> = <LA(k)>
|
|
<if(LEXER)>
|
|
look_ahead<decisionNumber>_<stateNumber> = -1 if look_ahead<decisionNumber>_<stateNumber> == :EOF
|
|
<endif>
|
|
|
|
if <edges; separator="\nelsif ">
|
|
else
|
|
<if(eotPredictsAlt)>
|
|
alt<decisionNumber> = <eotPredictsAlt><\n>
|
|
<else>
|
|
raise "Expected: <description>"<\n>
|
|
<endif>
|
|
end
|
|
>>
|
|
|
|
/** Same as a normal DFA state except that we don't examine lookahead
|
|
* for the bypass alternative. It delays error detection but this
|
|
* is faster, smaller, and more what people expect. For (X)? people
|
|
* expect "if ( LA(1)==X ) match(X);" and that's it.
|
|
*
|
|
* If a semPredState, don't force lookahead lookup; preds might not
|
|
* need.
|
|
*/
|
|
dfaOptionalBlockState(k,edges,eotPredictsAlt,description,stateNumber,semPredState)::=
|
|
<<
|
|
# <description>
|
|
look_ahead<decisionNumber>_<stateNumber> = <LA(k)>
|
|
|
|
<if(LEXER)>
|
|
look_ahead<decisionNumber>_<stateNumber> = -1 if look_ahead<decisionNumber>_<stateNumber> == :EOF
|
|
<endif>
|
|
|
|
if <edges; separator="\nelsif ">
|
|
end
|
|
>>
|
|
|
|
|
|
/** A DFA state that is actually the loopback decision of a closure
|
|
* loop. If end-of-token (EOT) predicts any of the targets then it
|
|
* should act like a default clause (i.e., no error can be generated).
|
|
* This is used only in the lexer so that for ('a')* on the end of a
|
|
* rule anything other than 'a' predicts exiting.
|
|
*
|
|
* If a semPredState, don't force lookahead lookup; preds might not
|
|
* need.
|
|
*/
|
|
dfaLoopbackState(k,edges,eotPredictsAlt,description,stateNumber,semPredState)::=
|
|
<<
|
|
# <description>
|
|
look_ahead<decisionNumber>_<stateNumber> = <LA(k)>
|
|
<if(LEXER)>
|
|
look_ahead<decisionNumber>_<stateNumber> = -1 if look_ahead<decisionNumber>_<stateNumber> == :EOF
|
|
<endif>
|
|
|
|
if <edges; separator="\nelsif ">
|
|
<if(eotPredictsAlt)>
|
|
else
|
|
alt<decisionNumber> = <eotPredictsAlt>
|
|
<endif>
|
|
end
|
|
>>
|
|
|
|
|
|
/** An accept state indicates a unique alternative has been predicted */
|
|
/** It is not clear that decisionNumber is available here */
|
|
dfaAcceptState(alt) ::= "alt<decisionNumber> = <alt>"
|
|
|
|
/** A simple edge with an expression. If the expression is satisfied,
|
|
* enter to the target state. To handle gated productions, we may
|
|
* have to evaluate some predicates for this edge.
|
|
*/
|
|
dfaEdge(labelExpr, targetState, predicates)::=
|
|
<<
|
|
<labelExpr> <if(predicates)>&& <predicates><endif>
|
|
<targetState>
|
|
>>
|
|
|
|
// F i x e d D F A (switch case)
|
|
|
|
/** A DFA state where a SWITCH may be generated. The code generator
|
|
* decides if this is possible: CodeGenerator.canGenerateSwitch().
|
|
*/
|
|
dfaStateSwitch(k,edges,eotPredictsAlt,description,stateNumber,semPredState)::=
|
|
<<
|
|
# <description>
|
|
case <LA(k)>
|
|
<edges; separator="\n">
|
|
else
|
|
<if(eotPredictsAlt)>
|
|
alt<decisionNumber> = <eotPredictsAlt><\n>
|
|
<else>
|
|
raise "Expected: <description>"<\n>
|
|
<endif>
|
|
end
|
|
>>
|
|
|
|
/**
|
|
* eotPredictsAlt is not relevant here
|
|
*/
|
|
dfaOptionalBlockStateSwitch(k,edges,eotPredictsAlt,description,stateNumber,semPredState)::=
|
|
<<
|
|
# <description>
|
|
case <LA(k)>
|
|
<edges; separator="\n">
|
|
end
|
|
>>
|
|
|
|
dfaLoopbackStateSwitch(k, edges,eotPredictsAlt,description,stateNumber,semPredState)::=
|
|
<<
|
|
# <description>
|
|
case <LA(k)>
|
|
<edges; separator="\n">
|
|
<if(eotPredictsAlt)><\n>
|
|
else
|
|
alt<decisionNumber> = <eotPredictsAlt>
|
|
<endif>
|
|
end
|
|
>>
|
|
|
|
dfaEdgeSwitch(labels, targetState)::=
|
|
<<
|
|
<if(PARSER)>
|
|
when <labels:{:<it>}; separator=","><\n>
|
|
<else>
|
|
when <labels:{<it>}; separator=","><\n>
|
|
<endif>
|
|
<targetState>
|
|
>>
|
|
|
|
// C y c l i c D F A
|
|
|
|
/** The code to initiate execution of a cyclic DFA; this is used
|
|
* in the rule to predict an alt just like the fixed DFA case.
|
|
* The <name> attribute is inherited via the parser, lexer, ...
|
|
*/
|
|
dfaDecision(decisionNumber,description)::=
|
|
<<
|
|
alt<decisionNumber> = DFA<decisionNumber>.predict(self, @input)
|
|
>>
|
|
|
|
/** Generate the tables and support code needed for the DFAState object
|
|
* argument. Unless there is a semantic predicate (or syn pred, which
|
|
* become sem preds), all states should be encoded in the state tables.
|
|
* Consequently, cyclicDFAState/cyclicDFAEdge,eotDFAEdge templates are
|
|
* not used except for special DFA states that cannot be encoded as
|
|
* a transition table.
|
|
*/
|
|
cyclicDFA(dfa)::=
|
|
<<
|
|
|
|
DFA<dfa.decisionNumber> = DFA.new(
|
|
[<dfa.eot; wrap="\n ", separator=",", null="-1">],
|
|
[<dfa.eof; wrap="\n ", separator=",", null="-1">],
|
|
[<dfa.min; wrap="\n ", separator=",", null="0">],
|
|
[<dfa.max; wrap="\n ", separator=",", null="0">],
|
|
[<dfa.accept; wrap="\n ", separator=",", null="-1">],
|
|
[<dfa.special; wrap="\n ", separator=",", null="-1">],
|
|
[
|
|
<dfa.transition:{s | [<s; wrap="\n ", separator=",", null="-1">]}; separator=",\n", null="">
|
|
])
|
|
|
|
def special_state_transition(s)
|
|
<if(dfa.specialStateSTs)>
|
|
case s
|
|
<dfa.specialStateSTs:{state |
|
|
when <i0>
|
|
<state>}; separator="\n">
|
|
end
|
|
|
|
raise "Expected: <dfa.description>"
|
|
<else>
|
|
-1
|
|
<endif>
|
|
end
|
|
|
|
public :special_state_transition
|
|
>>
|
|
|
|
/** A special state in a cyclic DFA; special means has a semantic predicate
|
|
* or it's a huge set of symbols to check.
|
|
*/
|
|
cyclicDFAState(decisionNumber,stateNumber,edges,needErrorClause,semPredState)::=
|
|
<<
|
|
<if(semPredState)>
|
|
@input.rewind(0)
|
|
<else>
|
|
look_ahead_<decisionNumber>_<stateNumber> = <LA(1)>
|
|
<endif>
|
|
s = -1
|
|
<edges>
|
|
return s if s >= 0
|
|
>>
|
|
|
|
/** Just like a fixed DFA edge, test the lookahead and indicate what
|
|
* state to jump to next if successful. Again, this is for special
|
|
* states.
|
|
*/
|
|
cyclicDFAEdge(labelExpr, targetStateNumber, edgeNumber, predicates)::=
|
|
<<
|
|
return s = <targetStateNumber> if (<labelExpr>) <if(predicates)>&& (<predicates>)<endif><\n>
|
|
>>
|
|
|
|
/** An edge pointing at end-of-token; essentially matches any char;
|
|
* always jump to the target.
|
|
*/
|
|
eotDFAEdge(targetStateNumber,edgeNumber, predicates)::=
|
|
<<
|
|
s = <targetStateNumber><\n>
|
|
>>
|
|
|
|
// D F A E X P R E S S I O N S
|
|
|
|
andPredicates(left,right)::= "(<left> && <right>)"
|
|
|
|
orPredicates(operands)::=
|
|
<<
|
|
(<operands; separator=" || ">)
|
|
>>
|
|
|
|
notPredicate(pred)::= "!(<evalPredicate(...)>)"
|
|
|
|
evalPredicate(pred,description)::= "(<pred>)"
|
|
|
|
evalSynPredicate(pred,description)::= "<pred>()"
|
|
|
|
/**
|
|
* It's not really clear that decisionNumber and stateNumber are available here
|
|
*/
|
|
lookaheadTest(atom,k,atomAsInt)::=
|
|
<<
|
|
<if(LEXER)>
|
|
look_ahead<decisionNumber>_<stateNumber> == <atom>
|
|
<else>
|
|
look_ahead<decisionNumber>_<stateNumber> == :<atom>
|
|
<endif>
|
|
>>
|
|
|
|
|
|
/** Sometimes a lookahead test cannot assume that LA(k) is in a temp variable
|
|
* somewhere. Must ask for the lookahead directly.
|
|
*/
|
|
isolatedLookaheadTest(atom,k,atomAsInt) ::=
|
|
<<
|
|
<if(LEXER)>
|
|
<LA(k)> == <atom>
|
|
<else>
|
|
<LA(k)> == :<atom>
|
|
<endif>
|
|
>>
|
|
|
|
/**
|
|
* It's not really clear that decisionNumber and stateNumber are available here
|
|
*/
|
|
lookaheadRangeTest(lower,upper,k,rangeNumber,lowerAsInt,upperAsInt)::=
|
|
<<
|
|
<if(LEXER)>
|
|
(look_ahead<decisionNumber>_<stateNumber> \>= <lower> && look_ahead<decisionNumber>_<stateNumber> \<= <upper>)
|
|
<else>
|
|
(TOKENS[look_ahead<decisionNumber>_<stateNumber>] \>= <lowerAsInt> && TOKENS[look_ahead<decisionNumber>_<stateNumber>] \<= <upperAsInt>)
|
|
<endif>
|
|
>>
|
|
|
|
|
|
isolatedLookaheadRangeTest(lower,upper,k,rangeNumber,lowerAsInt,upperAsInt) ::=
|
|
<<
|
|
<if(LEXER)>
|
|
(<LA(k)> \>= <lower> && <LA(k)> \<= <upper>)
|
|
<else>
|
|
(TOKENS[<LA(k)>] \>= <lowerAsInt> && TOKENS[<LA(k)>] \<= <upperAsInt>)
|
|
<endif>
|
|
>>
|
|
|
|
setTest(ranges) ::=
|
|
<<
|
|
<ranges; separator=" || ">
|
|
>>
|
|
|
|
// A T T R I B U T E S
|
|
|
|
parameterAttributeRef(attr)::= "<attr.name>"
|
|
parameterSetAttributeRef(attr,expr) ::= "<attr.name> = <expr>"
|
|
|
|
scopeAttributeRef(scope,attr,index,negIndex)::=
|
|
<<
|
|
raise "scopeAttributeRef not implemented"
|
|
>>
|
|
|
|
scopeSetAttributeRef(scope,attr,expr,index,negIndex) ::=
|
|
<<
|
|
raise "scopeSetAttributeRef not implemented"
|
|
>>
|
|
|
|
/** $x is either global scope or x is rule with dynamic scope; refers
|
|
* to stack itself not top of stack. This is useful for predicates
|
|
* like {$function.size()>0 && $function::name.equals("foo")}?
|
|
*/
|
|
isolatedDynamicScopeRef(scope)::=
|
|
<<
|
|
raise "isolatedDynamicScopeRef not implemented"
|
|
>>
|
|
|
|
/** reference an attribute of rule; might only have single return value */
|
|
ruleLabelRef(referencedRule,scope,attr)::=
|
|
<<
|
|
<if(referencedRule.hasMultipleReturnValues)>
|
|
_<scope>[:<attr.name>]
|
|
<else>
|
|
_<scope>
|
|
<endif>
|
|
>>
|
|
|
|
/**
|
|
<if(referencedRule.hasMultipleReturnValues)>
|
|
<scope>[<attr.index>] # TODO: need "Attribute.index" for this to work
|
|
<else>
|
|
<scope>
|
|
<endif>
|
|
>>
|
|
**/
|
|
|
|
returnAttributeRef(ruleDescriptor,attr)::=
|
|
<<
|
|
_retval_<attr.name>
|
|
>>
|
|
|
|
returnSetAttributeRef(ruleDescriptor,attr,expr) ::=
|
|
<<
|
|
_retval_<attr.name> = <expr>
|
|
>>
|
|
|
|
|
|
/** How to translate $tokenLabel */
|
|
tokenLabelRef(label)::= "_<label>"
|
|
|
|
/** ids+=ID {$ids} or e+=expr {$e} */
|
|
listLabelRef(label)::= "list_<label>"
|
|
|
|
|
|
// not sure the next are the right approach; and they are evaluated early;
|
|
// they cannot see TREE_PARSER or PARSER attributes for example. :(
|
|
|
|
tokenLabelPropertyRef_text(scope,attr)::= "_<scope>.text"
|
|
tokenLabelPropertyRef_type(scope,attr)::= "_<scope>.token_type"
|
|
tokenLabelPropertyRef_line(scope,attr)::= "_<scope>.line"
|
|
tokenLabelPropertyRef_pos(scope,attr) ::= "_<scope>.pos"
|
|
tokenLabelPropertyRef_channel(scope,attr)::= "_<scope>.channel"
|
|
tokenLabelPropertyRef_index(scope,attr)::= "_<scope>.index"
|
|
|
|
|
|
tokenLabelPropertyRef_tree(scope,attr)::= <<
|
|
raise "tokenLabelPropertyRef_tree not implemented"
|
|
>>
|
|
|
|
ruleLabelPropertyRef_start(scope,attr)::=
|
|
<<
|
|
raise "ruleLabelPropertyRef_start not implemented"
|
|
>>
|
|
|
|
ruleLabelPropertyRef_stop(scope,attr)::=
|
|
<<
|
|
raise "ruleLabelPropertyRef_stop not implemented"
|
|
>>
|
|
|
|
ruleLabelPropertyRef_tree(scope,attr)::=
|
|
<<
|
|
raise "ruleLabelPropertyRef_tree not implemented"
|
|
>>
|
|
|
|
ruleLabelPropertyRef_text(scope,attr)::=
|
|
<<
|
|
raise "ruleLabelPropertyRef_text not implemented"
|
|
>>
|
|
|
|
ruleLabelPropertyRef_st(scope,attr)::=
|
|
<<
|
|
raise "ruleLabelPropertyRef_st not implemented"
|
|
>>
|
|
|
|
|
|
/** Isolated $RULE ref ok in lexer as it's a Token */
|
|
lexerRuleLabel(label)::=
|
|
<<
|
|
raise "lexerRuleLabel not implemented"
|
|
>>
|
|
|
|
lexerRuleLabelPropertyRef_type(scope,attr)::=
|
|
<<
|
|
raise "lexerRuleLabelPropertyRef_type not implemented"
|
|
>>
|
|
|
|
lexerRuleLabelPropertyRef_line(scope,attr)::=
|
|
<<
|
|
raise "lexerRuleLabelPropertyRef_line not implemented"
|
|
>>
|
|
|
|
lexerRuleLabelPropertyRef_pos(scope,attr)::=
|
|
<<
|
|
raise "lexerRuleLabelPropertyRef_pos not implemented"
|
|
>>
|
|
|
|
lexerRuleLabelPropertyRef_channel(scope,attr)::=
|
|
<<
|
|
raise "lexerRuleLabelPropertyRef_channel not implemented"
|
|
>>
|
|
|
|
lexerRuleLabelPropertyRef_index(scope,attr)::=
|
|
<<
|
|
raise "lexerRuleLabelPropertyRef_index not implemented"
|
|
>>
|
|
|
|
lexerRuleLabelPropertyRef_text(scope,attr)::=
|
|
<<
|
|
raise "lexerRuleLabelPropertyRef_text not implemented"
|
|
>>
|
|
|
|
lexerRulePropertyRef_text(scope,attr) ::= "@text"
|
|
lexerRulePropertyRef_type(scope,attr) ::= <<
|
|
raise "lexerRulePropertyRef_type not implemented"
|
|
>>
|
|
|
|
lexerRulePropertyRef_line(scope,attr) ::= "@line"
|
|
lexerRulePropertyRef_pos(scope,attr) ::= "@pos"
|
|
|
|
lexerRulePropertyRef_index(scope,attr) ::= <<
|
|
raise "lexerRulePropertyRef_index not implemented"
|
|
>>
|
|
lexerRulePropertyRef_channel(scope,attr) ::= "@channel"
|
|
|
|
lexerRulePropertyRef_start(scope,attr) ::= "@start"
|
|
lexerRulePropertyRef_stop(scope,attr) ::= <<
|
|
raise "lexerRulePropertyRef_stop not implemented"
|
|
>>
|
|
|
|
|
|
ruleSetPropertyRef_tree(scope,attr,expr) ::= <<
|
|
raise "ruleSetPropertyRef_tree not implemented"
|
|
>>
|
|
ruleSetPropertyRef_st(scope,attr,expr) ::= <<
|
|
raise "ruleSetPropertyRef_st not implemented"
|
|
>>
|
|
|
|
|
|
// Somebody may ref $template or $tree or $stop within a rule:
|
|
rulePropertyRef_start(scope,attr)::=
|
|
<<
|
|
raise "rulePropertyRef_start not implemented"
|
|
>>
|
|
|
|
rulePropertyRef_stop(scope,attr)::=
|
|
<<
|
|
raise "rulePropertyRef_stop not implemented"
|
|
>>
|
|
|
|
rulePropertyRef_tree(scope,attr)::=
|
|
<<
|
|
raise "rulePropertyRef_tree not implemented"
|
|
>>
|
|
|
|
rulePropertyRef_text(scope,attr)::=
|
|
<<
|
|
raise "rulePropertyRef_text not implemented"
|
|
>>
|
|
|
|
|
|
rulePropertyRef_st(scope,attr)::=
|
|
<<
|
|
raise "rulePropertyRef_st not implemented"
|
|
>>
|
|
|
|
/** How to execute an action */
|
|
/** TODO: add syntactic predicate & bactracking gates **/
|
|
execAction(action)::=
|
|
<<
|
|
<action>
|
|
>>
|
|
|
|
// M I S C (properties, etc...)
|
|
|
|
codeFileExtension()::=".rb"
|
|
|
|
true()::= "true"
|
|
false()::= "false"
|
|
|
|
|
|
|
|
///////////// --------------------------- private templates --------------------------------
|
|
|
|
|
|
bitset()::=
|
|
<<
|
|
raise "bitset not implemented"
|
|
>>
|
|
|
|
|
|
element() ::= "<it.el>"
|
|
|
|
plainBlock(decls, alts, description) ::=
|
|
<<
|
|
<decls>
|
|
<alts>
|
|
>>
|
|
|
|
switchBlock(description, decisionNumber, maxAlt, alts, decls, decision) ::=
|
|
<<
|
|
# <description>
|
|
alt<decisionNumber> = <maxAlt>
|
|
<decls>
|
|
<decision>
|
|
case alt<decisionNumber>
|
|
<alts:switchCase(); separator="\n">
|
|
end
|
|
>>
|
|
|
|
switchCase() ::=
|
|
<<
|
|
when <i>
|
|
<it>
|
|
>>
|
|
|
|
LA(k) ::=
|
|
<<
|
|
<if(LEXER)>
|
|
@input.look_ahead(<k>)
|
|
<else>
|
|
look_ahead(<k>)
|
|
<endif>
|
|
>>
|
|
|
|
|
|
synpred(name) ::= <<
|
|
def <name>
|
|
start = @input.mark()
|
|
@backtracking += 1
|
|
<name>_fragment()
|
|
@backtracking -= 1
|
|
|
|
success = !@failed
|
|
@input.rewind(start)
|
|
@failed = false
|
|
|
|
return success
|
|
end
|
|
>>
|
|
|
|
parameterScope(scope) ::= <<
|
|
<scope.attributes:{<it.decl>}; separator=", ">
|
|
>>
|
|
|
|
|
|
dfaClass() ::= <<
|
|
<if(cyclicDFAs)>
|
|
class DFA
|
|
def initialize(eot, eof, min, max, accept, special, transition)
|
|
@eot = eot
|
|
@eof = eof
|
|
@min = min
|
|
@max = max
|
|
@accept = accept
|
|
@special = special
|
|
@transition = transition
|
|
end
|
|
|
|
def predict(parser, input)
|
|
mark = input.mark()
|
|
s = 0 # we always start at s0
|
|
begin
|
|
loop do
|
|
special_state = @special[s]
|
|
if special_state >= 0
|
|
s = parser.special_state_transition(special_state)
|
|
input.consume()
|
|
next
|
|
end
|
|
|
|
if @accept[s] >= 1
|
|
return @accept[s]
|
|
end
|
|
|
|
# look for a normal char transition
|
|
c = input.look_ahead(1).to_i
|
|
if c != :EOF && c >= @min[s] && c \<= @max[s]
|
|
next_state = @transition[s][c - @min[s]] # move to next state
|
|
if next_state \< 0
|
|
# was in range but not a normal transition
|
|
# must check EOT, which is like the else clause.
|
|
# eot[s]>=0 indicates that an EOT edge goes to another
|
|
# state.
|
|
if @eot[s] >= 0 # EOT Transition to accept state?
|
|
s = @eot[s]
|
|
input.consume()
|
|
next
|
|
end
|
|
raise "No viable alt"
|
|
end
|
|
s = next_state
|
|
input.consume()
|
|
next
|
|
end
|
|
if @eot[s] >= 0 # EOT Transition?
|
|
s = @eot[s]
|
|
input.consume()
|
|
next
|
|
end
|
|
if c == :EOF && @eof[s] >= 0 # EOF Transition to accept state?
|
|
return @accept[@eof[s]]
|
|
end
|
|
|
|
# not in range and not EOF/EOT, must be invalid symbol
|
|
raise "No viable alt"
|
|
end
|
|
ensure
|
|
input.rewind(mark)
|
|
end
|
|
end
|
|
end
|
|
<endif>
|
|
>> |