1 | var _ = require('underscore');
|
2 | var asyncControl = require('../asyncControl')
|
3 |
|
4 | module.exports = function (listOfTerminals) {
|
5 | var terms = this;
|
6 | return terms.oldTerm(function () {
|
7 | this.isComplexExpression = true;
|
8 | this.basicExpressions = _(listOfTerminals).map(function (terminals) {
|
9 | return terms.basicExpression(terminals);
|
10 | });
|
11 |
|
12 | this.subterms('basicExpressions');
|
13 |
|
14 | this.head = function () {
|
15 | return this._firstExpression || (this._firstExpression = this.basicExpressions[0]);
|
16 | };
|
17 |
|
18 | this.tail = function () {
|
19 | return this._tail || (this._tail = this.basicExpressions.slice(1));
|
20 | };
|
21 |
|
22 | this.hasTail = function () {
|
23 | return this.tail().length > 0;
|
24 | };
|
25 |
|
26 | this.isAsyncCall = function () {
|
27 | return this.head().hasAsyncArgument();
|
28 | };
|
29 |
|
30 | this.isFutureCall = function () {
|
31 | return this.head().hasFutureArgument();
|
32 | };
|
33 |
|
34 | this.isCallbackCall = function () {
|
35 | return this.head().hasCallbackArgument();
|
36 | };
|
37 |
|
38 | this.tailBlock = function () {
|
39 | if (this._hasTailBlock) {
|
40 | return this._tailBlock;
|
41 | } else {
|
42 | var tail = this.tail();
|
43 | if (tail.length > 0) {
|
44 | var block = tail[tail.length - 1].hashEntryBlock();
|
45 |
|
46 | this._hasTailBlock = block;
|
47 | return this._tailBlock = block;
|
48 | } else {
|
49 | this._hasTailBlock = false;
|
50 | this._tailBlock = undefined;
|
51 | }
|
52 | }
|
53 | }
|
54 |
|
55 | this.arguments = function () {
|
56 | if (this._arguments) {
|
57 | return this._arguments;
|
58 | } else {
|
59 | var args = this.head().arguments();
|
60 |
|
61 | var tailBlock = this.tailBlock();
|
62 |
|
63 | if (tailBlock) {
|
64 | return this._arguments = args.concat(tailBlock);
|
65 | } else {
|
66 | return this._arguments = args;
|
67 | }
|
68 | }
|
69 | }
|
70 |
|
71 | this.hasArguments = function () {
|
72 | return this._hasArguments || (this._hasArguments =
|
73 | this.head().hasArguments() || this.tailBlock()
|
74 | );
|
75 | };
|
76 |
|
77 | this.expression = function () {
|
78 | var head = this.head();
|
79 |
|
80 | if (head.hasName()) {
|
81 | if (this.hasArguments()) {
|
82 | return this.wrap(terms.functionCall(terms.variable(head.name(), {couldBeMacro: false, location: this.location()}), this.arguments(), {options: true}));
|
83 | } else {
|
84 | return this.wrap(terms.variable(head.name(), {location: this.location()}));
|
85 | }
|
86 | } else {
|
87 | if (!this.hasTail() && this.arguments().length === 1 && !this.head().isCall()) {
|
88 | return this.arguments()[0];
|
89 | } else {
|
90 | return this.wrap(terms.functionCall(this.arguments()[0], this.arguments().slice(1), {options: true}));
|
91 | }
|
92 | }
|
93 | };
|
94 |
|
95 | this.objectOperationExpression = function (object) {
|
96 | var head = this.head();
|
97 |
|
98 | if (head.hasName()) {
|
99 | if (this.hasArguments()) {
|
100 | return this.wrap(terms.methodCall(object, head.name(), this.arguments(), {options: true}));
|
101 | } else {
|
102 | return this.wrap(terms.fieldReference(object, head.name()));
|
103 | }
|
104 | } else {
|
105 | if (!this.hasTail() && !head.isCall() && !this.isAsyncCall()) {
|
106 | return terms.indexer(object, this.arguments()[0]);
|
107 | } else {
|
108 | return this.wrap(terms.functionCall(terms.indexer(object, this.arguments()[0]), this.arguments().slice(1), {options: true}));
|
109 | }
|
110 | }
|
111 | };
|
112 |
|
113 | this.wrap = function (term) {
|
114 | if (this.isAsyncCall()) {
|
115 | term = terms.resolve(term);
|
116 | }
|
117 |
|
118 | return term;
|
119 | };
|
120 |
|
121 | this.parameters = function (options) {
|
122 | return this.head().parameters(options);
|
123 | };
|
124 |
|
125 | this.hasParameters = function () {
|
126 | return this._hasParameters || (this._hasParameters =
|
127 | this.head().hasParameters()
|
128 | );
|
129 | };
|
130 |
|
131 | this.hashEntry = function () {
|
132 | if (this.hasTail()) {
|
133 | return terms.errors.addTermsWithMessage(this.tail(), 'cannot be a hash entry');
|
134 | }
|
135 | return this.head().hashEntry();
|
136 | };
|
137 |
|
138 | this.objectOperationDefinition = function (object, source) {
|
139 | var self = this;
|
140 |
|
141 | return {
|
142 | expression: function () {
|
143 | if (self.head().hasName()) {
|
144 | if (self.hasParameters()) {
|
145 | var block = source.blockify(self.parameters(), {returnPromise: self.isAsyncCall(), redefinesSelf: true});
|
146 | return terms.definition(terms.fieldReference(object, self.head().name()), block, {assignment: true});
|
147 | } else {
|
148 | return terms.definition(terms.fieldReference(object, self.head().name()), source.scopify(), {assignment: true});
|
149 | }
|
150 | } else {
|
151 | if (!self.hasTail() && self.arguments().length === 1 && !self.isAsyncCall()) {
|
152 | return terms.definition(terms.indexer(object, self.arguments()[0]), source.scopify(), {assignment: true});
|
153 | } else {
|
154 | var block = source.blockify(self.parameters({skipFirstParameter: true}), {returnPromise: self.isAsyncCall(), redefinesSelf: true});
|
155 | return terms.definition(terms.indexer(object, self.arguments()[0]), block, {assignment: true});
|
156 | }
|
157 | }
|
158 | }
|
159 | };
|
160 | };
|
161 |
|
162 | this.objectOperation = function (object) {
|
163 | var complexExpression = this;
|
164 |
|
165 | return new function () {
|
166 | this.operation = complexExpression;
|
167 | this.object = object;
|
168 |
|
169 | this.expression = function () {
|
170 | return this.operation.objectOperationExpression(this.object);
|
171 | };
|
172 |
|
173 | this.definition = function (source) {
|
174 | return this.operation.objectOperationDefinition(this.object, source);
|
175 | };
|
176 |
|
177 | this.hashEntry = function () {
|
178 | return terms.errors.addTermWithMessage(this.expression(), 'cannot be a hash entry');
|
179 | };
|
180 | };
|
181 | };
|
182 |
|
183 | this.definition = function (source, options) {
|
184 | var self = this;
|
185 | var assignment = options && Object.hasOwnProperty.call(options, 'assignment') && options.assignment;
|
186 |
|
187 | if (self.head().hasName()) {
|
188 | if (self.hasParameters()) {
|
189 | return {
|
190 | expression: function () {
|
191 | return terms.definition(terms.variable(self.head().name(), {location: self.location()}), source.blockify(self.parameters(), {returnPromise: self.isAsyncCall()}), {assignment: assignment});
|
192 | },
|
193 | hashEntry: function (isOptionalArgument) {
|
194 | var block = source.blockify(self.parameters(), {returnPromise: self.isAsyncCall(), redefinesSelf: !isOptionalArgument});
|
195 |
|
196 | return terms.hashEntry(self.head().name(), block);
|
197 | }
|
198 | };
|
199 | } else {
|
200 | return {
|
201 | expression: function () {
|
202 | return terms.definition(terms.variable(self.head().name(), {location: self.location()}), source.scopify(), {assignment: assignment});
|
203 | },
|
204 | hashEntry: function () {
|
205 | return terms.hashEntry(self.head().hashKey(), source.scopify());
|
206 | }
|
207 | };
|
208 | }
|
209 | } else if (self.isAsyncCall()) {
|
210 | return {
|
211 | hashEntry: function () {
|
212 | var head = self.head();
|
213 | return terms.hashEntry(head.hashKey(), source.blockify ([], {async: true}));
|
214 | }
|
215 | };
|
216 | } else {
|
217 | return {
|
218 | hashEntry: function () {
|
219 | var head = self.head();
|
220 | return terms.hashEntry(head.hashKey(), source);
|
221 | }
|
222 | };
|
223 | }
|
224 | };
|
225 | });
|
226 | };
|