This commit is contained in:
Dominic Grimm 2021-11-20 13:14:36 +01:00
commit 2c9de8cefd
16 changed files with 554 additions and 0 deletions

9
.editorconfig Normal file
View file

@ -0,0 +1,9 @@
root = true
[*.cr]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

19
.github/workflows/crystal.yml vendored Normal file
View file

@ -0,0 +1,19 @@
name: Crystal CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
container:
image: crystallang/crystal
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: shards install
- name: Run tests
run: crystal spec -v

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
/docs/
/lib/
/bin/
/.shards/
*.dwarf
# Libraries don't need dependency lock
# Dependencies will be locked in applications that use them
/shard.lock

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2021 Dominic Grimm <dominic.grimm@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

56
README.md Normal file
View file

@ -0,0 +1,56 @@
# lifo
[![GitHub release](https://img.shields.io/github/release/grimmigerFuchs/lifo.svg)](https://github.com/grimmigerFuchs/lifo/releases)
A library for working with LIFO like data structures (stacks, queues).
## Installation
1. Add the dependency to your `shard.yml`:
```yaml
dependencies:
stack:
github: grimmigerFuchs/lifo
```
2. Run `shards install`
## Usage
```crystal
require "lifo"
stack = Lifo::Stack(String).new(3)
stack.push("Foo")
stack.push("Bar")
stack.push("Baz")
p! stack.pop
# stack.pop # => "Baz"
p! stack.pop
# stack.pop # => "Bar"
p! stack.peek
# stack.peek # => "Foo"
queue = Lifo::Queue(String).new(3)
queue.enqueue("John Doe")
queue.enqueue("Jane Doe")
p! queue.dequeue
# queue.dequeue # => "John Doe"
p! queue.peek
# queue.peek # => "Jane Doe"
```
## Contributing
1. Fork it (<https://github.com/grimmigerFuchs/lifo/fork>)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request
## Contributors
- [Dominic Grimm](https://github.com/grimmigerFuchs) - creator and maintainer

10
shard.yml Normal file
View file

@ -0,0 +1,10 @@
name: lifo
description: A library for working with LIFO like data structures (stacks, queues).
version: 0.1.0
authors:
- Dominic Grimm <dominic.grimm@gmail.com>
crystal: 1.2.2
license: MIT

2
spec/spec_helper.cr Normal file
View file

@ -0,0 +1,2 @@
require "spec"
require "../src/lifo"

104
spec/stack/queue.cr Normal file
View file

@ -0,0 +1,104 @@
describe Lifo::Queue do
describe "#initialize" do
it "sets capacity to -1 if none is given" do
queue = Lifo::Queue(Int32).new
queue.capacity.should eq -1
end
end
describe "#enqueue" do
it "enqueues to the queue" do
queue = Lifo::Queue(Int32).new
queue.enqueue(1)
queue.peek.should eq(1)
queue.enqueue(3)
queue.peek.should eq(1)
queue.enqueue(5)
queue.peek.should eq(1)
end
it "returns queue size" do
queue = Lifo::Queue(Int32).new
queue.enqueue(1)
queue.enqueue(3)
queue.size.should eq(2)
end
it "raises OverflowError if queue is full" do
queue = Lifo::Queue(Int32).new(0)
expect_raises(Lifo::OverflowError) do
queue.enqueue(1)
end
end
it "doesn't raise an error when queue is infinite" do
queue = Lifo::Queue(Int32).new
queue.enqueue(1)
end
end
describe "#enqueue?" do
it "enqueues to the queue" do
queue = Lifo::Queue(Int32).new
queue.enqueue?(1)
queue.peek.should eq(1)
queue.enqueue?(3)
queue.peek.should eq(1)
queue.enqueue?(5)
queue.peek.should eq(1)
end
it "returns queue size" do
queue = Lifo::Queue(Int32).new
queue.enqueue?(1)
queue.enqueue?(3)
queue.size.should eq(2)
end
it "returns nil if error raised" do
queue = Lifo::Queue(Int32).new(0)
queue.enqueue?(1).should be_nil
end
end
describe "#dequeue" do
it "dequeues from the queue" do
queue = Lifo::Queue(Int32).new
queue.enqueue(1)
queue.enqueue(3)
queue.peek.should eq(1)
queue.dequeue
queue.peek.should eq(3)
queue.dequeue
end
it "returns dequeued element" do
queue = Lifo::Queue(Int32).new
queue.enqueue(1)
queue.enqueue(3)
queue.peek.should eq(1)
queue.dequeue.should eq(1)
queue.dequeue.should eq(3)
end
it "raises UnderflowError if queue is empty" do
queue = Lifo::Queue(Int32).new(0)
expect_raises(Lifo::UnderflowError) do
queue.dequeue
end
end
end
describe "#dequeue?" do
it "dequeues from the queue" do
queue = Lifo::Queue(Int32).new
queue.enqueue(1)
queue.enqueue(3)
queue.peek.should eq(1)
queue.dequeue?
queue.peek.should eq(3)
queue.dequeue?
end
end
end

165
spec/stack/stack.cr Normal file
View file

@ -0,0 +1,165 @@
describe Lifo::Stack do
describe "#initialize" do
it "sets capacity to -1 if none is given" do
stack = Lifo::Stack(Int32).new
stack.capacity.should eq -1
end
end
describe "#push" do
it "pushes to the stack" do
stack = Lifo::Stack(Int32).new
stack.push(1)
stack.peek.should eq(1)
stack.push(3)
stack.peek.should eq(3)
end
it "returns stack size" do
stack = Lifo::Stack(Int32).new
stack.push(1)
stack.push(3)
stack.size.should eq(2)
end
it "raises OverflowError if stack is full" do
stack = Lifo::Stack(Int32).new(0)
expect_raises(Lifo::OverflowError) do
stack.push(1)
end
end
it "doesn't raise an error when stack is infinite" do
stack = Lifo::Stack(Int32).new
stack.push(1)
end
end
describe "#push?" do
it "pushes to the stack" do
stack = Lifo::Stack(Int32).new
stack.push?(1)
stack.peek.should eq(1)
stack.push?(3)
stack.peek.should eq(3)
end
it "returns stack size" do
stack = Lifo::Stack(Int32).new
stack.push?(1)
stack.push?(3)
stack.size.should eq(2)
end
it "returns nil if error raised" do
stack = Lifo::Stack(Int32).new(0)
stack.push?(1).should be_nil
end
end
describe "#pop" do
it "pops from the stack" do
stack = Lifo::Stack(Int32).new
stack.push(1)
stack.push(3)
stack.peek.should eq(3)
stack.pop
stack.peek.should eq(1)
stack.pop
end
it "returns popped element" do
stack = Lifo::Stack(Int32).new
stack.push(1)
stack.push(3)
stack.pop.should eq(3)
stack.pop.should eq(1)
end
it "raises UnderflowError if stack is empty" do
stack = Lifo::Stack(Int32).new
expect_raises(Lifo::UnderflowError) do
stack.pop
end
end
end
describe "#pop?" do
it "pops from the stack" do
stack = Lifo::Stack(Int32).new
stack.push(1)
stack.push(3)
stack.peek.should eq(3)
stack.pop?
stack.peek.should eq(1)
stack.pop?
end
it "returns popped element" do
stack = Lifo::Stack(Int32).new
stack.push(1)
stack.push(3)
stack.peek.should eq(3)
stack.pop?.should eq(3)
stack.peek.should eq(1)
stack.pop?.should eq(1)
end
it "returns nil if stack is empty" do
stack = Lifo::Stack(Int32).new
stack.pop?.should be_nil
end
end
describe "#peek" do
it "returns top element" do
stack = Lifo::Stack(Int32).new
stack.push(2)
stack.peek.should eq(2)
end
it "raises UnderflowError if stack is empty" do
stack = Lifo::Stack(Int32).new
expect_raises(Lifo::UnderflowError) do
stack.peek
end
end
end
describe "#peek?" do
it "returns top element" do
stack = Lifo::Stack(Int32).new
stack.push(1)
stack.push(3)
stack.peek?.should eq(3)
end
it "returns nil if stack is empty" do
stack = Lifo::Stack(Int32).new
stack.peek?.should be_nil
end
end
describe "#size" do
it "returns stack size" do
stack = Lifo::Stack(Int32).new
stack.push(1)
stack.push(3)
stack.size.should eq(2)
end
end
describe "#empty?" do
it "returns true if stack is empty" do
stack = Lifo::Stack(Int32).new
stack.empty?.should be_true
end
it "returns false if stack is not empty" do
stack = Lifo::Stack(Int32).new
stack.push(1)
stack.empty?.should be_false
end
end
end

4
spec/stack_spec.cr Normal file
View file

@ -0,0 +1,4 @@
require "./spec_helper"
require "./stack/stack"
require "./stack/queue"

5
src/lifo.cr Normal file
View file

@ -0,0 +1,5 @@
require "./lifo/version"
require "./lifo/exception"
require "./lifo/lifolike"
require "./lifo/stack"
require "./lifo/queue"

7
src/lifo/exception.cr Normal file
View file

@ -0,0 +1,7 @@
module Lifo
class OverflowError < Exception
end
class UnderflowError < Exception
end
end

20
src/lifo/lifolike.cr Normal file
View file

@ -0,0 +1,20 @@
module Lifo
abstract class LifoLike(T)
protected property data = [] of T
getter capacity
def initialize(@capacity = -1)
if @capacity < -1
raise ArgumentError.new("capacity must be >= -1")
end
end
def size : Int32
@data.size
end
def empty? : Bool
size == 0
end
end
end

60
src/lifo/queue.cr Normal file
View file

@ -0,0 +1,60 @@
module Lifo
class Queue(T) < LifoLike(T)
def initialize(capacity = -1)
super(capacity)
end
def enqueue(e : T) : Int32
if @capacity == -1 || size < @capacity
@data << e
size
else
raise OverflowError.new
end
end
def enqueue?(e : T) : Int32?
begin
enqueue(e)
rescue OverflowError
nil
end
end
def dequeue : T
begin
res = @data.first
@data.shift
rescue Enumerable::EmptyError
raise UnderflowError.new
end
res
end
def dequeue? : T?
begin
dequeue
rescue UnderflowError
nil
end
end
def peek : T
begin
@data.first
rescue IndexError
raise UnderflowError.new
end
end
def peek? : T?
begin
peek
rescue UnderflowError
nil
end
end
end
end

60
src/lifo/stack.cr Normal file
View file

@ -0,0 +1,60 @@
module Lifo
class Stack(T) < LifoLike(T)
def initialize(capacity = -1)
super(capacity)
end
def push(e : T) : Int32
if @capacity == -1 || size < @capacity
@data << e
size
else
raise OverflowError.new
end
end
def push?(e : T) : Int32?
begin
push(e)
rescue OverflowError
nil
end
end
def pop : T
begin
res = @data.last
@data.pop
rescue IndexError
raise UnderflowError.new
end
res
end
def pop? : T?
begin
pop
rescue UnderflowError
nil
end
end
def peek : T
begin
@data.last
rescue IndexError
raise UnderflowError.new
end
end
def peek? : T?
begin
peek
rescue UnderflowError
nil
end
end
end
end

3
src/lifo/version.cr Normal file
View file

@ -0,0 +1,3 @@
module Lifo
VERSION = {{ `shards version`.stringify.chomp }}
end