← Back to Examples
Mathn Compatibility
RubyconceptualbeginnerParslet-compatible
Ruby mathn library compatibility
How the Code Works
This section explains how the parsing rules map to the format semantics.
The Mathn Problem
# Without mathn:
3 / 2 # => 1 (integer division)
# With mathn:
3 / 2 # => (3/2) (Rational)Ruby's deprecated `mathn` library changed integer division behavior: This broke Parslet's internal calculations.
The Grammar
cephalopod =
str('octopus') |
str('squid')
parenthesized_cephalopod =
str('(') >>
possible_whitespace >>
cephalopod >>
possible_whitespace >>
str(')')Simple cephalopod matching:
The Compatibility Fix
def attempt_parse
parser = possible_whitespace >>
parenthesized_cephalopod >>
possible_whitespace
# This would hang without the fix
parser.parse %{(\nsqeed)\n}
rescue Parsanol::ParseFailed
endParsanol includes a fix that works regardless of mathn:
Version Check
if RUBY_VERSION.gsub(/[^\d]/, '').to_i < 250
require 'mathn'
endThe example checks Ruby version: Mathn was deprecated in Ruby 2.5, so it's only loaded on older Rubies.
Version Check
if RUBY_VERSION.gsub(/[^\d]/, '').to_i < 250
require 'mathn'
endThe example checks Ruby version: Mathn was deprecated in Ruby 2.5, so it's only loaded on older Rubies.
Source Code
Ruby
# frozen_string_literal: true
# Demonstrates that we have a compatibility fix to mathn's weird idea of
# integer mathematics.
# Originally contributed to Parslet, ported to Parsanol as an example.
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
require "parsanol/parslet"
require "parsanol/convenience"
include Parsanol::Parslet
def attempt_parse
possible_whitespace = match['\s'].repeat
cephalopod =
str("octopus") |
str("squid")
parenthesized_cephalopod =
str("(") >>
possible_whitespace >>
cephalopod >>
possible_whitespace >>
str(")")
parser =
possible_whitespace >>
parenthesized_cephalopod >>
possible_whitespace
# This parse fails, but that is not the point. When mathn is in the current
# ruby environment, it modifies integer division in a way that makes
# parslet loop indefinitely.
parser.parse %{(\nsqeed)\n}
rescue Parsanol::ParseFailed
end
attempt_parse
puts "it terminates before we require mathn"
puts "requiring mathn now"
# mathn was deprecated as of Ruby 2.5
require "mathn" if RUBY_VERSION.gsub(/[^\d]/, "").to_i < 250
puts "and trying again (will hang without the fix)"
attempt_parse # but it doesn't terminate after requiring mathn
puts "okay!"