class Mingle::MingleParser
Constants
- QUANTS
- RANGE_CLOSE
- RANGE_OPEN
Public Class Methods
consume_string( s ) { |p| ... }
click to toggle source
# File lib/mingle.rb, line 1673 def self.consume_string( s ) p = self.for_string( s ) yield( p ).tap { p.check_trailing } end
for_string( s )
click to toggle source
# File lib/mingle.rb, line 1669 def self.for_string( s ) self.new( :lexer => MingleLexer.as_instance( s ) ) end
Public Instance Methods
check_trailing() { || ... }
click to toggle source
# File lib/mingle.rb, line 1262 def check_trailing res = yield if block_given? unless eof? tok, _ = read_tok fail_parse( "Unexpected token: #{tok}" ) end res end
expect_declared_type_name()
click to toggle source
# File lib/mingle.rb, line 1366 def expect_declared_type_name expect_typed( DeclaredTypeName ) end
expect_identified_name()
click to toggle source
# File lib/mingle.rb, line 1381 def expect_identified_name ns = expect_namespace names = [] begin if tok = poll_special( SpecialToken::FORWARD_SLASH ) names << expect_identifier end end while tok fail_parse( "Missing name", next_loc ) if names.empty? MingleIdentifiedName.send( :new, :namespace => ns, :names => names ) end
expect_identifier()
click to toggle source
# File lib/mingle.rb, line 1342 def expect_identifier expect_typed( MingleIdentifier ) end
expect_namespace()
click to toggle source
# File lib/mingle.rb, line 1347 def expect_namespace parts = [ expect_identifier ] begin if colon = poll_special( SpecialToken::COLON ) parts << expect_identifier end end while colon poll_special( SpecialToken::ASPERAND ) or fail_unexpected_token( "':' or '@'" ) ver = expect_identifier MingleNamespace.send( :new, :parts => parts, :version => ver ) end
expect_number()
click to toggle source
# File lib/mingle.rb, line 1327 def expect_number tok, _ = peek_tok if neg = ( tok == SpecialToken::MINUS ) read_tok end ParsedNumber.new( :negative => neg, :num => expect_typed( NumericToken ) ) end
expect_qname()
click to toggle source
# File lib/mingle.rb, line 1371 def expect_qname ns = expect_namespace expect_special( "type path", SpecialToken::FORWARD_SLASH ) nm = expect_declared_type_name QualifiedTypeName.send( :new, :namespace => ns, :name => nm ) end
expect_type_reference()
click to toggle source
# File lib/mingle.rb, line 1651 def expect_type_reference at = expect_atomic_type_reference poll_quants.inject( at ) do |typ, quant| case quant when SpecialToken::ASTERISK ListTypeReference.send( :new, :element_type => typ, :allows_empty => true ) when SpecialToken::PLUS ListTypeReference.send( :new, :element_type => typ, :allows_empty => false ) when SpecialToken::QUESTION_MARK NullableTypeReference.send( :new, :type => typ ) end end end
Private Instance Methods
adjacent_ints?( min, max )
click to toggle source
# File lib/mingle.rb, line 1561 def adjacent_ints?( min, max ) case min when MingleInt32, MingleInt64, MingleUint32, MingleUint64 min.num.succ == max.num else false end end
cast_range_value( val, nm, bound )
click to toggle source
# File lib/mingle.rb, line 1489 def cast_range_value( val, nm, bound ) return nil if val.nil? typ = AtomicTypeReference.send( :new, :name => nm ) unless COMPARABLE_TYPES.include?( typ ) fail_restriction_type( typ, "range" ) end check_allow_cast( val, typ, bound ) # s is a StringToken or a ParsedNumber s = val.is_a?( StringToken ) ? val.val : val.external_form exec_cast( MingleString.new( s ), typ ) end
check_allow_cast( val, typ, bound )
click to toggle source
# File lib/mingle.rb, line 1454 def check_allow_cast( val, typ, bound ) err_desc = nil case val when StringToken err_desc = "string" if NUM_TYPES.include?( typ ) when ParsedNumber if NUM_TYPES.include?( typ ) if INT_TYPES.include?( typ ) && ( ! val.integer? ) err_desc = "decimal" end else err_desc = "number" end end if err_desc raise RestrictionTypeError.new( "Got #{err_desc} as #{bound} value for range" ) end end
check_range_restriction_syntax( opts )
click to toggle source
# File lib/mingle.rb, line 1522 def check_range_restriction_syntax( opts ) min_closed, min_sx, max_sx, max_closed = opts.values_at( :min_closed, :min_syntax, :max_syntax, :max_closed ) err_desc, loc = case when min_sx.nil? && max_sx.nil? && ( min_closed || max_closed ) [ " ", opts[ :open_loc ] ] when min_sx.nil? && min_closed then [ " low ", opts[ :open_loc ] ] when max_sx.nil? && max_closed then [ " high ", opts[ :close_loc ] ] end fail_parse( "Infinite#{err_desc}range must be open", loc ) if loc end
check_satisfiable_range( opts )
click to toggle source
# File lib/mingle.rb, line 1571 def check_satisfiable_range( opts ) min_closed, min, max, max_closed = opts.values_at( :min_closed, :min, :max, :max_closed ) # If min,max are nil this is an infinite range and therefore # satisifiable (bounds will have been checked to be open elsewhere) return if min.nil? && max.nil? cmp, failed = min <=> max, false case when cmp == 0 then failed = ! ( min_closed && max_closed ) when cmp > 0 then failed = true else unless min_closed || max_closed failed = adjacent_ints?( min, max ) end end raise RestrictionTypeError, "Unsatisfiable range" if failed end
eof?()
click to toggle source
# File lib/mingle.rb, line 1209 def eof? @saved == nil && @lexer.eof? end
exec_cast( ms, typ )
click to toggle source
# File lib/mingle.rb, line 1478 def exec_cast( ms, typ ) begin Mingle.cast_value( MingleString.new( ms ), typ ) rescue MingleTimestamp::Rfc3339FormatError => e e = RestrictionTypeError.new( e.message ) if typ == TYPE_TIMESTAMP raise e end end
expect_atomic_type_reference()
click to toggle source
# File lib/mingle.rb, line 1621 def expect_atomic_type_reference nm = resolve_type_name( expect_type_name ) skip_ws restr = nil if poll_special( SpecialToken::TILDE ) skip_ws restr = expect_type_restriction( nm ) end AtomicTypeReference.send( :new, :name => nm, :restriction => restr ) end
expect_pattern_restriction( nm )
click to toggle source
# File lib/mingle.rb, line 1427 def expect_pattern_restriction( nm ) str, loc = expect_typed_loc( StringToken ) if nm == QNAME_STRING begin RegexRestriction.new( :ext_pattern => str.val ) rescue RegexpError => e raise RestrictionTypeError, "Invalid regex: #{e.message}" end else fail_restriction_type( nm, "regex" ) end end
expect_range_restriction( nm )
click to toggle source
# File lib/mingle.rb, line 1595 def expect_range_restriction( nm ) opts = read_range_restriction_syntax( nm ) check_range_restriction_syntax( opts ) opts[ :min ] = cast_range_value( opts[ :min_syntax ], nm, "min" ) opts[ :max ] = cast_range_value( opts[ :max_syntax ], nm, "max" ) check_satisfiable_range( opts ) RangeRestriction.new( opts ) end
expect_special_loc( desc, *spec )
click to toggle source
# File lib/mingle.rb, line 1289 def expect_special_loc( desc, *spec ) res = poll_special_loc( *spec ) if tok = res[ 0 ] res else fail_unexpected_token( desc ) end end
expect_type_name()
click to toggle source
# File lib/mingle.rb, line 1399 def expect_type_name tok, _ = peek_tok case tok when MingleIdentifier then expect_qname when DeclaredTypeName then expect_declared_type_name else fail_unexpected_token( "identifier or declared type name" ) end end
expect_type_restriction( nm )
click to toggle source
# File lib/mingle.rb, line 1609 def expect_type_restriction( nm ) tok, _ = peek_tok case when tok.is_a?( StringToken ) then expect_pattern_restriction( nm ) when RANGE_OPEN.include?( tok ) then expect_range_restriction( nm ) else fail_unexpected_token( "type restriction" ) end end
expect_typed( typ )
click to toggle source
# File lib/mingle.rb, line 1322 def expect_typed( typ ) expect_typed_loc( typ )[ 0 ] end
expect_typed_loc( typ )
click to toggle source
Does not allow for unread of its result
# File lib/mingle.rb, line 1308 def expect_typed_loc( typ ) if @saved if ( id = @saved[ 0 ] ).is_a?( typ ) @saved.tap { @saved = nil } else fail_parse( "Expected identifier" ) end else @lexer.expect_token( typ ) end end
fail_parse( msg, err_loc = loc )
click to toggle source
# File lib/mingle.rb, line 1234 def fail_parse( msg, err_loc = loc ) raise MingleParseError.new( :err => msg, :loc => err_loc ) end
fail_restriction_type( found, desc )
click to toggle source
# File lib/mingle.rb, line 1421 def fail_restriction_type( found, desc ) raise RestrictionTypeError.new( "Invalid target type for #{desc} restriction: #{found}" ) end
fail_unexpected_token( desc )
click to toggle source
# File lib/mingle.rb, line 1239 def fail_unexpected_token( desc ) tok, err_loc = read_tok unless tok tok, err_loc = "END", @lexer.create_loc( 1 ) end fail_parse( "Expected #{desc} but found: #{tok}", err_loc ) end
loc()
click to toggle source
# File lib/mingle.rb, line 1199 def loc @saved ? @saved[ 1 ] : @lexer.create_loc end
next_loc()
click to toggle source
# File lib/mingle.rb, line 1204 def next_loc @saved ? @saved[ 1 ] : @lexer.create_loc( 1 ) end
peek_tok( typ = nil )
click to toggle source
# File lib/mingle.rb, line 1229 def peek_tok( typ = nil ) read_tok.tap { |pair| unread_tok( *pair ) } end
poll_quants()
click to toggle source
# File lib/mingle.rb, line 1637 def poll_quants res = [] begin if tok = poll_special( *QUANTS ) res << tok end end while tok res end
poll_special_loc( *specs )
click to toggle source
# File lib/mingle.rb, line 1276 def poll_special_loc( *specs ) tok, loc = read_tok unless specs.include?( tok ) unread_tok( tok, loc ) tok, loc = nil, nil end [ tok, loc ] end
read_range_bound( err_desc, toks, close_test )
click to toggle source
Expects one of the tokens in toks, and returns the pair [ true, loc ] or [ false, loc ] based on whether the expected pair had the token matching close_test
# File lib/mingle.rb, line 1446 def read_range_bound( err_desc, toks, close_test ) pair = expect_special_loc( err_desc, *toks ) [ pair[ 0 ] == close_test, pair[ 1 ] ] end
read_range_restriction_syntax( nm )
click to toggle source
# File lib/mingle.rb, line 1539 def read_range_restriction_syntax( nm ) res = {} res[ :min_closed ], res[ :open_loc ] = read_range_bound( "range open", RANGE_OPEN, SpecialToken::OPEN_BRACKET ) res[ :min_syntax ] = read_range_value( nm ) skip_ws expect_special( ",", SpecialToken::COMMA ) res[ :max_syntax ] = read_range_value( nm ) skip_ws res[ :max_closed ], res[ :close_loc ] = read_range_bound( "range close", RANGE_CLOSE, SpecialToken::CLOSE_BRACKET ) res end
read_range_value( nm )
click to toggle source
# File lib/mingle.rb, line 1508 def read_range_value( nm ) tok, _ = peek_tok val = case when tok == SpecialToken::COMMA || RANGE_CLOSE.include?( tok ) then nil when tok == SpecialToken::MINUS || tok.is_a?( NumericToken ) expect_number when tok.is_a?( StringToken ) then expect_typed( StringToken ) else fail_unexpected_token( "range value" ) end end
read_tok( typ = nil )
click to toggle source
# File lib/mingle.rb, line 1214 def read_tok( typ = nil ) pair, @saved = @saved, nil pair || ( @lexer.eof? ? [ nil, nil ] : @lexer.read_token( typ ) ) end
resolve_type_name( nm )
click to toggle source
# File lib/mingle.rb, line 1411 def resolve_type_name( nm ) if res = QNAME_RESOLV_MAP[ nm ] res else nm end end
skip_ws()
click to toggle source
# File lib/mingle.rb, line 1251 def skip_ws begin tok, loc = read_tok unless tok.is_a?( WhitespaceToken ) unread_tok( tok, loc ) tok = nil end end while tok end
unread_tok( tok, loc )
click to toggle source
# File lib/mingle.rb, line 1221 def unread_tok( tok, loc ) pair = [ tok, loc ] raise "Attempt to unread #{pair} while @saved is #@saved" if @saved @saved = tok ? pair : nil end