Skip to content

Commit ed69115

Browse files
committedMay 16, 2015
update to version 0.5.0 which contains a complete re-write of the compiler by leveraging many new features available with the meta-class 0.3.0 version. unlike the prior yang-compiler version which only essentially generated meta data without actual instantiation capabilities, the latest version now uses those and shifted the compiler runspace from class object into class instance based operation. the new changes also include clean separation of compile memory context so that there is no longer bleed-over across compile executions. new features introduced to support import/export facilities and introduction of customizable module source map, resolvers, importers, etc. Also, the compiler itself is configuration managed according to the yang-compiler.yang schema itself. Many new features and much much cleaner version
1 parent c7601b0 commit ed69115

9 files changed

+975
-580
lines changed
 

‎lib/yang-compiler.js

+316-113
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎lib/yang-meta-compiler.js

+207-139
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎old.coffee

-79
This file was deleted.

‎package.json

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"name": "yang-compiler",
3-
"version": "0.4.2",
4-
"description": "A YANG module/compiler which parses text/json based input YANG schema into JS class hierarchy",
3+
"version": "0.5.0",
4+
"description": "A YANG module/compiler which parses text/json based input YANG schema into JS class hierarchy (among other things)",
55
"author": "Peter K Lee <peter@intercloud.net>",
6-
"main": "lib/yang-v1-compiler.js",
6+
"main": "lib/yang-compiler.js",
77
"scripts": {
88
"test": "mocha test",
99
"prepublish": "mkdir -p lib; coffee -o lib -c src"
@@ -20,7 +20,7 @@
2020
"stormstack",
2121
"data-storm",
2222
"yang-storm",
23-
"yang-compiler",
23+
"yang-compiler",
2424
"datastore",
2525
"datamodel",
2626
"model",
@@ -34,7 +34,8 @@
3434
},
3535
"homepage": "https://github.com/stormstack/yang-compiler",
3636
"dependencies": {
37-
"meta-class": "^0.2.0",
37+
"meta-class": "^0.3.0",
38+
"tosource": "^0.1.3",
3839
"yang-parser": "^0.1.0"
3940
}
4041
}

‎src/yang-compiler.litcoffee

+232-127
Large diffs are not rendered by default.

‎src/yang-meta-compiler.litcoffee

+159-103
Original file line numberDiff line numberDiff line change
@@ -8,141 +8,197 @@ The meta compiler only supports bare minium set of YANG statements and
88
should be used only to generate a new compiler such as 'yang-compiler'
99
which implements the version 1.0 of the YANG language specifications.
1010

11+
Meta = require 'meta-class'
12+
1113
class Extension
12-
constructor: (@name, @params) -> this
13-
14-
#@set resolver: undefined #(arg, params) -> o = {}; o[arg] = params; o
15-
refine: (params={}) -> @merge params
16-
resolve: (target, context) ->
17-
resolver = @resolver
18-
return target unless resolver instanceof Function
14+
constructor: (@params={}) -> this
15+
refine: (params={}) -> Meta.copy @params, params
16+
resolve: (target, compiler) ->
17+
# TODO should ignore 'date'
18+
# Also this bypass logic should also be based on whether sub-statements are allowed or not
19+
switch @params.argument
20+
when 'value','text','date'
21+
return name: (target.get 'yang'), value: (target.get 'name')
22+
23+
unless @params.resolver?
24+
throw new Error "no resolver found for '#{target.get 'yang'}' extension", target
25+
26+
arg = (target.get 'name').replace ':','.'
27+
# do something if arg has prefix:something
28+
1929
2030
# TODO: qualify some internal meta params against passed-in target...
21-
context ?= target
2231
params = {}
23-
(target.get 'children').forEach (e) -> params[e.get 'name'] = e
24-
resolver.call context, (target.get 'name'), params, target
32+
(target.get 'children')?.forEach (e) ->
33+
switch
34+
when (Meta.instanceof e) and (e.get 'children').length is 0
35+
params[e.get 'yang'] = e.get 'name'
36+
when e?.constructor is Object
37+
params[e.name] = e.value
38+
@params.resolver?.call? compiler, target, arg, params
2539
2640
First we declare the compiler class as an extension of the
2741
`meta-class`. For details on `meta-class` please refer to
2842
http://github.com/stormstack/meta-class
2943

