Skip to content

Commit

Permalink
Merge pull request #6 from maxfierke/mf-support_stdin
Browse files Browse the repository at this point in the history
Add support for reading source text from STDIN
  • Loading branch information
maxfierke authored Feb 12, 2022
2 parents 2f7bff9 + 2f0e8ba commit e924a4c
Show file tree
Hide file tree
Showing 14 changed files with 147 additions and 45 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CI

on:
push:
paths-ignore:
- 'docs/**'
- '*.md'

jobs:
tests:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v2

- name: Add Crystal repos
run: curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash

- name: Install crystal
run: sudo apt install -y crystal libevent-dev libpcre3-dev libreadline-dev libssl-dev libyaml-dev

- name: Install dependencies
run: shards install

- name: Run tests
run: make test
1 change: 0 additions & 1 deletion .travis.yml

This file was deleted.

13 changes: 13 additions & 0 deletions spec/io_scanner_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,19 @@ describe Fincher::IOScanner, "#rest" do
end
end

describe Fincher::IOScanner, "#gets_to_end" do
it "returns the remainder of the string from the offset" do
s = Fincher::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 Fincher::IOScanner, "#[]" do
it "allows access to subgroups of the last match" do
s = Fincher::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39"))
Expand Down
7 changes: 5 additions & 2 deletions spec/strategies/displacement/m_word_offset_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ describe Fincher::DisplacementStrategies::MWordOffset do
3
)
source_text_scanner = Fincher::IOScanner.new(IO::Memory.new("lorem ipsum test blerg smorgasboorg"))
output_io = IO::Memory.new

it "adds the configured offset to the StringScanner#offset" do
m_word_offsetter.advance_to_next!(source_text_scanner, Char::ZERO)
m_word_offsetter.advance_to_next!(source_text_scanner, Char::ZERO, io: output_io)
source_text_scanner.offset.should eq(17)
output_io.to_s.should eq("lorem ipsum test ")
end
end

Expand All @@ -25,10 +27,11 @@ describe Fincher::DisplacementStrategies::MWordOffset do
10
)
source_text_scanner = Fincher::IOScanner.new(IO::Memory.new("lorem"))
output_io = IO::Memory.new

it "raises an exception" do
expect_raises(Fincher::StrategyNotFeasibleError) do
m_word_offsetter.advance_to_next!(source_text_scanner, Char::ZERO)
m_word_offsetter.advance_to_next!(source_text_scanner, Char::ZERO, io: output_io)
end
end
end
Expand Down
7 changes: 5 additions & 2 deletions spec/strategies/displacement/matching_char_offset_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ describe Fincher::DisplacementStrategies::MatchingCharOffset do
3
)
source_text_scanner = Fincher::IOScanner.new(IO::Memory.new("lorem ipsum test blerg smorgasboorg"))
output_io = IO::Memory.new

it "adds the configured offset to the StringScanner#offset" do
matching_char_offsetter.advance_to_next!(source_text_scanner, 'r')
matching_char_offsetter.advance_to_next!(source_text_scanner, 'r', io: output_io)
source_text_scanner.offset.should eq(20)
output_io.to_s.should eq("lorem ipsum test ble")
end
end

Expand All @@ -25,10 +27,11 @@ describe Fincher::DisplacementStrategies::MatchingCharOffset do
10
)
source_text_scanner = Fincher::IOScanner.new(IO::Memory.new("lorem"))
output_io = IO::Memory.new

it "raises an exception" do
expect_raises(Fincher::StrategyNotFeasibleError) do
matching_char_offsetter.advance_to_next!(source_text_scanner, 'r')
matching_char_offsetter.advance_to_next!(source_text_scanner, 'r', io: output_io)
end
end
end
Expand Down
7 changes: 5 additions & 2 deletions spec/strategies/displacement/n_char_offset_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ describe Fincher::DisplacementStrategies::NCharOffset do
10
)
source_text_scanner = Fincher::IOScanner.new(IO::Memory.new("lorem ipsum test"))
output_io = IO::Memory.new

it "adds the configured offset to the StringScanner#offset" do
n_char_offsetter.advance_to_next!(source_text_scanner, Char::ZERO)
n_char_offsetter.advance_to_next!(source_text_scanner, Char::ZERO, io: output_io)
source_text_scanner.pos = 10
output_io.to_s.should eq("lorem ipsu")
end
end

Expand All @@ -25,10 +27,11 @@ describe Fincher::DisplacementStrategies::NCharOffset do
10
)
source_text_scanner = Fincher::IOScanner.new(IO::Memory.new("lorem"))
output_io = IO::Memory.new

