Cleanups
diff --git a/lib/src/char_reader.dart b/lib/src/char_reader.dart
index 4b7dfd9..c76a2d3 100644
--- a/lib/src/char_reader.dart
+++ b/lib/src/char_reader.dart
@@ -50,7 +50,7 @@
int peek() => _c;
- String readWhile(bool test(int charCode), Function endOfFile) {
+ String readWhile(bool test(int charCode), [Function endOfFile]) {
int start = _i;
diff --git a/lib/src/parse.dart b/lib/src/parse.dart
index 2fc0df5..9858de0 100644
--- a/lib/src/parse.dart
+++ b/lib/src/parse.dart
@@ -1,8 +1,5 @@
part of mustache;
-final RegExp _validTag = new RegExp(r'^[0-9a-zA-Z\_\-\.]+$');
-final RegExp _integerTag = new RegExp(r'^[0-9]+$');
-
_Node _parse(String source,
bool lenient,
String templateName,
@@ -15,17 +12,6 @@
tokens = _removeStandaloneWhitespace(tokens);
tokens = _mergeAdjacentText(tokens);
-
- //FIXME this should be handled by scanner now.
- checkTagChars(_Token t) {
- if (!lenient && !_validTag.hasMatch(t.value)) {
- throw new _TemplateException(
- 'Tag contained invalid characters in name, '
- 'allowed: 0-9, a-z, A-Z, underscore, and minus',
- templateName, source, t.start);
- }
- }
-
var stack = new List<_Node>()..add(new _Node(_OPEN_SECTION, 'root', 0, 0));
@@ -35,14 +21,11 @@
case _VARIABLE:
case _UNESC_VARIABLE:
case _PARTIAL:
- if (t.type == _VARIABLE || t.type == _UNESC_VARIABLE)
- checkTagChars(t);
stack.last.children.add(new _Node.fromToken(t));
break;
case _OPEN_SECTION:
case _OPEN_INV_SECTION:
- checkTagChars(t);
// Store the start, end of the inner string content not
// including the tag.
var child = new _Node.fromToken(t)..contentStart = t.end;
@@ -51,8 +34,6 @@
break;
case _CLOSE_SECTION:
- checkTagChars(t);
-
if (stack.last.value != t.value) {
throw new _TemplateException(
"Mismatched tag, expected: '${stack.last.value}', was: '${t.value}'",
diff --git a/lib/src/renderer.dart b/lib/src/renderer.dart
index 3a5744d..f59566b 100644
--- a/lib/src/renderer.dart
+++ b/lib/src/renderer.dart
@@ -1,5 +1,8 @@
part of mustache;
+final RegExp _validTag = new RegExp(r'^[0-9a-zA-Z\_\-\.]+$');
+final RegExp _integerTag = new RegExp(r'^[0-9]+$');
+
const Object _noSuchProperty = const Object();
class _Renderer {
diff --git a/lib/src/scanner.dart b/lib/src/scanner.dart
index adc4454..6967efd 100644
--- a/lib/src/scanner.dart
+++ b/lib/src/scanner.dart
@@ -102,113 +102,22 @@
}
} else if (c == _SPACE || c == _TAB) {
- var value = _r.readWhile((c) => c == _SPACE || c == _TAB, null); //FIXME remove null.
+ var value = _r.readWhile((c) => c == _SPACE || c == _TAB);
_tokens.add(new _Token(_WHITESPACE, value, start, _r.offset));
-
- //FIXME figure out why this is required
- } else if (c == _closeDelimiter || c == _closeDelimiterInner) {
- _read();
- var value = new String.fromCharCode(c);
- _tokens.add(new _Token(_TEXT, value, start, _r.offset));
-
+
} else {
- var value = _r.readWhile((c) => c != _openDelimiter
- && c != _NEWLINE,
- null); //FIXME remove null.
+ var value = _r.readWhile((c) => c != _openDelimiter && c != _NEWLINE);
_tokens.add(new _Token(_TEXT, value, start, _r.offset));
}
}
}
-
- //TODO consider changing the parsing here to use a regexp. It will probably
- // be simpler to read.
- _scanChangeDelimiterTag(int start) {
- // Open delimiter characters and = have already been read.
-
- var delimiterInner = _closeDelimiterInner;
- var delimiter = _closeDelimiter;
-
- _scanTagWhitespace();
-
- int c;
- c = _r.read();
-
- if (c == _EQUAL) throw 'syntax error'; //FIXME
- _openDelimiter = c;
-
- c = _r.read();
- if (_isWhitespace(c)) {
- _openDelimiterInner = null;
- } else {
- _openDelimiterInner = c;
- }
-
- _scanTagWhitespace();
-
- c = _r.read();
-
- if (_isWhitespace(c) || c == _EQUAL) throw 'syntax error'; //FIXME
-
- if (_isWhitespace(_peek()) || _peek() == _EQUAL) {
- _closeDelimiterInner = null;
- _closeDelimiter = c;
- } else {
- _closeDelimiterInner = c;
- _closeDelimiter = _read();
- }
-
- _scanTagWhitespace();
- _expect(_EQUAL);
- _scanTagWhitespace();
-
- _expect(delimiterInner);
- _expect(delimiter);
-
- var value = _delimiterString(
- _openDelimiter,
- _openDelimiterInner,
- _closeDelimiterInner,
- _closeDelimiter);
-
- _tokens.add(new _Token(_CHANGE_DELIMITER, value, start, _r.offset));
- }
-
- _errorEofInTag() => throw _error('Tag not closed before the end of the template.');
-
- _scanTagWhitespace() {
- const whitepsace = const [_SPACE, _NEWLINE, _RETURN, _TAB];
- if (_lenient) {
- _r.readWhile(_isWhitespace, _errorEofInTag);
- } else {
- _r.readWhile((c) => c == _SPACE, _errorEofInTag);
- if (_isWhitespace(_peek()))
- throw _error('Tags may not contain newlines or tabs.');
- }
- }
-
- String _scanTagIdentifier() {
- //FIXME put this in a getter.
- var delim = _closeDelimiterInner != null
- ? _closeDelimiterInner
- : _closeDelimiter;
- if (_lenient) {
- return _r.readWhile((c) => c != delim, _errorEofInTag).trim();
- } else {
- var id = _r.readWhile(_isAlphanum, _errorEofInTag);
- _scanTagWhitespace();
- if (_peek() != delim) throw _error('Unless in lenient mode tags may only '
- 'contain the characters a-z, A-Z, minus, underscore and period.');
- return id;
- }
- }
-
- _scanMustacheTag() {
+
+ void _scanMustacheTag() {
int start = _r.offset;
int sigil = 0;
_expect(_openDelimiter);
- //FIXME move this code into _scan(). Need a peek2()
// If just a single delimeter character then this is a text token.
if (_openDelimiterInner != null && _peek() != _openDelimiterInner) {
var value = new String.fromCharCode(_openDelimiter);
@@ -261,6 +170,37 @@
_tokens.add(new _Token(type, identifier, start, _r.offset, indent: indent));
}
+ _errorEofInTag() => throw _error('Tag not closed before the end of the template.');
+
+ _scanTagWhitespace() {
+ const whitepsace = const [_SPACE, _NEWLINE, _RETURN, _TAB];
+ if (_lenient) {
+ _r.readWhile(_isWhitespace, _errorEofInTag);
+ } else {
+ _r.readWhile((c) => c == _SPACE, _errorEofInTag);
+ if (_isWhitespace(_peek()))
+ throw _error('Tags may not contain newlines or tabs.');
+ }
+ }
+
+ String _scanTagIdentifier({bool tripleMo: false}) {
+ var delim = _closeDelimiterInner != null
+ ? _closeDelimiterInner
+ : _closeDelimiter;
+ if (_lenient) {
+ return _r.readWhile(
+ (c) => c != delim
+ || tripleMo && c != _CLOSE_MUSTACHE,
+ _errorEofInTag).trim();
+ } else {
+ var id = _r.readWhile(_isAlphanum, _errorEofInTag);
+ _scanTagWhitespace();
+ if (_peek() != delim) throw _error('Unless in lenient mode tags may only '
+ 'contain the characters a-z, A-Z, minus, underscore and period.');
+ return id;
+ }
+ }
+
// Capture whitespace preceding a partial tag so it can used for indentation
// during rendering.
String _getPrecedingWhitespace() {
@@ -279,17 +219,16 @@
return indent;
}
- _scanTripleMustacheTag(int start) {
+ void _scanTripleMustacheTag(int start) {
_expect(_OPEN_MUSTACHE);
- var value = _r.readWhile((c) => c != _CLOSE_MUSTACHE, _errorEofInTag).trim();
- //FIXME lenient/strict mode identifier parsing.
+ var value = _scanTagIdentifier();
_expect(_CLOSE_MUSTACHE);
if (_closeDelimiterInner != null) _expect(_closeDelimiterInner);
_expect(_closeDelimiter);
_tokens.add(new _Token(_UNESC_VARIABLE, value, start, _r.offset));
}
- _scanCommentTag(int start) {
+ void _scanCommentTag(int start) {
var value = _closeDelimiterInner != null
? _r.readWhile((c) => c != _closeDelimiterInner, _errorEofInTag).trim()
: _r.readWhile((c) => c != _closeDelimiter, _errorEofInTag).trim();
@@ -297,10 +236,65 @@
_expect(_closeDelimiter);
_tokens.add(new _Token(_COMMENT, value, start, _r.offset));
}
-
+
+ //TODO consider changing the parsing here to use a regexp. It will probably
+ // be simpler to read.
+ void _scanChangeDelimiterTag(int start) {
+ // Open delimiter characters and = have already been read.
+
+ var delimiterInner = _closeDelimiterInner;
+ var delimiter = _closeDelimiter;
+
+ _scanTagWhitespace();
+
+ int c;
+ c = _r.read();
+
+ if (c == _EQUAL) throw _error('Incorrect change delimiter tag.');
+ _openDelimiter = c;
+
+ c = _r.read();
+ if (_isWhitespace(c)) {
+ _openDelimiterInner = null;
+ } else {
+ _openDelimiterInner = c;
+ }
+
+ _scanTagWhitespace();
+
+ c = _r.read();
+
+ if (_isWhitespace(c) || c == _EQUAL)
+ throw _error('Incorrect change delimiter tag.');
+
+ if (_isWhitespace(_peek()) || _peek() == _EQUAL) {
+ _closeDelimiterInner = null;
+ _closeDelimiter = c;
+ } else {
+ _closeDelimiterInner = c;
+ _closeDelimiter = _read();
+ }
+
+ _scanTagWhitespace();
+ _expect(_EQUAL);
+ _scanTagWhitespace();
+
+ _expect(delimiterInner);
+ _expect(delimiter);
+
+ var value = _delimiterString(
+ _openDelimiter,
+ _openDelimiterInner,
+ _closeDelimiterInner,
+ _closeDelimiter);
+
+ _tokens.add(new _Token(_CHANGE_DELIMITER, value, start, _r.offset));
+ }
+
TemplateException _error(String message) {
return new _TemplateException(message, _templateName, _source, _r.offset);
}
+
}
_delimiterString(int open, int openInner, int closeInner, int close) {
@@ -325,7 +319,8 @@
s.codeUnits[3],
s.codeUnits[4]];
} else {
- throw 'Invalid delimiter string $s'; //FIXME
+ throw new _TemplateException(
+ 'Invalid delimiter string $s', null, null, null);
}
}
diff --git a/test/mustache_specs.dart b/test/mustache_specs.dart
index abb9033..82a3ab8 100644
--- a/test/mustache_specs.dart
+++ b/test/mustache_specs.dart
@@ -1,6 +1,7 @@
// Specification files can be downloaded here https://github.com/mustache/spec
-// Originally implemented by
+// Test implemented by Georgios Valotasios.
+// See: https://github.com/valotas/mustache4dart
library mustache_specs;