@@ -8,141 +8,197 @@ The meta compiler only supports bare minium set of YANG statements and
8
8
should be used only to generate a new compiler such as 'yang-compiler'
9
9
which implements the version 1.0 of the YANG language specifications.
10
10
11
+ Meta = require ' meta-class'
12
+
11
13
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
+
19
29
20
30
# TODO: qualify some internal meta params against passed-in target...
21
- context ?= target
22
31
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
25
39
26
40
First we declare the compiler class as an extension of the
27
41
`meta-class` . For details on `meta-class` please refer to
28
42
http://github.com/stormstack/meta-class
29
43
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
34
45
35
- class YangMetaCompiler extends (require ' meta-class' )
36
- @ set map : {}, extension : {}, module : {}
46
+ assert = require ' assert'
37
47
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'
42
80
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'
82
84
83
85
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
85
87
submodule schema as part of the preprocessing output. It always
86
88
expects a local file source which differs from more robust `import`
87
89
extension. The `yang-meta-compiler` does not natively provide any
88
90
`import` facilities.
89
91
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.
104
148
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)
110
152
111
- compileStatement = (statement ) ->
112
- return unless statement? and statement instanceof Object
153
+ return unless schema? and schema instanceof Object
113
154
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 ' :'
115
157
keyword = normalize statement
116
158
117
- results = (compileStatement stmt for stmt in statement .substmts ).filter (e) -> e?
159
+ results = (@ parse stmt for stmt in statement .substmts ).filter (e) -> e?
118
160
class extends (require ' meta-class' )
119
- @ set yang : keyword, name : statement .arg , children : results
161
+ @ set yang : keyword, name : statement .arg , children : results
120
162
121
163
The `compile` function is the primary method of the compiler which
122
164
takes in YANG text schema input and produces JS output representing
123
165
the input schema as meta data hierarchy.
124
166
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 .
127
169
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'
139
173
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.
143
179
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'
146
200
147
- module .exports = YangMetaCompiler
201
+ Here we return the new `YangMetaCompiler` class for import and use by
202
+ other modules.
148
203
204
+ module .exports = YangMetaCompiler
0 commit comments