30-
The primary function of the compiler is to `define` new language
31-
extension statements and to `resolve` the extensions with custom
32-
resolvers. The `compile` operation uses these definitions to produce
33-
the desired output.
44+
class YangMetaCompiler extends Meta
3445
35-
class YangMetaCompiler extends (require 'meta-class')
36-
@set map: {}, extension: {}, module: {}
46+
assert = require 'assert'
3747
38-
@map = (obj) -> @merge 'map', obj
39-
40-
@extensions = (obj) ->
41-
(@get "extension.#{name}")?.resolver = resolver for name, resolver of obj
48+
@set extensions:
49+
50+
We configure meta data of the compiler to initialize the built-in
51+
supported language extensions, first of which is the 'extension'
52+
statement itself. This allows any `extension` statement found in the
53+
input schema to define a new `Extension` object for handling the
54+
extension by the compiler.
55+
56+
extension: new Extension
57+
argument: 'extension-name'
58+
'sub:description': '0..1'
59+
'sub:reference': '0..1'
60+
'sub:status': '0..1'
61+
'sub:sub': '0..n'
62+
resolver: (self, arg, params) ->
63+
ext = @get "extensions.#{arg}"
64+
unless ext?
65+
params.resolver ?= @get "resolvers.#{arg}"
66+
ext = new Extension params
67+
@set "extensions.#{arg}", ext
68+
else
69+
ext.refine params
70+
ext
71+
72+
The following set of built-in Extensions are statements used in
73+
defining the extension itself.
74+
75+
argument: new Extension
76+
'sub:yin-element': '0..1'
77+
resolver: (self, arg) -> name: 'argument', value: arg
78+
79+
'yin-element': new Extension argument: 'value'
4280
43-
@define: (name, params={}) ->
44-
extension = @get "extension.#{name}"
45-
unless extension?
46-
extension = new Extension name, params
47-
@set "extension.#{name}", extension
48-
#extension.refine params
49-
extension
50-
51-
@resolver: (name, resolver) ->
52-
[ prefix..., name ] = (name.split ':' ) if typeof name is 'string'
53-
m = this
54-
m = m.get "module.#{prf}" for prf in prefix if prefix?
55-
extension = m.get "extension.#{name}"
56-
if resolver instanceof Function
57-
extension?.set? 'resolver', resolver
58-
extension ?= Extension
59-
extension
60-
61-
@find: (type, name) ->
62-
[ prefix..., name ] = (name.split ':' ) if typeof name is 'string'
63-
m = this
64-
m = m.get "module.#{prf}" for prf in prefix if prefix?
65-
m.get "#{type}.#{name}"
66-
67-
We utilize the above define/resolver mechanisms to initialize the
68-
built-in supported language extensions, first of all which is the
69-
'extension' statement itself. This allows any `extension` statement
70-
found in the input schema to define a new `Extension` object for
71-
handling the extension.
72-
73-
@define 'extension',
74-
argument: 'extension-name'
75-
description: '0..1'
76-
reference: '0..1'
77-
status: '0..1'
78-
sub: '0..n' # non YANG 1.0 compliant
79-
resolver: (name, params) -> @define name, params
80-
81-
@define 'argument', 'yin-element': '0..1'
81+
value: new Extension resolver: (self, arg) -> name: 'value', value: arg
82+
sub: new Extension resolver: (self, arg, params) -> name: "sub:#{arg}", value: params
83+
prefix: new Extension argument: 'value'
8284
8385
The `include` extension is also a built-in to the `yang-meta-compiler`
84-
and invoked during `preprocess` operation to pull-in the included
86+
and invoked during `compile` operation to pull-in the included
8587
submodule schema as part of the preprocessing output. It always
8688
expects a local file source which differs from more robust `import`
8789
extension. The `yang-meta-compiler` does not natively provide any
8890
`import` facilities.
8991