it "raises an exception" do
expect_raises(Fincher::StrategyNotFeasibleError) do
n_char_offsetter.advance_to_next!(source_text_scanner, Char::ZERO)
n_char_offsetter.advance_to_next!(source_text_scanner, Char::ZERO, io: output_io)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/transformer_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe Fincher::Transformer do
transformer.transform(transformed)

transformed.to_s.should eq(
"I am a test sentenceh The quick brown foe jumps over the lazl dog. That dog is llzy as fuck. God damo. How many dogs there gotta be that be like this man come on son. Why."
"I am a test sentenceh The quick brown foxejumps over the lazy log. That dog is lazylas fuck. God damn. How many dogs there gotta be that be like this man come on son. Why."
)
end
end
Expand Down
11 changes: 8 additions & 3 deletions src/fincher/cli.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ module Fincher
@seed : UInt32?

class Options
arg "source_text_file", required: true, desc: "source text file"
arg "message", required: true, desc: "message"
arg "source_text_file", required: false, desc: "source text file. STDIN, if omitted"
string "--seed",
var: "NUMBER",
required: false,
Expand Down Expand Up @@ -57,7 +57,12 @@ module Fincher
plaintext_scanner = ::IO::Memory.new(args.message)
displacement_strategy = options.displacement_strategy
replacement_strategy = options.replacement_strategy
source_file = File.open(args.source_text_file)

source_file = if source_text_file = args.source_text_file?
File.open(source_text_file)
else
STDIN
end

