| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 | 3 |
| 4 class TagStack { | 4 class TagStack { |
| 5 List<ASTNode> _stack; | 5 List<ASTNode> _stack; |
| 6 | 6 |
| 7 TagStack(var elem) : _stack = [] { | 7 TagStack(var elem) : _stack = [] { |
| 8 _stack.add(elem); | 8 _stack.add(elem); |
| 9 } | 9 } |
| 10 | 10 |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 101 _previousToken = _peekToken; | 101 _previousToken = _peekToken; |
| 102 _peekToken = tokenizer.next(inTag); | 102 _peekToken = tokenizer.next(inTag); |
| 103 return _previousToken; | 103 return _previousToken; |
| 104 } | 104 } |
| 105 | 105 |
| 106 bool _peekKind(int kind) { | 106 bool _peekKind(int kind) { |
| 107 return _peekToken.kind == kind; | 107 return _peekToken.kind == kind; |
| 108 } | 108 } |
| 109 | 109 |
| 110 /* Is the next token a legal identifier? This includes pseudo-keywords. */ | 110 /* Is the next token a legal identifier? This includes pseudo-keywords. */ |
| 111 bool _peekIdentifier() { | 111 bool _peekIdentifier([String name = null]) { |
| 112 return TokenKind.isIdentifier(_peekToken.kind); | 112 if (TokenKind.isIdentifier(_peekToken.kind)) { |
| 113 return (name != null) ? _peekToken.text == name : true; |
| 114 } |
| 115 |
| 116 return false; |
| 113 } | 117 } |
| 114 | 118 |
| 115 bool _maybeEat(int kind) { | 119 bool _maybeEat(int kind) { |
| 116 if (_peekToken.kind == kind) { | 120 if (_peekToken.kind == kind) { |
| 117 _previousToken = _peekToken; | 121 _previousToken = _peekToken; |
| 118 _peekToken = tokenizer.next(); | 122 if (kind == TokenKind.GREATER_THAN) { |
| 123 _peekToken = tokenizer.next(false); |
| 124 } else { |
| 125 _peekToken = tokenizer.next(); |
| 126 } |
| 119 return true; | 127 return true; |
| 120 } else { | 128 } else { |
| 121 return false; | 129 return false; |
| 122 } | 130 } |
| 123 } | 131 } |
| 124 | 132 |
| 125 void _eat(int kind) { | 133 void _eat(int kind) { |
| 126 if (!_maybeEat(kind)) { | 134 if (!_maybeEat(kind)) { |
| 127 _errorExpected(TokenKind.kindToString(kind)); | 135 _errorExpected(TokenKind.kindToString(kind)); |
| 128 } | 136 } |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 // List<String> arg1, etc). | 209 // List<String> arg1, etc). |
| 202 var type = processAsIdentifier(); | 210 var type = processAsIdentifier(); |
| 203 var paramName = processAsIdentifier(); | 211 var paramName = processAsIdentifier(); |
| 204 if (type != null && paramName != null) { | 212 if (type != null && paramName != null) { |
| 205 params.add({'type': type, 'name' : paramName}); | 213 params.add({'type': type, 'name' : paramName}); |
| 206 | 214 |
| 207 if (!_maybeEat(TokenKind.COMMA)) { | 215 if (!_maybeEat(TokenKind.COMMA)) { |
| 208 break; | 216 break; |
| 209 } | 217 } |
| 210 } else { | 218 } else { |
| 211 _error("Template paramter missing type and name", _makeSpan(start)); | 219 // No parameters we're done. |
| 212 break; | 220 break; |
| 213 } | 221 } |
| 214 } | 222 } |
| 215 | 223 |
| 216 _eat(TokenKind.RPAREN); | 224 _eat(TokenKind.RPAREN); |
| 217 | 225 |
| 218 TemplateSignature sig = | 226 TemplateSignature sig = |
| 219 new TemplateSignature(templateName.name, params, _makeSpan(start)); | 227 new TemplateSignature(templateName.name, params, _makeSpan(start)); |
| 220 | 228 |
| 221 TemplateContent content = processTemplateContent(); | 229 TemplateContent content = processTemplateContent(); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 236 return identifier(); | 244 return identifier(); |
| 237 } else if (TokenKind.validTagName(_peek())) { | 245 } else if (TokenKind.validTagName(_peek())) { |
| 238 var tok = _next(); | 246 var tok = _next(); |
| 239 return new Identifier(TokenKind.tagNameFromTokenId(tok.kind), | 247 return new Identifier(TokenKind.tagNameFromTokenId(tok.kind), |
| 240 _makeSpan(start)); | 248 _makeSpan(start)); |
| 241 } | 249 } |
| 242 } | 250 } |
| 243 | 251 |
| 244 css.Stylesheet processCSS() { | 252 css.Stylesheet processCSS() { |
| 245 // Is there a CSS block? | 253 // Is there a CSS block? |
| 246 if (_peekIdentifier()) { | 254 if (_peekIdentifier('css')) { |
| 255 _next(); |
| 256 |
| 247 int start = _peekToken.start; | 257 int start = _peekToken.start; |
| 248 if (identifier().name == 'css') { | 258 _eat(TokenKind.LBRACE); |
| 249 _eat(TokenKind.LBRACE); | |
| 250 | 259 |
| 251 css.Stylesheet cssCtx = processCSSContent(source, tokenizer.startIndex); | 260 css.Stylesheet cssCtx = processCSSContent(source, tokenizer.startIndex); |
| 252 | 261 |
| 253 // TODO(terry): Hack, restart template parser where CSS parser stopped. | 262 // TODO(terry): Hack, restart template parser where CSS parser stopped. |
| 254 tokenizer.index = lastCSSIndexParsed; | 263 tokenizer.index = lastCSSIndexParsed; |
| 255 _next(false); | 264 _next(false); |
| 256 | 265 |
| 257 _eat(TokenKind.RBRACE); // close } of css block | 266 _eat(TokenKind.RBRACE); // close } of css block |
| 258 | 267 |
| 259 return cssCtx; | 268 return cssCtx; |
| 269 } |
| 270 } |
| 271 |
| 272 // TODO(terry): get should be able to use all template control flow but return |
| 273 // a string instead of a node. Maybe expose both html and get |
| 274 // e.g., |
| 275 // |
| 276 // A.) |
| 277 // html { |
| 278 // <div>...</div> |
| 279 // } |
| 280 // |
| 281 // B.) |
| 282 // html foo() { |
| 283 // <div>..</div> |
| 284 // } |
| 285 // |
| 286 // C.) |
| 287 // get { |
| 288 // <div>...</div> |
| 289 // } |
| 290 // |
| 291 // D.) |
| 292 // get foo() { |
| 293 // <div>..</div> |
| 294 // } |
| 295 // |
| 296 // Only one default allower either A or C the constructor will |
| 297 // generate a string or a node. |
| 298 // Examples B and D would generate getters that either return |
| 299 // a node for B or a String for D. |
| 300 // |
| 301 List<TemplateGetter> processGetters() { |
| 302 List<TemplateGetter> getters = []; |
| 303 |
| 304 while (true) { |
| 305 if (_peekIdentifier('get')) { |
| 306 _next(); |
| 307 |
| 308 int start = _peekToken.start; |
| 309 if (_peekIdentifier()) { |
| 310 String getterName = identifier().name; |
| 311 |
| 312 List<Map<Identifier, Identifier>> params = |
| 313 new List<Map<Identifier, Identifier>>(); |
| 314 |
| 315 _eat(TokenKind.LPAREN); |
| 316 |
| 317 start = _peekToken.start; |
| 318 while (true) { |
| 319 // TODO(terry): Need robust Dart argument parser (e.g., |
| 320 // List<String> arg1, etc). |
| 321 var type = processAsIdentifier(); |
| 322 var paramName = processAsIdentifier(); |
| 323 if (paramName == null && type != null) { |
| 324 paramName = type; |
| 325 type = ""; |
| 326 } |
| 327 if (type != null && paramName != null) { |
| 328 params.add({'type': type, 'name' : paramName}); |
| 329 |
| 330 if (!_maybeEat(TokenKind.COMMA)) { |
| 331 break; |
| 332 } |
| 333 } else { |
| 334 // No parameters we're done. |
| 335 break; |
| 336 } |
| 337 } |
| 338 |
| 339 _eat(TokenKind.RPAREN); |
| 340 |
| 341 _eat(TokenKind.LBRACE); |
| 342 |
| 343 var elems = new TemplateElement.fragment(_makeSpan(_peekToken.start)); |
| 344 var templateDoc = processHTML(elems); |
| 345 |
| 346 _eat(TokenKind.RBRACE); // close } of get block |
| 347 |
| 348 getters.add(new TemplateGetter(getterName, params, templateDoc, |
| 349 _makeSpan(_peekToken.start))); |
| 350 } |
| 351 } else { |
| 352 break; |
| 260 } | 353 } |
| 261 } | 354 } |
| 355 |
| 356 return getters; |
| 357 |
| 358 /* |
| 359 get newTotal(value) { |
| 360 <div class="alignleft">${value}</div> |
| 361 } |
| 362 |
| 363 String get HTML_newTotal(value) { |
| 364 return '<div class="alignleft">${value}</div> |
| 365 } |
| 366 |
| 367 */ |
| 262 } | 368 } |
| 369 |
| 263 TemplateContent processTemplateContent() { | 370 TemplateContent processTemplateContent() { |
| 264 css.Stylesheet stylesheet; | 371 css.Stylesheet ss; |
| 265 | 372 |
| 266 _eat(TokenKind.LBRACE); | 373 _eat(TokenKind.LBRACE); |
| 267 | 374 |
| 268 int start = _peekToken.start; | 375 int start = _peekToken.start; |
| 269 | 376 |
| 270 stylesheet = processCSS(); | 377 ss = processCSS(); |
| 378 |
| 379 // TODO(terry): getters should be allowed anywhere not just after CSS. |
| 380 List<TemplateGetter> getters = processGetters(); |
| 271 | 381 |
| 272 var elems = new TemplateElement.fragment(_makeSpan(_peekToken.start)); | 382 var elems = new TemplateElement.fragment(_makeSpan(_peekToken.start)); |
| 273 var templateDoc = processHTML(elems); | 383 var templateDoc = processHTML(elems); |
| 274 | 384 |
| 275 // TODO(terry): Should allow css { } to be at beginning or end of the | 385 // TODO(terry): Should allow css { } to be at beginning or end of the |
| 276 // template's content. Today css only allow at beginning | 386 // template's content. Today css only allow at beginning |
| 277 // because the css {...} is sucked in as a text node. We'll | 387 // because the css {...} is sucked in as a text node. We'll |
| 278 // need a special escape for css maybe: | 388 // need a special escape for css maybe: |
| 279 // | 389 // |
| 280 // ${#css} | 390 // ${#css} |
| 281 // ${/css} | 391 // ${/css} |
| 282 // | 392 // |
| 283 // uggggly! | 393 // uggggly! |
| 284 | 394 |
| 395 |
| 285 _eat(TokenKind.RBRACE); | 396 _eat(TokenKind.RBRACE); |
| 286 | 397 |
| 287 return new TemplateContent(stylesheet, templateDoc, _makeSpan(start)); | 398 return new TemplateContent(ss, templateDoc, getters, _makeSpan(start)); |
| 288 } | 399 } |
| 289 | 400 |
| 290 int lastCSSIndexParsed; // TODO(terry): Hack, last good CSS parsed. | 401 int lastCSSIndexParsed; // TODO(terry): Hack, last good CSS parsed. |
| 291 | 402 |
| 292 css.Stylesheet processCSSContent(var cssSource, int start) { | 403 css.Stylesheet processCSSContent(var cssSource, int start) { |
| 293 try { | 404 try { |
| 294 css.Parser parser = new css.Parser(new SourceFile( | 405 css.Parser parser = new css.Parser(new SourceFile( |
| 295 SourceFile.IN_MEMORY_FILE, cssSource.text), start); | 406 SourceFile.IN_MEMORY_FILE, cssSource.text), start); |
| 296 | 407 |
| 297 css.Stylesheet stylesheet = parser.parse(false, new ErrorMsgRedirector()); | 408 css.Stylesheet stylesheet = parser.parse(false, new ErrorMsgRedirector()); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 328 Map<String, TemplateAttribute> attrs = processAttributes(); | 439 Map<String, TemplateAttribute> attrs = processAttributes(); |
| 329 | 440 |
| 330 String varName; | 441 String varName; |
| 331 if (attrs.containsKey('var')) { | 442 if (attrs.containsKey('var')) { |
| 332 varName = attrs['var'].value; | 443 varName = attrs['var'].value; |
| 333 attrs.remove('var'); | 444 attrs.remove('var'); |
| 334 } | 445 } |
| 335 | 446 |
| 336 int scopeType; // 1 implies scoped, 2 implies non-scoped element. | 447 int scopeType; // 1 implies scoped, 2 implies non-scoped element. |
| 337 if (_maybeEat(TokenKind.GREATER_THAN)) { | 448 if (_maybeEat(TokenKind.GREATER_THAN)) { |
| 338 scopeType = 1; | 449 // Scoped unless the tag is explicitly known as an unscoped tag |
| 450 // e.g., <br>. |
| 451 scopeType = TokenKind.unscopedTag(tagToken.kind) ? 2 : 1; |
| 339 } else if (_maybeEat(TokenKind.END_NO_SCOPE_TAG)) { | 452 } else if (_maybeEat(TokenKind.END_NO_SCOPE_TAG)) { |
| 340 scopeType = 2; | 453 scopeType = 2; |
| 341 } | 454 } |
| 342 if (scopeType > 0) { | 455 if (scopeType > 0) { |
| 343 var elem = new TemplateElement.attributes(tagToken.kind, | 456 var elem = new TemplateElement.attributes(tagToken.kind, |
| 344 attrs.getValues(), varName, _makeSpan(start)); | 457 attrs.getValues(), varName, _makeSpan(start)); |
| 345 stack.top().add(elem); | 458 stack.top().add(elem); |
| 346 | 459 |
| 347 if (scopeType == 1) { | 460 if (scopeType == 1) { |
| 348 // Maybe more nested tags/text? | 461 // Maybe more nested tags/text? |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 497 | 610 |
| 498 /* Map is used so only last unique attribute name is remembered and to quickly | 611 /* Map is used so only last unique attribute name is remembered and to quickly |
| 499 * find the var attribute. | 612 * find the var attribute. |
| 500 */ | 613 */ |
| 501 Map<String, TemplateAttribute> processAttributes() { | 614 Map<String, TemplateAttribute> processAttributes() { |
| 502 Map<String, TemplateAttribute> attrs = new Map(); | 615 Map<String, TemplateAttribute> attrs = new Map(); |
| 503 | 616 |
| 504 int start = _peekToken.start; | 617 int start = _peekToken.start; |
| 505 String elemName; | 618 String elemName; |
| 506 while (_peekIdentifier() || | 619 while (_peekIdentifier() || |
| 507 (elemName = TokenKind.elementsToName(_peek())) != null) { | 620 (elemName = TokenKind.tagNameFromTokenId(_peek())) != null) { |
| 508 var attrName; | 621 var attrName; |
| 509 if (elemName == null) { | 622 if (elemName == null) { |
| 510 attrName = identifier(); | 623 attrName = identifier(); |
| 511 } else { | 624 } else { |
| 512 attrName = new Identifier(elemName, _makeSpan(start)); | 625 attrName = new Identifier(elemName, _makeSpan(start)); |
| 513 _next(); | 626 _next(); |
| 514 } | 627 } |
| 515 | 628 |
| 516 var attrValue; | 629 var attrValue; |
| 517 | 630 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 559 _next(false); | 672 _next(false); |
| 560 } else if (_peek() != TokenKind.RBRACE) { | 673 } else if (_peek() != TokenKind.RBRACE) { |
| 561 // Yes, grab the chars after the > | 674 // Yes, grab the chars after the > |
| 562 stringValue.add(_previousToken.source.text.substring( | 675 stringValue.add(_previousToken.source.text.substring( |
| 563 this._previousToken.end, this._peekToken.start)); | 676 this._previousToken.end, this._peekToken.start)); |
| 564 } | 677 } |
| 565 } | 678 } |
| 566 | 679 |
| 567 // Gobble up everything until we hit < | 680 // Gobble up everything until we hit < |
| 568 while (_peek() != TokenKind.LESS_THAN && | 681 while (_peek() != TokenKind.LESS_THAN && |
| 682 _peek() != TokenKind.START_COMMAND && |
| 683 _peek() != TokenKind.END_COMMAND && |
| 569 (_peek() != TokenKind.RBRACE || | 684 (_peek() != TokenKind.RBRACE || |
| 570 (_peek() == TokenKind.RBRACE && inExpression)) && | 685 (_peek() == TokenKind.RBRACE && inExpression)) && |
| 571 _peek() != TokenKind.END_OF_FILE) { | 686 _peek() != TokenKind.END_OF_FILE) { |
| 572 | 687 |
| 573 // Beginning of expression? | 688 // Beginning of expression? |
| 574 if (_peek() == TokenKind.START_EXPRESSION) { | 689 if (_peek() == TokenKind.START_EXPRESSION) { |
| 575 if (stringValue.length > 0) { | 690 if (stringValue.length > 0) { |
| 576 // We have a real text node create the text node. | 691 // We have a real text node create the text node. |
| 577 nodes.add(new TemplateText(stringValue.toString(), _makeSpan(start))); | 692 nodes.add(new TemplateText(stringValue.toString(), _makeSpan(start))); |
| 578 stringValue = new StringBuffer(); | 693 stringValue = new StringBuffer(); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 596 } | 711 } |
| 597 | 712 |
| 598 if (stringValue.length > 0) { | 713 if (stringValue.length > 0) { |
| 599 nodes.add(new TemplateText(stringValue.toString(), _makeSpan(start))); | 714 nodes.add(new TemplateText(stringValue.toString(), _makeSpan(start))); |
| 600 } | 715 } |
| 601 | 716 |
| 602 return nodes; | 717 return nodes; |
| 603 } | 718 } |
| 604 | 719 |
| 605 } | 720 } |
| OLD | NEW |