guard-mthaml 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +34 -0
  3. data/lib/guard/mthaml.rb +140 -0
  4. data/lib/guard/mthaml/compiler/MtHaml.php +255 -0
  5. data/lib/guard/mthaml/templates/Guardfile +12 -0
  6. data/lib/guard/mthaml/version.rb +5 -0
  7. data/vendor/autoload.php +7 -0
  8. data/vendor/coffeescript/coffeescript/LICENSE +22 -0
  9. data/vendor/coffeescript/coffeescript/README.md +96 -0
  10. data/vendor/coffeescript/coffeescript/composer.json +23 -0
  11. data/vendor/coffeescript/coffeescript/grammar.y +309 -0
  12. data/vendor/coffeescript/coffeescript/make.php +115 -0
  13. data/vendor/coffeescript/coffeescript/src/CoffeeScript/Compiler.php +76 -0
  14. data/vendor/coffeescript/coffeescript/src/CoffeeScript/Error.php +15 -0
  15. data/vendor/coffeescript/coffeescript/src/CoffeeScript/Helpers.php +116 -0
  16. data/vendor/coffeescript/coffeescript/src/CoffeeScript/Init.php +96 -0
  17. data/vendor/coffeescript/coffeescript/src/CoffeeScript/Lexer.php +1356 -0
  18. data/vendor/coffeescript/coffeescript/src/CoffeeScript/Nodes.php +105 -0
  19. data/vendor/coffeescript/coffeescript/src/CoffeeScript/Parser.php +3326 -0
  20. data/vendor/coffeescript/coffeescript/src/CoffeeScript/Rewriter.php +552 -0
  21. data/vendor/coffeescript/coffeescript/src/CoffeeScript/Scope.php +196 -0
  22. data/vendor/coffeescript/coffeescript/src/CoffeeScript/SyntaxError.php +9 -0
  23. data/vendor/coffeescript/coffeescript/src/CoffeeScript/Value.php +20 -0
  24. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Access.php +31 -0
  25. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Arr.php +69 -0
  26. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Assign.php +353 -0
  27. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Base.php +288 -0
  28. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Block.php +294 -0
  29. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Call.php +283 -0
  30. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Class.php +282 -0
  31. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Closure.php +49 -0
  32. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Code.php +203 -0
  33. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Comment.php +39 -0
  34. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Existence.php +42 -0
  35. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Extends.php +26 -0
  36. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/For.php +250 -0
  37. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/If.php +161 -0
  38. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/In.php +99 -0
  39. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Index.php +27 -0
  40. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Literal.php +96 -0
  41. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Obj.php +126 -0
  42. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Op.php +292 -0
  43. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Param.php +119 -0
  44. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Parens.php +45 -0
  45. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Range.php +225 -0
  46. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Return.php +56 -0
  47. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Slice.php +47 -0
  48. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Splat.php +100 -0
  49. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Switch.php +121 -0
  50. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Throw.php +37 -0
  51. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Try.php +79 -0
  52. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Value.php +210 -0
  53. data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/While.php +112 -0
  54. data/vendor/coffeescript/coffeescript/tests/cases/arrays.coffee +77 -0
  55. data/vendor/coffeescript/coffeescript/tests/cases/assignment.coffee +352 -0
  56. data/vendor/coffeescript/coffeescript/tests/cases/booleans.coffee +21 -0
  57. data/vendor/coffeescript/coffeescript/tests/cases/classes.coffee +681 -0
  58. data/vendor/coffeescript/coffeescript/tests/cases/comments.coffee +207 -0
  59. data/vendor/coffeescript/coffeescript/tests/cases/compilation.coffee +72 -0
  60. data/vendor/coffeescript/coffeescript/tests/cases/comprehensions.coffee +501 -0
  61. data/vendor/coffeescript/coffeescript/tests/cases/control_flow.coffee +430 -0
  62. data/vendor/coffeescript/coffeescript/tests/cases/eval.coffee +29 -0
  63. data/vendor/coffeescript/coffeescript/tests/cases/exception_handling.coffee +102 -0
  64. data/vendor/coffeescript/coffeescript/tests/cases/formatting.coffee +146 -0
  65. data/vendor/coffeescript/coffeescript/tests/cases/function_invocation.coffee +552 -0
  66. data/vendor/coffeescript/coffeescript/tests/cases/functions.coffee +188 -0
  67. data/vendor/coffeescript/coffeescript/tests/cases/helpers.coffee +96 -0
  68. data/vendor/coffeescript/coffeescript/tests/cases/importing.coffee +18 -0
  69. data/vendor/coffeescript/coffeescript/tests/cases/interpolation.coffee +138 -0
  70. data/vendor/coffeescript/coffeescript/tests/cases/javascript_literals.coffee +10 -0
  71. data/vendor/coffeescript/coffeescript/tests/cases/numbers.coffee +76 -0
  72. data/vendor/coffeescript/coffeescript/tests/cases/objects.coffee +271 -0
  73. data/vendor/coffeescript/coffeescript/tests/cases/operators.coffee +277 -0
  74. data/vendor/coffeescript/coffeescript/tests/cases/option_parser.coffee +43 -0
  75. data/vendor/coffeescript/coffeescript/tests/cases/ranges.coffee +88 -0
  76. data/vendor/coffeescript/coffeescript/tests/cases/regexps.coffee +63 -0
  77. data/vendor/coffeescript/coffeescript/tests/cases/scope.coffee +43 -0
  78. data/vendor/coffeescript/coffeescript/tests/cases/slicing_and_splicing.coffee +143 -0
  79. data/vendor/coffeescript/coffeescript/tests/cases/soaks.coffee +134 -0
  80. data/vendor/coffeescript/coffeescript/tests/cases/strict.coffee +155 -0
  81. data/vendor/coffeescript/coffeescript/tests/cases/strings.coffee +107 -0
  82. data/vendor/coffeescript/coffeescript/tests/css/style.css +43 -0
  83. data/vendor/coffeescript/coffeescript/tests/index.php +119 -0
  84. data/vendor/coffeescript/coffeescript/tests/js/lib/coffeescript_1.1.1.js +8 -0
  85. data/vendor/coffeescript/coffeescript/tests/js/lib/coffeescript_1.2.0.js +8 -0
  86. data/vendor/coffeescript/coffeescript/tests/js/lib/coffeescript_1.3.0.js +8 -0
  87. data/vendor/coffeescript/coffeescript/tests/js/lib/coffeescript_1.3.1.js +8 -0
  88. data/vendor/coffeescript/coffeescript/tests/js/lib/diff.js +276 -0
  89. data/vendor/coffeescript/coffeescript/tests/js/main.js +123 -0
  90. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/LICENSE.txt +10 -0
  91. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/Lempar.php +948 -0
  92. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/Action.php +257 -0
  93. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/ActionTable.php +299 -0
  94. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/Config.php +574 -0
  95. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/Data.php +1857 -0
  96. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/Parser.php +851 -0
  97. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/PropagationLink.php +126 -0
  98. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/Rule.php +144 -0
  99. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/State.php +283 -0
  100. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/Symbol.php +288 -0
  101. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/cli.php +5 -0
  102. data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/ParserGenerator.php +811 -0
  103. data/vendor/composer/ClassLoader.php +383 -0
  104. data/vendor/composer/autoload_classmap.php +9 -0
  105. data/vendor/composer/autoload_namespaces.php +12 -0
  106. data/vendor/composer/autoload_psr4.php +10 -0
  107. data/vendor/composer/autoload_real.php +50 -0
  108. data/vendor/composer/installed.json +166 -0
  109. data/vendor/michelf/php-markdown/License.md +36 -0
  110. data/vendor/michelf/php-markdown/Michelf/Markdown.inc.php +10 -0
  111. data/vendor/michelf/php-markdown/Michelf/Markdown.php +3117 -0
  112. data/vendor/michelf/php-markdown/Michelf/MarkdownExtra.inc.php +11 -0
  113. data/vendor/michelf/php-markdown/Michelf/MarkdownExtra.php +38 -0
  114. data/vendor/michelf/php-markdown/Michelf/MarkdownInterface.inc.php +9 -0
  115. data/vendor/michelf/php-markdown/Michelf/MarkdownInterface.php +37 -0
  116. data/vendor/michelf/php-markdown/Readme.md +305 -0
  117. data/vendor/michelf/php-markdown/Readme.php +31 -0
  118. data/vendor/michelf/php-markdown/composer.json +31 -0
  119. data/vendor/mthaml/mthaml/CHANGELOG +48 -0
  120. data/vendor/mthaml/mthaml/LICENSE +44 -0
  121. data/vendor/mthaml/mthaml/README.markdown +262 -0
  122. data/vendor/mthaml/mthaml/composer.json +45 -0
  123. data/vendor/mthaml/mthaml/docs/Makefile +153 -0
  124. data/vendor/mthaml/mthaml/docs/_static/mthaml.css +30 -0
  125. data/vendor/mthaml/mthaml/docs/_theme/mthaml/theme.conf +4 -0
  126. data/vendor/mthaml/mthaml/docs/conf.py +242 -0
  127. data/vendor/mthaml/mthaml/docs/index.rst +18 -0
  128. data/vendor/mthaml/mthaml/docs/twig-syntax.rst +274 -0
  129. data/vendor/mthaml/mthaml/examples/README.md +5 -0
  130. data/vendor/mthaml/mthaml/examples/autoload.php +8 -0
  131. data/vendor/mthaml/mthaml/examples/example-php.haml +5 -0
  132. data/vendor/mthaml/mthaml/examples/example-php.php +37 -0
  133. data/vendor/mthaml/mthaml/examples/example-twig-noext.twig +11 -0
  134. data/vendor/mthaml/mthaml/examples/example-twig.haml +9 -0
  135. data/vendor/mthaml/mthaml/examples/example-twig.php +60 -0
  136. data/vendor/mthaml/mthaml/examples/example.twig.haml +8 -0
  137. data/vendor/mthaml/mthaml/lib/MtHaml/Autoloader.php +22 -0
  138. data/vendor/mthaml/mthaml/lib/MtHaml/Environment.php +178 -0
  139. data/vendor/mthaml/mthaml/lib/MtHaml/Escaping.php +33 -0
  140. data/vendor/mthaml/mthaml/lib/MtHaml/Exception.php +9 -0
  141. data/vendor/mthaml/mthaml/lib/MtHaml/Exception/SyntaxErrorException.php +9 -0
  142. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/AbstractFilter.php +43 -0
  143. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Cdata.php +16 -0
  144. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/CoffeeScript.php +33 -0
  145. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Css.php +26 -0
  146. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Escaped.php +22 -0
  147. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/FilterInterface.php +15 -0
  148. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Javascript.php +26 -0
  149. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Less.php +27 -0
  150. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Less/LeafoLess.php +20 -0
  151. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Less/OyejorgeLess.php +23 -0
  152. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Markdown.php +58 -0
  153. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Markdown/CebeMarkdown.php +22 -0
  154. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Markdown/Ciconia.php +21 -0
  155. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Markdown/MichelfMarkdown.php +21 -0
  156. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Markdown/Parsedown.php +21 -0
  157. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Php.php +37 -0
  158. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Plain.php +24 -0
  159. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Preserve.php +19 -0
  160. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Scss.php +37 -0
  161. data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Twig.php +47 -0
  162. data/vendor/mthaml/mthaml/lib/MtHaml/Indentation/Indentation.php +96 -0
  163. data/vendor/mthaml/mthaml/lib/MtHaml/Indentation/IndentationException.php +9 -0
  164. data/vendor/mthaml/mthaml/lib/MtHaml/Indentation/IndentationInterface.php +50 -0
  165. data/vendor/mthaml/mthaml/lib/MtHaml/Indentation/Undefined.php +41 -0
  166. data/vendor/mthaml/mthaml/lib/MtHaml/Node/Comment.php +71 -0
  167. data/vendor/mthaml/mthaml/lib/MtHaml/Node/Doctype.php +96 -0
  168. data/vendor/mthaml/mthaml/lib/MtHaml/Node/EscapableAbstract.php +19 -0
  169. data/vendor/mthaml/mthaml/lib/MtHaml/Node/Filter.php +51 -0
  170. data/vendor/mthaml/mthaml/lib/MtHaml/Node/Insert.php +42 -0
  171. data/vendor/mthaml/mthaml/lib/MtHaml/Node/InterpolatedString.php +73 -0
  172. data/vendor/mthaml/mthaml/lib/MtHaml/Node/NestAbstract.php +117 -0
  173. data/vendor/mthaml/mthaml/lib/MtHaml/Node/NestInterface.php +10 -0
  174. data/vendor/mthaml/mthaml/lib/MtHaml/Node/NodeAbstract.php +77 -0
  175. data/vendor/mthaml/mthaml/lib/MtHaml/Node/ObjectRefClass.php +42 -0
  176. data/vendor/mthaml/mthaml/lib/MtHaml/Node/ObjectRefId.php +42 -0
  177. data/vendor/mthaml/mthaml/lib/MtHaml/Node/Root.php +35 -0
  178. data/vendor/mthaml/mthaml/lib/MtHaml/Node/Run.php +71 -0
  179. data/vendor/mthaml/mthaml/lib/MtHaml/Node/Statement.php +45 -0
  180. data/vendor/mthaml/mthaml/lib/MtHaml/Node/Tag.php +90 -0
  181. data/vendor/mthaml/mthaml/lib/MtHaml/Node/TagAttribute.php +62 -0
  182. data/vendor/mthaml/mthaml/lib/MtHaml/Node/TagAttributeInterpolation.php +24 -0
  183. data/vendor/mthaml/mthaml/lib/MtHaml/Node/TagAttributeList.php +24 -0
  184. data/vendor/mthaml/mthaml/lib/MtHaml/Node/Text.php +37 -0
  185. data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/Autoclose.php +25 -0
  186. data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/Escaping.php +105 -0
  187. data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/MergeAttrs.php +110 -0
  188. data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/Midblock.php +37 -0
  189. data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/NodeVisitorAbstract.php +226 -0
  190. data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/NodeVisitorInterface.php +97 -0
  191. data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/PhpRenderer.php +288 -0
  192. data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/Printer.php +265 -0
  193. data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/RendererAbstract.php +581 -0
  194. data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/TwigRenderer.php +252 -0
  195. data/vendor/mthaml/mthaml/lib/MtHaml/Parser.php +862 -0
  196. data/vendor/mthaml/mthaml/lib/MtHaml/Parser/Buffer.php +147 -0
  197. data/vendor/mthaml/mthaml/lib/MtHaml/Runtime.php +218 -0
  198. data/vendor/mthaml/mthaml/lib/MtHaml/Runtime/AttributeInterpolation.php +16 -0
  199. data/vendor/mthaml/mthaml/lib/MtHaml/Runtime/AttributeList.php +16 -0
  200. data/vendor/mthaml/mthaml/lib/MtHaml/Support/Php/Executor.php +157 -0
  201. data/vendor/mthaml/mthaml/lib/MtHaml/Support/Twig/Extension.php +47 -0
  202. data/vendor/mthaml/mthaml/lib/MtHaml/Support/Twig/Lexer.php +48 -0
  203. data/vendor/mthaml/mthaml/lib/MtHaml/Support/Twig/Loader.php +81 -0
  204. data/vendor/mthaml/mthaml/lib/MtHaml/Target/Php.php +23 -0
  205. data/vendor/mthaml/mthaml/lib/MtHaml/Target/TargetAbstract.php +87 -0
  206. data/vendor/mthaml/mthaml/lib/MtHaml/Target/TargetInterface.php +12 -0
  207. data/vendor/mthaml/mthaml/lib/MtHaml/Target/Twig.php +23 -0
  208. data/vendor/mthaml/mthaml/lib/MtHaml/TreeBuilder.php +100 -0
  209. data/vendor/mthaml/mthaml/lib/MtHaml/TreeBuilderException.php +9 -0
  210. data/vendor/mthaml/mthaml/phpunit.xml +25 -0
  211. data/vendor/mthaml/mthaml/test/MtHaml/Tests/EnvironmentTest.php +59 -0
  212. data/vendor/mthaml/mthaml/test/MtHaml/Tests/HamlSpecTest.php +83 -0
  213. data/vendor/mthaml/mthaml/test/MtHaml/Tests/IndentationTest.php +140 -0
  214. data/vendor/mthaml/mthaml/test/MtHaml/Tests/Node/DoctypeTest.php +51 -0
  215. data/vendor/mthaml/mthaml/test/MtHaml/Tests/Node/NodeTest.php +110 -0
  216. data/vendor/mthaml/mthaml/test/MtHaml/Tests/NodeVisitor/PhpRendererTest.php +113 -0
  217. data/vendor/mthaml/mthaml/test/MtHaml/Tests/NodeVisitor/TwigRendererTest.php +68 -0
  218. data/vendor/mthaml/mthaml/test/MtHaml/Tests/NodeVisitorsTest.php +44 -0
  219. data/vendor/mthaml/mthaml/test/MtHaml/Tests/Parser/BufferTest.php +77 -0
  220. data/vendor/mthaml/mthaml/test/MtHaml/Tests/ParserTest.php +47 -0
  221. data/vendor/mthaml/mthaml/test/MtHaml/Tests/RuntimeTest.php +356 -0
  222. data/vendor/mthaml/mthaml/test/MtHaml/Tests/Support/Php/ExecutorTest.php +83 -0
  223. data/vendor/mthaml/mthaml/test/MtHaml/Tests/Support/Twig/LoaderTest.php +65 -0
  224. data/vendor/mthaml/mthaml/test/MtHaml/Tests/TestCase.php +49 -0
  225. data/vendor/mthaml/mthaml/test/MtHaml/Tests/TestCaseTest.php +50 -0
  226. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/Support/Php/Executor.001.haml +1 -0
  227. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/attr_list_php.test +20 -0
  228. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/attr_list_twig.test +12 -0
  229. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/boolean_attr_php.test +16 -0
  230. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/boolean_attr_twig.test +16 -0
  231. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/bug28_php.test +15 -0
  232. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/bug28_twig.test +15 -0
  233. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/bug42.test +13 -0
  234. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/bug8_twig.test +10 -0
  235. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/coffeescript_filter.test +31 -0
  236. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/comments.test +26 -0
  237. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/cond_cmt.test +25 -0
  238. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/doctype.test +18 -0
  239. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/doctype_html4.test +20 -0
  240. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/doctype_invalid.test +12 -0
  241. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/doctype_invalid_xhtml.test +12 -0
  242. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/doctype_php.test +18 -0
  243. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/doctype_xhtml.test +30 -0
  244. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/filters.test +51 -0
  245. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/insert_non_echo_php.test +11 -0
  246. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/insert_non_echo_twig.test +11 -0
  247. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/interpolation_in_html_attrs_php.test +31 -0
  248. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/interpolation_in_html_attrs_twig.test +31 -0
  249. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/less_filter_leafo.test +33 -0
  250. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/less_filter_oyejorge.test +49 -0
  251. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/markdown_filter_cebemarkdown.test +35 -0
  252. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/markdown_filter_ciconia.test +36 -0
  253. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/markdown_filter_michelf.test +34 -0
  254. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/markdown_filter_parsedown.test +31 -0
  255. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/markdown_optimization_filter_michelf.test +23 -0
  256. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/mergeattrs.test +18 -0
  257. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/nuke_inner_whitespace.test +77 -0
  258. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/nuke_outer_whitespace.test +122 -0
  259. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/object_ref_php.test +14 -0
  260. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/object_ref_twig.test +14 -0
  261. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/php.test +109 -0
  262. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/php_autoescaping.test +22 -0
  263. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/php_autoescaping_once.test +25 -0
  264. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/php_blocks.test +29 -0
  265. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/php_filters.test +42 -0
  266. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/scss_filter.test +32 -0
  267. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/strip_inline_comments_php.test +22 -0
  268. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/test.test +24 -0
  269. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/trailing_semicolon_php.test +14 -0
  270. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/try_catch_php.test +17 -0
  271. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/twig.test +112 -0
  272. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/twig_blocks.test +29 -0
  273. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/twig_filters.test +37 -0
  274. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/twig_whitespace.test +32 -0
  275. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/whitespace.test +63 -0
  276. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/autoclose.test +30 -0
  277. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/escaping.test +52 -0
  278. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/escaping_attr_once.test +52 -0
  279. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/escaping_html_false.test +52 -0
  280. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/mergeattrs.test +26 -0
  281. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/midblock.test +54 -0
  282. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/midblock_002.test +32 -0
  283. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/attr_list.test +14 -0
  284. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/attrs.test +42 -0
  285. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/attrs_002.test +35 -0
  286. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/attrs_003.test +30 -0
  287. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/attrs_html_linebreak.test +17 -0
  288. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/blocks.test +27 -0
  289. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/blocks_002.test +15 -0
  290. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/boolean_attr.test +19 -0
  291. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/bug28.test +15 -0
  292. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/bug8.test +11 -0
  293. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/cond_cmt.test +23 -0
  294. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/doctype.test +11 -0
  295. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/double_equal.test +7 -0
  296. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/empty_line_after_multiline.test +10 -0
  297. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_001.test +8 -0
  298. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_002.test +8 -0
  299. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_003.test +8 -0
  300. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_004.test +6 -0
  301. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_005.test +5 -0
  302. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_006.test +7 -0
  303. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_007.test +7 -0
  304. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_008.test +7 -0
  305. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_009.test +5 -0
  306. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_010.test +5 -0
  307. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_013.test +5 -0
  308. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_016.test +7 -0
  309. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_017.test +8 -0
  310. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_018.test +6 -0
  311. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_019.test +6 -0
  312. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_020.test +5 -0
  313. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/filters.test +20 -0
  314. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/filters_002.test +19 -0
  315. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/filters_003.test +13 -0
  316. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/filters_004.test +21 -0
  317. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/filters_005.test +22 -0
  318. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/filters_006.test +23 -0
  319. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/insertflags.test +17 -0
  320. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/interpolation.test +36 -0
  321. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/interpolation_in_html_attrs.test +17 -0
  322. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/multiline.test +18 -0
  323. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/multiline_code.test +59 -0
  324. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/multiple_attr_kinds.test +47 -0
  325. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/non_rendered_comment.test +31 -0
  326. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/ns.test +11 -0
  327. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/object_ref.test +22 -0
  328. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/object_ref_error1.test +5 -0
  329. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/object_ref_error2.test +5 -0
  330. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/quotes_in_haml.test +10 -0
  331. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/ruby19_attrs.test +11 -0
  332. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/ruby_attrs_comma.test +23 -0
  333. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/ruby_attrs_comma_errors_001.test +5 -0
  334. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/ruby_attrs_comma_errors_002.test +5 -0
  335. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/ruby_attrs_comma_errors_003.test +5 -0
  336. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/standard.test +114 -0
  337. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/tagflags.test +39 -0
  338. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/runtime/php_filters.test +28 -0
  339. data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/runtime/twig_filters.test +31 -0
  340. data/vendor/mthaml/mthaml/test/bootstrap.php +3 -0
  341. metadata +398 -0
