Skip to content

Commit

Permalink
added --conformance flag (implement|import)
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Björklund committed Apr 16, 2018
1 parent 0c9dbc9 commit 66c7a15
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 26 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ dialyzer:

# requires 'lux' in the PATH (https://github.com/hawk/lux)
# the tree test requires 'pyang' (https://github.com/mbj4668/pyang)
# checked out next to 'yanger'
# checked out next to 'yanger' and in the PATH
test:
(cd test; $(MAKE))
.PHONY: test
4 changes: 4 additions & 0 deletions include/yang.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,12 @@
%% from the module and all submodules
children = [] :: [#sn{}],
ignored = [] :: [#sn{}],
conformance = 'implement' :: yang:conformance(),
%% from the module and all submodules
remote_augments = [] :: [{ModuleName :: atom(), [#augment{}]}],
local_augments = [] :: [#augment{}],
local_deviations = [] :: [#deviation{}],
remote_deviations = [] :: [#deviation{}],
deviated_by = [] :: [yang:modrev()],
stmt :: yang:stmt(), %% pointer to raw statement
add_cause :: 'primary' | 'import' | 'include' | 'deviation' |
Expand Down Expand Up @@ -494,6 +496,8 @@
features :: 'undefined'
| 'none' % 'none' means compile for no features at all
| yang:map(ModName :: atom(), [FeatureName :: atom()]),
conformances = [] :: [yang:conformance() |
{ModName :: atom(), yang:conformance()}],
typemap :: yang:map(yang:builtin_type_name()
| {ModuleName :: atom(), TypeName :: atom()},
#type{}),
Expand Down
77 changes: 52 additions & 25 deletions src/yang.erl
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
revision/0, stmt/0,
grouping_rec/0, typedef_rec/0, augment_rec/0,
validate_status/0, yang_identifier/0, yang_status/0,
import/0, yang_version/0, modrev/0]).
import/0, yang_version/0, modrev/0, conformance/0]).

-type yang_version() :: '1' | '1.1'.

Expand Down Expand Up @@ -160,6 +160,7 @@

-type yang_status() :: 'current' | 'deprecated' | 'obsolete'.

-type conformance() :: 'implement' | 'import'.

-type map(Key, Val) :: gb_trees:tree(Key, Val).
-type map0() :: map(term(), term()).
Expand Down Expand Up @@ -923,7 +924,9 @@ parse_body(Stmts, M0, Ctx0) ->
_Mode = final, _Ancestors = [M2],
_Acc = SubChildren, []),
{ModKW, ModArg, ModPos, ModSubs} = M2#module.stmt,
M3 = M2#module{stmt = {ModKW, ModArg, ModPos, ModSubs ++ XAcc}},
Conformance = get_conformance(M2#module.modulename, Ctx3),
M3 = M2#module{stmt = {ModKW, ModArg, ModPos, ModSubs ++ XAcc},
conformance = Conformance},
%% Build the augments list.
{Augments, Ctx4} =
mk_augments(Stmts, M3#module.typedefs, M2#module.groupings, M3, Ctx3,
Expand Down Expand Up @@ -957,45 +960,69 @@ parse_body(Stmts, M0, Ctx0) ->
{DeviatedChildren, Ignored, Ctx9} =
deviate_children(SubModuleDeviations ++ LocalDeviations,
AugmentedChildren, [], [M3], Ctx8),
M4 = if Ctx9#yctx.apply_deviations ->
M3#module{children = DeviatedChildren,
M4 = M3#module{local_deviations = LocalDeviations,
remote_deviations = RemoteDeviations},
M5 = if Conformance == import ->
M4#module{children = [],
ignored = Ignored ++ DeviatedChildren,
local_augments = []};
Ctx9#yctx.apply_deviations ->
M4#module{children = DeviatedChildren,
ignored = Ignored,
local_augments = LocalAugments,
local_deviations = LocalDeviations};
local_augments = LocalAugments};
true ->
M3#module{children = AugmentedChildren,
local_augments = LocalAugments,
local_deviations = LocalDeviations}
M4#module{children = AugmentedChildren,
local_augments = LocalAugments}
end,
%% Apply all remote augments
{Ctx10, RemoteAugments1} =
apply_remote_augments(RemoteAugments0, M4, Ctx9, []),
RemoteAugments2 =
lists:usort(RemoteAugments1 ++
lists:append([SM#module.remote_augments ||
{SM, _Pos} <- M4#module.submodules])),
M5 = M4#module{remote_augments = RemoteAugments2},
{Ctx11, M6} =
if Conformance == import ->
{Ctx9, M5#module{remote_augments = []}};
true ->
{Ctx10, RemoteAugments1} =
apply_remote_augments(RemoteAugments0, M5, Ctx9, []),
RemoteAugments2 =
lists:usort(
RemoteAugments1 ++
lists:append(
[SM#module.remote_augments ||
{SM, _Pos} <- M5#module.submodules])),
{Ctx10, M5#module{remote_augments = RemoteAugments2}}
end,
%% Update the module in the context before applying remote deviations,
%% to allow the deviations to reference definitions from this module
ModRevs = map_update({M5#module.name, M5#module.revision}, M5,
Ctx10#yctx.modrevs),
Ctx11 = Ctx10#yctx{modrevs = ModRevs},
ModRevs = map_update({M6#module.name, M6#module.revision}, M6,
Ctx11#yctx.modrevs),
Ctx12 = Ctx11#yctx{modrevs = ModRevs},
%% Apply all remote deviations - after remote augments
Ctx12 = apply_remote_deviations(RemoteDeviations, M5, Ctx11),
Ctx13 = apply_remote_deviations(RemoteDeviations, M6, Ctx12),
%% Imports that are in #yctx.unused_imports but not in
%% PostDeviationsUnused have been used only for deviations
{_, MyPostDeviationsUnused} =
lists:keyfind(M5#module.name, 1, PostDeviationsUnused),
lists:keyfind(M6#module.name, 1, PostDeviationsUnused),
{value, {ModuleName, MyUnused}, RemUnused} =
lists:keytake(M5#module.name, 1, Ctx12#yctx.unused_imports),
lists:keytake(M6#module.name, 1, Ctx13#yctx.unused_imports),
MyDeviationsOnly = MyUnused -- MyPostDeviationsUnused,
Unused = [{ModuleName, MyUnused -- MyDeviationsOnly} | RemUnused],
DeviationImports =
[{ModuleName, MyDeviationsOnly} | Ctx12#yctx.deviation_imports],
Ctx13 = Ctx12#yctx{unused_imports = Unused,
[{ModuleName, MyDeviationsOnly} | Ctx13#yctx.deviation_imports],
Ctx14 = Ctx13#yctx{unused_imports = Unused,
deviation_imports = DeviationImports,
hooks = OrigHooks},
{Ctx13, M5}.
{Ctx14, M6}.

get_conformance(ModName, #yctx{conformances = ConfL}) ->
case lists:keyfind(ModName, 1, ConfL) of
{_ModName, Conformance} ->
Conformance;
_ ->
case lists:member(import, ConfL) of
true ->
import;
false ->
implement
end
end.

-spec get_imported_module(ModuleName :: atom(), #module{}, #yctx{}) ->
{value, #module{}} | none.
Expand Down
26 changes: 26 additions & 0 deletions src/yanger.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
transform = [] :: [yanger_plugin:transform_fun()],
deviations = [],
features = [],
conformances = [],
outfile,
no_deviation_apply = false,
debug_print = false
Expand Down Expand Up @@ -130,6 +131,7 @@ convert_options(Ctx1, Options) ->
Opts#opts.errors}},
Ctx4 = Ctx3#yctx{canonical = Opts#opts.canonical,
strict = Opts#opts.strict,
conformances = Opts#opts.conformances,
apply_deviations = not Opts#opts.no_deviation_apply},
Ctx5 = yang:init_ctx(Ctx4, Opts#opts.path),
FeaturesMap =
Expand Down Expand Up @@ -195,6 +197,9 @@ option_specs(Ctx) ->
{features, $F, "features", string,
"Features to support, default all."
" Format: <modname>:[<feature>,]*"},
{conformance, $C, "conformance", string,
"Conformance, default implement."
" Format: [<modname>:]implement|import"},
{outfile, $o, "output", string,
"Write the output to OUTFILE instead of stdout."},
{no_deviation_apply, undefined, "no-deviation-apply", undefined,
Expand Down Expand Up @@ -274,6 +279,27 @@ opts(Options, Ctx) ->
_ ->
throw({error, {bad_features, FStr}})
end;
{conformance, CStr} ->
case string:tokens(CStr, ":") of
["import"] ->
Opts#opts{conformances =
[import |
Opts#opts.conformances]};
["implement"] ->
Opts#opts{conformances =
[implement |
Opts#opts.conformances]};
[ModName, "import"] ->
Opts#opts{conformances =
[{?l2a(ModName), import} |
Opts#opts.conformances]};
[ModName, "implement"] ->
Opts#opts{conformances =
[{?l2a(ModName), implement} |
Opts#opts.conformances]};
_ ->
throw({error, {bad_conformance, CStr}})
end;
{outfile, F} ->
Opts#opts{outfile = F};
no_deviation_apply ->
Expand Down
8 changes: 8 additions & 0 deletions test/lux/conformance/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
include ../../support/*_testcases.mk

build:

clean:
rm -rf lux_logs _tmp_*

.PHONY: build clean
11 changes: 11 additions & 0 deletions test/lux/conformance/a.yang
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module a {
namespace urn:a;
prefix a;

container a;

augment /a {
container x;
}

}
12 changes: 12 additions & 0 deletions test/lux/conformance/b.yang
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module b {
namespace urn:b;
prefix b;

import a {
prefix a;
}

augment /a:a {
container b;
}
}
35 changes: 35 additions & 0 deletions test/lux/conformance/run.lux
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[doc Test --conformance flag]

[shell compilation]
-internal|module
!yanger -f tree -C a:import -C b:import a.yang b.yang
?SH-PROMPT
!yanger -f tree -C import a.yang b.yang
?SH-PROMPT
-internal
!yanger -f tree -C implement a.yang b.yang
"""??
module: a
+--rw a
+--rw x
+--rw b:b
SH-PROMPT
"""
-internal|b:b
!yanger -f tree -C a:implement -C b:import a.yang b.yang
"""??
module: a
+--rw a
+--rw x
SH-PROMPT
"""
!yanger -f tree -C b:import a.yang b.yang
"""??
module: a
+--rw a
+--rw x
SH-PROMPT
"""
-internal
!yanger --print-error-code -C a:import a.yang b.yang
?YANG_ERR_NODE_NOT_FOUND

0 comments on commit 66c7a15

Please sign in to comment.