Skip to content

Commit

Permalink
Merge pull request #1 from maxfierke/mf-string_scanner_api_compat
Browse files Browse the repository at this point in the history
Typhar::IOScanner API Compatibility with ::StringScanner
  • Loading branch information
maxfierke committed Aug 7, 2017
2 parents c9272c8 + 3711112 commit 9599a95
Show file tree
Hide file tree
Showing 7 changed files with 415 additions and 51 deletions.
8 changes: 4 additions & 4 deletions shard.lock
Expand Up @@ -2,17 +2,17 @@ version: 1.0
shards:
callback:
github: mosop/callback
version: 0.6.2
version: 0.6.3

cli:
github: mosop/cli
version: 0.6.9
version: 0.6.10

optarg:
github: mosop/optarg
version: 0.5.7
version: 0.5.8

string_inflection:
github: mosop/string_inflection
version: 0.2.0
version: 0.2.1

275 changes: 275 additions & 0 deletions spec/io_scanner_spec.cr
@@ -0,0 +1,275 @@
require "./spec_helper"

describe Typhar::IOScanner, "#scan" do
it "returns the string matched and advances the offset" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))
s.scan(/\w+\s/).should eq("this ")
s.scan(/\w+\s/).should eq("is ")
s.scan(/\w+\s/).should eq("a ")
s.scan(/\w+/).should eq("string")
end

it "returns nil if it can't match from the offset" do
s = Typhar::IOScanner.new(::IO::Memory.new("test string"))
s.scan(/\w+/).should_not be_nil # => "test"
s.scan(/\w+/).should be_nil
s.scan(/\s\w+/).should_not be_nil # => " string"
s.scan(/.*/).should_not be_nil # => ""
end
end

describe Typhar::IOScanner, "#scan_until" do
it "returns the string matched and advances the offset" do
s = Typhar::IOScanner.new(::IO::Memory.new("test string"))
s.scan_until(/tr/).should eq("test str")
s.offset.should eq(8)
s.scan_until(/g/).should eq("ing")
end

it "returns nil if it can't match from the offset" do
s = Typhar::IOScanner.new(::IO::Memory.new("test string"))
s.offset = 8
s.scan_until(/tr/).should be_nil
end
end

describe Typhar::IOScanner, "#skip" do
it "advances the offset but does not returns the string matched" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))

s.skip(/\w+\s/).should eq(5)
s.offset.should eq(5)
s[0]?.should_not be_nil

s.skip(/\d+/).should eq(nil)
s.offset.should eq(5)

s.skip(/\w+\s/).should eq(3)
s.offset.should eq(8)

s.skip(/\w+\s/).should eq(2)
s.offset.should eq(10)

s.skip(/\w+/).should eq(6)
s.offset.should eq(16)
end
end

describe Typhar::IOScanner, "#skip_until" do
it "advances the offset but does not returns the string matched" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))

s.skip_until(/not/).should eq(nil)
s.offset.should eq(0)
s[0]?.should be_nil

s.skip_until(/a\s/).should eq(10)
s.offset.should eq(10)
s[0]?.should_not be_nil

s.skip_until(/ng/).should eq(6)
s.offset.should eq(16)
end
end

describe Typhar::IOScanner, "#eos" do
it "it is true when the offset is at the end" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))
s.eos?.should eq(false)
s.skip(/(\w+\s?){4}/)
s.eos?.should eq(true)
end
end

describe Typhar::IOScanner, "#check" do
it "returns the string matched but does not advances the offset" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))
s.offset = 5

s.check(/\w+\s/).should eq("is ")
s.offset.should eq(5)
s.check(/\w+\s/).should eq("is ")
s.offset.should eq(5)
end

it "returns nil if it can't match from the offset" do
s = Typhar::IOScanner.new(::IO::Memory.new("test string"))
s.check(/\d+/).should be_nil
end
end

describe Typhar::IOScanner, "#check_until" do
it "returns the string matched and advances the offset" do
s = Typhar::IOScanner.new(::IO::Memory.new("test string"))
s.check_until(/tr/).should eq("test str")
s.offset.should eq(0)
s.check_until(/g/).should eq("test string")
s.offset.should eq(0)
end

it "returns nil if it can't match from the offset" do
s = Typhar::IOScanner.new(::IO::Memory.new("test string"))
s.offset = 8
s.check_until(/tr/).should be_nil
end
end

describe Typhar::IOScanner, "#rest" do
it "returns the remainder of the string from the offset" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))
s.rest.should eq("this is a string")

s.scan(/this is a /)
s.rest.should eq("string")

s.scan(/string/)
s.rest.should eq("")
end
end

describe Typhar::IOScanner, "#[]" do
it "allows access to subgroups of the last match" do
s = Typhar::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39"))
regex = /(?<wday>\w+) (?<month>\w+) (?<day>\d+)/
s.scan(regex).should eq("Fri Dec 12")
s[0].should eq("Fri Dec 12")
s[1].should eq("Fri")
s[2].should eq("Dec")
s[3].should eq("12")
s["wday"].should eq("Fri")
s["month"].should eq("Dec")
s["day"].should eq("12")
end

it "raises when there is no last match" do
s = Typhar::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39"))
s.scan(/this is not there/)

expect_raises { s[0] }
end

it "raises when there is no subgroup" do
s = Typhar::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39"))
regex = /(?<wday>\w+) (?<month>\w+) (?<day>\d+)/
s.scan(regex)

s[0].should_not be_nil
expect_raises { s[5] }
expect_raises { s["something"] }
end
end

describe Typhar::IOScanner, "#[]?" do
it "allows access to subgroups of the last match" do
s = Typhar::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39"))
result = s.scan(/(?<wday>\w+) (?<month>\w+) (?<day>\d+)/)