90-
@define 'include',
91-
argument: 'module'
92-
resolver: (name, params) ->
93-
source = @get "map.#{name}"
94-
return unless typeof source is 'string'
95-
submodule = @compile ->
96-
path = require 'path'
97-
file = path.resolve (path.dirname module.parent?.filename), source
98-
console.log "INFO: including '#{name}' using #{file}"
99-
(require 'fs').readFileSync file, 'utf-8'
100-
@merge 'extension', submodule.get 'extension'
101-
submodule
102-
103-
## compiling a new module given input
92+
include: new Extension
93+
argument: 'name'
94+
resolver: (self, arg, params) ->
95+
source = @get "map.#{arg}"
96+
assert typeof source is 'string', "unable to include '#{arg}' without mapping defined for source"
97+
@compile ->
98+
path = require 'path'
99+
file = path.resolve (path.dirname module.parent?.filename), source
100+
console.log "INFO: including '#{arg}' using #{file}"
101+
(require 'fs').readFileSync file, 'utf-8'
102+
103+
One of the key function of the compiler is to `resolve` language
104+
extension statements with custom resolvers given the meta class input.
105+
The `compile` operation uses the `resolve` definitions to produce the
106+
desired output.
107+
108+
resolver: (meta, context) ->
109+
yang = meta.get 'yang'
110+
[ prefix..., yang ] = (yang.split ':' ) if typeof yang is 'string'
111+
ext = switch
112+
when prefix.length > 0 then (@get "#{prefix[0]}")?.get "extensions.#{yang}"
113+
else @get "extensions.#{yang}"
114+
115+
try ext.resolve meta, this
116+
catch err
117+
@errors ?= []
118+
@errors.push
119+
yang: yang
120+
error: err
121+
undefined
122+
123+
The below `assembler` performs the task of combining the source object
124+
into the destination object by creating a binding between the two.
125+
This allows the source object to be auto constructed when the
126+
destination object is created. This is a helper routine used during
127+
compilation as part of reduce traversal.
128+
129+
assembler: (dest, src) ->
130+
objs = switch
131+
when src.collapse is true
132+
name: k, value: v for k, v of (src.get 'bindings')
133+
when (Meta.instanceof src)
134+
name: @normalizeKey src
135+
value: src
136+
when src.constructor is Object
137+
src
138+
objs = [ objs ] unless objs instanceof Array
139+
Meta.bind.apply dest, objs
140+
141+
normalizeKey: (meta) ->
142+
([ (meta.get 'yang'), (meta.get 'name') ].filter (e) -> e? and !!e).join '.'
143+
144+
The `parse` function performs recursive parsing of passed in statement
145+
and sub-statements and usually invoked in the context of the
146+
originating `compile` function below. It expects the `statement` as
147+
an Object containing prf, kw, arg, and any substmts as an array.
104148

105-
The internal/private `compileStatement` function performs recursive compilation of
106-
passed in statement and sub-statements and invoked within the cotext
107-
of the originating `compile` function above. It expects the
108-
`statement` as an Object containing prf, kw, arg, and any substmts as
109-
an array.
149+
parse: (schema, parser=(require 'yang-parser')) ->
150+
if typeof schema is 'string'
151+
return @parse (parser.parse schema)
110152
111-
compileStatement = (statement) ->
112-
return unless statement? and statement instanceof Object
153+
return unless schema? and schema instanceof Object
113154
114-
normalize = (statement) -> ([ statement.prf, statement.kw ].filter (e) -> e? and !!e).join ':'
155+
statement = schema
156+
normalize = (obj) -> ([ obj.prf, obj.kw ].filter (e) -> e? and !!e).join ':'
115157
keyword = normalize statement
116158
117-
results = (compileStatement stmt for stmt in statement.substmts).filter (e) -> e?
159+
results = (@parse stmt for stmt in statement.substmts).filter (e) -> e?
118160
class extends (require 'meta-class')
119-
@set yang: keyword, name: statement.arg, children: results
161+
@set yang: keyword, name: statement.arg, children: results
120162
121163
The `compile` function is the primary method of the compiler which
122164
takes in YANG text schema input and produces JS output representing
123165
the input schema as meta data hierarchy.
124166

125-
It accepts various forms of input: a text string, a function, and a
126-
meta class object.
167+
It accepts two forms of input: a YANG schema text string or a function
168+
that will return a YANG schema text string.
127169

