guard-mthaml 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +34 -0
- data/lib/guard/mthaml.rb +140 -0
- data/lib/guard/mthaml/compiler/MtHaml.php +255 -0
- data/lib/guard/mthaml/templates/Guardfile +12 -0
- data/lib/guard/mthaml/version.rb +5 -0
- data/vendor/autoload.php +7 -0
- data/vendor/coffeescript/coffeescript/LICENSE +22 -0
- data/vendor/coffeescript/coffeescript/README.md +96 -0
- data/vendor/coffeescript/coffeescript/composer.json +23 -0
- data/vendor/coffeescript/coffeescript/grammar.y +309 -0
- data/vendor/coffeescript/coffeescript/make.php +115 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/Compiler.php +76 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/Error.php +15 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/Helpers.php +116 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/Init.php +96 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/Lexer.php +1356 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/Nodes.php +105 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/Parser.php +3326 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/Rewriter.php +552 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/Scope.php +196 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/SyntaxError.php +9 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/Value.php +20 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Access.php +31 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Arr.php +69 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Assign.php +353 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Base.php +288 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Block.php +294 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Call.php +283 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Class.php +282 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Closure.php +49 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Code.php +203 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Comment.php +39 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Existence.php +42 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Extends.php +26 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/For.php +250 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/If.php +161 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/In.php +99 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Index.php +27 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Literal.php +96 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Obj.php +126 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Op.php +292 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Param.php +119 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Parens.php +45 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Range.php +225 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Return.php +56 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Slice.php +47 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Splat.php +100 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Switch.php +121 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Throw.php +37 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Try.php +79 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/Value.php +210 -0
- data/vendor/coffeescript/coffeescript/src/CoffeeScript/yy/While.php +112 -0
- data/vendor/coffeescript/coffeescript/tests/cases/arrays.coffee +77 -0
- data/vendor/coffeescript/coffeescript/tests/cases/assignment.coffee +352 -0
- data/vendor/coffeescript/coffeescript/tests/cases/booleans.coffee +21 -0
- data/vendor/coffeescript/coffeescript/tests/cases/classes.coffee +681 -0
- data/vendor/coffeescript/coffeescript/tests/cases/comments.coffee +207 -0
- data/vendor/coffeescript/coffeescript/tests/cases/compilation.coffee +72 -0
- data/vendor/coffeescript/coffeescript/tests/cases/comprehensions.coffee +501 -0
- data/vendor/coffeescript/coffeescript/tests/cases/control_flow.coffee +430 -0
- data/vendor/coffeescript/coffeescript/tests/cases/eval.coffee +29 -0
- data/vendor/coffeescript/coffeescript/tests/cases/exception_handling.coffee +102 -0
- data/vendor/coffeescript/coffeescript/tests/cases/formatting.coffee +146 -0
- data/vendor/coffeescript/coffeescript/tests/cases/function_invocation.coffee +552 -0
- data/vendor/coffeescript/coffeescript/tests/cases/functions.coffee +188 -0
- data/vendor/coffeescript/coffeescript/tests/cases/helpers.coffee +96 -0
- data/vendor/coffeescript/coffeescript/tests/cases/importing.coffee +18 -0
- data/vendor/coffeescript/coffeescript/tests/cases/interpolation.coffee +138 -0
- data/vendor/coffeescript/coffeescript/tests/cases/javascript_literals.coffee +10 -0
- data/vendor/coffeescript/coffeescript/tests/cases/numbers.coffee +76 -0
- data/vendor/coffeescript/coffeescript/tests/cases/objects.coffee +271 -0
- data/vendor/coffeescript/coffeescript/tests/cases/operators.coffee +277 -0
- data/vendor/coffeescript/coffeescript/tests/cases/option_parser.coffee +43 -0
- data/vendor/coffeescript/coffeescript/tests/cases/ranges.coffee +88 -0
- data/vendor/coffeescript/coffeescript/tests/cases/regexps.coffee +63 -0
- data/vendor/coffeescript/coffeescript/tests/cases/scope.coffee +43 -0
- data/vendor/coffeescript/coffeescript/tests/cases/slicing_and_splicing.coffee +143 -0
- data/vendor/coffeescript/coffeescript/tests/cases/soaks.coffee +134 -0
- data/vendor/coffeescript/coffeescript/tests/cases/strict.coffee +155 -0
- data/vendor/coffeescript/coffeescript/tests/cases/strings.coffee +107 -0
- data/vendor/coffeescript/coffeescript/tests/css/style.css +43 -0
- data/vendor/coffeescript/coffeescript/tests/index.php +119 -0
- data/vendor/coffeescript/coffeescript/tests/js/lib/coffeescript_1.1.1.js +8 -0
- data/vendor/coffeescript/coffeescript/tests/js/lib/coffeescript_1.2.0.js +8 -0
- data/vendor/coffeescript/coffeescript/tests/js/lib/coffeescript_1.3.0.js +8 -0
- data/vendor/coffeescript/coffeescript/tests/js/lib/coffeescript_1.3.1.js +8 -0
- data/vendor/coffeescript/coffeescript/tests/js/lib/diff.js +276 -0
- data/vendor/coffeescript/coffeescript/tests/js/main.js +123 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/LICENSE.txt +10 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/Lempar.php +948 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/Action.php +257 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/ActionTable.php +299 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/Config.php +574 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/Data.php +1857 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/Parser.php +851 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/PropagationLink.php +126 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/Rule.php +144 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/State.php +283 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/Symbol.php +288 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/PHP/ParserGenerator/cli.php +5 -0
- data/vendor/coffeescript/coffeescript/vendor/ParserGenerator/ParserGenerator.php +811 -0
- data/vendor/composer/ClassLoader.php +383 -0
- data/vendor/composer/autoload_classmap.php +9 -0
- data/vendor/composer/autoload_namespaces.php +12 -0
- data/vendor/composer/autoload_psr4.php +10 -0
- data/vendor/composer/autoload_real.php +50 -0
- data/vendor/composer/installed.json +166 -0
- data/vendor/michelf/php-markdown/License.md +36 -0
- data/vendor/michelf/php-markdown/Michelf/Markdown.inc.php +10 -0
- data/vendor/michelf/php-markdown/Michelf/Markdown.php +3117 -0
- data/vendor/michelf/php-markdown/Michelf/MarkdownExtra.inc.php +11 -0
- data/vendor/michelf/php-markdown/Michelf/MarkdownExtra.php +38 -0
- data/vendor/michelf/php-markdown/Michelf/MarkdownInterface.inc.php +9 -0
- data/vendor/michelf/php-markdown/Michelf/MarkdownInterface.php +37 -0
- data/vendor/michelf/php-markdown/Readme.md +305 -0
- data/vendor/michelf/php-markdown/Readme.php +31 -0
- data/vendor/michelf/php-markdown/composer.json +31 -0
- data/vendor/mthaml/mthaml/CHANGELOG +48 -0
- data/vendor/mthaml/mthaml/LICENSE +44 -0
- data/vendor/mthaml/mthaml/README.markdown +262 -0
- data/vendor/mthaml/mthaml/composer.json +45 -0
- data/vendor/mthaml/mthaml/docs/Makefile +153 -0
- data/vendor/mthaml/mthaml/docs/_static/mthaml.css +30 -0
- data/vendor/mthaml/mthaml/docs/_theme/mthaml/theme.conf +4 -0
- data/vendor/mthaml/mthaml/docs/conf.py +242 -0
- data/vendor/mthaml/mthaml/docs/index.rst +18 -0
- data/vendor/mthaml/mthaml/docs/twig-syntax.rst +274 -0
- data/vendor/mthaml/mthaml/examples/README.md +5 -0
- data/vendor/mthaml/mthaml/examples/autoload.php +8 -0
- data/vendor/mthaml/mthaml/examples/example-php.haml +5 -0
- data/vendor/mthaml/mthaml/examples/example-php.php +37 -0
- data/vendor/mthaml/mthaml/examples/example-twig-noext.twig +11 -0
- data/vendor/mthaml/mthaml/examples/example-twig.haml +9 -0
- data/vendor/mthaml/mthaml/examples/example-twig.php +60 -0
- data/vendor/mthaml/mthaml/examples/example.twig.haml +8 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Autoloader.php +22 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Environment.php +178 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Escaping.php +33 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Exception.php +9 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Exception/SyntaxErrorException.php +9 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/AbstractFilter.php +43 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Cdata.php +16 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/CoffeeScript.php +33 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Css.php +26 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Escaped.php +22 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/FilterInterface.php +15 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Javascript.php +26 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Less.php +27 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Less/LeafoLess.php +20 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Less/OyejorgeLess.php +23 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Markdown.php +58 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Markdown/CebeMarkdown.php +22 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Markdown/Ciconia.php +21 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Markdown/MichelfMarkdown.php +21 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Markdown/Parsedown.php +21 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Php.php +37 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Plain.php +24 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Preserve.php +19 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Scss.php +37 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Filter/Twig.php +47 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Indentation/Indentation.php +96 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Indentation/IndentationException.php +9 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Indentation/IndentationInterface.php +50 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Indentation/Undefined.php +41 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/Comment.php +71 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/Doctype.php +96 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/EscapableAbstract.php +19 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/Filter.php +51 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/Insert.php +42 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/InterpolatedString.php +73 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/NestAbstract.php +117 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/NestInterface.php +10 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/NodeAbstract.php +77 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/ObjectRefClass.php +42 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/ObjectRefId.php +42 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/Root.php +35 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/Run.php +71 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/Statement.php +45 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/Tag.php +90 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/TagAttribute.php +62 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/TagAttributeInterpolation.php +24 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/TagAttributeList.php +24 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Node/Text.php +37 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/Autoclose.php +25 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/Escaping.php +105 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/MergeAttrs.php +110 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/Midblock.php +37 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/NodeVisitorAbstract.php +226 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/NodeVisitorInterface.php +97 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/PhpRenderer.php +288 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/Printer.php +265 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/RendererAbstract.php +581 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/NodeVisitor/TwigRenderer.php +252 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Parser.php +862 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Parser/Buffer.php +147 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Runtime.php +218 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Runtime/AttributeInterpolation.php +16 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Runtime/AttributeList.php +16 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Support/Php/Executor.php +157 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Support/Twig/Extension.php +47 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Support/Twig/Lexer.php +48 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Support/Twig/Loader.php +81 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Target/Php.php +23 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Target/TargetAbstract.php +87 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Target/TargetInterface.php +12 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/Target/Twig.php +23 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/TreeBuilder.php +100 -0
- data/vendor/mthaml/mthaml/lib/MtHaml/TreeBuilderException.php +9 -0
- data/vendor/mthaml/mthaml/phpunit.xml +25 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/EnvironmentTest.php +59 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/HamlSpecTest.php +83 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/IndentationTest.php +140 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/Node/DoctypeTest.php +51 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/Node/NodeTest.php +110 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/NodeVisitor/PhpRendererTest.php +113 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/NodeVisitor/TwigRendererTest.php +68 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/NodeVisitorsTest.php +44 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/Parser/BufferTest.php +77 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/ParserTest.php +47 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/RuntimeTest.php +356 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/Support/Php/ExecutorTest.php +83 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/Support/Twig/LoaderTest.php +65 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/TestCase.php +49 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/TestCaseTest.php +50 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/Support/Php/Executor.001.haml +1 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/attr_list_php.test +20 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/attr_list_twig.test +12 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/boolean_attr_php.test +16 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/boolean_attr_twig.test +16 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/bug28_php.test +15 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/bug28_twig.test +15 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/bug42.test +13 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/bug8_twig.test +10 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/coffeescript_filter.test +31 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/comments.test +26 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/cond_cmt.test +25 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/doctype.test +18 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/doctype_html4.test +20 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/doctype_invalid.test +12 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/doctype_invalid_xhtml.test +12 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/doctype_php.test +18 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/doctype_xhtml.test +30 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/filters.test +51 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/insert_non_echo_php.test +11 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/insert_non_echo_twig.test +11 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/interpolation_in_html_attrs_php.test +31 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/interpolation_in_html_attrs_twig.test +31 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/less_filter_leafo.test +33 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/less_filter_oyejorge.test +49 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/markdown_filter_cebemarkdown.test +35 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/markdown_filter_ciconia.test +36 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/markdown_filter_michelf.test +34 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/markdown_filter_parsedown.test +31 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/markdown_optimization_filter_michelf.test +23 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/mergeattrs.test +18 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/nuke_inner_whitespace.test +77 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/nuke_outer_whitespace.test +122 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/object_ref_php.test +14 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/object_ref_twig.test +14 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/php.test +109 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/php_autoescaping.test +22 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/php_autoescaping_once.test +25 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/php_blocks.test +29 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/php_filters.test +42 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/scss_filter.test +32 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/strip_inline_comments_php.test +22 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/test.test +24 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/trailing_semicolon_php.test +14 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/try_catch_php.test +17 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/twig.test +112 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/twig_blocks.test +29 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/twig_filters.test +37 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/twig_whitespace.test +32 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/environment/whitespace.test +63 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/autoclose.test +30 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/escaping.test +52 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/escaping_attr_once.test +52 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/escaping_html_false.test +52 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/mergeattrs.test +26 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/midblock.test +54 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/nodevisitors/midblock_002.test +32 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/attr_list.test +14 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/attrs.test +42 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/attrs_002.test +35 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/attrs_003.test +30 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/attrs_html_linebreak.test +17 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/blocks.test +27 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/blocks_002.test +15 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/boolean_attr.test +19 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/bug28.test +15 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/bug8.test +11 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/cond_cmt.test +23 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/doctype.test +11 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/double_equal.test +7 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/empty_line_after_multiline.test +10 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_001.test +8 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_002.test +8 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_003.test +8 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_004.test +6 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_005.test +5 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_006.test +7 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_007.test +7 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_008.test +7 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_009.test +5 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_010.test +5 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_013.test +5 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_016.test +7 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_017.test +8 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_018.test +6 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_019.test +6 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/errors_020.test +5 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/filters.test +20 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/filters_002.test +19 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/filters_003.test +13 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/filters_004.test +21 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/filters_005.test +22 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/filters_006.test +23 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/insertflags.test +17 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/interpolation.test +36 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/interpolation_in_html_attrs.test +17 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/multiline.test +18 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/multiline_code.test +59 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/multiple_attr_kinds.test +47 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/non_rendered_comment.test +31 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/ns.test +11 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/object_ref.test +22 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/object_ref_error1.test +5 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/object_ref_error2.test +5 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/quotes_in_haml.test +10 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/ruby19_attrs.test +11 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/ruby_attrs_comma.test +23 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/ruby_attrs_comma_errors_001.test +5 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/ruby_attrs_comma_errors_002.test +5 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/ruby_attrs_comma_errors_003.test +5 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/standard.test +114 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/parser/tagflags.test +39 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/runtime/php_filters.test +28 -0
- data/vendor/mthaml/mthaml/test/MtHaml/Tests/fixtures/runtime/twig_filters.test +31 -0
- data/vendor/mthaml/mthaml/test/bootstrap.php +3 -0
- 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
|
+
}
|