result.should eq("Fri Dec 12")
s[0]?.should eq("Fri Dec 12")
s[1]?.should eq("Fri")
s[2]?.should eq("Dec")
s[3]?.should eq("12")
s["wday"]?.should eq("Fri")
s["month"]?.should eq("Dec")
s["day"]?.should eq("12")
end

it "returns nil when there is no last match" do
s = Typhar::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39"))
s.scan(/this is not there/)

s[0]?.should be_nil
end

it "raises when there is no subgroup" do
s = Typhar::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39"))
s.scan(/(?<wday>\w+) (?<month>\w+) (?<day>\d+)/)

s[0].should_not be_nil
s[5]?.should be_nil
s["something"]?.should be_nil
end
end

describe Typhar::IOScanner, "#string" do
it { Typhar::IOScanner.new(::IO::Memory.new("foo")).string.should eq("foo") }
end

describe Typhar::IOScanner, "#offset" do
it "returns the current position" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))
s.offset.should eq(0)
s.scan(/\w+/)
s.offset.should eq(4)
end
end

describe Typhar::IOScanner, "#offset=" do
it "sets the current position" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))
s.offset = 5
s.scan(/\w+/).should eq("is")
end

it "raises on negative positions" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))
expect_raises(IndexError) { s.offset = -2 }
end
end

describe Typhar::IOScanner, "#inspect" do
it "has information on the scanner" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))
s.inspect.should eq(%(#<Typhar::IOScanner 0/16 "this " >))
s.scan(/\w+\s/)
s.inspect.should eq(%(#<Typhar::IOScanner 5/16 "s is " >))
s.scan(/\w+\s/)
s.inspect.should eq(%(#<Typhar::IOScanner 8/16 "s a s" >))
s.scan(/\w+\s\w+/)
s.inspect.should eq(%(#<Typhar::IOScanner 16/16 "tring" >))
end

it "works with small strings" do
s = Typhar::IOScanner.new(::IO::Memory.new("hi"))
s.inspect.should eq(%(#<Typhar::IOScanner 0/2 "hi" >))
s.scan(/\w\w/)
s.inspect.should eq(%(#<Typhar::IOScanner 2/2 "hi" >))
end
end

describe Typhar::IOScanner, "#peek" do
it "shows the next len characters without advancing the offset" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))
s.offset.should eq(0)
s.peek(4).should eq("this")
s.offset.should eq(0)
s.peek(7).should eq("this is")
s.offset.should eq(0)
end
end

describe Typhar::IOScanner, "#reset" do
it "resets the scan offset to the beginning and clears the last match" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))
s.scan_until(/str/)
s[0]?.should_not be_nil
s.offset.should_not eq(0)

s.reset
s[0]?.should be_nil
s.offset.should eq(0)
end
end

describe Typhar::IOScanner, "#terminate" do
it "moves the scan offset to the end of the string and clears the last match" do
s = Typhar::IOScanner.new(::IO::Memory.new("this is a string"))
s.scan_until(/str/)
s[0]?.should_not be_nil
s.eos?.should eq(false)

s.terminate
s[0]?.should be_nil
s.eos?.should eq(true)
end
end
1 change: 0 additions & 1 deletion spec/strategies/displacement/m_word_offset_spec.cr
Expand Up @@ -14,7 +14,6 @@ describe Typhar::DisplacementStrategies::MWordOffset do

it "adds the configured offset to the StringScanner#offset" do
m_word_offsetter.advance_to_next!(source_text_scanner)
source_text_scanner.last_match.not_nil![0].should eq("test")
source_text_scanner.offset.should eq(16)
end
end
Expand Down
1 change: 0 additions & 1 deletion src/typhar.cr
@@ -1,6 +1,5 @@
require "cli"
require "secure_random"
require "string_scanner"
require "./typhar/*"

module Typhar
Expand Down
22 changes: 14 additions & 8 deletions src/typhar/cli.cr
Expand Up @@ -13,6 +13,8 @@ module Typhar
end

class Encode < ::Cli::Command
@seed : UInt32?

class Options
arg "source_text_file", required: true, desc: "source text file"
arg "message", required: true, desc: "message"
Expand Down Expand Up @@ -47,23 +49,23 @@ module Typhar
caption "encode message"
end

def run
def run(io = STDOUT)
plaintext_scanner = ::IO::Memory.new(args.message)
source_file = File.open(args.source_text_file)
displacement_strategy = options.displacement_strategy
replacement_strategy = options.replacement_strategy
source_file = File.open(args.source_text_file)

transformer = Typhar::Transformer.new(
plaintext_scanner,
source_file,
displacement_strategy_for(displacement_strategy, plaintext_scanner, options),
replacement_strategy_for(replacement_strategy, options)
).transform
).transform(io)
ensure
source_file.close if source_file
end

private def displacement_strategy_for(strategy, plaintext_scanner, options)
seed = options.seed.empty? ? generate_seed : options.seed.to_u32

case strategy
when "word-offset"
word_offset = options.word_offset.to_i
Expand All @@ -77,8 +79,6 @@ module Typhar
end

private def replacement_strategy_for(strategy, options)
seed = options.seed.empty? ? generate_seed : options.seed.to_u32

case strategy
when "n-shifter"
codepoint_shift = options.codepoint_shift.to_i
Expand All @@ -88,8 +88,14 @@ module Typhar
end
end

private def seed
@seed ||= options.seed.empty? ? generate_seed : options.seed.to_u32
end

private def generate_seed
SecureRandom.hex(4).to_u32(16)
s = SecureRandom.hex(4).to_u32(16)
STDERR.puts "INFO: Using #{s} as seed"
s
end
end

Expand Down

0 comments on commit 9599a95

Please sign in to comment.