-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathexpression.coffee
More file actions
122 lines (101 loc) · 3.25 KB
/
Copy pathexpression.coffee
File metadata and controls
122 lines (101 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# expression - evaluable Element
delegate = require 'delegates'
Element = require './element'
class Expression extends Element
logger: require('debug')('yang:expression')
#
# Source delegation
#
delegate @prototype, 'source'
.getter 'scope'
.getter 'resolve'
.getter 'transform'
.getter 'construct'
.getter 'predicate'
.getter 'compose'
delegate @prototype, 'state'
.access 'binding'
.access 'resolved'
@property 'argument',
get: -> @state.argument ? @source.argument
set: (value) -> @state.argument = value
@property 'exprs',
get: -> @children.filter (x) -> x instanceof Expression
@property 'nodes',
get: -> @exprs.filter (x) -> x.node is true
@property 'attrs',
get: -> @exprs.filter (x) -> x.node is false
@property 'node',
get: -> @construct instanceof Function
@property 'data',
get: -> Boolean @source.data
@property '*', get: -> @nodes
#debug: (f) -> if debug.enabled logger.namespace then logger @uri, [].concat(f())...
constructor: (kind, tag, source) ->
super kind, tag
@source = source
evaluate = (-> self.eval arguments...)
self = Object.setPrototypeOf evaluate, this
Object.defineProperties self,
inspect: value: -> @toJSON()
delete self.length # TODO: this may not work for Edge browser...
return self
clone: ->
copy = super arguments...
copy.convert = @convert if @convert?
return copy
compile: ->
@debug => "[compile] enter... (#{@resolved})"
@emit 'compiling', arguments
@resolve?.apply this, arguments unless @resolved
if @tag? and not @argument
throw @error "cannot contain argument '#{@tag}' for expression '#{@kind}'"
if @argument and not @tag?
throw @error "must contain argument '#{@argument}' for expression '#{@kind}'"
(@debug => "has sub-expressions: #{@exprs.map (x) -> x.kind}") if @exprs.length
@exprs.forEach (x) -> x.compile()
@resolved = true
@emit 'compiled'
@debug => "[compile] done"
return this
bind: (data) ->
if data?
@debug => "[bind] registering #{typeof data} binding"
@binding = data
@emit 'bind', data
else # allows unbinding...
@debug => "[bind] removing prior #{typeof @binding} binding"
@binding = undefined
return this
# internally used to apply the expression to the passed in data
apply: (data, ctx, opts) ->
@compile() unless @resolved
@emit 'transforming', data
if @transform instanceof Function
data = @transform.call this, data, ctx, opts
else
data = expr.eval data, ctx, opts for expr in @exprs when data?
try @predicate?.call this, data, opts
catch e
@debug => data
throw @error "predicate validation error: #{e}", data
@emit 'transformed', data
return data
# evalute the provided data
eval: (data, ctx, opts) ->
@compile() unless @resolved
if @construct instanceof Function
data ?= {}
@construct.call this, data, ctx, opts
else
@apply data, ctx, opts
update: (elem) ->
res = super arguments...
res.binding = elem.binding
return res
error: ->
res = super arguments...
res.message = "[#{@uri}] #{res.message}"
res.name = 'ExpressionError'
return res
module.exports = Expression