128-
@compile: (input, parser=(require 'yang-parser')) ->
129-
return unless input?
130-
131-
console.log "INFO: compiling a new module using extensions..."
132-
compiler = this
133-
input = (input.call this) ? input if input instanceof Function
134-
input = compileStatement (parser.parse schema=input) if typeof input is 'string'
135-
input.set "schema.#{input.get 'name'}", schema if schema?
136-
137-
The input module is then traversed to resolve the **currently known**
138-
extensions for this compiler.
170+
compile: (schema) ->
171+
schema = (schema.call this) if schema instanceof Function
172+
return unless typeof schema is 'string'
139173
140-
input.traverse (parent, root) ->
141-
console.log @get 'yang'
142-
(compiler.find 'extension', (@get 'yang'))?.resolve this, root
174+
The `fork` operation below performs the actual compile logic within
175+
the context of a **child** compiler instance, which is discarded
176+
unless it is returned as an output of the `fork` operation. This is
177+
to ensure that any `get/set` operations do not impact the primary
178+
compiler instance.
143179

144-
Here we return the new `YangMetaCompiler` class for import and use by other
145-
modules.
180+
@fork ->
181+
@set 'extensions', @constructor.get 'extensions'
182+
183+
# refine existing resolvers if new ones supplied during instantiation
184+
for name, ext of (@get 'extensions') when (@get "resolvers.#{name}") instanceof Function
185+
ext.refine resolver: @get "resolvers.#{name}"
186+
187+
output =
188+
@parse schema
189+
.map => @resolver.apply this, arguments
190+
.reduce => @assembler.apply this, arguments
191+
192+
if @errors?
193+
console.log "WARN: the following errors were encountered by the compiler"
194+
console.log @errors
195+
196+
self = this
197+
output.configure ->
198+
@set "schema", schema
199+
@merge self.extract 'map', 'extensions', 'exports'
146200
147-
module.exports = YangMetaCompiler
201+
Here we return the new `YangMetaCompiler` class for import and use by
202+
other modules.
148203

204+
module.exports = YangMetaCompiler

‎yang-compiler.yang

+53-7
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,76 @@ module yang-compiler {
2121
}
2222

2323
grouping compiler-rules {
24-
list module-map {
24+
description
25+
"The compiler-rules specify parameters used during compile operation.";
26+
27+
list map {
28+
description
29+
"The map defines a list of modules and their source description. It is
30+
referenced by the compiler to locate the module for retrieval
31+
during include/import operation.";
32+
2533
key name;
2634
leaf name { type string; }
2735
leaf source { type string; }
2836
}
2937

3038
list importers {
3139
leaf regexp { type regexp; }
32-
leaf handler { type function; }
40+
leaf importer { type binary; }
3341
ordered-by user;
3442
}
43+
44+
list resolvers {
45+
leaf name { type string; mandatory true; }
46+
leaf resolver { type binary; mandatory true; }
47+
}
3548
}
3649

37-
uses compiler-rules;
38-
39-
rpc compile {
40-
input {
41-
leaf schema { type string; mandatory true; }
50+
grouping compiler-state-info {
51+
description
52+
"The compiler-state-info describes current compiler internal state.";
53+
54+
list extensions {
55+
config false;
56+
key name;
57+
leaf name { type string; }
58+
leaf extension { type instance-identifier; }
4259
}
60+
61+
list modules {
62+
config false;
63+
key name;
64+
leaf name { type string; }
65+
leaf module { type instance-identifier; }
66+
}
67+
}
68+
69+
grouping meta-module {
70+
description
71+
"The meta-module describes configuration structure for import/export operations.";
72+
73+
leaf name { type string; mandatory true; }
74+
leaf source { type uri; }
75+
leaf schema { type string; }
76+
77+
uses compiler-rules;
4378
}
4479

80+
uses compiler-rules;
81+
uses compiler-state-info;
82+
4583
rpc import {
84+
input { uses meta-module; }
85+
output {
86+
leaf message { type string; }
87+
}
88+
}
89+
rpc export {
4690
input {
4791
leaf name { type string; mandatory true; }
92+
leaf format { type string; default json; }
4893
}
94+
output { uses meta-module; }
4995
}
5096
}

‎yang-meta-extensions.yang

-5
This file was deleted.

‎yang-v1-extensions.yang

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
submodule yang-v1-extensions {
2-
belongs-to yang-compiler { prefix yc; }
32

43
extension anyxml { } // undefined
54
extension augment {
@@ -391,6 +390,7 @@ submodule yang-v1-extensions {
391390
extension when { argument condition; }
392391
extension yang-version { argument value; }
393392
extension yin-element { argument value; }
394-
393+
394+
belongs-to yang-compiler { prefix yc; }
395395
yang-version 1;
396396
}

0 commit comments

Comments
 (0)
Please sign in to comment.