diff --git a/.travis.yml b/.travis.yml
index 5c4c3487df673560cd388b2064665905cf92b70c..74acb06755da80daa40af698d0a2777d7a0132a4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,8 @@
 language: erlang
+
 otp_release:
   - 17.4
-  - R16B
+  - R16B03
   - R15B03
 
 notifications:
diff --git a/include/gurka.hrl b/include/gurka.hrl
new file mode 100644
index 0000000000000000000000000000000000000000..412934605be8a05d5f51a7f00414534d02753352
--- /dev/null
+++ b/include/gurka.hrl
@@ -0,0 +1,16 @@
+-type phase() :: feature | background | scenario | scenario_outline | meta.
+-type action() :: desc | title | given | 'when' | then | examples | headers | values | tags.
+-type tokens() :: [binary()].
+-type row() :: pos_integer().
+-type meta() :: [{atom(),term()}].
+-type lines() :: [{row(), binary(), tokens()}].
+
+-record(step, {
+    phase :: phase(),
+    action = [] :: action(),
+    tokens = [] :: tokens(),
+    row = 0 :: row(),
+    meta = [] :: meta()
+}).
+
+-type feature() :: [#step{}].
diff --git a/rebar b/rebar
index 9bc9bd14eb3cae28559586bb78bcdd61b78b90ed..8978e55ff2a2853d7aabb298d411e63ccd7e9835 100755
Binary files a/rebar and b/rebar differ
diff --git a/src/gurka.app.src b/src/gurka.app.src
index 00994ad045f68eb67b39b3d4e8763dc2a59561a8..28577a8fb70cedec559c72807e5aff0ce18c1d93 100644
--- a/src/gurka.app.src
+++ b/src/gurka.app.src
@@ -1,7 +1,7 @@
 {application, gurka,
     [
         {description, "Erlang implementation of Cucumber"},
-        {vsn, "0.0.1"},
+        {vsn, "0.3.0"},
         {applications, [
             kernel,
             stdlib
diff --git a/src/gurka.erl b/src/gurka.erl
index 8c28323c6e95c130251b977ade37b3ffbae721af..850fe74e8494b44a309154ba04abe4dddc58a88f 100644
--- a/src/gurka.erl
+++ b/src/gurka.erl
@@ -1,16 +1,20 @@
--compile({no_auto_import, [apply/3]}).
 -module(gurka).
 
--export([run/1, run/2]).
+-include("gurka.hrl").
+
+-export([run/1, run/2, run/3]).
 
 run(File) ->
+    run(File, []).
+
+run(File, Options) ->
     Module = list_to_atom("feature_" ++ filename:basename(File, ".feature")),
-    run(File, Module).
+    run(File, Module, Options).
 
-run(File, Module) ->
+run(File, Module, Options) ->
     case gurka_parser:parse(File) of
         {ok, Feature} ->
-            Result = run(Module, undefined, undefined, undefined, Feature),
+            Result = run(Module, Options, undefined, undefined, undefined, Feature),
             Passed = has_passed(Result),
             if
                 Passed ->
@@ -22,103 +26,122 @@ run(File, Module) ->
             {error, Reason}
     end.
 
-run(Module, FeatureState, _ScenarioState, _PreviousPhase, []) ->
-    apply(Module, teardown_feature, [FeatureState]),
+run(Module, Options, FeatureState, _ScenarioState, _PreviousPhase, []) ->
+    apply(Module, Options, teardown_feature, [FeatureState]),
     [];
 
-run(DefaultModule, FeatureState, ScenarioState, PreviousPhase, [Step = {feature, start, Pattern, _RowNum} | Steps]) ->
-    Module = resolve_module(DefaultModule, Pattern),
+run(DefaultModule, Options, FeatureState, ScenarioState, PreviousPhase, [Step = #step{phase = feature, action = start, tokens = Tokens} | Steps]) ->
+    Module = resolve_module(DefaultModule, Tokens),
     case PreviousPhase of
         feature ->
-            apply(Module, teardown_feature, [FeatureState]);
+            apply(Module, Options, teardown_feature, [FeatureState]);
         background ->
-            apply(Module, teardown_feature, [FeatureState]);
+            apply(Module, Options, teardown_feature, [FeatureState]);
         scenario ->
-            apply(Module, teardown_scenario, [ScenarioState]),
-            apply(Module, teardown_feature, [FeatureState]);
+            apply(Module, Options, teardown_scenario, [ScenarioState]),
+            apply(Module, Options, teardown_feature, [FeatureState]);
         _ ->
             ok
     end,
-    case apply(Module, setup_feature, [Pattern]) of
+    case apply(Module, Options, setup_feature, [Tokens]) of
         {ok, State} ->
-            [{ok, Step} | run(Module, State, ScenarioState, feature, Steps)];
-        {error, undef} ->
-            [{ok, Step} | run(Module, undefined, ScenarioState, feature, Steps)];
+            [{ok, Step} | run(Module, Options, State, ScenarioState, feature, Steps)];
+        {error, undef, _Stack} ->
+            [{ok, Step} | run(Module, Options, undefined, ScenarioState, feature, Steps)];
         Term ->
             [{Term, Step}]
     end;
 
-run(Module, FeatureState, ScenarioState, _PreviousPhase, [Step = {background, given, Pattern, _RowNum} | Steps]) ->
-    case apply(Module, given, [Pattern, FeatureState]) of
-        ok ->
-            [{ok, Step} | run(Module, FeatureState, ScenarioState, background, Steps)];
-        {ok, State} ->
-            [{ok, Step} | run(Module, State, ScenarioState, background, Steps)];
+run(Module, Options, FeatureState, ScenarioState, _PreviousPhase, [Step = #step{phase = background, action = given, tokens = Tokens} | Steps]) ->
+    case apply(Module, Options, given, [Tokens, FeatureState]) of
+        Flag when Flag == ok; Flag == true ->
+            [{ok, Step} | run(Module, Options, FeatureState, ScenarioState, background, Steps)];
+        {Flag, State} when Flag == ok; Flag == true ->
+            [{ok, Step} | run(Module, Options, State, ScenarioState, background, Steps)];
         Term ->
+            apply(Module, Options, teardown_feature, [FeatureState]),
             [{Term, Step}]
     end;
 
-run(Module, FeatureState, ScenarioState, PreviousPhase, [Step = {scenario, start, Pattern, _RowNum} | Steps]) ->
-    case PreviousPhase of
-        scenario ->
-            apply(Module, teardown_scenario, [ScenarioState]);
+run(Module, Options, FeatureState, ScenarioState, PreviousPhase, [Step = #step{phase = scenario, action = start, tokens = Tokens} | Steps]) ->
+    case do_tags_match(Options, Step) of
+        true ->
+            case PreviousPhase of
+                scenario ->
+                    apply(Module, Options, teardown_scenario, [ScenarioState]);
+                _ ->
+                    ok
+            end,
+            case apply(Module, Options, setup_scenario, [Tokens, FeatureState]) of
+                {ok, State} ->
+                    [{ok, Step} | run(Module, Options, FeatureState, State, scenario, Steps)];
+                {error, undef, _Stack} ->
+                    [{ok, Step} | run(Module, Options, FeatureState, FeatureState, scenario, Steps)];
+                Term ->
+                    apply(Module, Options, teardown_feature, [FeatureState]),
+                    [{Term, Step}]
+            end;
         _ ->
-            ok
-    end,
-    case apply(Module, setup_scenario, [Pattern, FeatureState]) of
-        {ok, State} ->
-            [{ok, Step} | run(Module, FeatureState, State, scenario, Steps)];
-        {error, undef} ->
-            [{ok, Step} | run(Module, FeatureState, FeatureState, scenario, Steps)];
-        Term ->
-            [{Term, Step}]
+            [{ok, Step#step{action = skip}} | skip_scenario(Module, Options, FeatureState, Steps)]
     end;
 
-run(Module, FeatureState, ScenarioState, _PreviousPhase, [Step = {scenario, Action, Pattern, _RowNum} | Steps]) when Action == given; Action == 'when'; Action == then ->
-    case apply(Module, Action, [Pattern, ScenarioState]) of
-        ok ->
-            [{ok, Step} | run(Module, FeatureState, ScenarioState, scenario, Steps)];
-        {ok, State} ->
-            [{ok, Step} | run(Module, FeatureState, State, scenario, Steps)];
+run(Module, Options, FeatureState, ScenarioState, _PreviousPhase, [Step = #step{phase = scenario, action = Action, tokens = Tokens} | Steps]) when Action == given; Action == 'when'; Action == then ->
+    case apply(Module, Options, Action, [Tokens, ScenarioState]) of
+        Flag when Flag == ok; Flag == true ->
+            [{ok, Step} | run(Module, Options, FeatureState, ScenarioState, scenario, Steps)];
+        {Flag, State} when Flag == ok; Flag == true ->
+            [{ok, Step} | run(Module, Options, FeatureState, State, scenario, Steps)];
         Term ->
+            apply(Module, Options, teardown_scenario, [ScenarioState]),
+            apply(Module, Options, teardown_feature, [FeatureState]),
             [{Term, Step}]
     end;
 
-run(Module, _FeatureState, ScenarioState, _PreviousPhase, [{scenario_outline, 'end', _Pattern, _RowNum}]) ->
-    apply(Module, teardown_scenario, [ScenarioState]),
+run(Module, Options, _FeatureState, ScenarioState, _PreviousPhase, [#step{phase = scenario_outline, action = 'end'}]) ->
+    apply(Module, Options, teardown_scenario, [ScenarioState]),
     [];
 
-run(Module, FeatureState, ScenarioState, PreviousPhase, [{scenario_outline, start, Pattern, RowNum} | Steps]) ->
+run(Module, Options, FeatureState, ScenarioState, PreviousPhase, [Step = #step{phase = scenario_outline, action = start} | Steps]) ->
     case PreviousPhase of
         scenario ->
-            apply(Module, teardown_scenario, [ScenarioState]);
+            apply(Module, Options, teardown_scenario, [ScenarioState]);
         _ ->
             ok
     end,
-    run_outline(Module, FeatureState, ScenarioState, PreviousPhase, Steps, [{start, Pattern, RowNum}]);
+    run_outline(Module, Options, FeatureState, ScenarioState, PreviousPhase, Steps, [Step]);
 
-run(Module, FeatureState, ScenarioState, PreviousPhase, [{scenario_outline, Action, Pattern, RowNum} | Steps]) ->
-    run_outline(Module, FeatureState, ScenarioState, PreviousPhase, Steps, [{Action, Pattern, RowNum}]);
+run(Module, Options, FeatureState, ScenarioState, PreviousPhase, [Step = #step{phase = scenario_outline} | Steps]) ->
+    run_outline(Module, Options, FeatureState, ScenarioState, PreviousPhase, Steps, [Step]);
 
-run(Module, FeatureState, ScenarioState, _PreviousPhase, [Step = {Phase, _Action, _Pattern, _RowNum} | Steps]) ->
-    [{ok, Step} | run(Module, FeatureState, ScenarioState, Phase, Steps)].
+run(Module, Options, FeatureState, ScenarioState, _PreviousPhase, [Step = #step{phase = Phase} | Steps]) ->
+    [{ok, Step} | run(Module, Options, FeatureState, ScenarioState, Phase, Steps)].
 
-run_outline(Module, FeatureState, ScenarioState, _PreviousPhase, [{scenario_outline, examples, [Headers | Rows], _RowNum} | Steps], ScenarioOutline) ->
+run_outline(Module, Options, FeatureState, ScenarioState, _PreviousPhase, [ExamplesStep = #step{phase = scenario_outline, action = examples, row = ExampleRow, tokens = [Headers | Rows]} | Steps], ScenarioOutline) ->
     TaggedHeaders = [<<"<", Header/binary, ">">> || Header <- Headers],
-    Results = lists:map(fun(Row) ->
+    {Results, _} = lists:mapfoldl(fun(Row, Count) ->
         Example = lists:zip(TaggedHeaders, Row),
-        Scenario = lists:foldl(fun({Action, Pattern, RowNum}, ScenarioAcc) ->
-            ReplacedPattern = lists:map(fun(Token) ->
+        Scenario = lists:foldl(fun(Step, ScenarioAcc) ->
+            Tokens = lists:map(fun(Token) ->
                 lists:foldr(fun replace_tags/2, Token, Example)
-            end, Pattern),
-            [{scenario, Action, ReplacedPattern, RowNum} | ScenarioAcc]
-        end, [{scenario_outline, 'end', [], 0}], ScenarioOutline),
-        run(Module, FeatureState, ScenarioState, scenario_outline, Scenario)
-    end, Rows),
-    [Results | run(Module, FeatureState, ScenarioState, scenario_outline, Steps)];
-
-run_outline(Module, FeatureState, ScenarioState, PreviousPhase, [{scenario_outline, Action, Pattern, RowNum} | Steps], ScenarioOutline) ->
-    run_outline(Module, FeatureState, ScenarioState, PreviousPhase, Steps, [{Action, Pattern, RowNum} | ScenarioOutline]).
+            end, Step#step.tokens),
+            case Step#step.action of
+                start ->
+                    MergedTags = proplists:get_value(merged_tags, ExamplesStep#step.meta, []),
+                    Meta = [{example_row, {ExampleRow, Count}} | [{merged_tags, MergedTags} | proplists:delete(merged_tags, Step#step.meta)]],
+                    [Step#step{phase = scenario, tokens = Tokens, meta = Meta} | ScenarioAcc];
+                _ ->
+                    [Step#step{phase = scenario, tokens = Tokens} | ScenarioAcc]
+            end
+        end, [#step{phase = scenario_outline, action = 'end'}], ScenarioOutline),
+        {run(Module, Options, FeatureState, ScenarioState, scenario_outline, Scenario), Count + 1}
+    end, 1, Rows),
+    [Results | run_outline(Module, Options, FeatureState, ScenarioState, scenario_outline, Steps, ScenarioOutline)];
+
+run_outline(Module, Options, FeatureState, ScenarioState, PreviousPhase, [Step = #step{phase = scenario_outline} | Steps], ScenarioOutline) ->
+    run_outline(Module, Options, FeatureState, ScenarioState, PreviousPhase, Steps, [Step | ScenarioOutline]);
+
+run_outline(Module, Options, FeatureState, ScenarioState, PreviousPhase, Steps, _ScenarioOutline) ->
+    run(Module, Options, FeatureState, ScenarioState, PreviousPhase, Steps).
 
 replace_tags({Pattern, Replacement}, {Term, Subject}) ->
     {Term, replace_tags({Pattern, Replacement}, Subject)};
@@ -132,7 +155,26 @@ replace_tags({Pattern, Replacement}, Subject) when is_list(Subject) ->
 replace_tags(_, Subject) ->
     Subject.
 
-apply(Module, Function, Args) ->
+do_tags_match(Options, Step) ->
+    OptionsTags = proplists:get_value(tags, Options, []),
+    StepTags = proplists:get_value(merged_tags, Step#step.meta, []),
+    Flag = lists:foldl(
+        fun(<<$!, OptionTag/binary>>, undefined) -> not lists:member(OptionTag, StepTags);
+            (<<$+, $!, OptionTag/binary>>, undefined) -> not lists:member(OptionTag, StepTags);
+            (<<$+, OptionTag/binary>>, undefined) -> lists:member(OptionTag, StepTags);
+            (true, undefined) -> true;
+            (OptionTag, undefined) -> lists:member(OptionTag, StepTags);
+            (<<$!, OptionTag/binary>>, AccIn) -> AccIn or (not lists:member(OptionTag, StepTags));
+            (<<$+, OptionTag/binary>>, AccIn) -> AccIn and lists:member(OptionTag, StepTags);
+            (<<$+, $!, OptionTag/binary>>, AccIn) -> AccIn and (not lists:member(OptionTag, StepTags));
+            (OptionTag, AccIn) -> AccIn or lists:member(OptionTag, StepTags)
+        end, undefined, OptionsTags),
+    if
+        Flag == undefined -> true;
+        true -> Flag
+    end.
+
+apply(Module, _Options, Function, Args) ->
     case catch erlang:apply(Module, Function, Args) of
         {'EXIT', {Reason, Stack}} ->
             {error, Reason, Stack};
@@ -164,3 +206,13 @@ resolve_module(_Module, [<<"(module:", Term/binary>> | Pattern]) ->
 resolve_module(Module, [_ | Pattern]) ->
     resolve_module(Module, Pattern).
 
+skip_scenario(Module, Options, FeatureState, []) ->
+    apply(Module, Options, teardown_feature, [FeatureState]),
+    [];
+
+skip_scenario(Module, Options, FeatureState, Feature = [#step{action = start} | _Steps]) ->
+    run(Module, Options, FeatureState, undefined, undefined, Feature);
+
+skip_scenario(Module, Options, FeatureState, [_Step | Steps]) ->
+    skip_scenario(Module, Options, FeatureState, Steps).
+
diff --git a/src/gurka_eunit.erl b/src/gurka_eunit.erl
new file mode 100644
index 0000000000000000000000000000000000000000..0b405d4cf1cceb7e536e4847a995635da86e3574
--- /dev/null
+++ b/src/gurka_eunit.erl
@@ -0,0 +1,66 @@
+-module(gurka_eunit).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-export([setup/1, setup/2, setup/3]).
+
+setup(Setup) ->
+    setup(Setup, fun(_) -> ok end).
+
+setup(Setup, Cleanup) ->
+    setup(Setup, Cleanup, fun(Message) -> io:fwrite(user, "~s~n", [Message]) end).
+
+setup(Features, Cleanup, Log) when is_list(Features) ->
+    setup(fun() -> Features end, Cleanup, Log);
+
+setup(Setup, Cleanup, Log) ->
+    {setup, spawn, Setup, Cleanup, fun(Files) -> features(Log, Files) end}.
+
+features(Log, Files) ->
+    Timeout = parse_timeout(),
+    {inorder, [{timeout, Timeout, fun() -> feature(Log, File) end} || File <- Files]}.
+
+feature(Log, File) ->
+    erlang:group_leader(erlang:whereis(user), self()),
+    Tags = parse_tags(),
+    Formatter = parse_format(),
+    Options = [{file, File}, {tags, Tags}, {formatter, Formatter}, {log, Log}],
+    Result = gurka:run(File, Options),
+    Log(io_lib:format("~s", [Formatter:format(Result, Options)])),
+    case Result of
+        {ok, _} ->
+            ok;
+        {fail, _} ->
+            erlang:error({failed, File})
+    end.
+
+parse_timeout() ->
+    case os:getenv("TIMEOUT") of
+        false ->
+            60;
+        Timeout ->
+            list_to_number(Timeout)
+    end.
+
+parse_tags() ->
+    case os:getenv("TAGS") of
+        false ->
+            [];
+        Tags ->
+            [list_to_binary(Tag) || Tag <- string:tokens(Tags, ", ")]
+    end.
+
+parse_format() ->
+    case os:getenv("FORMAT") of
+        false ->
+            gurka_formatter_compact;
+        Format ->
+            list_to_atom("gurka_formatter_" ++ Format)
+    end.
+
+list_to_number(L) ->
+    try list_to_float(L)
+    catch
+        error:badarg ->
+            list_to_integer(L)
+    end.
\ No newline at end of file
diff --git a/src/gurka_formatter_compact.erl b/src/gurka_formatter_compact.erl
new file mode 100644
index 0000000000000000000000000000000000000000..8e419ee6d5094735dd3741b5684919b51d204250
--- /dev/null
+++ b/src/gurka_formatter_compact.erl
@@ -0,0 +1,27 @@
+-module(gurka_formatter_compact).
+
+-include("gurka.hrl").
+
+-export([format/2]).
+
+format({Status, Result}, Opts) ->
+    case proplists:get_value(file, Opts) of
+        undefined ->
+            format_steps(Result);
+        File when Status == ok ->
+            io_lib:format("\e[1;32m~s ~s\e[0m~n", ["PASS", File]) ++ format_steps(Result);
+        File ->
+            io_lib:format("\e[1;31m~s ~s\e[0m~n", ["FAIL", File]) ++ format_steps(Result)
+    end.
+
+format_steps([]) ->
+    [];
+
+format_steps([Step | Steps]) when is_list(Step) ->
+    format_steps(Step) ++ format_steps(Steps);
+
+format_steps([{ok, _} | Steps]) ->
+    format_steps(Steps);
+
+format_steps([{Result, #step{row = Row}} | Steps]) ->
+    io_lib:format("\e[0;31m~B: ~p\e[0m~n", [Row, Result]) ++ format_steps(Steps).
diff --git a/src/gurka_formatter_plain.erl b/src/gurka_formatter_plain.erl
index 19094d09c0eaed93c417c9db2372da046c70b453..c24d8b655e883fa527a52ea8178610410bd8d7c5 100644
--- a/src/gurka_formatter_plain.erl
+++ b/src/gurka_formatter_plain.erl
@@ -1,12 +1,30 @@
 -module(gurka_formatter_plain).
 
--export([format/1, format/2]).
-
-format(Result) ->
-    format(Result, []).
-
-format(Result, _Opts) ->
-    format_steps(Result, undefined).
+-include("gurka.hrl").
+
+-export([format/2]).
+
+format({Status, Result}, Opts) ->
+    format_header(Status, Opts) ++ format_steps(Result, undefined).
+
+format_header(Status, Opts) ->
+    H1 = case proplists:get_value(file, Opts) of
+             undefined ->
+                 [];
+             File when Status == ok ->
+                 io_lib:format("File: ~s~s~n", [string:left(File, 70, $\s), "PASS"]);
+             File ->
+                 io_lib:format("File: ~s~s~n", [string:left(File, 70, $\s), "FAIL"])
+         end,
+    H2 = case proplists:get_value(tags, Opts) of
+             undefined ->
+                 [];
+             [] ->
+                 [];
+             Tags ->
+                 io_lib:format("Tags: ~s~n", [string:join([binary_to_list(Tag) || Tag <- Tags],",")])
+         end,
+    H1 ++ H2.
 
 format_steps([], _LastAction) ->
     [];
@@ -18,16 +36,19 @@ format_steps([Step | Steps], LastAction) ->
     {FormattedStep, NewLastAction} = format_step(Step, LastAction),
     FormattedStep ++ format_steps(Steps, NewLastAction).
 
-format_step({ok, {Phase, Action, Pattern, RowNum}}, LastAction) ->
+format_step({ok, #step{phase = Phase, action = Action, tokens = Pattern, row = RowNum}}, LastAction) ->
     {format_pattern(Pattern, RowNum, format_phase(Phase, Action, LastAction)), Action};
 
-format_step({Result, {Phase, Action, Pattern, RowNum}}, LastAction) ->
+format_step({Result, #step{phase = Phase, action = Action, tokens = Pattern, row = RowNum}}, LastAction) ->
     FormattedPattern = format_pattern(Pattern, RowNum, format_phase(Phase, Action, LastAction)),
     FormattedFailure = io_lib:format("~s    FAILED: ~60p~n", [FormattedPattern, Result]),
     {FormattedFailure, Action}.
 
+format_phase(feature, skip, _LastAction) ->
+    "\nSkipped Feature: ";
+
 format_phase(feature, start, _LastAction) ->
-    "Feature: ";
+    "\nFeature: ";
 
 format_phase(feature, desc, _LastAction) ->
     "  ";
@@ -35,6 +56,9 @@ format_phase(feature, desc, _LastAction) ->
 format_phase(background, start, _LastAction) ->
     "\n  Background: ";
 
+format_phase(scenario, skip, _LastAction) ->
+    "\n  Skipped Scenario: ";
+
 format_phase(scenario, start, _LastAction) ->
     "\n  Scenario: ";
 
diff --git a/src/gurka_parser.erl b/src/gurka_parser.erl
index ceae568fbaa04a8fb3c1bab91f7a2756e1923e5d..2d0c9a4db6e3c524e15bd7940636d68391759f68 100644
--- a/src/gurka_parser.erl
+++ b/src/gurka_parser.erl
@@ -1,16 +1,8 @@
 -module(gurka_parser).
 
--export([parse/1, tokens/1]).
-
--export_type([phase/0, action/0, step/0, feature/0]).
+-include("gurka.hrl").
 
--type phase() :: feature | background | scenario | scenario_outline.
--type action() :: desc | title | given | 'when' | then | examples | headers | values.
--type pattern() :: [binary()].
--type row() :: pos_integer().
--type step() :: {phase(), action(), pattern(), row()}.
--type feature() :: [step()].
--type lines() :: [{row(), binary(), pattern()}].
+-export([parse/1, tokens/1]).
 
 -spec parse(File) -> {ok, Feature} | {error, Reason} when
     File :: file:name(),
@@ -22,7 +14,7 @@ parse(File) ->
             {_, Lines} = lists:foldl(fun(Line, {Row, AccIn}) ->
                 {Row + 1, [{Row, Line, tokens(Line)} | AccIn]}
             end, {1, []}, binary:split(Binary, [<<$\n>>, <<$\r>>], [global, trim])),
-            {ok, compact(process(undefined, undefined, lists:reverse(Lines)))};
+            {ok, inherit_tags(compact(process(undefined, undefined, lists:reverse(Lines))))};
         {error, Reason} ->
             {error, Reason}
     end.
@@ -36,54 +28,82 @@ process(_Phase, _Action, []) ->
     [];
 process(Phase, Action, [{_Row, _Line, []} | Lines]) ->
     process(Phase, Action, Lines);
+process(Phase, Action, [{Row, _Line, Tags = [<<$@, _/binary>> | _]} | Lines]) ->
+    [#step{phase = Phase, action = tags, tokens = Tags, row = Row} | process(Phase, Action, Lines)];
 process(Phase, Action, [{Row, Line, [<<"|">> | Tokens]} | Lines]) ->
     {Lines2, Table} = build_table([{Row, Line, [<<"|">> | Tokens]} | Lines], []),
-    [{Phase, table, Table, Row} | process(Phase, Action, Lines2)];
+    [#step{phase = Phase, action = table, tokens = Table, row = Row} | process(Phase, Action, Lines2)];
 process(Phase, Action, [{Row, _Line, [<<"\"\"\"">> | _Tokens]} | Lines]) ->
     {Lines2, Docstring} = build_docstring(Lines, []),
-    [{Phase, docstring, Docstring, Row} | process(Phase, Action, Lines2)];
+    [#step{phase = Phase, action = docstring, tokens = Docstring, row = Row} | process(Phase, Action, Lines2)];
 process(Phase, Action, [{Row, _Line, [FirstToken | Tokens]} | Lines]) ->
     case normalize_token(FirstToken) of
         "and" ->
-            [{Phase, Action, Tokens, Row} | process(Phase, Action, Lines)];
+            [#step{phase = Phase, action = Action, tokens = Tokens, row = Row} | process(Phase, Action, Lines)];
         "but" ->
-            [{Phase, Action, Tokens, Row} | process(Phase, Action, Lines)];
+            [#step{phase = Phase, action = Action, tokens = Tokens, row = Row} | process(Phase, Action, Lines)];
         "given" ->
-            [{Phase, given, Tokens, Row} | process(Phase, given, Lines)];
+            [#step{phase = Phase, action = given, tokens = Tokens, row = Row} | process(Phase, given, Lines)];
         "when" ->
-            [{Phase, 'when', Tokens, Row} | process(Phase, 'when', Lines)];
+            [#step{phase = Phase, action = 'when', tokens = Tokens, row = Row} | process(Phase, 'when', Lines)];
         "then" ->
-            [{Phase, then, Tokens, Row} | process(Phase, then, Lines)];
+            [#step{phase = Phase, action = then, tokens = Tokens, row = Row} | process(Phase, then, Lines)];
         "examples" ->
-            [{Phase, examples, Tokens, Row} | process(Phase, examples, Lines)];
+            [#step{phase = Phase, action = examples, tokens = Tokens, row = Row} | process(Phase, examples, Lines)];
         "feature" ->
-            [{feature, start, Tokens, Row} | process(feature, undefined, Lines)];
+            [#step{phase = feature, action = start, tokens = Tokens, row = Row} | process(feature, undefined, Lines)];
         "background" ->
-            [{background, start, Tokens, Row} | process(background, undefined, Lines)];
+            [#step{phase = background, action = start, tokens = Tokens, row = Row} | process(background, undefined, Lines)];
         "scenario" ->
             case normalize_token(hd(Tokens)) of
                 "outline" ->
-                    [{scenario_outline, start, tl(Tokens), Row} | process(scenario_outline, undefined, Lines)];
+                    [#step{phase = scenario_outline, action = start, tokens = tl(Tokens), row = Row} | process(scenario_outline, undefined, Lines)];
                 _ ->
-                    [{scenario, start, Tokens, Row} | process(scenario, undefined, Lines)]
+                    [#step{phase = scenario, action = start, tokens = Tokens, row = Row} | process(scenario, undefined, Lines)]
             end;
         _ ->
-            [{Phase, desc, [FirstToken | Tokens], Row} | process(Phase, Action, Lines)]
+            [#step{phase = Phase, action = desc, tokens = [FirstToken | Tokens], row = Row} | process(Phase, Action, Lines)]
     end.
 
-compact(Lines) ->
-    compact(lists:reverse(Lines), []).
+compact(Feature) ->
+    compact(lists:reverse(Feature), []).
 
 compact([], Acc) ->
     Acc;
-compact([{_, docstring, Docstring, _} | [{Phase, Action, Tokens, Row} | Lines]], Acc) ->
-    compact(Lines, [{Phase, Action, Tokens ++ [{docstring, Docstring}], Row} | Acc]);
-compact([{_, table, Table, _} | [{Phase, examples, _Tokens, Row} | Lines]], Acc) ->
-    compact(Lines, [{Phase, examples, Table, Row} | Acc]);
-compact([{_, table, Table, _} | [{Phase, Action, Tokens, Row} | Lines]], Acc) ->
-    compact(Lines, [{Phase, Action, Tokens ++ [{table, Table}], Row} | Acc]);
-compact([Line | Lines], Acc) ->
-    compact(Lines, [Line | Acc]).
+compact([#step{action = docstring, tokens = Docstring} | [Step | Steps]], Acc) ->
+    compact([Step#step{tokens = Step#step.tokens ++ [{docstring, Docstring}]} | Steps], Acc);
+compact([#step{action = table, tokens = Table} | [Step = #step{action = examples} | Steps]], Acc) ->
+    compact([Step#step{tokens = Table} | Steps], Acc);
+compact([#step{action = table, tokens = Table} | [Step | Steps]], Acc) ->
+    compact([Step#step{tokens = Step#step.tokens ++ [{table, Table}]} | Steps], Acc);
+compact([Step = #step{phase = Phase, action = start} | [#step{action = tags, tokens = Tags} | Steps]], Acc) when Phase == feature; Phase == scenario; Phase == scenario_outline ->
+    compact([Step#step{meta = [{tags, [Tag || <<$@, Tag/binary>> <- Tags]}]} | Steps], Acc);
+compact([Step = #step{phase = scenario_outline, action = examples} | [#step{action = tags, tokens = Tags} | Steps]], Acc) ->
+    compact([Step#step{meta = [{tags, [Tag || <<$@, Tag/binary>> <- Tags]}]} | Steps], Acc);
+compact([#step{action = tags} | Steps], Acc) ->
+    compact(Steps, Acc);
+compact([Step | Steps], Acc) ->
+    compact(Steps, [Step | Acc]).
+
+inherit_tags(Feature) ->
+    inherit_tags(Feature, {[], []}).
+
+inherit_tags([], _) ->
+    [];
+inherit_tags([Step = #step{phase = feature, action = start} | Steps], _) ->
+    FeatureTags = proplists:get_value(tags, Step#step.meta, []),
+    NewMeta = [{merged_tags, FeatureTags} | Step#step.meta],
+    [Step#step{meta = NewMeta} | inherit_tags(Steps, {FeatureTags, []})];
+inherit_tags([Step = #step{phase = Phase, action = start} | Steps], {FeatureTags, _}) when Phase == scenario; Phase == scenario_outline ->
+    ScenarioTags = proplists:get_value(tags, Step#step.meta, []),
+    NewMeta = [{merged_tags, FeatureTags ++ ScenarioTags} | Step#step.meta],
+    [Step#step{meta = NewMeta} | inherit_tags(Steps, {FeatureTags, ScenarioTags})];
+inherit_tags([Step = #step{phase = scenario_outline, action = examples} | Steps], {FeatureTags, ScenarioTags}) ->
+    ExamplesTags = proplists:get_value(tags, Step#step.meta, []),
+    NewMeta = [{merged_tags, FeatureTags ++ ScenarioTags ++ ExamplesTags} | Step#step.meta],
+    [Step#step{meta = NewMeta} | inherit_tags(Steps, {FeatureTags, ScenarioTags})];
+inherit_tags([Step | Steps], Tags) ->
+    [Step | inherit_tags(Steps, Tags)].
 
 build_table([], Table) ->
     {[], lists:reverse(Table)};
diff --git a/src/gurka_transform.erl b/src/gurka_transform.erl
index 32b3aa6aed1e6321fae82dab96d16fa39eae6db6..48c585cb9f68657d3763d94324adede1d56618ad 100644
--- a/src/gurka_transform.erl
+++ b/src/gurka_transform.erl
@@ -43,7 +43,9 @@ clause(Clause) ->
 process_string(Row, String) ->
     Tokens = gurka_parser:tokens(list_to_binary(String)),
     Pattern = build_pattern(Row, Tokens),
-    Pattern.
+    {match, Row,
+        {var, Row, 'Tokens'},
+        Pattern}.
 
 build_pattern(Row, []) ->
     {nil, Row};
diff --git a/test/feature_parser.erl b/test/feature_parser.erl
index 86a7e961451acf36fa48e96600463ee562c35646..5758095d04a9031830796b6517251955ed3e1024 100644
--- a/test/feature_parser.erl
+++ b/test/feature_parser.erl
@@ -1,6 +1,8 @@
 -compile({parse_transform, gurka_transform}).
 -module(feature_parser).
 
+-include("gurka.hrl").
+
 -export([setup_feature/1, setup_scenario/2, teardown_feature/1, teardown_scenario/1, given/2, 'when'/2, then/2]).
 
 -record(state, {feature_dir, feature}).
@@ -17,6 +19,7 @@ setup_scenario(_Tokens, State) ->
 teardown_scenario(_State) ->
     ok.
 
+%% noinspection ErlangUnboundVariable
 given("feature files are loaded from $Directory", State) ->
     case filelib:is_dir(Directory) of
         true -> {ok, State#state{feature_dir = Directory}};
@@ -35,15 +38,17 @@ given(Tokens, State) ->
     io:format("given ~p~n", [Tokens]),
     {ok, State}.
 
+%% noinspection ErlangUnboundVariable
 'when'(Tokens, State) ->
     io:format("when ~p~n", [Tokens]),
     {ok, State}.
 
+%% noinspection ErlangUnboundVariable
 then("the parsed result should have $StepCount $StepType steps", State) ->
     ExpectedCount = list_to_integer(binary_to_list(StepCount)),
     Type = binary_to_atom(StepType, utf8),
     RealCount = lists:foldl(
-        fun({_, Action, _, _}, Sum) when Action == Type -> Sum + 1;
+        fun(Step, Sum) when Step#step.action == Type -> Sum + 1;
             (_, Sum) -> Sum
         end, 0, State#state.feature),
     if
diff --git a/test/features.erl b/test/features.erl
index 689cbe2c469568700813491324d57314eb390d14..96ee999d58976f0bea296bd3a455832e388d8ae0 100644
--- a/test/features.erl
+++ b/test/features.erl
@@ -3,29 +3,8 @@
 -include_lib("eunit/include/eunit.hrl").
 
 gurka_test_() ->
-    {setup,
-        spawn,
-        fun setup/0,
-        fun teardown/1,
-        fun features/1}.
-
-setup() ->
-    file:set_cwd(".."),
-    filelib:fold_files("features", ".*[.]feature", true, fun(File, Files) -> [File | Files] end, []).
-
-teardown(_Files) ->
-    ok.
-
-features(Files) ->
-    {inorder, [{timeout, 60, ?_test(feature(File))} || File <- Files]}.
-
-feature(File) ->
-    erlang:group_leader(erlang:whereis(user), self()),
-    case gurka:run(File) of
-        {ok, Result} ->
-            io:format("File: ~sOK~n~s~n", [string:left(File, 72, $\s), gurka_formatter_plain:format(Result)]);
-        {fail, Result} ->
-            io:format("File: ~sFAIL~n~s~n", [string:left(File, 70, $\s), gurka_formatter_plain:format(Result)]),
-            erlang:error({failed, File})
-    end.
-
+    Features = fun() ->
+        file:set_cwd(".."),
+        filelib:fold_files("features", ".*[.]feature", true, fun(File, Files) -> [File | Files] end, [])
+    end,
+    gurka_eunit:setup(Features).