transformer = Fincher::Transformer.new(
plaintext_scanner,
Expand Down Expand Up @@ -122,7 +127,7 @@ module Fincher
end

def run
puts "Fincher #{version}"
puts "fincher #{version}"
end
end
end
Expand Down
77 changes: 65 additions & 12 deletions src/fincher/io_scanner.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ module Fincher
def initialize(@io : Fincher::IO)
end

def stdin?
io == STDIN
end

def [](index)
@last_match.not_nil![index]
end
Expand All @@ -35,6 +39,10 @@ module Fincher
rest_of_buffer + io.gets_to_end
end

def gets_to_end
rest
end

def string
@buffer + io.gets_to_end
end
Expand All @@ -58,6 +66,10 @@ module Fincher
match(pattern, advance: false, options: Regex::Options::None)
end

def skip(bytes_count : Int)
self.offset += bytes_count
end

def skip(pattern : Regex)
match = scan(pattern)
match.size if match
Expand All @@ -81,11 +93,38 @@ module Fincher
end

def offset=(position)
raise IndexError.new if position < 0 || position >= size
@buffer_io_offset = position
io.pos = position
@eof_reached = false
next_buffer!(position)
raise IndexError.new if position < 0

buffer_io_offset = @buffer_io_offset
buffer_cursor = @buffer_cursor
buffer = @buffer
buffer_io_end_offset = buffer_io_offset + buffer.bytesize

advance = (position - (buffer_io_offset + buffer_cursor)).to_i32

if position > buffer_io_offset && position < buffer_io_end_offset
@buffer_cursor += advance
elsif stdin?
# We cannot go backwards with STDIN
raise IndexError.new if position < buffer_io_offset

@buffer_io_offset = position

begin
io.skip(advance)
rescue ::IO::EOFError
# next_buffer! will handle EOF
end

next_buffer!(position)
else
# We don't want to overrun
raise IndexError.new if position >= size
@buffer_io_offset = position
io.pos = position
@eof_reached = false
next_buffer!(position)
end
end

def pos
Expand All @@ -96,6 +135,11 @@ module Fincher
self.offset = position
end

def print_buffer_position
puts @buffer
puts "#{" " * (Math.max(0, @buffer_cursor - 1))}^"
end

def size
case _io = io
when ::IO::FileDescriptor
Expand Down Expand Up @@ -126,7 +170,7 @@ module Fincher
end

private def rest_of_buffer
@buffer[@buffer_cursor, buffer_size]
buffer[@buffer_cursor, buffer_size]
end

private def buffer_size
Expand Down Expand Up @@ -182,15 +226,24 @@ module Fincher
private def next_buffer!(anchor_position = nil)
before_offset = anchor_position || offset

begin
buf = io.read_string(BUFFER_SIZE)
rescue ::IO::EOFError
io.pos = before_offset
buf = io.gets_to_end
buf = Bytes.new(BUFFER_SIZE)
bytes_read = io.read(buf)

if bytes_read < BUFFER_SIZE
@eof_reached = true
end

@buffer_io_offset = io.pos - buf.bytesize
if bytes_read > 0
buf = String.new(buf[0, bytes_read])
else
buf = String.new
end

@buffer_io_offset = if stdin?
before_offset + bytes_read
else
io.pos - bytes_read
end
@buffer_cursor = 0
@buffer = buf
buf
Expand Down
2 changes: 1 addition & 1 deletion src/fincher/strategies/displacement/base.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Fincher
def initialize(@plaintext_scanner : Fincher::IO, @seed : UInt32)
end

abstract def advance_to_next!(scanner : Fincher::IOScanner, msg_char : Char) : Fincher::IOScanner
abstract def advance_to_next!(scanner : Fincher::IOScanner, msg_char : Char, io : ::IO) : Fincher::IOScanner

abstract def is_feasible?(scanner : Fincher::IOScanner)
end
Expand Down
4 changes: 2 additions & 2 deletions src/fincher/strategies/displacement/m_word_offset.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ module Fincher
def initialize(@plaintext_scanner : Fincher::IO, @seed : UInt32, @offset : Int32)
end

def advance_to_next!(scanner : Fincher::IOScanner, msg_char : Char) : Fincher::IOScanner
def advance_to_next!(scanner : Fincher::IOScanner, msg_char : Char, io : ::IO) : Fincher::IOScanner
offset.times do
scanner.scan_until(/\b[\w\-\']+\b\W+\b/im)
io << scanner.scan_until(/\b[\w\-\']+\b\W+\b/im)

raise StrategyNotFeasibleError.new(
"Cannot advance #{offset} words at scanner position #{scanner.pos}"
Expand Down
11 changes: 8 additions & 3 deletions src/fincher/strategies/displacement/matching_char_offset.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@ module Fincher
def initialize(@plaintext_scanner : Fincher::IO, @seed : UInt32, @offset : Int32)
end

def advance_to_next!(scanner : Fincher::IOScanner, msg_char : Char) : Fincher::IOScanner
def advance_to_next!(scanner : Fincher::IOScanner, msg_char : Char, io : IO) : Fincher::IOScanner
offset.times do
scanner.scan_until(/\b[\w\-\']+\b\W+\b/im)
io << scanner.scan_until(/\b[\w\-\']+\b\W+\b/im)

raise StrategyNotFeasibleError.new(
"Cannot advance #{offset} words for character '#{msg_char}' at scanner position #{scanner.pos}"
) unless is_feasible?(scanner)
end
scanner.skip_until(/#{msg_char}/i)

matching_char_str = scanner.scan_until(/#{msg_char}/i)
raise StrategyNotFeasibleError.new(
"Cound not find character '#{msg_char}' after #{offset} words at scanner position #{scanner.pos}"
) unless matching_char_str
io << matching_char_str[0...-1]
scanner.offset = scanner.offset - 1
scanner
end
Expand Down
7 changes: 4 additions & 3 deletions src/fincher/strategies/displacement/n_char_offset.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ module Fincher
def initialize(@plaintext_scanner : Fincher::IO, @seed : UInt32, @offset : Int32)
end

def advance_to_next!(scanner : Fincher::IOScanner, msg_char : Char) : Fincher::IOScanner
def advance_to_next!(scanner : Fincher::IOScanner, msg_char : Char, io : IO) : Fincher::IOScanner
raise StrategyNotFeasibleError.new(
"Cannot advance #{offset} chars at scanner position #{scanner.pos}"
) unless is_feasible?(scanner)

scanner.pos += offset
io << scanner.scan(/.{#{offset}}/)

scanner
end

def is_feasible?(scanner : Fincher::IOScanner)
scanner.size > scanner.pos + offset
scanner.stdin? || (scanner.size > scanner.pos + offset)
end
end
end
Expand Down
16 changes: 3 additions & 13 deletions src/fincher/transformer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,16 @@ module Fincher

plaintext_scanner.each_char do |msg_char|
# Advance position
displacer.advance_to_next!(source_scanner, msg_char)

# Grab previously unmodified section
read_size = source_scanner.offset - current_offset
source_scanner.seek(current_offset)

if read_size > 0
unmodified = source_scanner.read_string(read_size)
io << unmodified
end
displacer.advance_to_next!(source_scanner, msg_char, io: io)

# Replace the next char
replaced_char = replacer.replace(msg_char)
io << replaced_char

# Record this offset (+ 1 skipping the current char)
current_offset = source_scanner.offset + 1
# Skip the current char, since we just replace it
source_scanner.skip(1)
end

source_scanner.skip(1)
io << source_scanner.gets_to_end
io
end
Expand Down

0 comments on commit e924a4c

Please sign in to comment.