@@ -0,0 +1,252 @@
1
+ <?php
2
+
3
+ namespace MtHaml\NodeVisitor;
4
+
5
+ use MtHaml\Node\Filter;
6
+ use MtHaml\Node\Insert;
7
+ use MtHaml\Node\Run;
8
+ use MtHaml\Node\InterpolatedString;
9
+ use MtHaml\Node\Tag;
10
+ use MtHaml\Node\ObjectRefClass;
11
+ use MtHaml\Node\ObjectRefId;
12
+ use MtHaml\Node\NodeAbstract;
13
+ use MtHaml\Node\TagAttributeInterpolation;
14
+ use MtHaml\Node\TagAttributeList;
15
+
16
+ class TwigRenderer extends RendererAbstract
17
+ {
18
+ protected function escapeLanguage($string, $context)
19
+ {
20
+ // If there is a '%' or '{' at the begining of the string, it could
21
+ // become '{%' or '{{' when concatenated with previous output. So we
22
+ // need to escape '{' and '%' when appearing at the begining of the
23
+ // string, unless we know that previous output doesn't end with '{'.
24
+ $re = '~(^[{%][{%]?|\{[{%])~';
25
+
26
+ // when context is empty, consider that we don't know what's before
27
+ if (0 < strlen($context)) {
28
+ $len = strlen($context);
29
+ $char = $context[$len-1];
30
+ if ('{' !== $char) {
31
+ $re = '~(\{[{%])~';
32
+ }
33
+ }
34
+
35
+ return preg_replace($re, "{{ '\\1' }}", $string);
36
+ }
37
+
38
+ protected function stringLiteral($string)
39
+ {
40
+ return var_export((string) $string, true);
41
+ }
42
+
43
+ public function enterInterpolatedString(InterpolatedString $node)
44
+ {
45
+ if (!$this->isEchoMode() && 1 < count($node->getChilds())) {
46
+ $this->raw('(');
47
+ }
48
+ }
49
+
50
+ public function betweenInterpolatedStringChilds(InterpolatedString $node)
51
+ {
52
+ if (!$this->isEchoMode()) {
53
+ $this->raw(' ~ ');
54
+ }
55
+ }
56
+
57
+ public function leaveInterpolatedString(InterpolatedString $node)
58
+ {
59
+ if (!$this->isEchoMode() && 1 < count($node->getChilds())) {
60
+ $this->raw(')');
61
+ }
62
+ }
63
+
64
+ public function enterInsert(Insert $node)
65
+ {
66
+ if ($this->isEchoMode()) {
67
+ $escaping = $node->getEscaping()->isEnabled();
68
+ if (true === $escaping) {
69
+ $fmt = '{{ (%s)|escape }}';
70
+ } elseif (false === $escaping) {
71
+ $fmt = '{{ (%s)|raw }}';
72
+ } else {
73
+ $fmt = '{{ %s }}';
74
+ }
75
+ $this->addDebugInfos($node);
76
+ $this->raw(sprintf($fmt, $node->getContent()));
77
+ } else {
78
+ $content = $node->getContent();
79
+ if (!preg_match('~^[a-zA-Z0-9_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$~', $content)) {
80
+ $this->raw('(' . $node->getContent() . ')');
81
+ } else {
82
+ $this->raw($node->getContent());
83
+ }
84
+ }
85
+ }
86
+
87
+ public function enterTopblock(Run $node)
88
+ {
89
+ $this->renderBlockTop($node);
90
+ }
91
+
92
+ public function enterMidblock(Run $node)
93
+ {
94
+ $this->renderBlockTop($node);
95
+ }
96
+
97
+ public function leaveTopBlock(Run $node)
98
+ {
99
+ if ($node->isBlock()) {
100
+ if (preg_match('~^(?:-\s*)?(\w+)~', $node->getContent(), $match)) {
101
+ $this->write($this->renderTag('end'.$match[1]));
102
+ }
103
+ }
104
+ }
105
+
106
+ protected function renderBlockTop(Run $node)
107
+ {
108
+ $this->addDebugInfos($node);
109
+ $this->write($this->renderTag($node->getContent()));
110
+ }
111
+
112
+ public function enterObjectRefClass(ObjectRefClass $node)
113
+ {
114
+ if ($this->isEchoMode()) {
115
+ $this->raw('{{ ');
116
+ }
117
+ $this->raw('mthaml_object_ref_class(');
118
+
119
+ $this->pushEchoMode(false);
120
+ }
121
+
122
+ public function leaveObjectRefClass(ObjectRefClass $node)
123
+ {
124
+ $this->raw(')');
125
+
126
+ $this->popEchoMode(true);
127
+ if ($this->isEchoMode()) {
128
+ $this->raw(' }}');
129
+ }
130
+ }
131
+
132
+ public function enterObjectRefId(ObjectRefId $node)
133
+ {
134
+ if ($this->isEchoMode()) {
135
+ $this->raw('{{ ');
136
+ }
137
+ $this->raw('mthaml_object_ref_id(');
138
+
139
+ $this->pushEchoMode(false);
140
+ }
141
+
142
+ public function leaveObjectRefId(ObjectRefId $node)
143
+ {
144
+ $this->raw(')');
145
+
146
+ $this->popEchoMode(true);
147
+ if ($this->isEchoMode()) {
148
+ $this->raw(' }}');
149
+ }
150
+ }
151
+
152
+ public function enterObjectRefPrefix(NodeAbstract $node)
153
+ {
154
+ $this->raw(', ');
155
+ }
156
+
157
+ public function enterFilter(Filter $node)
158
+ {
159
+ $filter = $this->env->getFilter($node->getFilter());
160
+
161
+ if (!$filter->isOptimizable($this, $node, $this->env->getOptions())) {
162
+ $this->write('{% filter mthaml_'.$node->getFilter().' %}', true, false);
163
+ $this->savedIndent[] = $this->indent;
164
+ $this->indent = 0;
165
+ }
166
+ }
167
+
168
+ public function leaveFilter(Filter $node)
169
+ {
170
+ $filter = $this->env->getFilter($node->getFilter());
171
+
172
+ if (!$filter->isOptimizable($this, $node, $this->env->getOptions())) {
173
+ $this->write('{% endfilter %}');
174
+ $this->indent = $this->popSavedIndent();
175
+ }
176
+ }
177
+
178
+ protected function renderTag($content)
179
+ {
180
+ $prefix = ' ';
181
+ $suffix = ' ';
182
+
183
+ if (preg_match('/^-/', $content)) {
184
+ $prefix = '';
185
+ }
186
+ if (preg_match('/-$/', $content)) {
187
+ $suffix = '';
188
+ }
189
+
190
+ return sprintf('{%%%s%s%s%%}', $prefix, $content, $suffix);
191
+ }
192
+
193
+ protected function writeDebugInfos($lineno)
194
+ {
195
+ $infos = sprintf('{%% line %d %%}', $lineno);
196
+ $this->raw($infos);
197
+ }
198
+
199
+ protected function renderDynamicAttributes(Tag $tag)
200
+ {
201
+ $this->raw(' ');
202
+
203
+ foreach ($tag->getAttributes() as $attr) {
204
+ $this->addDebugInfos($attr);
205
+ break;
206
+ }
207
+
208
+ $this->raw('{{ mthaml_attributes([');
209
+
210
+ $this->setEchoMode(false);
211
+
212
+ foreach (array_values($tag->getAttributes()) as $i => $attr) {
213
+
214
+ if (0 !== $i) {
215
+ $this->raw(', ');
216
+ }
217
+
218
+ if ($attr instanceof TagAttributeInterpolation) {
219
+ $this->raw('mthaml_attribute_interpolation(');
220
+ $attr->getValue()->accept($this);
221
+ $this->raw(')');
222
+ } elseif ($attr instanceof TagAttributeList) {
223
+ $this->raw('mthaml_attribute_list(');
224
+ $attr->getValue()->accept($this);
225
+ $this->raw(')');
226
+ } else {
227
+ $this->raw('[');
228
+ $attr->getName()->accept($this);
229
+ $this->raw(', ');
230
+ if ($attr->getValue()) {
231
+ $attr->getValue()->accept($this);
232
+ } else {
233
+ $this->raw('true');
234
+ }
235
+ $this->raw(']');
236
+ }
237
+ }
238
+
239
+ $this->raw(']');
240
+
241
+ $this->setEchoMode(true);
242
+
243
+ $this->raw(', ');
244
+ $this->raw($this->stringLiteral($this->env->getOption('format')));
245
+ $this->raw(', ');
246
+ $this->raw($this->stringLiteral($this->charset));
247
+ $this->raw( ($this->env->getOption('enable_escaper') && $this->env->getOption('escape_attrs'))?
248
+ '' : ', false');
249
+
250
+ $this->raw(')|raw }}');
251
+ }
252
+ }
@@ -0,0 +1,862 @@
1
+ <?php
2
+
3
+ namespace MtHaml;
4
+
5
+ use MtHaml\Exception\SyntaxErrorException;
6
+ use MtHaml\Node\NodeAbstract;
7
+ use MtHaml\Parser\Buffer;
8
+ use MtHaml\Node\Doctype;
9
+ use MtHaml\Node\Tag;
10
+ use MtHaml\Node\TagAttribute;
11
+ use MtHaml\Node\Comment;
12
+ use MtHaml\Node\Insert;
13
+ use MtHaml\Node\Text;
14
+ use MtHaml\Node\InterpolatedString;
15
+ use MtHaml\Node\Run;
16
+ use MtHaml\Node\Statement;
17
+ use MtHaml\Node\Filter;
18
+ use MtHaml\Node\ObjectRefClass;
19
+ use MtHaml\Node\ObjectRefId;
20
+ use MtHaml\Node\TagAttributeInterpolation;
21
+ use MtHaml\Node\TagAttributeList;
22
+ use MtHaml\Indentation\IndentationException;
23
+ use MtHaml\TreeBuilder;
24
+
25
+ /**
26
+ * MtHaml Parser
27
+ */
28
+ class Parser
29
+ {
30
+ protected $filename;
31
+ protected $column;
32
+ protected $lineno;
33
+
34
+ /**
35
+ * @var \MtHaml\Indentation\IndentationInterface
36
+ */
37
+ private $prevIndent;
38
+
39
+ /**
40
+ * @var \MtHaml\Indentation\IndentationInterface
41
+ */
42
+ private $indent;
43
+
44
+ /**
45
+ * @var \MtHaml\TreeBuilder
46
+ */
47
+ private $treeBuilder;
48
+
49
+ public function __construct()
50
+ {
51
+ $this->treeBuilder = new TreeBuilder;
52
+ $this->indent = new Indentation\Undefined();
53
+ $this->prevIndent = $this->indent;
54
+ }
55
+
56
+ /**
57
+ * Updates the indentation state
58
+ *
59
+ * @param Buffer $buf
60
+ * @param string $indent The indentation characters of the current line
61
+ */
62
+ private function updateIndent(Buffer $buf, $indent)
63
+ {
64
+ $this->prevIndent = $this->indent;
65
+
66
+ try {
67
+ $this->indent = $this->indent->newLevel($indent);
68
+ } catch (IndentationException $e) {
69
+ throw $this->syntaxError($buf, $e->getMessage());
70
+ }
71
+
72
+ if (!$this->treeBuilder->hasStatements() && 0 < $this->indent->getLevel()) {
73
+ throw $this->syntaxError($buf, 'Indenting at the beginning of the document is illegal');
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Processes a statement
79
+ *
80
+ * Inserts a new $node in the tree
81
+ *
82
+ * @param Buffer $buf
83
+ * @param NodeAbstract $node Node to insert in the tree
84
+ */
85
+ public function processStatement(Buffer $buf, NodeAbstract $node)
86
+ {
87
+ $level = $this->indent->getLevel() - $this->prevIndent->getLevel();
88
+
89
+ try {
90
+ $this->treeBuilder->addChild($level, $node);
91
+ } catch(TreeBuilderException $e) {
92
+ throw $this->syntaxError($buf, $e->getMessage());
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Parses a HAML document
98
+ *
99
+ * @param string $string A HAML document
100
+ * @param string $fileaname Filename to report in error messages
101
+ * @param string $lineno Line number of the first line of $string in
102
+ * $filename (for error messages)
103
+ */
104
+ public function parse($string, $filename, $lineno = 1)
105
+ {
106
+ $this->filename = $filename;
107
+
108
+ $buf = new Buffer($string, $lineno);
109
+ while ($buf->nextLine()) {
110
+ $this->handleMultiline($buf);
111
+ $this->parseLine($buf);
112
+ }
113
+
114
+ return $this->treeBuilder->getRoot();
115
+ }
116
+
117
+ /**
118
+ * Handles HAML multiline syntax
119
+ *
120
+ * Any line terminated by ` |` is concatenated with the following lines
121
+ * also terminated by ` |`. Empty or whitespace-only lines are ignored. The
122
+ * current line is replaced by the resulting line in $buf.
123
+ *
124
+ * @param Buffer $buf
125
+ */
126
+ public function handleMultiline(Buffer $buf)
127
+ {
128
+ $line = $buf->getLine();
129
+
130
+ if (!$this->isMultiline($line)) {
131
+ return;
132
+ }
133
+
134
+ $line = substr(rtrim($line), 0, -1);
135
+
136
+ while ($next = $buf->peekLine()) {
137
+ if (trim($next) == '') {
138
+ $buf->nextLine();
139
+ continue;
140
+ }
141
+ if (!$this->isMultiline($next)) break;
142
+ $line .= substr(trim($next), 0, -1);
143
+ $buf->nextLine();
144
+ }
145
+
146
+ $buf->replaceLine($line);
147
+ }
148
+
149
+ public function isMultiline($string)
150
+ {
151
+ return ' |' === substr(rtrim($string), -2);
152
+ }
153
+
154
+ /**
155
+ * Parses a HAML line
156
+ */
157
+ protected function parseLine(Buffer $buf)
158
+ {
159
+ if ('' === trim($buf->getLine())) {
160
+ return;
161
+ }
162
+
163
+ $buf->match('/[ \t]*/A', $match);
164
+ $indent = $match[0];
165
+ $this->updateIndent($buf, $indent);
166
+
167
+ if (null === $node = $this->parseStatement($buf)) {
168
+ throw $this->syntaxErrorExpected($buf, 'statement');
169
+ }
170
+ $this->processStatement($buf, $node);
171
+ }
172
+
173
+ protected function parseStatement(Buffer $buf)
174
+ {
175
+ if (null !== $node = $this->parseTag($buf)) {
176
+ return $node;
177
+
178
+ } elseif (null !== $node = $this->parseFilter($buf)) {
179
+ return $node;
180
+
181
+ } elseif (null !== $comment = $this->parseComment($buf)) {
182
+ return $comment;
183
+
184
+ } else if (null !== $run = $this->parseRun($buf)) {
185
+ return $run;
186
+
187
+ } elseif (null !== $doctype = $this->parseDoctype($buf)) {
188
+ return $doctype;
189
+
190
+ } else {
191
+ if (null !== $node = $this->parseNestableStatement($buf)) {
192
+ return new Statement($node->getPosition(), $node);
193
+ }
194
+ }
195
+ }
196
+
197
+ protected function parseDoctype(Buffer $buf)
198
+ {
199
+ $doctypeRegex = '/
200
+ !!! # start of doctype decl
201
+ (?:
202
+ \s(?P<type>[^\s]+) # optional doctype id
203
+ (?:\s(?P<options>.*))? # doctype options (e.g. charset, for
204
+ # xml decls)
205
+ )?$/Ax';
206
+
207
+ if ($buf->match($doctypeRegex, $match)) {
208
+
209
+ $type = empty($match['type']) ? null : $match['type'];
210
+ $options = empty($match['options']) ? null : $match['options'];
211
+ $node = new Doctype($match['pos'][0], $type, $options);
212
+
213
+ return $node;
214
+ }
215
+ }
216
+
217
+ protected function parseComment(Buffer $buf)
218
+ {
219
+ if ($buf->match('!(-#|/)\s*!A', $match)) {
220
+ $pos = $match['pos'][0];
221
+ $rendered = '/' === $match[1];
222
+ $condition = null;
223
+
224
+ if ($rendered) {
225
+ // IE conditional comments
226
+ // example: [if IE lte 8]
227
+ //
228
+ // matches nested [...]
229
+ if ($buf->match('!(\[ ( [^\[\]]+ | (?1) )+ \])$!Ax', $match)) {
230
+ $condition = $match[0];
231
+ }
232
+ }
233
+
234
+ $node = new Comment($pos, $rendered, $condition);
235
+
236
+ if ('' !== $line = trim($buf->getLine())) {
237
+ $content = new Text($buf->getPosition(), $line);
238
+ $node->setContent($content);
239
+ }
240
+
241
+ if (!$rendered) {
242
+
243
+ while (null !== $next = $buf->peekLine()) {
244
+
245
+ $indent = '';
246
+
247
+ if ('' !== trim($next)) {
248
+ $indent = $this->indent->getString(1, $next);
249
+ if ('' === $indent) {
250
+ break;
251
+ }
252
+ if (strpos($next, $indent) !== 0) {
253
+ break;
254
+ }
255
+ }
256
+
257
+ $buf->nextLine();
258
+
259
+ if ('' !== trim($next)) {
260
+ $buf->eatChars(strlen($indent));
261
+ $str = new Text($buf->getPosition(), $buf->getLine());
262
+ $node->addChild(new Statement($str->getPosition(), $str));
263
+ }
264
+ }
265
+ }
266
+
267
+ return $node;
268
+ }
269
+ }
270
+
271
+ protected function getMultilineCode(Buffer $buf)
272
+ {
273
+ $code = $buf->getLine();
274
+ while (preg_match('/,\s*$/', $code)) {
275
+ $buf->nextLine();
276
+ $line = trim($buf->getLine());
277
+ if ('' !== $line) {
278
+ $code .= ' ' . $line;
279
+ }
280
+ }
281
+ return $code;
282
+ }
283
+
284
+ protected function parseRun(Buffer $buf)
285
+ {
286
+ if ($buf->match('/-(?!#)/A', $match)) {
287
+ $buf->skipWs();
288
+ $code = $this->getMultilineCode($buf);
289
+ return new Run($match['pos'][0], $code);
290
+ }
291
+ }
292
+
293
+ protected function parseTag(Buffer $buf)
294
+ {
295
+ $tagRegex = '/
296
+ %(?P<tag_name>[\w:-]+) # explicit tag name ( %tagname )
297
+ | (?=[.#][\w-]) # implicit div followed by class or id
298
+ # ( .class or #id )
299
+ /xA';
300
+
301
+ if ($buf->match($tagRegex, $match)) {
302
+ $tag_name = empty($match['tag_name']) ? 'div' : $match['tag_name'];
303
+
304
+ $attributes = $this->parseTagAttributes($buf);
305
+
306
+ $flags = $this->parseTagFlags($buf);
307
+
308
+ $node = new Tag($match['pos'][0], $tag_name, $attributes, $flags);
309
+
310
+ $buf->skipWs();
311
+
312
+ if (null !== $nested = $this->parseNestableStatement($buf)) {
313
+
314
+ if ($flags & Tag::FLAG_SELF_CLOSE) {
315
+ $msg = 'Illegal nesting: nesting within a self-closing tag is illegal';
316
+ throw $this->syntaxError($buf, $msg);
317
+ }
318
+
319
+ $node->setContent($nested);
320
+ }
321
+
322
+ return $node;
323
+ }
324
+ }
325
+
326
+ protected function parseTagFlags(Buffer $buf)
327
+ {
328
+ $flags = 0;
329
+ while (null !== $char = $buf->peekChar()) {
330
+ switch ($char) {
331
+ case '<':
332
+ $flags |= Tag::FLAG_REMOVE_INNER_WHITESPACES;
333
+ $buf->eatChar();
334
+ break;
335
+ case '>':
336
+ $flags |= Tag::FLAG_REMOVE_OUTER_WHITESPACES;
337
+ $buf->eatChar();
338
+ break;
339
+ case '/':
340
+ $flags |= Tag::FLAG_SELF_CLOSE;
341
+ $buf->eatChar();
342
+ break;
343
+ default:
344
+ break 2;
345
+ }
346
+ }
347
+
348
+ return $flags;
349
+ }
350
+
351
+ protected function parseTagAttributes(Buffer $buf)
352
+ {
353
+ $attrs = array();
354
+
355
+ // short notation for classes and ids
356
+
357
+ while ($buf->match('/(?P<type>[#.])(?P<name>[\w-]+)/A', $match)) {
358
+ if ($match['type'] == '#') {
359
+ $name = 'id';
360
+ } else {
361
+ $name = 'class';
362
+ }
363
+ $name = new Text($match['pos'][0], $name);
364
+ $value = new Text($match['pos'][1], $match['name']);
365
+ $attr = new TagAttribute($match['pos'][0], $name, $value);
366
+ $attrs[] = $attr;
367
+ }
368
+
369
+ $hasRubyAttrs = false;
370
+ $hasHtmlAttrs = false;
371
+ $hasObjectRef = false;
372
+
373
+ // accept ruby-attrs, html-attrs, and object-ref in any order,
374
+ // but only one of each
375
+
376
+ while (true) {
377
+ switch ($buf->peekChar()) {
378
+ case '{':
379
+ if ($hasRubyAttrs) {
380
+ break 2;
381
+ }
382
+ $hasRubyAttrs = true;
383
+ $newAttrs = $this->parseTagAttributesRuby($buf);
384
+ $attrs = array_merge($attrs, $newAttrs);
385
+ break;
386
+ case '(':
387
+ if ($hasHtmlAttrs) {
388
+ break 2;
389
+ }
390
+ $hasHtmlAttrs = true;
391
+ $newAttrs = $this->parseTagAttributesHtml($buf);
392
+ $attrs = array_merge($attrs, $newAttrs);
393
+ break;
394
+ case '[':
395
+ if ($hasObjectRef) {
396
+ break 2;
397
+ }
398
+ $hasObjectRef = true;
399
+ $newAttrs = $this->parseTagAttributesObject($buf);
400
+ $attrs = array_merge($attrs, $newAttrs);
401
+ break;
402
+ default:
403
+ break 2;
404
+ }
405
+ }
406
+
407
+ return $attrs;
408
+ }
409
+
410
+ protected function parseTagAttributesRuby(Buffer $buf)
411
+ {
412
+ $attrs = array();
413
+
414
+ if ($buf->match('/\{\s*/')) {
415
+ do {
416
+ $attrs[] = $this->parseTagAttributeRuby($buf);
417
+
418
+ $buf->skipWs();
419
+
420
+ if ($buf->match('/}/A')) {
421
+ break;
422
+ }
423
+
424
+ $buf->skipWs();
425
+ if (!$buf->match('/,\s*/A')) {
426
+ throw $this->syntaxErrorExpected($buf, "',' or '}'");
427
+ }
428
+ // allow line break after comma
429
+ if ($buf->isEol()) {
430
+ $buf->nextLine();
431
+ $buf->skipWs();
432
+ }
433
+ } while (true);
434
+ }
435
+
436
+ return $attrs;
437
+ }
438
+
439
+ protected function parseTagAttributeRuby(Buffer $buf)
440
+ {
441
+ if ($expr = $this->parseInterpolation($buf)) {
442
+ return new TagAttributeInterpolation($expr->getPosition(), $expr);
443
+ }
444
+
445
+ list ($name, $ruby19) = $this->parseTagAttributeNameRuby($buf);
446
+
447
+ $buf->skipWs();
448
+
449
+ if (!$ruby19 && !$buf->match('/=>\s*/A')) {
450
+ return new TagAttributeList($name->getPosition(), $name);
451
+ }
452
+
453
+ $value = $this->parseTagAttributeValueRuby($buf);
454
+
455
+ return new TagAttribute($name->getPosition(), $name, $value);
456
+ }
457
+
458
+ protected function parseTagAttributeNameRuby(Buffer $buf)
459
+ {
460
+ try {
461
+ if ($name = $this->parseTagAttributeNameRuby19($buf)) {
462
+ return array($name, true);
463
+ }
464
+
465
+ return array($this->parseAttrExpression($buf, '=,'), false);
466
+ } catch (SyntaxErrorException $e) {
467
+ // Allow line break after comma
468
+ if ($buf->match('/,\s*$/', $match, false) && $buf->hasNextLine()) {
469
+ $buf->mergeNextLine();
470
+ return $this->parseTagAttributeNameRuby($buf);
471
+ } else {
472
+ throw $e;
473
+ }
474
+ }
475
+ }
476
+
477
+ protected function parseTagAttributeNameRuby19(Buffer $buf)
478
+ {
479
+ if ($buf->match('/(\w+):/A', $match)) {
480
+ return new Text($match['pos'][0], $match[1]);
481
+ }
482
+ }
483
+
484
+ protected function parseTagAttributeValueRuby(Buffer $buf)
485
+ {
486
+ try {
487
+ return $this->parseAttrExpression($buf, ',');
488
+ } catch (SyntaxErrorException $e) {
489
+ // Allow line break after comma
490
+ if ($buf->match('/,\s*$/', $match, false) && $buf->hasNextLine()) {
491
+ $buf->mergeNextLine();
492
+ return $this->parseTagAttributeValueRuby($buf);
493
+ } else {
494
+ throw $e;
495
+ }
496
+ }
497
+ }
498
+
499
+ protected function parseTagAttributesHtml(Buffer $buf)
500
+ {
501
+ if (!$buf->match('/\(\s*/A')) {
502
+ return null;
503
+ }
504
+
505
+ $attrs = array();
506
+
507
+ do {
508
+
509
+ $attrs[] = $this->parseTagAttributeHtml($buf);
510
+
511
+ if ($buf->match('/\s*\)/A')) {
512
+ break;
513
+ }
514
+
515
+ if (!$buf->match('/\s+/A')) {
516
+ if (!$buf->isEol()) {
517
+ throw $this->syntaxErrorExpected($buf, "' ', ')' or end of line");
518
+ }
519
+ }
520
+
521
+ // allow line break
522
+ if ($buf->isEol()) {
523
+ $buf->nextLine();
524
+ $buf->skipWs();
525
+ }
526
+
527
+ } while (true);
528
+
529
+ return $attrs;
530
+ }
531
+
532
+ private function parseTagAttributeHtml(Buffer $buf)
533
+ {
534
+ if ($expr = $this->parseInterpolation($buf)) {
535
+ return new TagAttributeInterpolation($expr->getPosition(), $expr);
536
+ }
537
+
538
+ if ($buf->match('/[\w+:-]+/A', $match)) {
539
+ $name = new Text($match['pos'][0], $match[0]);
540
+
541
+ if (!$buf->match('/\s*=\s*/A')) {
542
+ $value = null;
543
+ } else {
544
+ $value = $this->parseAttrExpression($buf, ' ');
545
+ }
546
+
547
+ return new TagAttribute($name->getPosition(), $name, $value);
548
+ }
549
+
550
+ throw $this->syntaxErrorExpected($buf, 'html attribute name or #{interpolation}');
551
+ }
552
+
553
+ protected function parseTagAttributesObject(Buffer $buf)
554
+ {
555
+ $nodes = array();
556
+ $attrs = array();
557
+
558
+ if (!$buf->match('/\[\s*/A', $match)) {
559
+ return $attrs;
560
+ }
561
+
562
+ $pos = $match['pos'][0];
563
+
564
+ do {
565
+ if ($buf->match('/\s*\]\s*/A')) {
566
+ break;
567
+ }
568
+
569
+ list($expr, $pos) = $this->parseExpression($buf, ',\\]');
570
+ $nodes[] = new Insert($pos, $expr);
571
+
572
+ if ($buf->match('/\s*\]\s*/A')) {
573
+ break;
574
+ } elseif (!$buf->match('/\s*,\s*/A')) {
575
+ throw $this->syntaxErrorExpected($buf, "',' or ']'");
576
+ }
577
+
578
+ } while (true);
579
+
580
+ list ($object, $prefix) = array_pad($nodes, 2, null);
581
+
582
+ if (!$object) {
583
+ return $attrs;
584
+ }
585
+
586
+ $class = new ObjectRefClass($pos, $object, $prefix);
587
+ $id = new ObjectRefId($pos, $object, $prefix);
588
+
589
+ $name = new Text($pos, 'class');
590
+ $attrs[] = new TagAttribute($pos, $name, $class);
591
+
592
+ $name = new Text($pos, 'id');
593
+ $attrs[] = new TagAttribute($pos, $name, $id);
594
+
595
+ return $attrs;
596
+ }
597
+
598
+ protected function parseAttrExpression(Buffer $buf, $delims)
599
+ {
600
+ $sub = clone $buf;
601
+
602
+ list($expr, $pos) = $this->parseExpression($buf, $delims);
603
+
604
+ // hack to return a parsed string or symbol instead of an expression
605
+ // if the whole expression can be parsed as string or symbol.
606
+
607
+ if (preg_match('/"/A', $expr)) {
608
+ try {
609
+ $string = $this->parseInterpolatedString($sub);
610
+ if ($sub->getColumn() >= $buf->getColumn()) {
611
+ $buf->eatChars($sub->getColumn() - $buf->getColumn());
612
+
613
+ return $string;
614
+ }
615
+ } catch (SyntaxErrorException $e) {
616
+ }
617
+ } elseif (preg_match('/:/A', $expr)) {
618
+ try {
619
+ $sym = $this->parseSymbol($sub);
620
+ if ($sub->getColumn() >= $buf->getColumn()) {
621
+ $buf->eatChars($sub->getColumn() - $buf->getColumn());
622
+
623
+ return $sym;
624
+ }
625
+ } catch (SyntaxErrorException $e) {
626
+ }
627
+ }
628
+
629
+ return new Insert($pos, $expr);
630
+ }
631
+
632
+ protected function parseExpression(Buffer $buf, $delims)
633
+ {
634
+ // matches everything until a delimiter is found
635
+ // delimiters are allowed inside quoted strings,
636
+ // {}, and () (recursive)
637
+
638
+ $re = "/(?P<expr>(?:
639
+
640
+ # anything except \", ', (), {}, []
641
+ (?:[^(){}\[\]\"\'\\\\$delims]+(?=(?P>expr)))
642
+ |(?:[^(){}\[\]\"\'\\\\ $delims]+)
643
+
644
+ # double quoted string
645
+ | \"(?: [^\"\\\\]+ | \\\\[\#\"\\\\] )*\"
646
+
647
+ # single quoted string
648
+ | '(?: [^'\\\\]+ | \\\\[\#'\\\\] )*'
649
+
650
+ # { ... } pair
651
+ | \{ (?: (?P>expr) | [ $delims] )* \}
652
+
653
+ # ( ... ) pair
654
+ | \( (?: (?P>expr) | [ $delims] )* \)
655
+
656
+ # [ ... ] pair
657
+ | \[ (?: (?P>expr) | [ $delims] )* \]
658
+ )+)/xA";
659
+
660
+ if ($buf->match($re, $match)) {
661
+ return array($match[0], $match['pos'][0]);
662
+ }
663
+
664
+ throw $this->syntaxErrorExpected($buf, 'target language expression');
665
+ }
666
+
667
+ protected function parseSymbol(Buffer $buf)
668
+ {
669
+ if (!$buf->match('/:(\w+)/A', $match)) {
670
+ throw $this->syntaxErrorExpected($buf, 'symbol');
671
+ }
672
+
673
+ return new Text($match['pos'][0], $match[1]);
674
+ }
675
+
676
+ protected function parseInterpolatedString(Buffer $buf, $quoted = true)
677
+ {
678
+ if ($quoted && !$buf->match('/"/A', $match)) {
679
+ throw $this->syntaxErrorExpected($buf, 'double quoted string');
680
+ }
681
+
682
+ $node = new InterpolatedString($buf->getPosition());
683
+
684
+ if ($quoted) {
685
+ $stringRegex = '/(
686
+ [^\#"\\\\]+ # anything without hash or " or \
687
+ |\\\\(?:["\\\\]|\#\{) # or escaped quote slash or hash followed by {
688
+ |\#(?!\{) # or hash, but not followed by {
689
+ )+/Ax';
690
+ } else {
691
+ $stringRegex = '/(
692
+ [^\#\\\\]+ # anything without hash or \
693
+ |\\\\(?:\#\{|[^\#]) # or escaped hash followed by { or anything without hash
694
+ |\#(?!\{) # or hash, but not followed by {
695
+ )+/Ax';
696
+ }
697
+
698
+ do {
699
+ if ($buf->match($stringRegex, $match)) {
700
+ $text = $match[0];
701
+ if ($quoted) {
702
+ // strip slashes
703
+ $text = preg_replace('/\\\\(["\\\\])/', '\\1', $match[0]);
704
+ }
705
+ // strip back slash before hash followed by {
706
+ $text = preg_replace('/\\\\\#\{/', '#{', $text);
707
+ $text = new Text($match['pos'][0], $text);
708
+ $node->addChild($text);
709
+ } elseif ($expr = $this->parseInterpolation($buf)) {
710
+ $node->addChild($expr);
711
+ } elseif ($quoted && $buf->match('/"/A')) {
712
+ break;
713
+ } elseif (!$quoted && $buf->match('/$/A')) {
714
+ break;
715
+ } else {
716
+ throw $this->syntaxErrorExpected($buf, 'string or #{...}');
717
+ }
718
+ } while (true);
719
+
720
+ // ensure that the InterpolatedString has at least one child
721
+ if (0 === count($node->getChilds())) {
722
+ $text = new Text($buf->getPosition(), '');
723
+ $node->addChild($text);
724
+ }
725
+
726
+ return $node;
727
+ }
728
+
729
+ protected function parseInterpolation(Buffer $buf)
730
+ {
731
+ // This matches an interpolation:
732
+ // #{ expr... }
733
+ $exprRegex = '/
734
+ \#\{(?P<insert>(?P<expr>
735
+ # do not allow {}"\' in expr
736
+ [^\{\}"\']+
737
+ # allow balanced {}
738
+ | \{ (?P>expr)* \}
739
+ # allow balanced \'
740
+ | \'([^\'\\\\]+|\\\\[\'\\\\])*\'
741
+ # allow balanced "
742
+ | "([^"\\\\]+|\\\\["\\\\])*"
743
+ )+)\}
744
+ /AxU';
745
+
746
+ if ($buf->match($exprRegex, $match)) {
747
+ return new Insert($match['pos']['insert'], $match['insert']);
748
+ }
749
+ }
750
+
751
+ protected function parseNestableStatement(Buffer $buf)
752
+ {
753
+ if ($insert = $this->parseInsert($buf)) {
754
+ return $insert;
755
+ }
756
+
757
+ if (null !== $comment = $this->parseComment($buf)) {
758
+ return $comment;
759
+ }
760
+
761
+ if ('\\' === $buf->peekChar()) {
762
+ $buf->eatChar();
763
+ }
764
+
765
+ if (strlen(trim($buf->getLine())) > 0) {
766
+ return $this->parseInterpolatedString($buf, false);
767
+ }
768
+ }
769
+
770
+ protected function parseInsert(Buffer $buf)
771
+ {
772
+ if ($buf->match('/([&!]?)(==?|~)\s*/A', $match)) {
773
+
774
+ if ($match[2] == '==') {
775
+ $node = $this->parseInterpolatedString($buf, false);
776
+ } else {
777
+ $code = $this->getMultilineCode($buf);
778
+ $node = new Insert($match['pos'][0], $code);
779
+ }
780
+
781
+ if ($match[1] == '&') {
782
+ $node->getEscaping()->setEnabled(true);
783
+ } elseif ($match[1] == '!') {
784
+ $node->getEscaping()->setEnabled(false);
785
+ }
786
+
787
+ $buf->skipWs();
788
+
789
+ return $node;
790
+ }
791
+ }
792
+
793
+ protected function parseFilter(Buffer $buf)
794
+ {
795
+ if (!$buf->match('/:(.*)/A', $match)) {
796
+ return null;
797
+ }
798
+
799
+ $node = new Filter($match['pos'][0], $match[1]);
800
+
801
+ while (null !== $next = $buf->peekLine()) {
802
+
803
+ $indent = '';
804
+
805
+ if ('' !== trim($next)) {
806
+ $indent = $this->indent->getString(1, $next);
807
+ if ('' === $indent) {
808
+ break;
809
+ }
810
+ if (strpos($next, $indent) !== 0) {
811
+ break;
812
+ }
813
+ }
814
+
815
+ $buf->nextLine();
816
+ $buf->eatChars(strlen($indent));
817
+ $str = $this->parseInterpolatedString($buf, false);
818
+ $node->addChild(new Statement($str->getPosition(), $str));
819
+ }
820
+
821
+ return $node;
822
+ }
823
+
824
+ protected function syntaxErrorExpected(Buffer $buf, $expected)
825
+ {
826
+ $unexpected = $buf->peekChar();
827
+ if ($unexpected) {
828
+ $unexpected = "'$unexpected'";
829
+ } else {
830
+ $unexpected = 'end of line';
831
+ }
832
+ $msg = sprintf("Unexpected %s, expected %s", $unexpected, $expected);
833
+ return $this->syntaxError($buf, $msg);
834
+ }
835
+
836
+ protected function syntaxError(Buffer $buf, $msg)
837
+ {
838
+ $this->column = $buf->getColumn();
839
+ $this->lineno = $buf->getLineno();
840
+
841
+ $msg = sprintf('%s in %s on line %d, column %d',
842
+ $msg, $this->filename, $this->lineno, $this->column);
843
+
844
+ return new SyntaxErrorException($msg);
845
+ }
846
+
847
+ public function getColumn()
848
+ {
849
+ return $this->column;
850
+ }
851
+
852
+ public function getLineno()
853
+ {
854
+ return $this->lineno;
855
+ }
856
+
857
+ public function getFilename()
858
+ {
859
+ return $this->filename;
860
+ }
861
+
862
+ }